refactor: layer fast slow routing knowledge

This commit is contained in:
wuyang6
2026-07-23 21:35:18 +08:00
parent 6116d10e8f
commit ecc02c06b7
21 changed files with 776 additions and 925 deletions
@@ -1,85 +1,66 @@
#!/usr/bin/env python3
"""Validate fast/slow/ambiguous label records without external dependencies."""
"""Validate 2026.7 hierarchical fast/slow routing records."""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Any
POLICY_VERSION = "XA4.1-2026.6"
REASON_CODES_BY_LABEL: dict[str, set[str]] = {
"": {
"DIRECT_ACTION",
"DIRECT_DETERMINISTIC_QUERY",
"EXPLICIT_DEVICE_MULTI_ACTION",
"DIRECT_OPERATION_FLOW",
},
"": {
"INTENT_INFERENCE",
"SCENE_GOAL_MAPPING",
"MULTI_STEP_PLANNING",
"COMPLEX_REASONING",
"TRUE_DISAMBIGUATION",
"CONTEXT_REQUIRED",
"MISSING_REQUIRED_SLOT",
"AUTOMATION_SLOW_POLICY",
},
"模糊": {
"OPEN_CHAT_OR_QA",
"PRODUCT_KNOWLEDGE",
"FAULT_DIAGNOSIS",
"DEVICE_OR_ENV_STATE_QUERY",
"POLICY_UNCERTAIN",
},
}
POLICY_VERSION = "FAST-SLOW-2026.7"
SKILL_ROOT = Path(__file__).resolve().parent.parent
CONFLICT_FILE = SKILL_ROOT / "references" / "policy-conflicts.md"
DEFAULT_ROUTE_BY_LABEL = {
"": "快系统",
"": "慢系统",
"模糊": "慢系统",
}
LEGACY_LABELS = {
"FAST_DIRECT",
"SLOW_FILTER_RANK",
"SLOW_DISAMBIGUATE",
"SLOW_GOAL_STATE",
"SLOW_WORKFLOW",
"SLOW_CONTEXT_DEPENDENT",
"SLOW_UNCLEAR",
"SLOW_ADVANCED_CAPABILITY",
}
CONFIDENCE_VALUES = {"high", "medium", "low"}
DECISION_LEVELS = {"domain", "structure", "core"}
DOMAIN_VALUES = {
"导航/生服",
"车控",
"产品问答",
"内容",
"系统/App控制",
"通用工具",
"IoT",
"自动化",
}
RULE_ID_PATTERN = re.compile(r"^[A-Z][A-Z0-9_]+$")
CONFLICT_ID_PATTERN = re.compile(r"^CONFLICT_[A-Z0-9_]+$")
REQUIRED_FIELDS = {
"query",
"label",
"default_route",
"primary_reason_code",
"reason_codes",
"domain",
"candidate_domains",
"decision_level",
"decision_rule_id",
"decision_source",
"decision_path",
"reason",
"evidence",
"structural_signals",
"context_used",
"fast_system_pending",
"route_override",
"policy_conflict",
"conflict_refs",
"confidence",
"review_required",
"uncertainties",
"policy_version",
}
OPTIONAL_FIELDS = {
"id",
"counterevidence",
"domain_label",
"candidate_domain_labels",
"structural_signals",
"legacy_eval_label",
}
@@ -107,6 +88,82 @@ def _check_string_list(
errors.append(f"{field} 不允许重复项")
def _validate_decision_source(
record: dict[str, Any],
*,
errors: list[str],
) -> None:
source = record.get("decision_source")
rule_id = record.get("decision_rule_id")
level = record.get("decision_level")
if not _is_nonempty_string(source):
errors.append("decision_source 必须是非空字符串")
return
relative_path = source.split("#", 1)[0]
if not relative_path.startswith("references/") or not relative_path.endswith(".md"):
errors.append("decision_source 必须指向 references/*.md")
return
resolved = (SKILL_ROOT / relative_path).resolve()
references_root = (SKILL_ROOT / "references").resolve()
if references_root not in resolved.parents:
errors.append("decision_source 不允许跳出 references 目录")
return
if not resolved.is_file():
errors.append(f"decision_source 文件不存在: {relative_path}")
return
try:
source_text = resolved.read_text(encoding="utf-8")
except OSError as exc:
errors.append(f"无法读取 decision_source: {exc}")
return
if _is_nonempty_string(rule_id) and rule_id not in source_text:
errors.append(f"decision_rule_id 未出现在来源文件中: {rule_id}")
filename = resolved.name
if level == "domain" and not filename.startswith("domain-"):
errors.append("decision_level=domain 时来源文件必须是 domain-*.md")
if level == "structure" and not filename.startswith("structure-"):
errors.append("decision_level=structure 时来源文件必须是 structure-*.md")
if level == "core" and filename != "core-policy.md":
errors.append("decision_level=core 时来源文件必须是 core-policy.md")
def _validate_conflicts(
record: dict[str, Any],
*,
errors: list[str],
) -> None:
policy_conflict = record.get("policy_conflict")
conflict_refs = record.get("conflict_refs")
if not isinstance(policy_conflict, bool):
errors.append("policy_conflict 必须是布尔值")
_check_string_list(
conflict_refs,
field="conflict_refs",
errors=errors,
unique=True,
)
if not isinstance(conflict_refs, list):
return
if policy_conflict is True and not conflict_refs:
errors.append("policy_conflict=true 时 conflict_refs 至少包含一项")
if policy_conflict is False and conflict_refs:
errors.append("policy_conflict=false 时 conflict_refs 必须为空")
try:
known_conflicts = CONFLICT_FILE.read_text(encoding="utf-8")
except OSError as exc:
errors.append(f"无法读取冲突表: {exc}")
return
for conflict_id in conflict_refs:
if not isinstance(conflict_id, str):
continue
if not CONFLICT_ID_PATTERN.fullmatch(conflict_id):
errors.append(f"冲突 ID 格式非法: {conflict_id!r}")
elif conflict_id not in known_conflicts:
errors.append(f"冲突 ID 未登记: {conflict_id}")
def validate_record(record: Any, index: int) -> dict[str, Any]:
errors: list[str] = []
warnings: list[str] = []
@@ -118,7 +175,6 @@ def validate_record(record: Any, index: int) -> dict[str, Any]:
"errors": ["记录必须是 JSON 对象"],
"warnings": [],
}
if "__parse_error__" in record:
return {
"index": index,
@@ -130,61 +186,75 @@ def validate_record(record: Any, index: int) -> dict[str, Any]:
missing = sorted(REQUIRED_FIELDS - set(record))
if missing:
errors.append(f"缺少必填字段: {', '.join(missing)}")
unknown = sorted(set(record) - REQUIRED_FIELDS - OPTIONAL_FIELDS)
if unknown:
errors.append(f"存在未知字段: {', '.join(unknown)}")
query = record.get("query")
if not _is_nonempty_string(query):
if not _is_nonempty_string(record.get("query")):
errors.append("query 必须是非空字符串")
label = record.get("label")
if label not in REASON_CODES_BY_LABEL:
if not isinstance(label, str) or label not in DEFAULT_ROUTE_BY_LABEL:
errors.append("label 必须是 快、慢、模糊 之一")
default_route = record.get("default_route")
expected_route = DEFAULT_ROUTE_BY_LABEL.get(label)
if expected_route is not None and default_route != expected_route:
expected_route = DEFAULT_ROUTE_BY_LABEL.get(label) if isinstance(label, str) else None
if expected_route is not None and record.get("default_route") != expected_route:
errors.append(f"label={label} 时 default_route 必须是 {expected_route}")
primary_reason_code = record.get("primary_reason_code")
reason_codes = record.get("reason_codes")
allowed_reasons = REASON_CODES_BY_LABEL.get(label, set())
if primary_reason_code not in allowed_reasons:
errors.append(
f"primary_reason_code={primary_reason_code!r} 与 label={label!r} 不兼容"
)
if not isinstance(reason_codes, list):
errors.append("reason_codes 必须是数组")
else:
if not reason_codes:
errors.append("reason_codes 至少包含一项")
if all(isinstance(code, str) for code in reason_codes):
if len(reason_codes) != len(set(reason_codes)):
errors.append("reason_codes 不允许重复")
invalid_reason_codes = [
code
for code in reason_codes
if not isinstance(code, str) or code not in allowed_reasons
domain = record.get("domain")
if domain is not None and (
not isinstance(domain, str) or domain not in DOMAIN_VALUES
):
errors.append("domain 不是受支持的粗粒度垂域")
_check_string_list(
record.get("candidate_domains"),
field="candidate_domains",
errors=errors,
unique=True,
)
candidate_domains = record.get("candidate_domains")
if isinstance(candidate_domains, list):
unknown_domains = [
value
for value in candidate_domains
if not isinstance(value, str) or value not in DOMAIN_VALUES
]
if invalid_reason_codes:
rendered_codes = ", ".join(repr(code) for code in invalid_reason_codes)
errors.append(f"reason_codes 包含与 label 不兼容的值: {rendered_codes}")
if primary_reason_code not in reason_codes:
errors.append("primary_reason_code 必须同时出现在 reason_codes 中")
if unknown_domains:
errors.append(f"candidate_domains 包含未知垂域: {unknown_domains}")
level = record.get("decision_level")
if not isinstance(level, str) or level not in DECISION_LEVELS:
errors.append("decision_level 必须是 domain、structure、core 之一")
if level == "domain" and domain is None:
errors.append("decision_level=domain 时 domain 不能为空")
rule_id = record.get("decision_rule_id")
if not _is_nonempty_string(rule_id) or not RULE_ID_PATTERN.fullmatch(rule_id):
errors.append("decision_rule_id 必须是大写下划线规则 ID")
_check_string_list(
record.get("decision_path"),
field="decision_path",
errors=errors,
require_nonempty=True,
)
decision_path = record.get("decision_path")
if (
isinstance(decision_path, list)
and _is_nonempty_string(rule_id)
and not any(rule_id in str(item) for item in decision_path)
):
errors.append("decision_path 必须包含 decision_rule_id")
_validate_decision_source(record, errors=errors)
if not _is_nonempty_string(record.get("reason")):
errors.append("reason 必须是非空字符串")
_check_string_list(
record.get("evidence"),
field="evidence",
errors=errors,
require_nonempty=True,
)
if "counterevidence" in record:
_check_string_list(
record.get("counterevidence"),
@@ -192,35 +262,26 @@ def validate_record(record: Any, index: int) -> dict[str, Any]:
errors=errors,
)
if "domain_label" in record:
domain_label = record.get("domain_label")
if domain_label is not None and not _is_nonempty_string(domain_label):
errors.append("domain_label 必须是非空字符串或 null")
if "candidate_domain_labels" in record:
_check_string_list(
record.get("candidate_domain_labels"),
field="candidate_domain_labels",
errors=errors,
unique=True,
)
structural_signals = record.get("structural_signals")
if structural_signals is not None:
if not isinstance(structural_signals, dict):
errors.append("structural_signals 必须是对象")
else:
expected_keys = {"complex", "multi_instruction", "auto_task"}
actual_keys = set(structural_signals)
if actual_keys != expected_keys:
errors.append(
"structural_signals 必须且只能包含 "
"complex、multi_instruction、auto_task"
)
for key in expected_keys:
value = structural_signals.get(key)
if value is not None and not isinstance(value, bool):
errors.append(f"structural_signals.{key} 必须是布尔值或 null")
expected_signal_keys = {
"complex",
"multi_instruction",
"auto_task",
"context_dependent",
}
if not isinstance(structural_signals, dict):
errors.append("structural_signals 必须是对象")
else:
actual_keys = set(structural_signals)
if actual_keys != expected_signal_keys:
errors.append(
"structural_signals 必须且只能包含 "
"complex、multi_instruction、auto_task、context_dependent"
)
for key in expected_signal_keys:
value = structural_signals.get(key)
if value is not None and not isinstance(value, bool):
errors.append(f"structural_signals.{key} 必须是布尔值或 null")
if not isinstance(record.get("context_used"), bool):
errors.append("context_used 必须是布尔值")
@@ -231,20 +292,15 @@ def validate_record(record: Any, index: int) -> dict[str, Any]:
if fast_system_pending is True and label != "":
errors.append("只有 label=快 时 fast_system_pending 才能为 true")
legacy_eval_label = record.get("legacy_eval_label")
if (
"legacy_eval_label" in record
and legacy_eval_label is not None
and legacy_eval_label not in LEGACY_LABELS
):
errors.append("legacy_eval_label 不是受支持的旧八类标签")
if label == "模糊" and legacy_eval_label is not None:
warnings.append("模糊档通常无法无损映射到旧二值标签,请确认迁移依据")
route_override = record.get("route_override")
if route_override is not None and not _is_nonempty_string(route_override):
errors.append("route_override 必须是非空字符串或 null")
_validate_conflicts(record, errors=errors)
confidence = record.get("confidence")
if confidence not in CONFIDENCE_VALUES:
if not isinstance(confidence, str) or confidence not in CONFIDENCE_VALUES:
errors.append("confidence 必须是 high、medium、low 之一")
review_required = record.get("review_required")
if not isinstance(review_required, bool):
errors.append("review_required 必须是布尔值")
@@ -301,7 +357,7 @@ def _load_records_from_file(path: Path) -> list[Any]:
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="校验 XA4.1-2026.6 快慢分流打标 JSON/JSONL"
description="校验 FAST-SLOW-2026.7 分层打标 JSON/JSONL"
)
source_group = parser.add_mutually_exclusive_group(required=True)
source_group.add_argument("--record", help="单条 JSON 对象字符串")