feat(check): 偏离栏直读、响应错位判断;复查复用和硬预算(C2)

- deviation.py:模型判已响应的条目,证据所在表格行的偏离格写着「偏离 / 部分满足」等
  就改判负偏离;照抄要求格、响应格写的是别的内容的改判「响应可能对不上」
- reuse.py:按「提示词 + 要求 + 候选句原文」算缓存键,存模型原始结论;复查时内容没变、
  证据找得回的条目直接沿用,不调模型
- 每次检查送给模型的字数有上限,超出的条目列为请人工核对,写进覆盖范围
- 开发集 17 份:召回 61% → 73%,负偏离未说明 64% → 90%,规矩投标文件问题数不变

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-04 17:47:12 +08:00
parent 81c58798d2
commit df3ebbb6a1
12 changed files with 430 additions and 23 deletions

View File

@@ -21,6 +21,8 @@ REASONS = {
"quote_too_short": "引文太短,无法核对",
"missing_answer": "这一条没有得到判断结果",
"model_error": "模型服务暂时不可用",
"over_budget": "超出本次检查的预算",
"response_mismatch": "响应格写的是别的内容",
}
@@ -66,13 +68,23 @@ def spec(result: Result, item: dict[str, Any]) -> Spec | None:
why = REASONS.get(result.reason_code or "", result.reason_code or "")
text = f"模型认为投标文件{where},但出处没能核对({why}),按「未找到响应」处理。请人工核对。"
return Spec("unresponded", sev, f"未找到响应:{name}", text, {**base, "evidence_status": "unverified"})
if result.verdict == "not_found" and result.detail.get("mismatch"):
text = (
f"投标文件照抄了这条要求,但同一行的响应格写的是「{result.detail['mismatch'][:40]}」,"
"和这条要求对不上,像是别的条目的内容。请核对。"
)
return Spec("unresponded", sev, f"响应可能对不上:{name}", text, base)
if result.verdict == "not_found":
text = f"在投标文件({scope})中没有找到对这条要求的响应。" if scope else "没有找到对这条要求的响应。"
return Spec("unresponded", sev, f"未找到响应:{name}", text, base)
if result.verdict == "negative_deviation":
return Spec(
"negative_deviation", sev, f"负偏离未说明:{name}", "投标文件写明了偏离或不满足,没有找到说明。", base
cell = result.detail.get("direct_read")
text = (
f"投标文件这一行的偏离 / 响应栏写着「{cell}」,没有找到说明。"
if cell
else "投标文件写明了偏离或不满足,没有找到说明。"
)
return Spec("negative_deviation", sev, f"负偏离未说明:{name}", text, base)
if result.verdict == "suspected_unmet":
value = result.detail.get("response_value")
text = (