From 1da6be3687f13a9be39caef6e755cb387561220b Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 16 Sep 2026 16:57:54 +0800 Subject: [PATCH] =?UTF-8?q?revert(substance=5Fgate):=20=E5=9B=9E=E9=80=80?= =?UTF-8?q?=E4=B8=89=E5=A4=84=E6=AD=A3=E5=88=99=E9=97=A8=E7=A6=81=E6=8C=82?= =?UTF-8?q?=E8=BD=BD(submit=5Fchapter/review=5Fchapter/finalize=5Fbid=5Fsc?= =?UTF-8?q?ore)+material=5Fmissing=E5=AE=88=E5=8D=AB=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=90=8Cpipeline-service=20c0cab53=E5=9B=9E=E9=80=80=E7=90=86?= =?UTF-8?q?=E7=94=B1:=E8=BE=93=E5=87=BA=E7=AB=AF=E5=A0=86=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E6=89=93=E5=9C=B0=E9=BC=A0=E4=B8=8D=E6=B2=BB=E6=A0=B9?= =?UTF-8?q?,=E5=AE=9E=E8=B4=A8=E6=80=A7=E5=88=A4=E6=96=AD=E5=9B=9E?= =?UTF-8?q?=E5=BD=92LLM=E8=AF=84=E5=AE=A1=E8=A3=81=E5=86=B3(=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E5=B7=B2=E6=94=B9)=E3=80=82=E4=BF=9D=E7=95=99docx?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E8=BD=AC=E6=8D=A2(write=E4=BE=A7=E7=9C=9Fbug?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_bidding/bid_review_capability.py | 21 ------- pipeline_bidding/bid_score_capability.py | 68 +---------------------- pipeline_bidding/bid_write_capability.py | 12 ---- 3 files changed, 2 insertions(+), 99 deletions(-) diff --git a/pipeline_bidding/bid_review_capability.py b/pipeline_bidding/bid_review_capability.py index c3e296b..a0cf47e 100644 --- a/pipeline_bidding/bid_review_capability.py +++ b/pipeline_bidding/bid_review_capability.py @@ -80,19 +80,6 @@ async def review_chapter(chapter_id, score, max_score="", comments="", detail="" "删除 mermaid/ASCII 字符画后重写提交。" % ";".join(sorted(set(_dg)))) comments = (dg_note + "\n" + (comments or "")).strip() - # ── 实质性响应门禁(2026-09-16 用户报障根治):指南体/表单大面积待填必退 ── - # submit_chapter 已拦一道;此处兜底存量正文/绕行写入,章节评审级即给反馈 - # (不必等整书评分),意见自动附带。确定性检测,不采信评分器满分。 - try: - from pipeline_service.substance_gate import find_substance_problems, gate_message - _sg = find_substance_problems(c.get("content") or "") - _sg_note = gate_message(_sg, scene="本章") if _sg else "" - except ImportError: - _sg_note = "" - if _sg_note: - passed = False - comments = (_sg_note + "\n" + (comments or "")).strip() - _substance_fail = bool(_sg_note) # 实质门禁不通过 → 禁止 material_missing 豁免绕过 if not passed and not (comments or "").strip(): return False, ("评审不通过必须给出改进意见(comments)," "写清缺哪些评分维度、应补什么内容、对照哪条得分规则") @@ -135,14 +122,6 @@ async def review_chapter(chapter_id, score, max_score="", comments="", detail="" # 未回传 → 章节通过(真实分数+材料缺失披露注记,整书评分如实反映), # 同时发 material_supply 信息流待办通知人类补料,不冻结任何流转。 _mat_missing = str(material_missing).strip().lower() in ("1", "true", "yes", "y") - # 守卫(2026-09-16):实质门禁不通过的章节禁止用 material_missing 豁免绕过—— - # 「指南体/表单大面积待填」不是实体材料缺失,是写作本身没写实质内容; - # 若放行会把我上一轮加的占位符豁免变成质量门禁的后门(实测满分放行的帮凶)。 - if _mat_missing and _substance_fail: - _mat_missing = False - comments = ("【material_missing 豁免被机制拒绝】本章未通过实质性响应门禁" - "(是指南体/表单待填,非实体材料缺失),不适用材料缺失豁免通道," - "必须按实质门禁意见重写正文。\n" + (comments or "")).strip() if _mat_missing: await sor.sqlExe( "UPDATE " + TABLE + " SET status=${st}$, review_score=${sc}$, max_score=${ms}$, " diff --git a/pipeline_bidding/bid_score_capability.py b/pipeline_bidding/bid_score_capability.py index 078817b..cc394e0 100644 --- a/pipeline_bidding/bid_score_capability.py +++ b/pipeline_bidding/bid_score_capability.py @@ -200,30 +200,6 @@ async def finalize_bid_score(review_id, comments="", who=None, agent_id=None): ratio = (total / maxsum) if maxsum > 0 else 0.0 passed = (ratio >= th["bid_pass_ratio"]) and not veto_fail - # ── 实质性响应确定性门禁(2026-09-16 用户报障根治,最关键的一道)── - # 实测:指南体标书(投标函全是【待填】、实施方案写「本章覆盖评分子项①②③」) - # 被 LLM 评分器判 100/100 满分放行——评分报告自己都列了「无效标」却 passed。 - # 根治:质量闸门不靠 LLM 自觉,机制层用 substance_gate 确定性扫全书章节正文, - # 命中指南体/表单大面积待填 → 强制 passed=False,命中章节退回重写。 - # 与 diagram_gate 同一设计哲学(花钱/交付闸门必须确定性校验兜底)。 - substance_chs = [] # [(chapter_id, chapter_no, title, problems)] - try: - from pipeline_service.substance_gate import find_substance_problems - _chrecs = await sor.sqlExe( - "SELECT id, chapter_no, title, content FROM bid_chapters " - "WHERE project_id=${p}$ ORDER BY order_no, chapter_no", {"p": pid}) - await sor.sqlExe("COMMIT", {}) - for _c in (_chrecs or []): - _cd = rec_to_dict(_c) - _probs = find_substance_problems(_cd.get("content") or "") - if _probs: - substance_chs.append((_cd.get("id"), _cd.get("chapter_no"), - _cd.get("title"), _probs)) - except ImportError: - pass # 宿主未装 pipeline-service 时跳过(逃逸阀) - if substance_chs: - passed = False - weak = [s for s in scores if to_float(s.get("max_score"), 0.0) > 0 and to_float(s.get("score"), 0.0) / to_float(s.get("max_score"), 1.0) < th["bid_pass_ratio"]] @@ -231,13 +207,6 @@ async def finalize_bid_score(review_id, comments="", who=None, agent_id=None): % (total, maxsum, ratio * 100, th["bid_pass_ratio"] * 100)] if veto_fail: summary_lines.append("⛔ 否决项未响应:" + "、".join(veto_fail[:5])) - if substance_chs: - summary_lines.append("⛔ 实质性响应门禁不通过:%d 个章节是「编写指南体/表单大面积待填」" - "(不是投标文件正文本身),机制确定性拦截(不采信评分满分):" - % len(substance_chs)) - for _cid, _cno, _ct, _probs in substance_chs[:12]: - summary_lines.append(" - 第%s章《%s》:%s" - % (_cno, (_ct or "")[:24], ";".join(_probs)[:200])) for s in weak[:20]: summary_lines.append("- %s %.1f/%.1f → %s" % (s.get("item_name"), to_float(s.get("score")), @@ -291,43 +260,10 @@ async def finalize_bid_score(review_id, comments="", who=None, agent_id=None): # 把改进意见落到问题章节:approved → rejected(reconciler 会派重写任务) touched = 0 - # ① 实质性门禁命中章节强制退回(评分器可能给它们满分,但机制判定是指南体/待填) - for _cid, _cno, _ct, _probs in substance_chs: - if not _cid: - continue - _crec = await sor.sqlExe( - "SELECT id, status, revise_count FROM bid_chapters WHERE id=${c}$", {"c": _cid}) - await sor.sqlExe("COMMIT", {}) - if not _crec: - continue - _c = rec_to_dict(_crec[0]) - if _c.get("status") == CH_REJECTED: - continue # 已退回,避免 revise_count 重复累加 - _imp = ("【实质性响应门禁 · 第%s轮 · 机制确定性拦截】本章是「编写指南/作业指导书」" - "或表单大面积未填实,不是投标文件正文本身。检出:%s\n" - "必须重写为正文:① 删除全部「编制说明/编制依据/填写口径/自查表/人工填写清单」" - "等讲『怎么写』的元内容;② 投标函/承诺/报价按招标格式**直接填真实内容**" - "(投标人/报价/日期等能从项目或知识库取到的填实,实体材料缺失用规范占位符" - "【材料待补:具体材料名】,禁止【待填-xxx】自造占位);③ 技术/实施方案写实质" - "措施(架构/流程/量化指标/责任人/工期),不写「覆盖评分子项①②③」式说明。" - % (review.get("round"), ";".join(_probs)[:600])) - await sor.sqlExe( - "UPDATE bid_chapters SET status=${st}$, review_comment=${cm}$, " - "revise_count=${rc}$, updated_at=NOW() WHERE id=${c}$", - {"st": CH_REJECTED, "cm": _imp[:60000], - "rc": to_int(_c.get("revise_count"), 0) + 1, "c": _cid}) - await sor.sqlExe("COMMIT", {}) - await record(sor, pid, "bid_chapters", _cid, "substance_reject", - from_state=_c.get("status"), to_state=CH_REJECTED, - who=who, agent_id=agent_id, - detail="实质性门禁退回:%s" % (";".join(_probs))[:300]) - touched += 1 - # ② 评分不达标章节退回(原有逻辑) - _substance_cids = set(_cid for _cid, _, _, _ in substance_chs if _cid) for s in weak: cid = s.get("target_chapter_id") or "" - if not cid or cid in _substance_cids: - continue # 已被 substance 门禁退回,避免 revise_count 重复累加 + if not cid: + continue crec = await sor.sqlExe( "SELECT id, status, revise_count, title FROM bid_chapters WHERE id=${c}$", {"c": cid}) diff --git a/pipeline_bidding/bid_write_capability.py b/pipeline_bidding/bid_write_capability.py index 725b17d..08d484f 100644 --- a/pipeline_bidding/bid_write_capability.py +++ b/pipeline_bidding/bid_write_capability.py @@ -169,18 +169,6 @@ async def submit_chapter(chapter_id, note="", who=None, agent_id=None): d = rec_to_dict(recs[0]) if len((d.get("content") or "").strip()) < MIN_CONTENT_LEN: return False, "章节正文为空或过短,请先 write_chapter 写入正文" - # ── 实质性响应硬门禁(2026-09-16 用户报障根治)── - # 拦「编写指南体/表单大面积待填」——交付的必须是投标文件正文本身,不是 - # 「教人怎么写标书」的元内容(编制说明/填写口径/自查表/【待填-xxx】占位)。 - # 确定性检测(substance_gate),不靠评审 LLM 自觉(实测指南体曾拿 100/100 满分)。 - # 挂在 submit_chapter(定稿提交唯一关口):append 分批写中途不误判,提交时查全文。 - try: - from pipeline_service.substance_gate import find_substance_problems, gate_message - _sg = find_substance_problems(d.get("content") or "") - if _sg: - return False, gate_message(_sg, scene="章节「%s」" % (d.get("title") or chapter_id)) - except ImportError: - pass # 宿主未装 pipeline-service 时放行(逃逸阀,与产线零接触分层一致) if d.get("status") not in (CH_WRITING, CH_PENDING, CH_REJECTED): return False, "当前状态 %s 不可提交(需 writing/pending/rejected)" % d.get("status") await sor.sqlExe(