fix(bid_flow): 三连修(2026-09-15甘肃项目实测)——①audit_log查询列名updated_at→created_at(表只有created_at,1054崩掉整个reconcile_project,第3轮复审派发路径12天首次触雷累计崩175次全项目停摆) ②资质不阻塞标书推进(用户裁定):CH_SECTION_DEPS.business移除QUALS依赖+删除hd_pending/biz_ok商务章门禁(资质缺口=信息流待办,非门禁流)+DIM_UPSTREAM.cost_benefit去掉quals(与声明层analysis_cost_benefit.deps对齐,报价链不再被资质QC冻结)

This commit is contained in:
yumoqing 2026-09-15 21:51:21 +08:00
parent 07e4bf7a7a
commit 0f23c0d8de

View File

@ -104,7 +104,11 @@ DIM_UPSTREAM = {
"scoring": (),
"quals": ("scoring",),
"reqs_outline": ("scoring",),
"cost_benefit": ("scoring", "quals", "reqs_outline"),
# 2026-09-15 用户裁定:资质有无/资质QC不阻塞标书推进。cost_benefit 测算
# (保证金/预算/付款条件)实际依据评分项与投标文件要求,不依赖资质清单;
# 且 BID_FLOW_STAGES 声明层 analysis_cost_benefit.deps 本就无 analysis_quals
# ——机制层与声明层对齐(原不一致:quals QC 卡住连报价章全链冻结)。
"cost_benefit": ("scoring", "reqs_outline"),
}
# 维度 → 落库表(落库核验用;reqs_outline 维度同时写两张表)
@ -119,10 +123,12 @@ DIM_TABLES = {
# 通过 QC——某一维度 QC 卡住只阻塞依赖它的章节,其余章节照常流转。
# 公共事实:章节骨架(bid_chapters)本身就是章节,OUTLINE 过了章节才可信;
# REQS(投标文件格式要求)决定章节怎么写,所有章节都要。
# 2026-09-15 用户裁定:资质有无/QC过否不阻塞标书编写——business 移除 QUALS 依赖
# (资质缺口走 human_docs_request 待办通知人类,属信息流不是门禁流)。
CH_SECTION_DEPS = {
"technical": (QC_TYPE_SCORING, QC_TYPE_REQS, QC_TYPE_OUTLINE),
"price": (QC_TYPE_SCORING, QC_TYPE_COST, QC_TYPE_OUTLINE),
"business": (QC_TYPE_QUALS, QC_TYPE_REQS, QC_TYPE_OUTLINE),
"business": (QC_TYPE_REQS, QC_TYPE_OUTLINE),
}
@ -999,15 +1005,18 @@ async def reconcile_project(sor, project_id, project_name=""):
await sor.sqlExe("COMMIT", {})
if prev:
prev_imp = rec_to_dict(prev[0]).get("improvement") or ""
# ⚠ audit_log 表只有 created_at(models/audit_log.json 与库一致),
# 曾写 updated_at → 1054 使整个 reconcile_project 崩溃、全项目派发停摆
# (2026-09-15 甘肃项目实测:第3轮复审派发路径首次触雷,累计崩 175 次)。
fx = await sor.sqlExe(
"SELECT action, entity, detail, updated_at FROM audit_log "
"SELECT action, entity, detail, created_at FROM audit_log "
"WHERE tenant_id=${p}$ AND entity IN ('bid_chapters','bid_doc_requirements',"
"'bid_scoring_items','bid_qualifications','bid_cost_benefit') "
"ORDER BY updated_at DESC LIMIT 30",
"ORDER BY created_at DESC LIMIT 30",
{"p": project_id})
await sor.sqlExe("COMMIT", {})
fixes = "\n".join(
"- [%s] %s/%s: %s" % (rec_to_dict(f).get("updated_at", "")[:16],
"- [%s] %s/%s: %s" % (rec_to_dict(f).get("created_at", "")[:16],
rec_to_dict(f).get("entity", ""),
rec_to_dict(f).get("action", ""),
(rec_to_dict(f).get("detail") or "")[:80])
@ -1030,7 +1039,8 @@ async def reconcile_project(sor, project_id, project_name=""):
# ── B. 资料准备(知识库资质匹配 + 人类文件清单)──
# 2026-09-03 章节级依赖:资料准备是商务章(business)的上游依赖,
# 不再阻塞全局流转——技术/报价章在上游 QC 通过后立即开写。
# 2026-09-11 流程裁剪:prep 被裁 → 不派;商务章 biz_ok 判定同步视为完成。
# 2026-09-11 流程裁剪:prep 被裁 → 不派。
# 2026-09-15 起资料准备不再阻塞商务章(资质缺口=信息流,见下方 C 段注释)。
prep_done = (not _stage_on(STAGE_PREP)) or await has_done_task(sor, project_id, R_PREP)
if _stage_on(STAGE_PREP) and n_quals > 0 and not prep_done:
if await count_open_tasks(sor, project_id, role=R_PREP) > 0:
@ -1054,8 +1064,12 @@ async def reconcile_project(sor, project_id, project_name=""):
# 其余章节照常流转(根治:此前五类 QC 全过才放行,cost_benefit 一个维度慢
# 导致全部章节冻结)。商务章额外等资料准备完成。
qc_passed = await _qc_passed_eff()
hd_pending = await find_human_task(sor, project_id, HT_HUMAN_DOCS, status="pending")
biz_ok = (prep_done or n_quals == 0) and not hd_pending
# 2026-09-15 用户裁定:资质文件待办(human_docs_request)不再阻塞任何章节编写。
# 原 biz_ok=(prep_done or n_quals==0) and not hd_pending 会把「人类没上传资质文件」
# 变成商务章硬门禁——用户明确说忽略也不管用(答复还会被 agent QC 退回,见
# human_task_capability 的人类权威规则)。资质缺口是信息流(待办通知),不是门禁流。
# prep(资料准备)也不再挡商务章:与声明层对齐(chapter_write.deps 只有
# analysis_reqs_outline),写者需要资质信息时自查知识库/待办清单。
created_w = 0
waiting_deps = {}
for c in chapters:
@ -1075,9 +1089,6 @@ async def reconcile_project(sor, project_id, project_name=""):
if unmet:
waiting_deps.setdefault("/".join(sorted(unmet)), []).append(str(c.get("chapter_no")))
continue
if section == "business" and not biz_ok:
waiting_deps.setdefault("bid_prep(资料准备)", []).append(str(c.get("chapter_no")))
continue
stage = "revise" if c.get("status") == CH_REJECTED else "write"
tid = await create_role_task(
sor, project_id, role,