Compare commits

...

2 Commits

Author SHA1 Message Date
pzhang_zywl a24408521c fix: step3 _normalize_rule 为空 sources 的 rule 添加最小 text source - Closes #64
CI / test (pull_request) Successful in 11s
防御性处理 LLM 输出中 sources 为空数组的情况,避免 Layer A schema 失败。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 17:25:12 +08:00
pzhang_zywl c091b6c256 Merge pull request 'fix: [bug] IR 覆盖率回归:Layer B 从 92.6% 降至 63% + Layer A 新 schema 错误 - 来自 #18 - Closes #57' (#63) from dev/issue-57-round2-ir-normalize-on-load into main
CI / test (push) Successful in 11s
2026-06-02 16:58:35 +08:00
2 changed files with 33 additions and 15 deletions
@@ -172,10 +172,9 @@ def _normalize_rule(rule: dict) -> dict:
# Ensure table/text sources have a section field (defensive against LLM omission)
# Also normalize invalid source types (LLM hallucinations like function_unit_description)
sources = rule.get("sources", [])
if sources:
valid_types = {"table", "text", "logic_tree"}
# try to infer a default section from sibling sources or the rule path
# try to infer a default section from the rule path
default_section = ""
for s in sources:
sec = s.get("section", "")
@@ -187,15 +186,22 @@ def _normalize_rule(rule: dict) -> dict:
if path:
default_section = path.split(" > ")[0] if " > " in path else path
if sources:
for src in sources:
stype = src.get("type", "")
# Normalize invalid source types to "text"
if stype and stype not in valid_types:
src["type"] = "text"
stype = "text"
if stype in ("table", "text"):
if not src.get("section"):
src["section"] = default_section
else:
# Empty sources list — add a minimal text source (defensive against schema failure)
src = {"type": "text", "text_snippet": "inferred from rule context"}
if default_section:
src["section"] = default_section
sources.append(src)
rule["sources"] = sources
return rule
@@ -526,3 +526,15 @@ class TestNormalizeRule:
assert normalized["sources"][0]["type"] == "text"
assert normalized["sources"][1]["type"] == "text"
assert normalized["sources"][0]["section"] == "3.1 功能"
def test_normalize_empty_sources(self):
"""Rules with empty sources get a minimal text source (defensive)."""
rule = {
"trigger": {"conditions": [{"signal": "x", "operator": "==", "value": "1"}]},
"path": "3.1 策略 > decision_speed",
"sources": [],
}
normalized = _normalize_rule(rule)
assert len(normalized["sources"]) == 1
assert normalized["sources"][0]["type"] == "text"
assert normalized["sources"][0]["section"] == "3.1 策略"