feat(check): 响应方式识别(C12)和「这条要不要投标人回答」;评测集纠错
- checklist/response_mode.py:按类别(技术 / 商务)识别招标文件的响应方式(逐条响应 / 默认接受),规则优先; 两种写法冲突按逐条,认不出按逐条并写进覆盖范围。 - check/applicability.py:无效情形(★ / 斜体下划线标注的条款除外)、日期与递交、没要求附材料的评分标准、 无需提供证明或由采购人查询的资格,不逐条找响应,写进覆盖范围;要求附证明材料按原文判断,中标后才交的不算。 - 默认接受的要求没找到响应:一般要求记 implicit_accepted、不出问题;实质性要求记 implicit_review、提示级 人工核对;要附证明材料的不免(迁移 0006)。 - match.py:修同一行单元格又是别的条目候选句时的 KeyError(每条清单项单独记候选句,句子从总表里查)。 - 评测集:注入器同一要求的所有表格行一起改;散文、附件、格式文件里也响应了的要求不挂漏响应类问题; 错位响应必须是另一个参数;复核判不成立的单元不再挂;regen.py 按固定种子重新生成。 开发集 17 份:规矩投标文件报出的问题 639 → 229,误报 21.3 → 12.4 / 100,召回 68% → 65%,致命级 20 → 21 / 32 (docs/architecture/check-eval.md §4)。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,8 @@ from bid_doctor.ai import prompts
|
||||
from bid_doctor.ai import service as ai_service
|
||||
from bid_doctor.ai.providers.base import near_limit
|
||||
from bid_doctor.ai.service import OutputIncomplete, provider
|
||||
from bid_doctor.check import findings, match
|
||||
from bid_doctor.check import applicability, findings, match
|
||||
from bid_doctor.checklist import response_mode
|
||||
from bid_doctor.core.config import get_settings
|
||||
from bid_doctor.core.db import TenantContext
|
||||
from bid_doctor.document.pipeline import text_layer
|
||||
@@ -120,6 +121,7 @@ async def main() -> None:
|
||||
ap.add_argument("--name", default="base")
|
||||
ap.add_argument("--baseline", action="store_true", help="只检索、不调模型的基线")
|
||||
ap.add_argument("--v1", action="store_true", help="第 1 步的做法:提示词 v1、不带同一行的单元格(对比基线)")
|
||||
ap.add_argument("--no-modes", action="store_true", help="不按响应方式和「要不要回答」筛选(C12 之前的做法)")
|
||||
args = ap.parse_args()
|
||||
d = OUT / args.bid
|
||||
manifest = json.loads((d / "manifest.json").read_text())
|
||||
@@ -137,9 +139,34 @@ async def main() -> None:
|
||||
return None
|
||||
|
||||
ctx = TenantContext(tenant_id=UUID(int=0), user_id=UUID(int=0))
|
||||
all_items = items
|
||||
decisions: dict[UUID, applicability.Decision] = {}
|
||||
if not args.no_modes:
|
||||
modes = response_mode.detect(gold.sentences(manifest["tender"]))
|
||||
decisions = {i: applicability.decide(p, modes) for i, p in items.items()}
|
||||
items = {i: p for i, p in items.items() if decisions[i].match}
|
||||
results, stats = await match.run(ctx, uuid4(), items, docs, progress)
|
||||
if decisions:
|
||||
match.apply_modes(results, items, decisions)
|
||||
rows = []
|
||||
for item_id, item in items.items():
|
||||
for item_id, item in all_items.items():
|
||||
if item_id not in items:
|
||||
rows.append(
|
||||
{
|
||||
"title": item["title"],
|
||||
"sentences": item["sentences"],
|
||||
"categories": item["categories"],
|
||||
"importance": item["importance"],
|
||||
"verdict": f"skipped:{decisions[item_id].skip}",
|
||||
"layer": None,
|
||||
"evidence_status": "none",
|
||||
"reason_code": None,
|
||||
"evidence": [],
|
||||
"detail": {},
|
||||
"finding": None,
|
||||
}
|
||||
)
|
||||
continue
|
||||
res = results[item_id]
|
||||
spec = findings.spec(res, item)
|
||||
rows.append(
|
||||
|
||||
Reference in New Issue
Block a user