工作流运行时: - 状态机与编排:modules/workflow_state_manager.py + server/workflow_flow.py (激活快照/阶段推进/审核节点/分支决策/柔性通知/max_stage_rounds 撞限询问) - 五个工具(activate/report_stage/choose_branch/get_status/deactivate) 与 REST API(server/workflow_runtime_api.py) - 前端:QuickDock 工作流窗口(三段式进度,推进/驳回/完成/退出动画)、 slash 菜单激活与退出、轮询事件消费、进入对话状态回填 - 审核:modules/workflow_review_agent.py(pass/reject 把关节点) 审核智能体统一配置: - 个人空间新增「审核智能体」标签页:自动审批/目标/工作流三个审核智能体 统一选择模型+思考模式+超时/轮次参数 - modules/review_agent_config.py 统一解析(复用子智能体模型库), 废除独立 json 配置(auto_approval/goal_review/workflow_review) - goal 审核接入 max_rounds 上限(原常量未接线);workflow 审核硬编码 6 轮改为可配 联调修复: - /new 空对话激活:后端自动创建对话并完整继承模式参数 (work_mode/permission/execution/reasoning_effort,修复思考模式丢失) - 激活/通知消息 starts_work=True,恢复智能体回复头部与工作计时 - 节点目录改为从开始节点拓扑遍历(修复按保存顺序显示错乱) - QuickDock 乐观掩码不再掩盖工作流实时状态(修复 /new 激活窗口瞬关+延迟瞬开); /new 路由不套用全局内容缓存(修复空对话展开空白数秒后收回) - 工作流完成先广播完成态快照再摘牌,窗口播完落定+退出动画再收起 - 激活提示中的工具名修正为 report_workflow_stage
261 lines
18 KiB
Markdown
261 lines
18 KiB
Markdown
# 工作流(Workflow)功能设计与实施方案
|
||
|
||
> 状态:**设计定稿(2026-08-21)**。工作流库页与可视化编辑器已实现;本文档是运行时实施的最终依据。
|
||
> 调研依据:`workflow_research/` 三份报告(外部产品 / 范式要素 / 代码挂载点)。
|
||
> 历史说明:本文档前身是 2026-08-18 的讨论稿;当时「审核作为阶段属性」「兜底提醒轮询」「整体结束审核」等设想已在定稿中推翻,以本文档为准。
|
||
|
||
## 1. 功能定义与核心理念
|
||
|
||
用户把「一套既定流程:工作方式 → 验证方式 → 结束方式」存为一个**工作流**(workflow),之后在任何普通对话中激活它,让主智能体复用现有循环与执行引擎按流程推进;阶段间可由审核智能体软审核。
|
||
|
||
三条核心理念(定稿):
|
||
|
||
1. **结构在边上,自主在节点内**——画布拓扑(节点 + 路由)是结构约束;阶段内主智能体完全自主(自由调工具、跑脚本)。外部调研(LangGraph / Dify / CrewAI / n8n)一致验证的范式。
|
||
2. **工作流是智能体的辅助流程,不是宿主**——任何工作流异常/终态都不得掐断智能体工作。所有退出/失败/超限统一为**柔性通知**:摘牌(改状态)+ 一条 user 消息,处置权交回模型与用户。
|
||
3. **对话级持续状态**——工作流状态不随单次任务结束、模型停止输出、用户按停止按钮而结束。模型停下后用户可自由穿插讨论,说「继续」即接着推进。
|
||
|
||
## 2. 已拍板决策(定稿汇总)
|
||
|
||
| 决策点 | 结论 |
|
||
|---|---|
|
||
| 运行方式 | 不做独立模式/特殊 URL/新对话类型;在普通对话中激活 |
|
||
| 激活入口 | slash 菜单人工激活(仅智能体空闲可用)+ AI 工具 `activate_workflow` 自主激活 |
|
||
| 同时激活数 | **一个对话同时只有一个工作流处于激活**;重复激活同工作流幂等返回进度,激活另一个被拒绝 |
|
||
| 审核建模 | **独立 review 节点**(菱形,一等公民),非阶段属性;要结束审核就在 end 前显式放 review 节点 |
|
||
| 结束节点语义 | end 即终点,汇报到 end 即 completed;**无隐藏的整体结束审核** |
|
||
| 分支节点语义 | 单出线 = 并线器(自动穿过,无需决策);多出线 = AI 决策点(等 `choose_workflow_branch`) |
|
||
| 审核异常 | 视为驳回(计入 reject_counts),工具返回中含「请告知用户」 |
|
||
| maxRejects 撞限 | 工作流 failed;经工具返回告知(模型已在场),不另发 user 消息 |
|
||
| max_stage_rounds 撞限 | **不停工作流、不停智能体**;注入 user 消息让模型立刻停下、告知用户、询问是否继续 |
|
||
| deactivate(用户/模型/撞限) | 一律柔性通知退出,见 §4.7 |
|
||
| 兜底提醒轮询 | **取消**。模型停止输出即停止,工作流挂起不断,期间可与用户讨论 |
|
||
| 停止任务联动 | **不做**(不仿 stop_goal_user_cancel) |
|
||
| 版本快照 | 激活时把 WORKFLOW.md 复制进状态目录;运行期一切读取只读快照,库文件被改不影响运行中实例 |
|
||
| 前后端通信 | 轮询(复用 task 轮询 session_data 快照链路)+ 统一通知池轮询器(user 消息) |
|
||
| 状态隔离 | 对话级状态目录,多对话互不惊扰 |
|
||
| 硬校验 | 不做步骤级硬校验;保存时做结构校验(对齐前后端 validate) |
|
||
|
||
## 3. 数据模型与存储(已实现)
|
||
|
||
### 3.1 节点模型(对齐 `static/src/components/workflow/workflowModel.ts`)
|
||
|
||
五种节点,串行边界(无并行语义):
|
||
|
||
| kind | 形态 | 语义 |
|
||
|---|---|---|
|
||
| `start` | 胶囊 | 入口,右 1 出,恰好 1 个 |
|
||
| `end` | 胶囊 | 终点,左 n 入,至少 1 个 |
|
||
| `stage` | 矩形 | AI 执行阶段:`goal` + `instructions` + `next`(单值,显式连接) |
|
||
| `review` | 菱形 | 审核把关:`prompt`(审核关注点)+ `next`(通过路由)+ `rejectTo`(驳回路由)+ `maxRejects`(连续驳回上限,超限 failed) |
|
||
| `branch` | 虚线矩形 | 分线/并线器:`next[]` 路由数组,每条带 `condition`(自然语言,AI 决策依据;多出线必填) |
|
||
|
||
所有路径显式化(`next` 为 null 校验不通过);`position` 仅画布坐标,与执行解耦。
|
||
|
||
### 3.2 工作流库(已实现)
|
||
|
||
- WORKFLOW.md = YAML frontmatter(上述结构)+ markdown 正文(工作方式/验证方式/结束方式)
|
||
- 双源合并:源码树 `workflows/`(内置种子)+ 运行态用户库(host:`~/.astrion/astrion/host/workflows/`)
|
||
- 模块:`modules/workflow_manager.py`(CRUD + 校验 + camelCase↔snake_case)
|
||
- REST:`server/workflow_page.py`(`/workflows`、`/workflow/<name>` 页面 + `/api/workflows` CRUD)
|
||
|
||
### 3.3 运行时状态目录(待实现)
|
||
|
||
```
|
||
{workspace.data_dir}/workflow_states/<conversation_id>/
|
||
├── state.json # 运行状态
|
||
└── WORKFLOW.md # 激活时刻的原样快照
|
||
```
|
||
|
||
`state.json` schema:
|
||
|
||
```json
|
||
{
|
||
"workflow_name": "code-review-pipeline",
|
||
"status": "active | completed | stopped | failed",
|
||
"exit_reason": "user | model | max_rejects | round_limit_ack …",
|
||
"current_node_id": "review-target-stage",
|
||
"stage_rounds": 3,
|
||
"round_limit_notified": false,
|
||
"stage_start_msg_index": 142,
|
||
"reject_counts": {"review-gate": 1},
|
||
"history": [
|
||
{"node_id": "explore", "kind": "stage", "summary": "…", "rounds": 5, "completed_at": 169…},
|
||
{"node_id": "review-gate", "kind": "review", "decision": "reject", "message": "…", "at": 169…}
|
||
],
|
||
"pending_notices": [
|
||
{"type": "deactivated_by_user", "message": "完整文本", "created_at": 169…}
|
||
],
|
||
"started_at": 169…
|
||
}
|
||
```
|
||
|
||
要点:
|
||
|
||
- **`current_node_id` 只停 stage / branch / end**——review 是**瞬态节点**:汇报时同步审完直接走到下一站,review 只作为 history 记录。状态机与前端进度展示因此大幅简化。
|
||
- **`stage_start_msg_index`(消息游标)**:进入阶段时记录 conversation_history 长度,审核 payload 截取增量作为阶段工作痕迹(§4.5)。
|
||
- **`pending_notices`**:柔性通知池(§4.7),落盘持久,取出即清除。
|
||
|
||
## 4. 运行时机制(待实现)
|
||
|
||
### 4.1 上下文注入:目录常存,详情跟走
|
||
|
||
| 信息 | 载体 | 时机 |
|
||
|---|---|---|
|
||
| 全景目录(工作流名/描述/正文三段/全部节点一句话清单/当前位置) | system 段(**不冻结**,每次 build_messages 现生成) | 激活期间恒在,压缩免疫 |
|
||
| 当前节点详情(stage 的 goal + instructions 全文) | 工具返回(tool 消息进历史) | 激活时、每次推进时 |
|
||
| 迷失自查 | `get_workflow_status` 工具 | 模型主动调 |
|
||
|
||
注意:主循环单次任务内 messages 只构建一次,阶段推进后同一任务后续迭代的 system 段会滞后。实施时二选一:推进时在工具 handler 内同步刷新 messages 中的工作流段;或 system 段注明「以最新工具返回/状态查询为准」。首选前者。
|
||
|
||
### 4.2 激活(两路入口,共享 `build_activation_text()`)
|
||
|
||
**AI 自主激活**:`activate_workflow(name)` 工具 → 加载定义 → 复制快照 → 初始化 state → 工具返回全景 + 入口阶段详情。重复激活规则:同工作流幂等返回当前进度;不同工作流拒绝(提示先退出)。
|
||
|
||
**slash 菜单激活**(REST):
|
||
|
||
```
|
||
POST /api/workflow/activate {conversation_id, name}
|
||
→ 检查该对话无运行中主任务(门闸/terminal 状态),忙则 409「智能体运行中,无法激活」
|
||
→ 复制 WORKFLOW.md 快照 + 初始化 state
|
||
→ 构造完整 user 消息(含全景 + 入口详情 + 「请开始执行」),走正常消息链路:
|
||
持久化 + 广播 + metadata 齐全(正常 user 消息参数一个不少,
|
||
另打 auto_message_type: "workflow_activate" 标记供前端识别样式)
|
||
→ 创建主任务过门闸 → 模型收到消息直接开干(无需再调 activate_workflow)
|
||
```
|
||
|
||
### 4.3 阶段推进矩阵(核心)
|
||
|
||
`report_workflow_stage(summary)` 仅在当前节点为 stage 时可调。引擎读当前 stage 的 `next`,按目标类型分派:
|
||
|
||
| 下一节点 | 引擎动作 | 工具返回 |
|
||
|---|---|---|
|
||
| `stage` | 直接推进 | 「已记录完成。下一阶段「X」:goal + instructions」 |
|
||
| `review` | **同步 await 审核智能体**(§4.5) | 通过 → 「审核通过。下一阶段「X」…」;驳回 → reject_counts+1,回到 rejectTo:「审核未通过,意见:…。你已回到阶段「Y」整改」 |
|
||
| `branch`(多出线) | 不推进,停在 branch 等选择 | 「前方分支点:→ A(条件:…)→ B(条件:…)。请调 choose_workflow_branch」 |
|
||
| `branch`(单出线) | 自动穿过(并线器无需决策) | 同 stage |
|
||
| `end` | status=completed | 「工作流已完成。请输出总结后结束」(模型随后自然停止) |
|
||
|
||
`choose_workflow_branch(target_node_id)`:仅当前停在 branch 时可调;校验 target 在候选路由集;推进后返回新阶段详情。
|
||
|
||
技术可行性已验证:`handle_tool_call` 为 async(tools_execution.py L960),`submit_plan` 已证明工具可长阻塞等待外部事件——审核在工具内同步 await 成立。
|
||
|
||
### 4.4 审核智能体
|
||
|
||
`modules/workflow_review_agent.py`(fork `goal_review_agent.py`),内部工具 `report_workflow_review(decision: pass|reject, message)`(仿 `report_goal_status`:唯一结论出口 + 强制工具调用重试)。配置统一走个人空间「审核智能体」页(`modules/review_agent_config.py::resolve_review_agent_config`,模型复用子智能体模型库);`review_mode: active` 时注入只读 run_command 取证。
|
||
|
||
**payload 构建**(核心:让审核看证据而非听汇报):
|
||
|
||
```
|
||
【工作流】{name}:{description}
|
||
【本次审核把关】{review 节点名}
|
||
审核关注点:{review.prompt}
|
||
|
||
【被审核阶段】{stage 名}
|
||
阶段目标:{stage.goal}
|
||
阶段要求:{stage.instructions}
|
||
|
||
【阶段执行痕迹】(消息游标 stage_start_msg_index 截取的对话增量,截断控长)
|
||
1. run_command: git diff --stat → …
|
||
2. read_file: server/chat.py L120-180 → …
|
||
|
||
【主智能体阶段汇报】{summary}
|
||
|
||
【历史审核意见】(若是重审,带上前几次驳回意见)
|
||
```
|
||
|
||
### 4.5 审核结果与边界
|
||
|
||
- **pass** → 推进到 review.next(若为 branch 多出线 → 停在 branch 等选择,返回分支菜单)。
|
||
- **reject** → reject_counts[node]+1 → 回到 review.rejectTo(stage 或 branch),工具返回带审核意见。
|
||
- **审核异常/超时** → 视为驳回(计入计数),返回中含「审核服务可能异常,**请告知用户**」。
|
||
- **撞 maxRejects** → status=failed(exit_reason=max_rejects),工具返回「连续驳回超限,工作流已终止,请告知用户」。模型在场同轮闭环,不另发 user 消息。
|
||
|
||
### 4.6 max_stage_rounds 撞限(柔性询问)
|
||
|
||
- `stage_rounds` 跨任务累计:主循环**每轮迭代** +1(挂点在主循环层,不在工具循环内——遵守注入时序铁律),进入新阶段清零。
|
||
- 撞限 → 注入 inline user 消息(`inject_runtime_user_message(inline=True)`,下轮模型即看到):
|
||
「工作流阶段「X」已进行 N 轮,达到上限。请立刻停下当前工作,告知用户已超过 N 轮,并询问是否还要继续。」
|
||
- **工作流不停**:status 保持 active,原地挂起等用户发话。
|
||
- 防重复:撞限后 `round_limit_notified=true`。
|
||
- 清零时机:下一条真实用户消息到达、新任务开始时清零(视作用户已知情交互);之后若再跑 X 轮会再次询问——每超 X 轮问一次。
|
||
|
||
### 4.7 柔性退出与通知池
|
||
|
||
**模型自主 deactivate / completed**:工具返回同轮闭环,不需要 user 消息;前端 UI 走进度事件。
|
||
|
||
**用户 slash deactivate**(REST,随时可用,不做空闲检查——摘牌不打断正在跑的任务):
|
||
|
||
- 智能体**运行中** → 事件入 `pending_notices`;在 `execute_tool_calls` 末尾统一消费点注入 inline 通知(与 `process_sub_agent_updates` 同位置;遵守「循环内收集 → 循环后注入」铁律),模型下轮看到。
|
||
- 智能体**空闲** → REST 直接派发:预占主任务门闸 → 完整 user 消息(持久化+广播+metadata 齐全)→ 创建主任务。与 slash activate 共用派发函数。
|
||
- **兜底**:运行中模型在通知被消费前就停了 → 任务结尾轮询器 spawn 条件兜住(见下)。
|
||
|
||
**通知池 = 统一完成通知轮询器的第三路**(`poll_completion_notifications`,2026-06 实施、2026-08 门闸化),不新造轮子。改动点(均在 `server/chat_flow_task_main.py`):
|
||
|
||
1. `_collect_pending_completion_notices`:增加 workflow 一路(与子智能体、后台命令按时间混排)。
|
||
2. `needs_completion_poll`(L2478 附近):扩展条件 `or _has_pending_workflow_notices(...)`——否则无后台任务时工作流通知产生后轮询器不 spawn。
|
||
3. 轮询器主体零改动(门闸预占、批量预写+末条触发、新任务续轮询全复用)。
|
||
|
||
通知消息 metadata 新增 `message_source: "workflow"`(未识别时回落 "user")。
|
||
|
||
**摘牌后的工具行为**:stopped/failed 后模型再调 `report_workflow_stage` / `choose_workflow_branch` → 返回 `success:false` + 「工作流已退出(原因),如需重新开始请重新激活」——返回错误但不炸任务。
|
||
|
||
### 4.8 与 goal 模式叠加
|
||
|
||
不互斥。工作流活跃时主循环尾部 no-tool-calls 分支不做任何工作流拦截(兜底提醒已取消);goal 审核照常。
|
||
|
||
## 5. 工具清单
|
||
|
||
### 主智能体(5 个)
|
||
|
||
| 工具 | 参数 | 行为/返回 |
|
||
|---|---|---|
|
||
| `activate_workflow` | `name` | 加载+快照+初始化;返回全景目录+入口阶段详情。同工作流幂等;不同工作流拒绝 |
|
||
| `report_workflow_stage` | `summary` | 阶段汇报核心状态机,返回按 §4.3 矩阵分派 |
|
||
| `choose_workflow_branch` | `target_node_id` | 分支选择;校验候选集;返回新阶段详情 |
|
||
| `get_workflow_status` | — | 返回当前节点/已走路径/各阶段轮数/审核历史(迷失自查+用户问进度) |
|
||
| `deactivate_workflow` | `reason` | 柔性退出:摘牌+工具返回确认(模型自主退出无需 user 消息) |
|
||
|
||
### 审核智能体内部(1 个)
|
||
|
||
| 工具 | 参数 | 说明 |
|
||
|---|---|---|
|
||
| `report_workflow_review` | `decision: pass\|reject`, `message` | 唯一结论出口,仿 report_goal_status |
|
||
|
||
### REST(3 个)
|
||
|
||
- `POST /api/workflow/activate`:仅智能体空闲(409 检查);快照+初始化+派发完整 user 消息任务
|
||
- `POST /api/workflow/deactivate`:随时可用;忙入通知池、闲直发任务
|
||
- `GET /api/workflow/status?conversation_id=`:轮询/刷新恢复
|
||
|
||
## 6. 通信与前端
|
||
|
||
- **轮询事件**(sender → session_data 快照 → REST 轮询,对齐 goal 链路):`workflow_progress`(推进/驳回)/ `workflow_review_progress`(审核进行中)/ `workflow_completed` / `workflow_stopped` / `workflow_failed`。快照自带 conversation_id。
|
||
- **slash 菜单**:新增 workflows 模式,数据用现成 `GET /api/workflows`。
|
||
- **进度展示**:仿 goal 进度组件——当前阶段名 · 已走路径 · 审核状态 · 轮数。
|
||
- **刷新恢复**:`GET /api/workflow/status`。
|
||
|
||
## 7. 已实现部分清单(编辑器期)
|
||
|
||
- `modules/workflow_manager.py`、`server/workflow_page.py`
|
||
- `static/src/components/workflow/`:`workflowModel.ts`(模型/校验/dagre 自动排版)、`WorkflowLibraryView.vue`、`WorkflowEditorView.vue`、四种节点组件
|
||
- `workflows/` 四个内置示例(code-review-pipeline / bug-fix-triage / feature-development / research-report)
|
||
|
||
## 8. 实施清单(文件级,按依赖顺序)
|
||
|
||
1. `modules/workflow_state_manager.py`(新增):状态目录读写、快照复制、计数、通知池、消息游标。
|
||
2. `modules/workflow_review_agent.py`(新增,fork goal_review_agent)+ `prompts/workflow_review_agent.txt`(配置后改为统一审核智能体设置,见 `modules/review_agent_config.py`)。
|
||
3. `server/workflow_flow.py`(新增):编排——`build_activation_text`、system 段构建、推进矩阵、审核调用、通知文本构造、状态查询。
|
||
4. 工具注册:`core/main_terminal_parts/tools_definition/` 新增 5 个定义;`tools_execution.py` 注册 handler。
|
||
5. system 段注入:`core/main_terminal_parts/context/messages.py` 追加工作流段(不冻结)+ 推进时刷新机制。
|
||
6. 主循环挂点(`server/chat_flow_task_main.py`):stage_rounds 计数与撞限注入;`needs_completion_poll` 扩展;`_collect_pending_completion_notices` 加 workflow 路;工具循环末尾消费工作流通知。
|
||
7. REST:`server/tasks/workflows.py`(新增):activate / deactivate / status。
|
||
8. 前端:slash 菜单 workflows 模式、进度组件、轮询事件消费(`taskPolling/lifecycle.ts`)、刷新恢复。
|
||
9. 验证:`python3 -m py_compile` 改动文件 + `python -m pytest test/test_server_refactor_smoke.py -q`。
|
||
|
||
## 9. 硬约束(实施必须遵守)
|
||
|
||
- **单写者/门闸**:所有工作流逻辑发生在主任务内部(工具执行 + 主循环层),不新增主任务入口;REST 派发走门闸预占(AGENTS.md §12)。
|
||
- **注入时序铁律**:工具循环内禁止直接 `inject_runtime_user_message`——一律「循环内收集 → 循环后注入」(记忆 `runtime_injected_message_convention`)。
|
||
- **运行期注入标记**:注入的 user 消息必须走 `inject_runtime_user_message`(自动带 `runtime_injected` 等标记),否则刷新恢复重建会重复显示。
|
||
- **不掐断智能体**:任何工作流路径不得 raise/return 导致主任务异常终止;柔性优先。
|