Add intervention data skill

This commit is contained in:
wuyang6
2026-05-19 11:59:49 +08:00
parent 201058dfb2
commit 5684df3caf
7 changed files with 1316 additions and 0 deletions
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""分析历史干预文件,归纳设备枚举和 code 到下发 domain 的映射。"""
from __future__ import annotations
import argparse
from pathlib import Path
from intervention_common import DEFAULT_EXACT_SOURCE, DEFAULT_REGEX_SOURCE, build_source_summary, json_dumps
def main() -> int:
parser = argparse.ArgumentParser(description="分析历史干预 TSV")
parser.add_argument("--exact-source", default=str(DEFAULT_EXACT_SOURCE), help="单句精确干预 TSV 路径")
parser.add_argument("--regex-source", default=str(DEFAULT_REGEX_SOURCE), help="正则干预 TSV 路径")
parser.add_argument("--top-mapping", type=int, default=30, help="输出前 N 个 code-domain 映射")
args = parser.parse_args()
summary = build_source_summary(Path(args.exact_source), Path(args.regex_source))
mapping_items = sorted(
summary["code_domain_mapping"].items(),
key=lambda item: item[1]["count"],
reverse=True,
)
summary["code_domain_mapping"] = dict(mapping_items[: args.top_mapping])
print(json_dumps(summary))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,247 @@
#!/usr/bin/env python3
"""审计现有干预文件和当前规则的贴合度。
这个脚本用于回答两个问题:
1. 现有单句精确干预/正则干预文件自身是否存在明显格式问题。
2. 当前 label-master 校验、type_match_agent 下发表和历史数据是否匹配。
它不会修改任何文件,只输出 JSON 摘要和少量样例。
"""
from __future__ import annotations
import argparse
import re
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any
from intervention_common import (
DEFAULT_EXACT_SOURCE,
DEFAULT_REGEX_SOURCE,
build_source_summary,
domain_compatible,
infer_configured_dispatch_domain,
json_dumps,
load_exact_records,
load_label_validator,
load_regex_records,
)
def sample_append(bucket: list[dict[str, Any]], item: dict[str, Any], limit: int) -> None:
if len(bucket) < limit:
bucket.append(item)
def validate_unique_targets(targets: set[str]) -> dict[str, dict[str, Any]]:
validator = load_label_validator()
return {target: validator.validate(target) for target in sorted(targets)}
def audit_exact_records(summary: dict[str, Any], target_results: dict[str, dict[str, Any]], sample_limit: int) -> dict[str, Any]:
records = load_exact_records(Path(summary["source_files"]["exact"]))
empty_fields = Counter()
target_invalid = Counter()
configured_mismatch = Counter()
duplicate_keys: dict[tuple[str, str], list[Any]] = defaultdict(list)
samples = {
"empty_fields": [],
"invalid_targets": [],
"configured_domain_mismatch": [],
"conflicts": [],
}
for record in records:
if not record.device:
empty_fields["device"] += 1
if not record.query:
empty_fields["query"] += 1
if not record.target:
empty_fields["target"] += 1
if not record.dispatch_domain:
empty_fields["dispatch_domain"] += 1
if any(not value for value in (record.device, record.query, record.target, record.dispatch_domain)):
sample_append(samples["empty_fields"], record.__dict__, sample_limit)
result = target_results.get(record.target)
if result and not result.get("valid"):
target_invalid[record.target] += 1
sample_append(
samples["invalid_targets"],
{
"line_no": record.line_no,
"target": record.target,
"errors": result.get("errors", []),
},
sample_limit,
)
configured = infer_configured_dispatch_domain(record.target, record.device, summary)
if configured and not domain_compatible(record.dispatch_domain, configured["dispatch_domain"]):
configured_mismatch[configured["matched_key"]] += 1
sample_append(
samples["configured_domain_mismatch"],
{
"line_no": record.line_no,
"device": record.device,
"query": record.query,
"target": record.target,
"dispatch_domain": record.dispatch_domain,
"configured": configured,
},
sample_limit,
)
duplicate_keys[(record.device, record.query)].append(record)
conflicts = []
duplicate_count = 0
for (device, query), items in duplicate_keys.items():
if len(items) <= 1:
continue
duplicate_count += len(items)
variants = {(item.target, item.dispatch_domain) for item in items}
if len(variants) > 1:
conflict = {
"device": device,
"query": query,
"lines": [item.line_no for item in items[:10]],
"variants": sorted([{"target": target, "dispatch_domain": domain} for target, domain in variants], key=str),
}
conflicts.append(conflict)
sample_append(samples["conflicts"], conflict, sample_limit)
return {
"total": len(records),
"empty_fields": dict(empty_fields),
"invalid_target_total": sum(target_invalid.values()),
"invalid_target_top": dict(target_invalid.most_common(20)),
"configured_domain_mismatch_total": sum(configured_mismatch.values()),
"configured_domain_mismatch_top": dict(configured_mismatch.most_common(20)),
"duplicate_row_count_by_device_query": duplicate_count,
"conflict_key_count": len(conflicts),
"samples": samples,
}
def audit_regex_records(target_results: dict[str, dict[str, Any]], summary: dict[str, Any], sample_limit: int) -> dict[str, Any]:
records = load_regex_records(Path(summary["source_files"]["regex"]))
empty_fields = Counter()
regex_errors = Counter()
anchor_warnings = 0
missing_query_fragment = 0
target_invalid = Counter()
duplicate_keys: dict[tuple[str, str], list[Any]] = defaultdict(list)
samples = {
"empty_fields": [],
"regex_errors": [],
"missing_query_fragment": [],
"invalid_targets": [],
"conflicts": [],
}
for record in records:
if not record.device:
empty_fields["device"] += 1
if not record.pattern:
empty_fields["pattern"] += 1
if not record.target:
empty_fields["target"] += 1
if any(not value for value in (record.device, record.pattern, record.target)):
sample_append(samples["empty_fields"], record.__dict__, sample_limit)
if record.pattern:
try:
re.compile(record.pattern)
except re.error as exc:
regex_errors[str(exc)] += 1
sample_append(
samples["regex_errors"],
{"line_no": record.line_no, "pattern": record.pattern, "error": str(exc)},
sample_limit,
)
if "query#" not in record.pattern:
missing_query_fragment += 1
sample_append(samples["missing_query_fragment"], record.__dict__, sample_limit)
if not record.pattern.startswith("^") or not record.pattern.endswith("$"):
anchor_warnings += 1
result = target_results.get(record.target)
if result and not result.get("valid"):
target_invalid[record.target] += 1
sample_append(
samples["invalid_targets"],
{
"line_no": record.line_no,
"target": record.target,
"errors": result.get("errors", []),
},
sample_limit,
)
duplicate_keys[(record.device, record.pattern)].append(record)
conflicts = []
duplicate_count = 0
for (device, pattern), items in duplicate_keys.items():
if len(items) <= 1:
continue
duplicate_count += len(items)
variants = {item.target for item in items}
if len(variants) > 1:
conflict = {
"device": device,
"pattern": pattern,
"lines": [item.line_no for item in items[:10]],
"variants": sorted(variants),
}
conflicts.append(conflict)
sample_append(samples["conflicts"], conflict, sample_limit)
return {
"total": len(records),
"empty_fields": dict(empty_fields),
"regex_error_total": sum(regex_errors.values()),
"regex_error_top": dict(regex_errors.most_common(20)),
"missing_query_fragment": missing_query_fragment,
"anchor_warning_count": anchor_warnings,
"invalid_target_total": sum(target_invalid.values()),
"invalid_target_top": dict(target_invalid.most_common(20)),
"duplicate_row_count_by_device_pattern": duplicate_count,
"conflict_key_count": len(conflicts),
"samples": samples,
}
def main() -> int:
parser = argparse.ArgumentParser(description="审计现有干预文件")
parser.add_argument("--exact-source", default=str(DEFAULT_EXACT_SOURCE), help="单句精确干预 TSV 路径")
parser.add_argument("--regex-source", default=str(DEFAULT_REGEX_SOURCE), help="正则干预 TSV 路径")
parser.add_argument("--sample-limit", type=int, default=5, help="每类问题最多输出样例数")
args = parser.parse_args()
summary = build_source_summary(Path(args.exact_source), Path(args.regex_source))
exact_records = load_exact_records(Path(args.exact_source))
regex_records = load_regex_records(Path(args.regex_source))
target_results = validate_unique_targets({record.target for record in exact_records + regex_records if record.target})
payload = {
"source_files": summary["source_files"],
"devices": summary["devices"],
"counts": summary["counts"],
"target_validation": {
"unique_targets": len(target_results),
"valid_unique_targets": sum(1 for item in target_results.values() if item.get("valid")),
"invalid_unique_targets": sum(1 for item in target_results.values() if not item.get("valid")),
},
"exact": audit_exact_records(summary, target_results, args.sample_limit),
"regex": audit_regex_records(target_results, summary, args.sample_limit),
}
print(json_dumps(payload))
has_hard_errors = bool(payload["exact"]["empty_fields"] or payload["regex"]["regex_error_total"])
return 1 if has_hard_errors else 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,141 @@
#!/usr/bin/env python3
"""生成单句精确干预或正则干预候选 TSV,并返回校验结果。"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from pathlib import Path
from typing import Any
from intervention_common import (
DEFAULT_EXACT_SOURCE,
DEFAULT_REGEX_SOURCE,
build_query_pattern,
build_source_summary,
exact_tsv_line,
infer_configured_dispatch_domain,
infer_dispatch_domain_for_device,
json_dumps,
normalize_device,
normalize_target,
regex_tsv_line,
)
SCRIPT_DIR = Path(__file__).resolve().parent
VALIDATE_SCRIPT = SCRIPT_DIR / "validate_intervention_records.py"
def load_payload(path: str | None) -> dict[str, Any]:
text = Path(path).read_text(encoding="utf-8") if path else sys.stdin.read()
if not text.strip():
raise ValueError("输入 JSON 不能为空")
return json.loads(text)
def run_validator(mode: str, tsv: str, exact_source: str, regex_source: str) -> dict[str, Any]:
proc = subprocess.run(
[
sys.executable,
str(VALIDATE_SCRIPT),
"--mode",
mode,
"--exact-source",
exact_source,
"--regex-source",
regex_source,
],
input=tsv,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
if not proc.stdout.strip():
return {
"valid": False,
"errors": [proc.stderr.strip() or f"校验脚本退出:{proc.returncode}"],
}
return json.loads(proc.stdout)
def generate_exact(payload: dict[str, Any], exact_source: str, regex_source: str) -> dict[str, Any]:
device = normalize_device(str(payload.get("device", "")))
query = str(payload.get("query", "")).strip()
target = normalize_target(str(payload.get("target") or payload.get("code") or ""))
dispatch_domain = str(payload.get("dispatch_domain") or payload.get("domain") or "").strip()
summary = build_source_summary(Path(exact_source), Path(regex_source))
inferred = None
configured = infer_configured_dispatch_domain(target, device, summary)
if not dispatch_domain:
inferred = infer_dispatch_domain_for_device(target, device, summary)
if inferred:
dispatch_domain = inferred["dispatch_domain"]
tsv = exact_tsv_line(device, query, target, dispatch_domain)
validation = run_validator("exact", tsv, exact_source, regex_source)
return {
"mode": "exact",
"tsv": tsv,
"record": {
"device": device,
"query": query,
"target": target,
"dispatch_domain": dispatch_domain,
},
"inferred_dispatch_domain": inferred,
"configured_dispatch_domain": configured,
"validation": validation,
}
def generate_regex(payload: dict[str, Any], exact_source: str, regex_source: str) -> dict[str, Any]:
device = normalize_device(str(payload.get("device", "")))
target = normalize_target(str(payload.get("target") or payload.get("label") or ""))
raw_pattern = str(payload.get("pattern") or payload.get("regex") or "").strip()
query = str(payload.get("query") or payload.get("current_query") or "").strip()
before_queries = payload.get("before_queries") or []
if not isinstance(before_queries, list):
raise ValueError("before_queries 必须是字符串数组")
pattern = raw_pattern or build_query_pattern(query, [str(item) for item in before_queries])
tsv = regex_tsv_line(device, pattern, target)
validation = run_validator("regex", tsv, exact_source, regex_source)
return {
"mode": "regex",
"tsv": tsv,
"record": {
"device": device,
"pattern": pattern,
"target": target,
},
"validation": validation,
}
def main() -> int:
parser = argparse.ArgumentParser(description="生成干预候选 TSV")
parser.add_argument("--input", help="输入 JSON 文件;不传则读取 stdin")
parser.add_argument("--exact-source", default=str(DEFAULT_EXACT_SOURCE), help="历史单句精确干预 TSV")
parser.add_argument("--regex-source", default=str(DEFAULT_REGEX_SOURCE), help="历史正则干预 TSV")
args = parser.parse_args()
payload = load_payload(args.input)
mode = str(payload.get("mode", "")).strip().lower()
if mode == "exact":
result = generate_exact(payload, args.exact_source, args.regex_source)
elif mode == "regex":
result = generate_regex(payload, args.exact_source, args.regex_source)
else:
raise ValueError("mode 必须是 exact 或 regex")
print(json_dumps(result))
return 0 if result.get("validation", {}).get("valid") else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,330 @@
#!/usr/bin/env python3
"""干预数据脚本的公共能力。
这里尽量只做确定性处理:
- 读取历史干预 TSV。
- 归纳设备枚举和 code 到下发 domain 的多数映射。
- 复用 label-master 校验 target 语法。
- 校验候选干预条目是否和历史数据重复或冲突。
"""
from __future__ import annotations
import csv
import importlib.util
import json
import re
import sys
from collections import Counter, defaultdict
from dataclasses import dataclass
from pathlib import Path
from typing import Any
SCRIPT_DIR = Path(__file__).resolve().parent
SKILL_ROOT = SCRIPT_DIR.parent
REPO_ROOT = SKILL_ROOT.parent.parent
DEFAULT_EXACT_SOURCE = REPO_ROOT / "干预skill" / "20260134"
DEFAULT_REGEX_SOURCE = REPO_ROOT / "干预skill" / "20260207"
LABEL_VALIDATOR_PATH = REPO_ROOT / "skills" / "label-master" / "scripts" / "validate_label_output.py"
CONFIG_MAPPING_PATH = SKILL_ROOT / "knowledge" / "type_match_agent.json"
AGENT_TARGET_RE = re.compile(r'^Agent\(\s*tag\s*=\s*["\'](.+?)["\']\s*\)$')
@dataclass(frozen=True)
class ExactRecord:
device: str
query: str
target: str
dispatch_domain: str
line_no: int
@dataclass(frozen=True)
class RegexRecord:
device: str
pattern: str
target: str
line_no: int
def normalize_device(device: str) -> str:
"""归一化历史数据里的设备写法,但保留常见大小写。"""
value = device.strip()
if value.startswith("#"):
value = value[1:].strip()
aliases = {
"micar": "miCar",
"miai": "miai",
"tVV2": "tvV2",
"tvv2": "tvV2",
"unify": "unify",
}
return aliases.get(value, value)
def normalize_target(target: str) -> str:
return target.replace("", '"').replace("", '"').replace("", "'").replace("", "'").strip()
def read_tsv_rows(path: Path) -> list[list[str]]:
with path.open("r", encoding="utf-8-sig", newline="") as handle:
return list(csv.reader(handle, delimiter="\t"))
def load_exact_records(path: Path = DEFAULT_EXACT_SOURCE) -> list[ExactRecord]:
rows = read_tsv_rows(path)
records: list[ExactRecord] = []
for index, row in enumerate(rows, start=1):
if index == 1 and row[:4] == ["#设备名", "query", "code", "下发domain"]:
continue
if not row or all(not cell.strip() for cell in row):
continue
padded = [*row, "", "", "", ""]
records.append(
ExactRecord(
device=normalize_device(padded[0]),
query=padded[1].strip(),
target=normalize_target(padded[2]),
dispatch_domain=padded[3].strip(),
line_no=index,
)
)
return records
def load_regex_records(path: Path = DEFAULT_REGEX_SOURCE) -> list[RegexRecord]:
rows = read_tsv_rows(path)
records: list[RegexRecord] = []
for index, row in enumerate(rows, start=1):
if not row or all(not cell.strip() for cell in row):
continue
padded = [*row, "", ""]
records.append(
RegexRecord(
device=normalize_device(padded[0]),
pattern=padded[1].strip(),
target=normalize_target(padded[2]),
line_no=index,
)
)
return records
def build_source_summary(
exact_path: Path = DEFAULT_EXACT_SOURCE,
regex_path: Path = DEFAULT_REGEX_SOURCE,
) -> dict[str, Any]:
exact_records = load_exact_records(exact_path) if exact_path.exists() else []
regex_records = load_regex_records(regex_path) if regex_path.exists() else []
exact_devices = Counter(record.device for record in exact_records)
regex_devices = Counter(record.device for record in regex_records)
devices = sorted(set(exact_devices) | set(regex_devices))
code_domain_counts: dict[str, Counter[str]] = defaultdict(Counter)
for record in exact_records:
if record.target and record.dispatch_domain:
code_domain_counts[record.target][record.dispatch_domain] += 1
code_domain_mapping: dict[str, dict[str, Any]] = {}
for target, counts in code_domain_counts.items():
domain, count = counts.most_common(1)[0]
total = sum(counts.values())
code_domain_mapping[target] = {
"dispatch_domain": domain,
"count": count,
"total": total,
"confidence": round(count / total, 4) if total else 0,
"alternatives": [
{"dispatch_domain": item_domain, "count": item_count}
for item_domain, item_count in counts.most_common(5)
],
}
config_mapping = load_type_match_agent_mapping()
return {
"source_files": {
"exact": str(exact_path),
"regex": str(regex_path),
},
"counts": {
"exact_records": len(exact_records),
"regex_records": len(regex_records),
},
"devices": devices,
"device_counts": {
"exact": dict(exact_devices.most_common()),
"regex": dict(regex_devices.most_common()),
},
"code_domain_mapping": code_domain_mapping,
"configured_code_domain_mapping": config_mapping,
"historical_targets": sorted({record.target for record in exact_records + regex_records if record.target}),
"top_exact_targets": dict(Counter(record.target for record in exact_records).most_common(50)),
"top_regex_targets": dict(Counter(record.target for record in regex_records).most_common(50)),
}
def historical_targets(summary: dict[str, Any]) -> set[str]:
targets = set(summary.get("top_exact_targets", {})) | set(summary.get("top_regex_targets", {}))
targets.update(summary.get("code_domain_mapping", {}))
targets.update(summary.get("historical_targets", []))
return {normalize_target(target) for target in targets if target}
def infer_dispatch_domain(target: str, summary: dict[str, Any]) -> dict[str, Any] | None:
normalized = normalize_target(target)
historical = summary.get("code_domain_mapping", {}).get(normalized)
if historical:
return {**historical, "source": "historical"}
configured = infer_configured_dispatch_domain(normalized, "unify", summary)
if configured:
return {
"dispatch_domain": configured["dispatch_domain"],
"count": 0,
"total": 0,
"confidence": None,
"alternatives": [],
"source": "type_match_agent",
"matched_key": configured["matched_key"],
}
return None
def infer_dispatch_domain_for_device(target: str, device: str, summary: dict[str, Any]) -> dict[str, Any] | None:
"""按设备推断推荐下发 domain。
优先级:
1. `标签#设备` 这种明确配置。
2. 历史精确干预表里的全局多数映射。
3. `标签` 通用配置。
"""
normalized = normalize_target(target)
configured = infer_configured_dispatch_domain(normalized, device, summary)
if configured and "#" in configured["matched_key"]:
return {
"dispatch_domain": configured["dispatch_domain"],
"count": 0,
"total": 0,
"confidence": None,
"alternatives": [],
"source": "type_match_agent",
"matched_key": configured["matched_key"],
}
historical = summary.get("code_domain_mapping", {}).get(normalized)
if historical:
return {**historical, "source": "historical"}
if configured:
return {
"dispatch_domain": configured["dispatch_domain"],
"count": 0,
"total": 0,
"confidence": None,
"alternatives": [],
"source": "type_match_agent",
"matched_key": configured["matched_key"],
}
return None
def load_type_match_agent_mapping(path: Path = CONFIG_MAPPING_PATH) -> dict[str, str]:
if not path.exists():
return {}
data = json.loads(path.read_text(encoding="utf-8"))
return {str(key): str(value) for key, value in data.items()}
def extract_agent_tag(target: str) -> str | None:
match = AGENT_TARGET_RE.match(normalize_target(target))
if not match:
return None
return match.group(1)
def infer_configured_dispatch_domain(target: str, device: str, summary: dict[str, Any]) -> dict[str, str] | None:
tag = extract_agent_tag(target)
if not tag:
return None
mapping = summary.get("configured_code_domain_mapping", {})
normalized_device = normalize_device(device)
for key in (f"{tag}#{normalized_device}", tag):
if key in mapping:
return {"matched_key": key, "dispatch_domain": mapping[key]}
return None
def split_domain(domain: str) -> list[str]:
return [part for part in domain.split("|") if part]
def domain_compatible(actual: str, expected: str) -> bool:
"""判断实际下发 domain 是否和配置基线兼容。
下发表里有些是宽泛入口,例如 `音乐 -> contentCopilot`
历史干预里常见更细路径,例如 `contentCopilot|music`。
只要二者一方的管道 token 是另一方的子集,就认为兼容。
"""
actual_parts = set(split_domain(actual))
expected_parts = set(split_domain(expected))
if not actual_parts or not expected_parts:
return False
return actual_parts.issubset(expected_parts) or expected_parts.issubset(actual_parts)
def load_label_validator() -> Any:
spec = importlib.util.spec_from_file_location("label_master_validate_label_output", LABEL_VALIDATOR_PATH)
if spec is None or spec.loader is None:
raise RuntimeError(f"无法加载 label-master 校验器:{LABEL_VALIDATOR_PATH}")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
manifest_path = module.DEFAULT_OUTPUT
validator = module.LabelOutputValidator(module.load_manifest(manifest_path))
return validator
def validate_target(target: str) -> dict[str, Any]:
validator = load_label_validator()
# 历史 TSV 中多行 function/Agent 目标常以字面量 `\n` 存在,
# label-master 校验器需要真实换行才能按多行程序解析。
return validator.validate(normalize_target(target).replace("\\n", "\n"))
def escape_regex_literal(text: str) -> str:
return re.escape(text.strip())
def build_query_pattern(query: str, before_queries: list[str] | None = None) -> str:
before_queries = before_queries or []
parts: list[str] = []
for before_query in before_queries:
if before_query.strip():
parts.append(f"beforeQuery#{escape_regex_literal(before_query)}")
parts.append(f"query#{escape_regex_literal(query)}")
return "^" + "#".join(parts) + "$"
def exact_tsv_line(device: str, query: str, target: str, dispatch_domain: str) -> str:
return "\t".join([normalize_device(device), query.strip(), normalize_target(target), dispatch_domain.strip()])
def regex_tsv_line(device: str, pattern: str, target: str) -> str:
return "\t".join([normalize_device(device), pattern.strip(), normalize_target(target)])
def parse_candidate_lines(text: str) -> list[list[str]]:
rows = []
for line in text.splitlines():
if line.strip():
rows.append(next(csv.reader([line], delimiter="\t")))
return rows
def json_dumps(payload: Any) -> str:
return json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True)
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""校验单句精确干预和正则干预候选 TSV。"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Any
from intervention_common import (
DEFAULT_EXACT_SOURCE,
DEFAULT_REGEX_SOURCE,
build_source_summary,
domain_compatible,
historical_targets,
infer_configured_dispatch_domain,
infer_dispatch_domain_for_device,
load_exact_records,
load_regex_records,
normalize_device,
normalize_target,
parse_candidate_lines,
validate_target,
json_dumps,
)
def load_input_text(path: str | None) -> str:
if path:
return Path(path).read_text(encoding="utf-8")
return sys.stdin.read()
def validate_exact_rows(rows: list[list[str]], args: argparse.Namespace) -> dict[str, Any]:
summary = build_source_summary(Path(args.exact_source), Path(args.regex_source))
devices = set(summary["devices"])
known_historical_targets = historical_targets(summary)
existing: dict[tuple[str, str], list[Any]] = {}
for record in load_exact_records(Path(args.exact_source)):
existing.setdefault((record.device, record.query), []).append(record)
results = []
for index, row in enumerate(rows, start=1):
errors: list[str] = []
warnings: list[str] = []
if len(row) != 4:
errors.append(f"单句精确干预必须是 4 列,当前 {len(row)}")
results.append({"line": index, "valid": False, "errors": errors, "warnings": warnings, "row": row})
continue
device, query, target, dispatch_domain = [cell.strip() for cell in row]
device = normalize_device(device)
target = normalize_target(target)
if device not in devices:
errors.append(f"设备不在历史枚举中:{device},可选:{', '.join(sorted(devices))}")
if not query:
errors.append("query 不能为空")
if "\t" in query or "\n" in query or "\r" in query:
errors.append("query 不能包含制表符或换行")
target_result = validate_target(target)
if not target_result["valid"]:
if target in known_historical_targets:
warnings.append("target 未通过 label-master 知识库校验,但历史干预文件中已存在;建议后续补齐标签知识库")
target_result["normalized_output"] = target
else:
errors.extend([f"target 不合法:{message}" for message in target_result.get("errors", [])])
if not dispatch_domain:
errors.append("下发domain 不能为空")
inferred = infer_dispatch_domain_for_device(target, device, summary)
if inferred and dispatch_domain and dispatch_domain != inferred["dispatch_domain"]:
message = (
f"下发domain 与推断映射不一致:当前 {dispatch_domain}"
f"推断为 {inferred['dispatch_domain']},来源 {inferred.get('source', 'historical')},置信度 {inferred.get('confidence')}"
)
if args.strict_domain:
errors.append(message)
else:
warnings.append(message)
if not inferred:
warnings.append("未在历史精确干预表中找到该 target 的下发domain 映射,需要人工确认")
configured = infer_configured_dispatch_domain(target, device, summary)
if configured and dispatch_domain and not domain_compatible(dispatch_domain, configured["dispatch_domain"]):
warnings.append(
f"下发domain 与 type_match_agent 配置不兼容:当前 {dispatch_domain}"
f"配置 {configured['matched_key']} -> {configured['dispatch_domain']}"
)
if not configured:
warnings.append("type_match_agent 下发表中未找到该 Agent tag 的设备/通用映射")
conflicts = existing.get((device, query), [])
for item in conflicts:
if item.target == target and item.dispatch_domain == dispatch_domain:
warnings.append(f"历史文件已存在相同单句干预:line {item.line_no}")
else:
errors.append(
f"历史文件存在同设备同 query 的不同干预:line {item.line_no}"
f"{item.target} / {item.dispatch_domain}"
)
results.append(
{
"line": index,
"valid": not errors,
"errors": errors,
"warnings": warnings,
"normalized": {
"device": device,
"query": query,
"target": target_result.get("normalized_output", target),
"dispatch_domain": dispatch_domain,
},
"configured_dispatch_domain": configured,
}
)
return summarize_results(results)
def validate_regex_rows(rows: list[list[str]], args: argparse.Namespace) -> dict[str, Any]:
summary = build_source_summary(Path(args.exact_source), Path(args.regex_source))
devices = set(summary["devices"])
known_historical_targets = historical_targets(summary)
existing: dict[tuple[str, str], list[Any]] = {}
for record in load_regex_records(Path(args.regex_source)):
existing.setdefault((record.device, record.pattern), []).append(record)
results = []
for index, row in enumerate(rows, start=1):
errors: list[str] = []
warnings: list[str] = []
if len(row) != 3:
errors.append(f"正则干预必须是 3 列,当前 {len(row)}")
results.append({"line": index, "valid": False, "errors": errors, "warnings": warnings, "row": row})
continue
device, pattern, target = [cell.strip() for cell in row]
device = normalize_device(device)
target = normalize_target(target)
if device not in devices:
errors.append(f"设备不在历史枚举中:{device},可选:{', '.join(sorted(devices))}")
if not pattern:
errors.append("正则不能为空")
else:
try:
re.compile(pattern)
except re.error as exc:
errors.append(f"正则无法编译:{exc}")
if "query#" not in pattern:
errors.append("正则必须包含 query# 片段")
if not pattern.startswith("^") or not pattern.endswith("$"):
warnings.append("历史常见正则通常使用 ^...$ 完整锚定,建议确认是否需要锚定")
target_result = validate_target(target)
if not target_result["valid"]:
if target in known_historical_targets:
warnings.append("target 未通过 label-master 知识库校验,但历史干预文件中已存在;建议后续补齐标签知识库")
target_result["normalized_output"] = target
else:
errors.extend([f"target 不合法:{message}" for message in target_result.get("errors", [])])
conflicts = existing.get((device, pattern), [])
for item in conflicts:
if item.target == target:
warnings.append(f"历史文件已存在相同正则干预:line {item.line_no}")
else:
errors.append(
f"历史文件存在同设备同正则的不同干预:line {item.line_no}{item.target}"
)
results.append(
{
"line": index,
"valid": not errors,
"errors": errors,
"warnings": warnings,
"normalized": {
"device": device,
"pattern": pattern,
"target": target_result.get("normalized_output", target),
},
}
)
return summarize_results(results)
def summarize_results(results: list[dict[str, Any]]) -> dict[str, Any]:
return {
"valid": all(item["valid"] for item in results),
"total": len(results),
"invalid": sum(1 for item in results if not item["valid"]),
"warning_count": sum(len(item.get("warnings", [])) for item in results),
"results": results,
}
def main() -> int:
parser = argparse.ArgumentParser(description="校验干预 TSV 候选")
parser.add_argument("--mode", choices=["exact", "regex"], required=True, help="干预类型")
parser.add_argument("--input", help="候选 TSV 文件路径;不传则读取 stdin")
parser.add_argument("--exact-source", default=str(DEFAULT_EXACT_SOURCE), help="历史单句精确干预 TSV")
parser.add_argument("--regex-source", default=str(DEFAULT_REGEX_SOURCE), help="历史正则干预 TSV")
parser.add_argument("--strict-domain", action="store_true", help="下发domain 和历史多数映射不一致时直接报错")
args = parser.parse_args()
text = load_input_text(args.input)
rows = parse_candidate_lines(text)
if args.mode == "exact":
payload = validate_exact_rows(rows, args)
else:
payload = validate_regex_rows(rows, args)
print(json_dumps(payload))
return 0 if payload["valid"] else 1
if __name__ == "__main__":
raise SystemExit(main())