pipeline-bidding/README.md
yumoqing 2fe7c784be feat(a3): 标准流程技术标模版裁剪链——bid_standard内置tech_analysis→tech_qc→template_confirm三段(与纯技术方案流程共用对账段,逻辑只此一份)
- bid_flow A3段: 链激活五条件(有招标文件+两阶段启用+骨架QC已过+technical章全pending+无kind='summary'摘要行),任一不满足整链跳过=存量行为(甘肃等已开写项目零影响);完成判定用摘要行(换代闭环:新招标文件reset清bid_tech_items+作废pending确认待办)
- C段门禁_tech_gate_on: 链激活只挡technical章,商务/报价章照常流转(HT_TEMPLATE_CONFIRM移出BLOCKING_HT_TYPES降级章节级)
- confirm_tech_template双模式分流: 有非technical章=标准流程只删建technical章+全序列重排order_no(走查修复:原min+idx顺延在新骨架章数多于原技术章时与后置商务/报价章撞号,合成ORDER BY会把报价章插进技术章中间);无=纯技术流程整表清落;重确认守卫收窄到待替换范围
- record_tech_summary: 裁剪摘要落bid_tech_items kind='summary'(六类覆盖+模版来源+章数),幂等覆盖,失败不阻塞确认
- _reset_analysis_for_new_file: bid_tech_items入清空表清单+作废pending骨架确认待办
- chapter_detail: technical章注入tech_summary+list_tech_items提示(老库无表静默降级)
- BID_FLOW_STAGES 14阶段: +tech_analysis(yes)/tech_qc(warn)/template_confirm(warn,dep=tech_analysis,裁头段必连带裁确认段)
- bid_task_tree: analyst加tech维度组/QC加tech_items组/DIM_QCS补tech
- README+DESIGN v1.6同步(3.7节A3设计+门禁表tech_template_confirm行)
2026-09-17 16:23:54 +08:00

201 lines
15 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' 裁剪摘要行,商务/报价章原样保留;纯技术方案流程整表清落**;
驳回意见由对账器注入重派模版任务。
- **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 即生效。
## 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)。