pipeline_service
产线执行引擎 —— 任务调度、步骤执行、人工任务交互、LLM 桥接。
功能
- 任务执行:DAG 步骤调度与状态机
- 人工任务:审批/输入等待与交互
- LLM 桥接:统一 LLM 调用接口
- Agent Loop:AI Agent 多轮任务执行
- 意图分类:自然语言意图识别
- 产物管理:步骤输入输出存储
数据表
| 表 | 说明 |
|---|---|
| pipeline_tasks | 任务实例 |
| pipeline_task_steps | 步骤执行记录 |
| pipeline_artifacts | 步骤产物 |
| pipeline_human_tasks | 人工任务 |
| pipeline_step_types | 步骤类型注册 |
| pipeline_session_settings | 会话设置(多 tab 项目上下文) |
| pipeline_conversations | 平台会话消息(回放缓存;2026-09-10 归籍本模块——唯一写入方 agent_loop_v2._save_turn,此前是无 models 定义的孤儿表) |
| pipeline_agent_instances | 角色 agent 实例注册(项目×角色→稳定实例 id;唯一写入方 agent_instance.py;m0002 建表未进 models,2026-09-10 归籍) |
| pipeline_agent_settings | 用户 agent 偏好(每用户一行 uk_user:默认模型/当前项目指针;写入方 agent_loop_v2、set_agent_model.dspy、workspace.py;2026-09-10 归籍) |
| pipeline_project_agents | 项目角色 agent 配置(驾驶舱 v1 start_agent 链路;v2 已走 pipeline_agent_instances,保留至 v1 链路删除决议;2026-09-10 归籍) |
安装
cd pkgs/pipeline-service && pip install .
核心模块
| 文件 | 职责 |
|---|---|
executor.py |
任务/步骤调度引擎 |
storage.py |
数据库读写 |
llm_bridge.py |
LLM API 调用 |
agent_loop.py |
AI Agent 多轮执行 |
human.py |
人工任务处理 |
intent_classifier.py |
意图识别 |
state.py |
状态机 |
step_registry.py |
步骤类型注册表 |
agent_loop_v2.py |
Agent Executor v2(会话 agent 执行引擎) |
workspace.py |
项目/通用会话工作空间目录管理 |
sdlc_ability.py |
SDLC 产线能力包(工具定义 + handler) |
skill_live.py |
技能实时发布/管理(publish_skill_live + manage_skill_live 多租户隔离) |
bg_jobs.py |
run_command 后台任务(状态文件化 .bg/,跨 worker 可见) |
subagents.py |
后台并行子 agent 委派(.sub/,spawn/steer/stop/result) |
diagram_gate.py |
文档配图形态硬门禁(2026-09-15):交付文档/标书章节中的图必须 invoke_model t2i/i2i 真图,确定性检测 mermaid/plantuml/ASCII 伪图;deliver 双路径拦截 + pipeline-bidding write/review_chapter 复用;豁免=文中如实标注「配图缺失:平台无可用文生图模型」 |
通用会话产线隔离(2026-09-05,7 层纵深防御)
规范:通用会话(generic=True,agent_chat_generic.dspy)= 纯通用 agent, 不挂任何产线工具/技能/角色/记忆,不得看到或操作任何产线的项目。
隔离层(缺一层即泄漏,详见技能 cockpit-agent-patterns):
- 工具注册表:ToolRegistry 每 executor 独立实例(禁全局单例,防跨会话残留)
- 默认产线回退:generic 会话 pipeline_id 空不回退 DEFAULT_ABILITY_ID
- 产线能力包:_execute_ability_tool 对 generic 直接拒绝
- 工具 schema:load_agent_config(pipeline-core)generic 时剔除 project/shell 类工具
- 执行层兜底:_dispatch_sdlc_tool 对 generic 拒绝项目管理工具(别名归一后拦截)
- 工作空间:generic 会话文件根 =
_general/{user_id}专属目录(generic_workspace_dir), _resolve_ws_path 越界保护圈死;run_command 剔除(shell 无法圈禁); resolve_upload_dir(pipeline-core)generic=True 上传落同一专属目录 - 历史回放:_load_history / _t_session_search 对 generic 只读写
pipeline_id='' OR IS NULL的自身历史(与存库端 store_pl='' 对称), 禁止按 created_by 全量回放(会带出用户各产线对话中的项目信息)
Hermes 能力对齐:写入侧 + 运行时侧(2026-09-10)
四个 handler 在 agent_loop_v2.py,工具定义在 pipeline-core agent_config.py
(GENERAL_TOOLS)。全部经测试机服务端直调 31/31 + HTTP 端到端 5/5 实测通过。
1. memory 工具(持久记忆写入,_t_memory)
- 写入门禁(代码强制,多机构多用户):scope 白名单 user/project/pipeline, global/org 种子域禁写;org_id/user_id 强制注入会话真实身份(忽略 LLM 传值); 无 user_id(无人值守)拒写;remove 只能删本人条目(org+user 双过滤)。
- 记忆注入改可见性版(
_build_system_prompt传 org_id/user_id → store.visible_to 过滤),修了旧「按域叠加」会带出他机构归属记忆的隐患。
2. manage_skill 工具(技能增改删,skill_live.manage_skill_live)
- 五动作 create/patch/write_file/remove_file/delete,隔离全在 skill_live: 只落本租户 orgs/{org}(org≠0)或 users/{uid}(org 0 降级/个人);
- fork-on-write:改 global 原版先整目录继承拷贝到本租户再改(同名覆盖仅本租户 生效);校验通过才 fork(旧「先 fork 后校验」失败会留副本遮蔽原版,已修);
- org+user 双副本同步改(user 遮蔽 org,防改了被遮蔽那份);产线/角色/项目层拒改; delete 只删本租户副本(global 删不到,删后重新可见);子文件限四目录 + realpath 双检。
3. run_command background + process 工具(bg_jobs.py,_t_run_command/_t_process)
- 状态文件化 workspace/.bg/{job_id}/(meta.json+output.log),跨 worker/进程可见;
- 沙箱档位与前台一致(generic 强制 strict bwrap,无 bwrap 拒绝,绝不降级裸 shell);
- start_new_session 独立进程组 + 超时 SIGTERM 整组(不留孤儿);心跳 stale 判活。
4. delegate_subtask background + subagent 工具(subagents.py,_t_delegate_subtask/_t_subagent)
- 后台并行(同 workspace 上限 3),状态文件化 .sub/{sid}/;深度限制 1(子禁再委派);
- 子会话 session_isolation='none':不读父历史、不写 pipeline_conversations(防污染回放);
- steer/stop 走文件传递,子 agent 每轮 tool-loop 边界
subagents.heartbeat消费; result.txt 流式落盘,stop/崩溃即部分结果(绝不空手)。
第二批 Hermes 能力对齐:五级工具作用域 + patch_file + 原生视觉(2026-09-10)
五级作用域工具解析接线(_resolve_tool_scopes,解析逻辑在 core tool_sources.py)
_init_components第 8 步:调 core 五级解析(global/org/pipeline/role/project), registry 重建为解析后工具集;capability 工具(技能 frontmatter 声明)合成 ToolDefinition 注册,执行路由capability_tools.exec_capability_tool。- 执行层门禁(
_execute_tool第 0 步):_allowed_tools非 None 时,作用域外 工具名(幻觉/越权)直接拒绝并回可行动提示(列出本会话可用工具)。解析失败 降级_allowed_tools=None不拦截(回退旧行为,不阻断会话)。 - 隔离走查修复(capability 两层语义):global 层 all/ 概念技能的 capability 声明 不作为注入依据(只当映射手册),否则 generic 会话拿到 ~70 个产线工具击穿 七层隔离;注入只认 org/pipeline/role/project/user 层技能的声明。
patch_file 工具(_t_patch_file)
old_string 唯一性校验(0/多次拒绝,replace_all 放行)+ 原子写 + _resolve_ws_path
越界防护 + UTF-8 校验(二进制拒绝)。
原生视觉(多模态消息链)
run(user_input, image_parts):image_parts 非空时首条用户消息构造 OpenAI 多模态 content 数组(text + image_url data URL);llm_bridge→llm_v1 端点→inference 原样透传。_call_llm降级兜底:含图调用失败 →_degrade_images_if_needed剥离图片就地改写 消息(文本 + 系统说明「如实告知用户当前模型看不了图,建议切视觉模型或 invoke_model i2t」)重试一次——绝不假装看过图。只降级一次。_estimate_tokens/_summarize兼容 list content(_content_as_text归一,图片按 1000 token 估)。gateway.run_message(image_paths):build_image_parts 构造 + 超限 notes 注入 content。- 实测:中性文件名红蓝两图,agent 正确答「第一张红色、第二张蓝色」,degrade 0 次。
deliver 代码真实性三重门禁(2026-09-16 pbls M1a 空壳事故根治)
事故形状:develop agent 声称「已用 write_file 落盘 17141 字符、G1~G6 严格退出码门禁」, 实测交付的 verify_gate.py 只有 9 行纯注释 1225 字节——LLM 把「对文件的描述」当文件内容写入; 且 git 证据由 agent 自称(实为引擎代收口)。QC 抓到时代价已是 4 轮烧光 + fault + pause。
三层确定性门禁(全部引擎层,不靠 LLM 自觉):
- git 收口引擎化(38efb3e):deliver 时引擎代为 add+commit 本任务写入的 apps/modules 仓库,收口核验记录(改了哪些仓库/几个文件/commit hash)回填交付件正文—— agent 无法再自称「已提交」伪造 git 证据。
- 代码空壳硬门禁
_validate_stub_code(0d02ac6):deliver 时本任务 write_file 实写的每个.py必须 ast 可解析且有真实可执行语句(纯注释/pass/纯 docstring/ 语法错误一律拒绝,__init__.py/conftest.py豁免),.json必须可解析;files参数先验后写(_validate_code_files_params),防空壳落盘后被收口变既成事实。 拒绝时回填可行动 FAIL(指出文件+原因),agent 当轮重写——空壳死在 deliver 入口, 不再烧 QC 轮次。native + 文本兜底两条 deliver 路径都挂载。 - 产出文件机械核验段:引擎自动计算每个代码产出文件的实测字节/行数/语法/语句数, 以「产出文件机械核验(引擎自动计算,非 agent 声明)」标题回填交付件正文—— 「声称 17141 实测 1225」类矛盾一眼可见,QC 拿到引擎级证据底座, 不再依赖自己 run_shell 逐个取证(取证失败盲判 0 分退也在烧重试预算,实测 9 轮里 3 轮是盲审)。
配套技能(pipeline-core 7bfa8d6):develop 角色「代码真实性铁律」(write_file 必须完整正文、 大文件拆小分批、声称数字须与引擎核验一致、勿自称 commit/push);QC review-develop 「优先读引擎回填两段核验当权威证据;声称 vs 实测矛盾直接按造假退;取证失败禁盲判 0 分, 改 read_file 逐项或 ask_question 冒泡」。
通用规则:质量门禁的判定证据必须来自引擎机械计算(字节/行数/ast/commit hash), 不能来自 agent 的自我声明;QC 尺子要配「取证失败 ≠ 判 0 分」的出路,否则盲审烧轮次。
LLM 超时预算统一治理(2026-09-16 用户裁定:单点收敛,禁散落)
事故背景:09-05 提取、09-14 develop、09-16 QC 三次事故同一根因——调用方忘传 timeout → payload 无 _timeout → inference 按供应商端点配置(百炼 120s)掐断长生成; 且超时公式散落在各产线各角色调用点(570/330/+60/-60/min+60 各自为政),按角色 逐个修必然复发(09-14 只修 develop native,09-16 QC 复发实锤)。
统一架构(全平台只有两处单点):
llm_bridge._resolve_budget(timeout, default):timeout 语义=一次调用【总预算】 (0=平台缺省 chat 300 / 生成类 900;封顶 900 下限 60)→ 返回 (payload._timeout=总预算, 客户端 aiohttp=总预算+60)。四个公开入口 (llm_call/llm_call_msgs/llm_call_msgs_native/llm_infer)全走它, payload._timeout 恒传——忘传 timeout 也结构性安全。pipeline-llm inference._post_upstreamdeadline:attempt 超时=剩余预算, 总时长≈预算(消灭 3× 放大→客户端先断连→模糊 TimeoutError→误判永久错误链); 预算耗尽(<15s)不再发起必然超时 attempt;端点配置 timeout 不再掐断 attempt (仅直连未传 _timeout 时与 _TOTAL_TIMEOUT 取大兜底)。
三层预算对齐铁律:调用方总预算 T → payload._timeout=T → 客户端=T+60 → 外层 wait_for(若有)≥ T+60。agent_loop:510→570→600 实证对齐。 验证特征串:inference 日志「预算=Ns」;llm_call_trace status_code=0 计数。 新增 LLM 调用点只需(可选)声明总预算,禁止自算客户端等待。