feat(check): 需人工核对(P2)和分项 / 单价上限(C7)

- 需人工核对:份数、密封、递交、CA 签章加密、解密、保证金这类文件里看不出来的要求,
  从正式清单按主题合并、带招标原文出处;manual-checks 接口按项目存勾选、写勾选人和时间,
  复查沿用;查标页加「需人工核对」一块,覆盖范围写明列了几条
- 「报价」类清单项:读标从采购需求的清单表规则读出分项 / 单价上限(checklist/price.py);
  查标交给规则 PRICE.OVER_CEILING_ITEM 比对分项报价表,不交给模型;规则集 2026.11-r2
- 版面工具(分页、分行、归栏)挪到 document/pipeline/layout.py,读标和查标共用
- 评测:price_defects.py 从规矩投标文件派生只带报价问题的 4 份(P01、P02、P03)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-04 17:31:32 +08:00
parent db1b1f6f9f
commit 81c58798d2
22 changed files with 888 additions and 84 deletions

View File

@@ -166,3 +166,28 @@ def test_scanned_fallback_turns_missing_evidence_into_a_hint() -> None:
and "2 页扫描件" in spec.explanation
)
assert results[b].verdict == "not_found" # 技术参数不是证明材料,照常报未找到
def test_manual_topics_merge_by_topic_and_skip_irrelevant() -> None:
"""需人工核对(FR-M3-04):同一主题合并成一条;技术参数、免收保证金、围串标情形、点名要交的文件不算。"""
from bid_doctor.check import manual
items = [
{
"title": "投标文件递交截止 2027年3月5日 9:30",
"categories": ["schedule"],
"spec": {"schedule": {"at": "2027-03-05T09:30"}},
},
{"title": "电子投标文件须在截止时间前上传", "categories": ["schedule"]},
{"title": "未在规定时间内解密投标文件的投标无效", "categories": ["invalidity"]},
{"title": "须提供加盖电子签章的《开标一览表》", "categories": ["format_doc"]},
{"title": "须用CA数字证书加密投标文件", "categories": ["schedule"]},
{"title": "本项目免收投标保证金", "categories": ["schedule"]},
{"title": "数据库支持国密加密存储", "categories": ["tech_param"]},
{"title": "不同投标人的投标文件装订明显相似的投标无效", "categories": ["invalidity"]},
{"title": "纸质投标文件正本一份、副本四份,密封递交", "categories": ["schedule"]},
]
got = {t.key: [i["title"] for i in t.items] for t in manual.topics(items)}
assert list(got) == ["DECRYPT", "SIGN_ENCRYPT", "PAPER", "SUBMIT"]
assert got["SIGN_ENCRYPT"] == ["须用CA数字证书加密投标文件"]
assert got["SUBMIT"][0].startswith("投标文件递交截止") # 写明截止时间的做代表