pipeline-service/README.md

187 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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_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.pym0002 建表未进 models2026-09-10 归籍) |
| pipeline_agent_settings | 用户 agent 偏好(每用户一行 uk_user默认模型/当前项目指针;写入方 agent_loop_v2、set_agent_model.dspy、workspace.py2026-09-10 归籍) |
| pipeline_project_agents | 项目角色 agent 配置(驾驶舱 v1 start_agent 链路v2 已走 pipeline_agent_instances保留至 v1 链路删除决议2026-09-10 归籍) |
## 安装
```bash
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-057 层纵深防御)
规范通用会话generic=Trueagent_chat_generic.dspy= 纯通用 agent
不挂任何产线工具/技能/角色/记忆,不得看到或操作任何产线的项目。
隔离层(缺一层即泄漏,详见技能 cockpit-agent-patterns
1. **工具注册表**ToolRegistry 每 executor 独立实例(禁全局单例,防跨会话残留)
2. **默认产线回退**generic 会话 pipeline_id 空不回退 DEFAULT_ABILITY_ID
3. **产线能力包**_execute_ability_tool 对 generic 直接拒绝
4. **工具 schema**load_agent_configpipeline-coregeneric 时剔除 project/shell 类工具
5. **执行层兜底**_dispatch_sdlc_tool 对 generic 拒绝项目管理工具(别名归一后拦截)
6. **工作空间**generic 会话文件根 = `_general/{user_id}` 专属目录generic_workspace_dir
_resolve_ws_path 越界保护圈死run_command 剔除shell 无法圈禁);
resolve_upload_dirpipeline-coregeneric=True 上传落同一专属目录
7. **历史回放**_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 URLllm_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 自觉):
1. **git 收口引擎化**38efb3edeliver 时引擎代为 add+commit 本任务写入的
apps/modules 仓库,收口核验记录(改了哪些仓库/几个文件/commit hash回填交付件正文——
agent 无法再自称「已提交」伪造 git 证据。
2. **代码空壳硬门禁 `_validate_stub_code`**0d02ac6deliver 时本任务 write_file
实写的每个 `.py` 必须 ast 可解析且有真实可执行语句(纯注释/pass/纯 docstring/
语法错误一律拒绝,`__init__.py`/`conftest.py` 豁免),`.json` 必须可解析;
`files` 参数先验后写(`_validate_code_files_params`),防空壳落盘后被收口变既成事实。
拒绝时回填可行动 FAIL指出文件+原因agent 当轮重写——空壳死在 deliver 入口,
不再烧 QC 轮次。native + 文本兜底两条 deliver 路径都挂载。
3. **产出文件机械核验段**:引擎自动计算每个代码产出文件的实测字节/行数/语法/语句数,
以「产出文件机械核验(引擎自动计算,非 agent 声明)」标题回填交付件正文——
「声称 17141 实测 1225」类矛盾一眼可见QC 拿到引擎级证据底座,
不再依赖自己 run_shell 逐个取证(取证失败盲判 0 分退也在烧重试预算,实测 9 轮里 3 轮是盲审)。
配套技能pipeline-core 7bfa8d6develop 角色「代码真实性铁律」write_file 必须完整正文、
大文件拆小分批、声称数字须与引擎核验一致、勿自称 commit/pushQC 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 native09-16 QC 复发实锤)。
**统一架构(全平台只有两处单点)**
1. `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 也结构性安全。
2. `pipeline-llm inference._post_upstream` deadlineattempt 超时=剩余预算,
总时长≈预算(消灭 3× 放大→客户端先断连→模糊 TimeoutError→误判永久错误链
预算耗尽(<15s)不再发起必然超时 attempt端点配置 timeout 不再掐断 attempt
仅直连未传 _timeout 时与 _TOTAL_TIMEOUT 取大兜底)。
**三层预算对齐铁律**调用方总预算 T payload._timeout=T 客户端=T+60
外层 wait_for若有)≥ T+60agent_loop510570600 实证对齐
验证特征串inference 日志预算=Ns」llm_call_trace status_code=0 计数
新增 LLM 调用点只需可选声明总预算禁止自算客户端等待