Add intervention data skill
This commit is contained in:
@@ -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())
|
||||
Reference in New Issue
Block a user