ChatDev/docs/user_guide/zh/execution_logic.md
2026-01-07 16:24:01 +08:00

230 lines
7.1 KiB
Markdown
Executable File
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.

# 图执行逻辑
> 版本2025-12-16
本文档详细说明 DevAll 后端如何解析和执行工作流图,特别是对于包含循环结构的复杂图的处理机制。
## 1. 执行引擎概述
DevAll 工作流执行引擎支持两类图结构:
| 图类型 | 特征 | 执行策略 |
|--------|------|----------|
| **DAG有向无环图** | 节点间无循环依赖 | 拓扑排序 + 同层并发执行 |
| **含环有向图** | 存在一个或多个循环结构 | 递归式超级节点调度 |
执行引擎会自动检测图结构,选择合适的执行策略。
## 2. DAG 执行流程
对于不包含循环的工作流图,执行引擎采用标准的 DAG 调度策略:
1. **构建前驱/后继关系**:解析边定义,为每个节点建立 `predecessors``successors` 列表
2. **计算入度**:统计每个节点的前驱数量
3. **拓扑排序**:将入度为 0 的节点放入第一层,执行后将后继节点入度减 1新的入度为 0 节点进入下一层
4. **同层并发**:同一层内的节点无依赖关系,可以并行执行
```mermaid
flowchart LR
subgraph Layer1["执行层 1"]
A["节点 A"]
B["节点 B"]
end
subgraph Layer2["执行层 2"]
C["节点 C"]
end
subgraph Layer3["执行层 3"]
D["节点 D"]
end
A --> C
B --> C
C --> D
```
## 3. 循环图执行流程
### 3.1 Tarjan 强连通分量检测
当图中存在循环结构时,执行引擎首先使用 **Tarjan 算法** 检测所有强连通分量Strongly Connected Components, SCC。Tarjan 算法通过深度优先搜索,在 O(|V|+|E|) 时间复杂度内识别图中的所有环路。
包含多于一个节点的 SCC 即为环路结构。
### 3.2 超级节点构建
检测到环路后,执行引擎将每个环路抽象为一个"超级节点"Super Node
- 环路内部的所有节点被封装在超级节点中
- 超级节点之间的依赖关系来源于原始节点间的跨环边
- 封装后的超级节点图一定是 DAG可以进行拓扑排序
```mermaid
flowchart TB
subgraph Original["原始图"]
direction TB
A1["A"] --> B1["B"]
B1 --> C1["C"]
C1 --> B1
C1 --> D1["D"]
end
subgraph Abstracted["超级节点图"]
direction TB
A2["节点 A"] --> S1["超级节点<br/>(B, C 环路)"]
S1 --> D2["节点 D"]
end
Original -.->|"抽象"| Abstracted
```
### 3.3 递归式环路执行策略
对于环路超级节点,系统采用递归式执行策略:
#### 步骤 1唯一初始节点识别
分析环路边界,识别当前被唯一触发的入口节点作为"初始节点"。该节点必须满足:
- 被环路外部的前驱节点通过满足条件的边触发
- 有且仅有一个节点满足此条件
#### 步骤 2构建作用域子图
以当前环路的所有节点为作用域,**逻辑上移除初始节点的所有入边**。这一操作打破外层环的边界,使后续的环路检测仅针对环内部的嵌套结构进行。
#### 步骤 3嵌套环路检测
对构建的子图再次应用 Tarjan 算法,检测作用域内的嵌套环路。由于初始节点的入边已被移除,检测到的强连通分量仅为真正的内层嵌套环。
#### 步骤 4内层超级节点构建与拓扑排序
若检测到嵌套环路:
- 将每个内层环路抽象为超级节点
- 构建作用域内的超级节点依赖图
- 对该超级节点图执行拓扑排序
若未检测到嵌套环路,则直接进行 DAG 拓扑排序。
#### 步骤 5分层执行
按拓扑排序得到的执行层次依次执行:
- **普通节点**:检查触发状态后执行,首轮迭代时初始节点强制执行
- **内层环路超级节点****递归调用步骤 1-6**,形成嵌套执行结构
#### 步骤 6退出条件检查
每完成一轮环内执行后,系统检查以下退出条件:
- **出口边触发**:若任一环内节点触发了环外节点的边,则退出环路
- **最大迭代次数**:若达到配置的最大迭代次数(默认 100强制终止
- **初始节点未被重触发**:若初始节点未被环内前驱节点重新触发,环路自然终止
若条件均不满足,则返回步骤 2 开始下一轮迭代。
### 3.4 环路执行流程图
```mermaid
flowchart TB
A["环路超级节点被调度"] --> B["识别唯一触发的初始节点"]
B --> C{"是否有有效初始节点?"}
C -->|"无"| D["跳过该环路"]
C -->|"有多个"| E["报告配置错误"]
C -->|"唯一"| F["构建作用域子图<br/>移除初始节点入边"]
F --> G["Tarjan算法检测嵌套环路"]
G --> H{"存在内层嵌套环?"}
H -->|"否"| I["DAG拓扑排序"]
H -->|"是"| J["构建内层超级节点<br/>执行拓扑排序"]
I --> K["分层执行"]
J --> K
K --> L["执行普通节点"]
K --> M["递归执行内层环路"]
L --> N{"检查退出条件"}
M --> N
N -->|"出口边被触发"| O["退出环路"]
N -->|"达到最大迭代次数"| O
N -->|"初始节点未被重触发"| O
N -->|"继续迭代"| F
```
## 4. 边条件与触发机制
### 4.1 边触发trigger
每条边有一个 `trigger` 属性,决定该边是否参与执行顺序计算:
| trigger 值 | 行为 |
|------------|------|
| `true`(默认) | 该边参与拓扑排序,目标节点等待源节点完成 |
| `false` | 该边不参与拓扑排序,仅用于数据传递 |
### 4.2 边条件condition
边条件决定数据是否沿该边流动:
- `true`(默认):总是传递
- `keyword`:检查上游输出是否包含/不包含特定关键词
- `function`:调用自定义函数判断
- 其他自定义条件类型
只有当条件满足时,目标节点才会被触发执行。
## 5. 典型循环场景示例
### 5.1 人工审阅循环
```yaml
nodes:
- id: Writer
type: agent
config:
name: gpt-4o
role: 你是一位专业的技术作家
- id: Reviewer
type: human
config:
description: 请审阅文章,满意请输入 ACCEPT
edges:
- from: Writer
to: Reviewer
- from: Reviewer
to: Writer
condition:
type: keyword
config:
none: [ACCEPT] # 不包含 ACCEPT 时继续循环
```
执行流程:
1. Writer 生成文章
2. Reviewer 人工审阅
3. 若输入不包含 "ACCEPT",返回 Writer 修改
4. 若输入包含 "ACCEPT",退出循环
### 5.2 嵌套循环
系统支持任意深度的嵌套循环。例如,一个外层"审阅-修订"循环内部可以包含一个"生成-验证"循环:
```
外层循环 (Writer -> Reviewer -> Writer)
└── 内层循环 (Generator -> Validator -> Generator)
```
递归式执行策略会自动处理这种嵌套结构。
## 6. 关键代码模块
| 模块 | 功能 |
|------|------|
| `workflow/cycle_manager.py` | Tarjan 算法实现、环路信息管理 |
| `workflow/topology_builder.py` | 超级节点图构建、拓扑排序 |
| `workflow/executor/cycle_executor.py` | 递归式环路执行器 |
| `workflow/graph.py` | 图执行主入口 |
## 7. 变更记录
- **2025-12-16**:新增图执行逻辑文档,详细说明 DAG 与循环图的执行策略。