feat(tech-split): tech评估项抽取接机械拆解(2026-09-18用户裁定)+LENGTH字节口径修复——①_reconcile_tech_analysis_qc派发段:正文超split_chars拆N段并行子任务(char_range互不重叠,四维度A块同款);单体全文抽取实测弊病(甘肃r1):2.7小时串行+agent自选offset步进(700-1000字符)窗口大量重叠,(category,item_name)精确去重对跨窗口近义命名失效→733条注水QC6.8退回;不相交区间从源头消除跨窗口重复;②轮次口径改_dim_round_count(round去重):旧COUNT条数口径拆解后一轮N子任务会虚高N倍误触上限;③_tender_text_len修LENGTH(字节)→CHAR_LENGTH(字符):抽取工具按Python字符串索引切片,char_range是字符坐标;甘肃实测LENGTH=179031 vs CHAR_LENGTH=67658(CJK≈2.65字节/字),按字节拆段第2段仅尾部有效第3段越界空串——2026-09-15四维度拆解'实测通过'实际带病运行(区间错位浪费1/3子任务),本次修正全维度受益

This commit is contained in:
yumoqing 2026-09-18 10:48:25 +08:00
parent a1a97c6669
commit 05e5c6bff2

View File

@ -176,9 +176,16 @@ async def _open_dim_tasks(sor, project_id, dim):
async def _tender_text_len(sor, project_id): async def _tender_text_len(sor, project_id):
"""项目最新招标文件正文字符数(无文件/无正文返回 0)。拆解阈值判定用。""" """项目最新招标文件正文字符数(无文件/无正文返回 0)。拆解阈值判定用。
⚠️ 必须 CHAR_LENGTH(字符)不能 LENGTH(字节):抽取工具按 Python 字符串
索引切片(txt[offset:offset+length]),char_range 区间是字符坐标。甘肃实测
LENGTH=179031 vs CHAR_LENGTH=67658(CJK utf8mb4 ≈2.65字节/字)——按字节拆
3 段时第 2 段仅尾部 8k 字符有效、第 3 段完全越界读空串(2026-09-15 四维度
拆解"实测通过"实际带病运行:区间错位浪费 1/3 子任务,2026-09-18 tech 接入时修正)。
"""
recs = await sor.sqlExe( recs = await sor.sqlExe(
"SELECT LENGTH(content_text) AS n FROM bid_tender_files " "SELECT CHAR_LENGTH(content_text) AS n FROM bid_tender_files "
"WHERE project_id=${p}$ ORDER BY created_at DESC LIMIT 1", {"p": project_id}) "WHERE project_id=${p}$ ORDER BY created_at DESC LIMIT 1", {"p": project_id})
await sor.sqlExe("COMMIT", {}) await sor.sqlExe("COMMIT", {})
if not recs: if not recs:
@ -1537,28 +1544,46 @@ async def _reconcile_tech_analysis_qc(sor, project_id, pname, n_items, latest_qc
return False, False, True return False, False, True
qc_imp = (latest_qc.get("improvement") or "") if ( qc_imp = (latest_qc.get("improvement") or "") if (
latest_qc and str(latest_qc.get("passed")) != "1") else "" latest_qc and str(latest_qc.get("passed")) != "1") else ""
attempts = await _count( # 轮次口径(2026-09-18 拆解配套,四维度 A 块同款):一个拆解轮派 N 个分段
sor, "SELECT COUNT(*) AS c FROM pipeline_tasks WHERE tenant_id=${p}$ " # 子任务(同 round 标签),按任务条数计轮会把一轮误算 N 轮触发上限误判。
"AND pipeline_id='role_task' AND role=${r}$ AND state IN " # 旧口径 COUNT(params LIKE '%bid_tech_analysis%') 数条数,拆解后同样虚高。
"('approved','completed','qc_rejected') AND params LIKE ${kw}$", n_first = await _dim_round_count(sor, project_id, TECH_KIND, redo=False)
{"p": project_id, "r": R_ANALYST, "kw": '%bid_tech_analysis%'}) n_redo = await _dim_round_count(sor, project_id, TECH_KIND, redo=True)
if attempts >= MAX_ANALYST_ATTEMPT + th["qc_max_round"]: _over = (n_redo >= th["qc_max_round"]) if qc_imp else (n_first >= MAX_ANALYST_ATTEMPT)
if _over:
ht = await find_human_task(sor, project_id, "general", status="pending") ht = await find_human_task(sor, project_id, "general", status="pending")
if not ht: if not ht:
await create_human_task( await create_human_task(
sor, project_id, "general", sor, project_id, "general",
"技术评估项解析多次未产出,需人工介入:%s" % pname, "技术评估项解析多轮未产出,需人工介入:%s" % pname,
("技术评估项解析任务已执行 %d 次,产出仍为空。\n" ("技术评估项解析已执行 %d 轮(首跑 %d / QC重做 %d),产出仍为空或未过审。\n"
"请检查招标文件技术部分是否可读(bid_tender_files.content_text)," "请检查招标文件技术部分是否可读(bid_tender_files.content_text),"
"或用 list_tech_items/add_tech_item 人工补录评估项。" % attempts), "或用 list_tech_items/add_tech_item 人工补录评估项。"
% (n_first + n_redo, n_first, n_redo)),
assignee_role="owner.superuser") assignee_role="owner.superuser")
await sor.sqlExe("COMMIT", {}) await sor.sqlExe("COMMIT", {})
acts.append("escalated: 技术评估项解析重试超限 → 人工介入") acts.append("escalated: 技术评估项解析轮次超限 → 人工介入")
return False, False, True return False, False, True
tparams = {"stage": "tech_analysis", "task_kind": "bid_tech_analysis", tparams = {"stage": "tech_analysis", "task_kind": "bid_tech_analysis",
"analysis_dim": TECH_KIND, "flow_key": flow_key} "analysis_dim": TECH_KIND, "flow_key": flow_key}
if qc_imp: if qc_imp:
tparams["qc_redo"] = 1 tparams["qc_redo"] = 1
# 大文件机械拆解(2026-09-18 用户要求,四维度 A 块同款机制):正文超阈值
# → 拆 N 段并行子任务,char_range 互不重叠。单体全文抽取实测弊病(甘肃 r1):
# 2.7 小时串行 + agent 自选 offset 步进造成窗口大量重叠,(category,item_name)
# 精确去重对跨窗口近义命名失效 → 733 条注水(QC 6.8 退回,判重复条目/source_ref
# 不可回溯)。不相交区间从源头消除跨窗口重复。
_tlen = await _tender_text_len(sor, project_id)
if _tlen > th["split_chars"]:
_ids = await _create_split_tasks(
sor, project_id, TECH_KIND, "技术评估项抽取", pname, tparams, _tlen,
th["split_chars"], ("redo%d" % (n_redo + 1)) if qc_imp else "run1",
qc_imp=["[tech_items] %s" % qc_imp[:2000]] if qc_imp else None)
await sor.sqlExe("COMMIT", {})
acts.append("created: 技术评估项抽取拆解 %d 个分段子任务(正文 %d 字符%s)"
% (len(_ids), _tlen, ",QC退回重做" if qc_imp else ""))
return False, False, True
if qc_imp:
tparams["qc_improvements"] = ["[tech_items] %s" % qc_imp[:2000]] tparams["qc_improvements"] = ["[tech_items] %s" % qc_imp[:2000]]
tid = await create_role_task( tid = await create_role_task(
sor, project_id, R_ANALYST, sor, project_id, R_ANALYST,