pipeline-bidding/README.md

244 lines
19 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pipeline-bidding — 投标产线模块
## 模块定位
产线平台(pipeline-app / pipeline-core)的第二条产线能力包:**投标产线 `bidding_general`**。
验证并落地「产线能力包住自己仓库、宿主不装则零接触」的分层原则——能力模块全部住在本仓库,
不进 `pipeline-service`;宿主只要不 `import pipeline_bidding`,一切都不会发生。
模块 = 数据(`bid_*` 表 + CRUD)+ 能力(`bid_*_capability`)+ 流转(`bid_flow` 状态对账器)
+ 主 agent 能力包(`bid_ability`)+ 角色技能(部署期随 pipeline-core skills_library 分发)。
**产线起点 = 上传招标文件**(`import_tender_file` 无 project_id 时自动立项)。阶段链:
> 收到招标文件(自动立项)→ 解析(评分项/资质/投标文件要求/商务要素/章节骨架)
> → QC 契合度审核(四类逐类,10 分制,>9.5 放行)→ **技术方案部分:模版裁剪链(A3 段,
> 2026-09-17 起)** → 资料准备 → 分角色并发编写
> (business→bid_biz_writer / technical→bid_writer)→ 章节评审(含篇幅门禁)→ 合成
> → 整书评分 → 交付确认。
**A3 段:标准流程技术标模版裁剪链(v1.6,2026-09-17)**:标准投标流程内置与纯技术方案
流程同源的三段链 `tech_analysis(六类技术评估项,对标招标文件技术需求部分)→ tech_qc
(qc_type=tech_items)→ template_confirm(PM 检索模版按评估项裁剪骨架 → 用户确认待办)`。
对账器共用段 `_reconcile_tech_analysis_qc` / `_reconcile_template_confirm`(两条流程逻辑
只此一份,flow_key 写进任务 params 区分来源)。**链激活五条件**:有招标文件 +
tech_analysis/template_confirm 两阶段启用 + 骨架维度 QC 已过 + 存在 technical 章且全部
pending + 无裁剪摘要行(`bid_tech_items kind='summary'`)——任一不满足整链跳过,技术章
直接按招标骨架编写(存量项目零影响)。链激活期间 C 段只挡 technical 章(`_tech_gate_on`),
商务/报价章照常流转;用户确认后 `confirm_tech_template` **标准模式只替换 technical 章**
(新骨架落原技术章 min(order_no) 位置,商务/报价章不动),并落 kind='summary' 裁剪摘要行
作为链完成的持久证据(confirm 落库、新招标文件 reset 清除;不用 done 待办/章节状态判完成
——换代后会锁死新链或复活死循环)。BID_FLOW_STAGES 扩为 14 阶段(+tech_analysis
trim=yes / tech_qc trim=warn / template_confirm trim=warn;裁 tech_analysis 必连带裁
template_confirm)。tech 维度不进 ANALYSIS_DIMS、不走 PM 编排,由对账器直接派发。
**多流程模板(2026-09-15 批2)**:本产线声明两条流程(`BID_FLOW_TEMPLATES`,引擎
FlowTemplate 机制首个接入)——`bid_standard`(上述标准链)与 `bid_tech_proposal`
(**纯技术方案流程**:输入=技术需求书而非招标文件,产出=技术方案书):
> 技术需求书 → 六类技术评估项解析(功能清单/功能点/硬件配置/技术架构/功能架构/部署架构,
> 落 `bid_tech_items`)→ QC 对标需求书(qc_type=tech_items)→ 模版三级检索(知识库→RAG→网络)
> + 骨架拟定(六类覆盖度硬校验,漏类拒绝)+ **用户确认待办**(tech_template_confirm,全局阻塞,
> agent 无确认权)→ 分章编写 → 章节评审(对标需求书覆盖度)→ 合成 → 交付确认。
首文件导入后对账器 LLM 自动判流(`detect_flow_template`,置信度+判据摘录进确认待办);
判流失败发人工待办不默认硬跑。已确认计划 `base_flow_key=bid_tech_proposal` 的项目走
`bid_flow._reconcile_tech_proposal` 独立对账分支,不碰评分项/资质/成本收益。
规范见技能 `bid-tech-proposal`;确认端点 `api/tech_template_confirm.dspy`。
核心机制:
- **角色任务流转不用 RoleSpec.next_role**(全留空):投标是「一章一任务 + 两级回退循环」
扇出结构,由 `bid_flow` 状态对账器(每 15s 一轮)补派,对账器是角色任务的**唯一创建者**。
- **阻塞门禁在创建源头**(对账器判 `pipeline_human_tasks.project_id + pending`),
不写进引擎 poller SQL。`tenant_id=project_id` 只是角色任务约定,禁止写进引擎层。
- **两级回退循环**:章节级(得分率 < `bid_chapter_pass_ratio` 或篇幅不达标 → rejected+意见 →
自动重写,超 `bid_max_revise` 抛人工);整书级(< `bid_pass_ratio` 或否决项未响应 →
按 `target_chapter_id` 打回 → 重写→重审→重合成→重评分,超 `bid_max_round` 抛人工)。
- **QC 门禁**:四类解析产出逐类审核落 `bid_qc_reviews`;不通过 → 清空该类产出 → 对账器重派
解析(`params.qc_redo=1` + 改进意见)→ 重审;轮次超 `bid_qc_max_round` → `qc_escalation`
阻塞人工(逃逸阀:人工修产出后在记录页把 passed 改 1 即生效)。
- **门限参数全部读 appbase `params` 表 + DEFAULT_PARAMS 兜底**,不硬编码:
`bid_chapter_pass_ratio`(0.8) / `bid_pass_ratio`(0.85) / `bid_max_revise`(3) /
`bid_max_round`(3) / `bid_qc_pass_score`(9.5) / `bid_qc_max_round`(3) /
`bid_chapter_min_words`(800) / `bid_chapter_min_words_biz`(500) / `bid_write_concurrency`(4)。
- **章节配图形态硬门禁(2026-09-15 用户要求)**:技术方案/标书中的图必须 invoke_model 调
t2i/i2i 生成真实图片(`![图N 标题](URL)` 嵌入,合成 docx 自动嵌真图),禁 mermaid/plantuml/
ASCII 字符画。`write_chapter` 机制层确定性检测拒写伪图,`review_chapter` 评审兜底同款检测必退
(存量正文/绕行写入);检测器复用 `pipeline_service.diagram_gate`(ImportError 逃逸阀);
豁免钥匙=文中如实标注「配图缺失:平台无可用文生图模型」(仅 t2i/i2i 全无时)。
## 表清单(models/*.json,建表产物 mysql.ddl.sql)
| 表名 | 说明 | 关键字段 |
|---|---|---|
| `bid_tenders` | 招标商务要素 | project_id, source, title, purchaser, tender_no, industry |
| `bid_tender_files` | 招标文件 | project_id, tender_id, file_name, file_path, file_type, content_text |
| `bid_scoring_items` | 评分项与得分规则 | project_id, section, item_no, item_name, max_score, scoring_rule, is_veto |
| `bid_qualifications` | 招标所需资质 | project_id, qual_name, requirement, is_mandatory, owner, kb_doc_id, match_status |
| `bid_doc_requirements` | 投标文件要求 | project_id, req_type, chapter_no, chapter_title, requirement, page_limit, source_ref |
| `bid_chapters` | 投标文件章节 | project_id, chapter_no, parent_no, title, section, outline, status, content, word_count, review_score |
| `bid_reviews` | 标书评审记录 | project_id, scope, chapter_id, document_id, round, reviewer, review_type |
| `bid_scores` | 标书评分明细 | project_id, review_id, scoring_item_id, item_name, score, max_score, reason |
| `bid_documents` | 合成标书 | project_id, version, doc_name, file_path, page_count, chapter_count, total_score |
| `bid_qc_reviews` | 解析产出 QC 审核记录 | project_id, qc_type, round, reviewer, review_type, fit_score, pass_score |
| `bid_analysis_history` | QC 分析历史快照(每轮 QC 逐审核记录归档;2026-09-10 归籍补 models 定义,此前是孤儿表) | project_id, qc_type, round, task_id, fit_score, improvement, payload |
| `bid_cost_benefit` | 成本收益分析(分析维度4,单项+summary 结论行;m0006 建表未进 models,2026-09-10 归籍) | project_id, kind, category, item_name, amount, confidence |
| `bid_tech_items` | 技术评估项(纯技术方案流程与标准流程 A3 裁剪链 tech_analysis 产出;kind=item/summary,summary=骨架裁剪摘要行,category 六类;m0034 建表) | project_id, kind, category, item_name, requirement, mandatory, source_ref |
| `bid_kb_docs` | 公司投标知识库 | org_id, doc_type(sample_bid 等), doc_name, doc_no, tags, file_path, content_text |
| `bid_material_gaps` | 投标材料缺口清单(占位符规范:正文 `【材料待补:…】` 自动登记,合成后出清单,人工/产线补齐;m0035 建表) | project_id, chapter_no, material_name, placeholder_text, fingerprint, status(pending/filled/waived) |
| `bid_members` | 投标项目参与人员 | project_id, user_id, user_name, member_role, duty, status |
种子数据(`init/data.json`,build.sh 幂等写入):`pipelines` 产线主记录 `bidding_general` +
`appcodes` 12 组码表(bid_qc_type / bid_tender_source / bid_member_role / bid_file_type /
bid_scoring_section / bid_qual_match_status / bid_qual_owner / bid_req_type /
bid_chapter_status / bid_review_scope / bid_kb_doc_type / bid_doc_status)。
## 对外 API / dspy 端点(wwwroot/)
部署后挂载在宿主 `wwwroot/pipeline-bidding/`(软链):
- **api/bid_probe.dspy** — 服务内探针(诊断)。`?action=status` 返回模块装载状态 +
poller 心跳 + 活跃投标项目数;`?action=reconcile` 在服务进程内跑一轮全量对账返回动作明细。
- **api/bid_task_tree.dspy** — 任务树懒加载(根 → role:阶段 → 任务 → step:/del: 叶子)。
项目解析三级兜底:session_settings(default_bidding_general) → agent_settings →
最近 in_progress 项目。
- **api/bid_task_io.dspy** — 任务树右侧详情面板:输入 = 任务描述 + QC 改进意见,
执行步骤,输出 = 交付件(复用 `pipeline_service/task_io_render.py` 统一渲染规范)。
- **api/tech_template_confirm.dspy** — 技术方案骨架用户确认/驳回端点(2026-09-15;
2026-09-17 双模式分流)。入参 human_task_id/decision(confirm|reject)/comment(驳回必填)。
用户专属:同机构+超管/创建者校验复用引擎 `_check_confirm_operator`;agent 无对应工具无法代确认。
确认后骨架落 bid_chapters(section=technical) 开写——**标准流程(项目存在非 technical 章)
只替换技术章并落 kind='summary' 裁剪摘要行,商务/报价章原样保留;纯技术方案流程整表清落**;
驳回意见由对账器注入重派模版任务。
- **api/bid_delivery_reject.dspy** — 交付退回端点(2026-09-17,用户报障「交付确认待办只有
确认没有退回」后补)。入参 human_task_id/comment(退回意见必填)。用户专属:权限校验复用
`_check_confirm_operator`;agent 无此工具(退回是用户裁决)。机制 `reject_delivery`:
待办置 rejected+意见存 result_data → 最新 passed 标书置 rejected → 派 scorer
`bid_delivery_reject` 任务(params.reject_comment)→ scorer 按意见 request_chapter_revise
打回问题章节 → 既有链路自动重写→重审→重合成→重评分→重发交付确认;approved 章节
revise_count 复位(用户级退回=新一轮修订预算)。对账器 E/T6 段配套门禁:驳回处理任务
在办期间不派合成(防章节还没被打回就抢先重合成同内容新版,静默绕过用户意见)。
- **bid_task_tree_popup.ui** — ResourceBrowser 复合 widget 弹窗(左树右详情联动)。
- **agent/index.ui** — 驾驶舱入口页(顶栏含任务树按钮)。
- **12 个 CRUD 目录**(bid_tenders / bid_tender_files / bid_scoring_items /
bid_qualifications / bid_doc_requirements / bid_chapters / bid_reviews / bid_scores /
bid_documents / bid_qc_reviews / bid_kb_docs / bid_members)— 每个含
index.ui + get/add/update/delete_*.dspy 标准五件套。
⚠️ RBAC 铁律:`/api/` 前缀不覆盖具体 dspy,**新 API 必须逐条显式注册**(scripts/load_path.py),
否则 403;注册后 redis db0 FLUSHDB 即生效。
## 流程修订 / 定向重做(2026-09-17,引擎通用机制 + 投标钩子)
用户裁定:**用户有权调整流程,并要求从流程的任何一步按照意见重做;或者干脆只给意见,
由 LLM 判定从哪个节点注入**。
- 引擎层(pipeline-service flow_plan_capability,产线无关):
- `revise_flow_plan`:对已 confirmed 计划提修订版(v+1 → pending_confirm;确认前旧计划
持续生效无空窗;确认时 CAS 换版旧版 superseded + 回调产线钩子执行阶段重置)。
可改裁剪(trim_keys)和/或定向重做(reentry_stage)。
- `detect_reentry_stage`:用户只给意见时 LLM 判注入点(意见+阶段声明+产线进度摘要 →
stage_key+confidence+evidence;对齐 detect_flow_template 判流先例;evidence 进确认
待办由用户核对,错判由确认门兜住;判不出诚实报错禁默认)。
- `_affected_stage_keys`:注入阶段+deps 依赖闭包下游。
- 会话工具 revise_flow_plan/detect_reentry_stage 进 FLOW_PLAN_TOOLS → shared_ability
→ 三产线会话 agent 零改动继承。propose_flow_plan 遇已确认计划时的错误指引改指 revise。
- pipeline_flow_plans 加列 reentry_stage/reentry_detect_note(m0039)。
- 投标钩子(`pipeline_bidding/bid_stage_reentry.py`,经 PipelineAbility 三钩子字段注册):
- `bid_stage_reentry`:按注入阶段重置——分析维度清产出表+QC 记录(reqs_outline 连带毁
章节,确认待办如实预告);qc_analysis 单独注入只清五类 QC 记录(产出保留自动重派复审);
**tech 链重置(补技术方案场景)**=清 bid_tech_items 含 summary+tech QC 记录+技术章
**置 pending 保正文**(⚠️ 删章则 A3 链激活条件 bool(_tech_chs) 不成立永不激活;正文
保留至模版确认时被新骨架整体替换)+作废模版确认待办+技术章在办编写任务清场
(LIKE 走 ${kw}$ 参数绑定防 % 格式化坑)+标书 rejected+交付待办 cancelled →
A3 裁剪链重新激活:抽取→tech QC→PM 模版裁剪→用户确认骨架→技术章重写;商务/报价章
零接触;chapter_write 清正文全复位/chapter_review 复位 written 保正文;compose/score/
delivery 标书+待办作废。统一收尾:affected 阶段角色在办任务取消(角色集运行时从阶段
声明取)+材料缺口 pending 记录 waived 防孤儿+审计落库。
- `bid_reentry_describe`:影响说明(破坏性预告进确认待办,用户确认前看得到将清什么)。
- `bid_progress_summary`:进度摘要(章节分布/标书版本/tech_items 含 summary 判定/
QC 记录/在办待办——detect 的 LLM 上下文)。
- 钩子失败不回滚计划确认(换版是用户裁决已生效)——诚实上报+抛人工待办。
- E2E 实测(2026-09-18 测试机):合成项目防火墙全链断言过(v2 pending→confirm→v1
superseded/钩子九项效果/审计);甘肃真实项目 detect 判 tech_analysis(high 置信度,
依据=用户意见+进度「技术评估项0行、裁剪链未完成」)→修订确认待办已发。
## load 注册函数
宿主入口(pipeline_app.py)两步,缺一不可:
```python
from pipeline_bidding.init import load_pipeline_bidding
load_pipeline_bidding() # import 后必须再显式调用,漏了无任何日志(静默大坑)
```
`load_pipeline_bidding()`(幂等)做三件事:
1. `role_tool_schemas.register_role_tools()` — 把约 60 个投标角色工具 schema
(read_tender_file / extract_* / add_* / list_cost_benefit / update_cost_benefit_item /
delete_cost_benefit_item / patch_chapters / write_chapter(支持 mode=append 分批追加超长章节)/
review_chapter(支持 material_missing=1 实体材料缺失如实披露放行)/ start_qc / finish_qc /
compose_bid / start_bid_score /
finalize_bid_score / dispatch_analysis_dim 等)注册进引擎
`pipeline_service.capability_tools.TOOL_SCHEMAS`(引擎 `exec_capability_tool` 的
`module` 支持全路径,裸名仍走 `pipeline_service.*`,SDLC 零变化)。
2. `import bid_ability` — 主 agent 能力包 + slash 命令注册(import 即注册)。
3. `bid_flow.start_poller()` — 状态对账器启动(`PIPELINE_MODE=web` 时不注册,
与引擎 poller 同款模式开关)。
角色工具装配链:角色技能 frontmatter `capability+tools` → `_capability_to_tools_map`
→ `TOOL_SCHEMAS` → **两者交集才是可见工具**(技能声明了但 schema 没注册 = 工具静默缺失)。
独立脚本验证必须先显式调 `load_pipeline_bidding()`,仅 import 包不触发注册。
码表导入自持:`scripts/import_init_bidding.py`(幂等 + 废弃清理 + 产线描述),
不依赖宿主 `import_init.py` 的 INIT_MODULES。
## 宿主集成(部署)
在宿主应用(pipeline-app)根目录或本目录执行一键脚本:
```bash
./build.sh
```
build.sh 职责(运行期禁止 schema 变更,建表只在部署期):
1. **建表**:读 `mysql.ddl.sql`(json2ddl 产物),把 `drop table if exists` 剔除、
`CREATE TABLE` 改为 `CREATE TABLE IF NOT EXISTS` 幂等化,用宿主 config 的
`pipeline` 库连接执行。
2. **种子数据**:`init/data.json` 幂等写入 pipelines 产线记录 + appcodes/appcodes_kv 码表。
3. **wwwroot 软链**:`ln -sf 本仓库/wwwroot → $APP_ROOT/wwwroot/pipeline-bidding`。
4. **pip install**:`$APP_ROOT/py3/bin/pip install 本目录`(非 editable 安装)。
宿主负责的后续:app 入口 import + 调用 `load_pipeline_bidding()`;
RBAC 用 `scripts/load_path.py`(其中 set_role_perm.py 必须用宿主根**绝对路径**);
i18n 合并用 merge_i18n.py。
## 部署注意(实测踩坑)
1. **非 editable 安装**:测试机只 `git pull` 仓库不会更新 site-packages,改码后必须
`./py3/bin/pip install pkgs/pipeline-bidding` 再重启(可对比 site-packages 与仓库文件
md5 确认加载版本)。
2. **运行时技能树独立仓库**:在 `/d/pipeline/pipeline-app/skills/`(git:
yumoqing/pipeline-sdlc.git),不是 pipeline_core 的 skills_library;改了 bid-workflow
等技能必须同步 SKILL.md 到 `pipelines/bidding_general/common/<技能名>/` 再重启。
3. **宿主入口漏调用 `load_pipeline_bidding()` 是静默大坑**:无任何日志。诊断走
`api/bid_probe.dspy?action=status` 心跳探针。
4. **RBAC 缓存**:新路径注册后 redis db0 FLUSHDB 即生效(旧说须重启已过时);
精确 path 匹配,父路径不覆盖子路径。
5. **角色任务 failed 必须是对账器阻塞态**:否则每 15s 重复造任务(机构未配 LLM 时必现);
测试环境无 LLM 模型时任务 failed 属预期。
6. **自动立项的项目不继承模型**:不带 default_model,解析链兜底全局默认名可能不存在 → 401;
建后必查/补 `sd_projects.default_model`。
7. **bid_chapters 键归一**:`resolve_project_id` 先按 id 校验、按 name 反查归一,
名字歧义拒绝(防「项目名当 project_id」写库产生孤儿骨架)。
8. 对账器结构铁律:维度过滤(等上游 QC 的不算掉链)必须在 `_open_orch_tasks` 判定前
无条件执行,编排任务创建/掉链计数留在「无编排任务在办」的 else 分支内,
否则每 15s 重复建编排任务堆积。
9. 共享工具 `create_task` 有产线角色校验守卫(get_role_spec 查不到即拒绝),
`list_roles` 查看本产线可用角色。
详细设计见 `docs/DESIGN.md`;技能库见 pipeline-core
`skills_library/pipelines/bidding_general/`(common 7 技能 + roles 9 角色含 bid_biz_writer)。