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