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 放行)→ 资料准备 → 分角色并发编写 (business→bid_biz_writer / technical→bid_writer)→ 章节评审(含篇幅门禁)→ 合成 → 整书评分 → 交付确认。
核心机制:
- 角色任务流转不用 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 生成真实图片(
嵌入,合成 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_kb_docs |
公司投标知识库 | org_id, doc_type(sample_bid 等), doc_name, doc_no, tags, file_path, content_text |
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统一渲染规范)。 - 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)两步,缺一不可:
from pipeline_bidding.init import load_pipeline_bidding
load_pipeline_bidding() # import 后必须再显式调用,漏了无任何日志(静默大坑)
load_pipeline_bidding()(幂等)做三件事:
role_tool_schemas.register_role_tools()— 把约 55 个投标角色工具 schema (read_tender_file / extract_* / add_* / patch_chapters / write_chapter / review_chapter / 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 零变化)。import bid_ability— 主 agent 能力包 + slash 命令注册(import 即注册)。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)根目录或本目录执行一键脚本:
./build.sh
build.sh 职责(运行期禁止 schema 变更,建表只在部署期):
- 建表:读
mysql.ddl.sql(json2ddl 产物),把drop table if exists剔除、CREATE TABLE改为CREATE TABLE IF NOT EXISTS幂等化,用宿主 config 的pipeline库连接执行。 - 种子数据:
init/data.json幂等写入 pipelines 产线记录 + appcodes/appcodes_kv 码表。 - wwwroot 软链:
ln -sf 本仓库/wwwroot → $APP_ROOT/wwwroot/pipeline-bidding。 - 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。
部署注意(实测踩坑)
- 非 editable 安装:测试机只
git pull仓库不会更新 site-packages,改码后必须./py3/bin/pip install pkgs/pipeline-bidding再重启(可对比 site-packages 与仓库文件 md5 确认加载版本)。 - 运行时技能树独立仓库:在
/d/pipeline/pipeline-app/skills/(git: yumoqing/pipeline-sdlc.git),不是 pipeline_core 的 skills_library;改了 bid-workflow 等技能必须同步 SKILL.md 到pipelines/bidding_general/common/<技能名>/再重启。 - 宿主入口漏调用
load_pipeline_bidding()是静默大坑:无任何日志。诊断走api/bid_probe.dspy?action=status心跳探针。 - RBAC 缓存:新路径注册后 redis db0 FLUSHDB 即生效(旧说须重启已过时); 精确 path 匹配,父路径不覆盖子路径。
- 角色任务 failed 必须是对账器阻塞态:否则每 15s 重复造任务(机构未配 LLM 时必现); 测试环境无 LLM 模型时任务 failed 属预期。
- 自动立项的项目不继承模型:不带 default_model,解析链兜底全局默认名可能不存在 → 401;
建后必查/补
sd_projects.default_model。 - bid_chapters 键归一:
resolve_project_id先按 id 校验、按 name 反查归一, 名字歧义拒绝(防「项目名当 project_id」写库产生孤儿骨架)。 - 对账器结构铁律:维度过滤(等上游 QC 的不算掉链)必须在
_open_orch_tasks判定前 无条件执行,编排任务创建/掉链计数留在「无编排任务在办」的 else 分支内, 否则每 15s 重复建编排任务堆积。 - 共享工具
create_task有产线角色校验守卫(get_role_spec 查不到即拒绝),list_roles查看本产线可用角色。
详细设计见 docs/DESIGN.md;技能库见 pipeline-core
skills_library/pipelines/bidding_general/(common 7 技能 + roles 9 角色含 bid_biz_writer)。