Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions docs/ai/agent/workflow-graph-loop.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
# AI 工作流中的 Workflow、Graph 与 Loop:从概念到实现

## 一、为什么 AI 系统会需要工作流

单轮对话虽然可以回答问题,但很难稳定地**交付结果**。在真实场景中,一个完整任务往往不仅仅是“生成答案”,还包含检索信息、调用工具、输出结构化结果、质量检查、失败重试,以及在结果不满意时进行多轮修正。这些行为并不是临时补丁,而是系统结构本身的一部分。因此,我们需要一种**可分支、可循环、可观测**的执行路径,而不是把所有逻辑堆进一段超长 Prompt。

传统软件流程通常是确定性的:输入固定、步骤固定、输出相对稳定。但 LLM 的特点恰恰相反——它“能力很强,但不完全稳定”。它可能答非所问、格式错误、产生幻觉,或者在调用工具时失败。这就引出了三个核心问题:

1. 下一步并不唯一,需要根据当前结果动态决策路径;
2. 当结果不理想时,系统需要自动修正,而不是直接失败;
3. 中间状态必须被记录,否则难以调试、追踪与恢复。

正是在这样的背景下,工作流思维变得必要。它并不是把问题复杂化,而是在解释:为什么现代 AI 系统必须以这种方式构建。

以一个简单例子来看:当我们让 AI 写一篇文章时,一次生成的结果往往不够理想。直觉做法是手动复制结果,再附加新要求继续提问,但这种方式既不高效,也会快速消耗上下文。如果将这一过程结构化为“**审查 → 修改 → 再审查**”的循环,并设定停止条件(如达到质量标准或触达迭代上限),就能显著提升稳定性。

这正是 AI 工作流的核心价值:把一次性的生成过程,转变为一个**可迭代、可收敛、可控制**的系统化流程。

---

## 二、工作流是什么:从传统 Workflow 到 AI Workflow
![](传统Workflow VS AI Workflow.png)

上图可以直观看到两类工作流的差异:传统 Workflow 更偏向“固定步骤 + 明确分支”的过程编排;AI Workflow 则更依赖运行时的状态(State)来动态决定下一步,并通过循环(Loop)把“生成—评估—修正”变成可收敛的过程。

### 2.1 传统工作流:在做什么?

先说基本定义:**Workflow** 就是为了完成某个目标,把任务拆成若干步骤,并规定这些步骤如何协作推进。它回答的问题是:“这件事怎么做完?”

在传统工作流体系中,流程设计通常强调**顺序性**与**确定性**。也就是说,大多数流程都可以被描述为一条清晰的路径:从起点出发,按照既定步骤逐步推进,最终抵达终点。例如审批流程、订单流转、ETL 数据管道等,基本都遵循“步骤 A 完成后进入步骤 B”的线性逻辑。即使存在分支,也往往是基于明确条件触发的有限选择,而不是开放式的不确定路径。

### 2.2 AI 工作流:为什么一定会走向 Graph、Loop

到了 AI 场景,同样的“流程”一词,承载的内涵发生了变化。相比传统工作流强调的顺序性与确定性,AI 工作流需要处理的是一个充满不确定性的执行环境。我们面对的不再只是“按步骤执行”,还包括:

- 结果是否达标要在**运行时**判断。
- 是否需要继续重试,要由**当前状态**决定。
- 某一步失败后,系统不再是简单的报错然后结束, 而是考虑是否应该降级、回退或换一种策略。
- 节点之间传递的不只是参数,还包括上下文、草稿、评分、错误信息、历史轮次等**状态**。

所以 AI Workflow 与传统 Workflow 的差异,不在于“有没有流程”,而在于它更强调**动态决策 和 状态驱动**。一旦我们想要表达**下一步不唯一 或者 不满意就再来一轮**,线性列表就不够用,自然会落到 **Graph(结构)** 与 **Loop(回流)** 这两类概念上。

---

## 三、Graph(图) 是工作流的结构表达(重要)

沿用贯穿案例:假如我们要搭一条「生成初稿 → 质量审核 → 不达标则修改 → 再回到审核」的路径。这里每一步对应图的 **Node**,步骤之间的走向由 **Edge** 表达,整条链路读写的共享上下文就是 **State**。

图里最基础的元素有三个:

- **Node(节点)**:表示一个执行单元, 其主要有三大功能: 读取state; 执行业务逻辑,加工state; 将加工好的state放回去。在文章审核例子里,典型有「生成初稿」「质量审核」「按反馈修改」; 此外还可以扩展检索、格式校验、人工审批等。
- **Edge(边)**:是流程图中的控制流抽象,用于描述节点之间的执行路径及其触发条件,决定流程在运行时如何在不同节点之间进行调度与跳转。常见的边类型如下:

| 边的类型 | 解释 |
| --------------------- | ----------------------------------------------------------- |
| 顺序边(Sequential Edge) | 节点按固定顺序执行,执行完当前节点后直接进入下一个节点,不依赖条件或状态判断。 |
| 条件边(Conditional Edge) | 根据当前 state 中的条件判断结果,选择不同的后续节点路径,实现分支逻辑。 |
| 动态边(Dynamic Edge) | 下一节点由运行时逻辑决定(如函数、规则引擎或 LLM 决策),路径在执行时动态生成。 |
| 循环边(Loop Edge) | 节点可以回到自身或前序节点重复执行,用于重试、迭代优化或循环推理,直到满足终止条件, 通常是由条件边与顺序边结合形成。 |
| 终止边(Terminal Edge) | 将流程引导至结束状态,不再继续执行后续节点,用于输出最终结果或结束工作流。 |
| 并行边(Parallel Edge) | 一个节点同时分发到多个后续节点并行执行,用于多任务处理、RAG/工具并发等场景。 |


- **State(状态)**:表示在流程执行过程中持续被读写的共享上下文,是节点之间真正传递的“工作记忆”。它本质上是一个**键值对结构(Key-Value Store)的上下文容器**,用于在各节点之间传递和修改数据。在不同语言或框架中,通常使用等价的数据结构实现(如 Java 的 `Map<String, Object>`、Python 的 `dict`、TypeScript 的 `Record<string, any>` 等),而不是限定于某一种具体实现。

下面是一些常用的状态字段(可根据实际业务自由扩展,不必拘泥于样例):


| Key(字段名) | Value类型 | 说明 | 生命周期 |
| ------------------ | ------- | -------- | ---- |
| input | String | 用户输入问题 | 全流程 |
| messages | List | 对话历史 | 全流程 |
| retrieval_result | List | RAG检索结果 | 中间 |
| tool_result | Object | 工具调用结果 | 中间 |
| llm_response | String | LLM原始输出 | 中间 |
| intermediate_steps | List | 中间执行步骤记录 | 全流程 |
| next_step | String | 控制流跳转节点 | 当前执行 |
| output | String | 最终输出结果 | 结束 |


如果只看 Node 和 Edge,我们会得到一张“能跑起来的路径图”;而把 State 一起放进来,我们才真正拥有了一张“可以在运行时做决策的图”。

总之图结构比线性结构更贴近 AI 系统的真实形态,因为很多 AI 应用的控制流本来就是图,只是早期常被临时写成 `if-else`、重试逻辑或分散在不同模块里的状态机。

---

## 四、Loop 是Graph上的回溯能力(重要)

在同一套「文章审核」里:**审核不通过**时,控制流不应结束,而应沿某条边回到「修改」或「重新生成」——这就是 Loop 在业务上的含义。技术上,它表现为图上的**回流边**。
![](Loop概览.png)
很多人第一次接触 AI 工作流时,会把 `Loop` 理解成“多跑几次”。这不算错,但还不够准确。更合适的解释是:**Loop 不是独立系统,而是图结构上的一种控制模式**。当某条边根据当前状态把控制流送回到先前节点时,就形成了Loop, 正如上图所示, 重点在判断是否达标, 在循环的内部LLM会根据提示词的要求对结果进行"评分"如果满足就会输出否则"打回重写"。

常见的 Loop 主要有两种:

1. **固定次数循环**:更像 `for`。例如“最多重试 3 次”。
2. **条件驱动循环**:更像 `while`。例如“只要评分低于 80 分,就继续修改”。

AI 场景里,第**二**类通常更有代表性。因为“跑几次”往往不是先验确定的,而是由内容质量、工具执行结果、外部反馈共同决定的, 但是实际开发中两者必须同时使用, 因为LLM的不确定性可能会导致生成的内容会一直不合格, 此时我们就需要参考固定次数循环思想对内容进行降级兜底处理。

总之, 一个可靠的 Loop 一定包含三件事:

- 继续条件:为什么还要再来一轮。
- 退出条件:什么时候已经足够好,可以结束。
- 安全边界:最大轮次、超时、预算、熔断条件。

如果没有这些约束,Loop 很容易从“自我修正”变成“无限打转”。

仍然放回文章审核的例子里,Loop 并不是“**多试几次**”这么简单,而是“**审核结论驱动下一跳**”。只有当评分未达标、且还没超过最大轮次时,流程才会从 `ReviewNode` 回到 `ReviseNode`;一旦达到阈值或触发边界条件,就应该退出并给出结果。这时我们看到的就不只是循环,而是一种可控的回流机制。

---

## 五、概念整合:把 Workflow、Graph、Loop 串起来

![](Workflow_Graph_loop概览.png)
到这里我们基本上了解差不多了,可以用一句话把三者的层次关系收束起来:**Workflow 是目标与过程,Graph 是结构与载体,Loop 是图上的控制模式。**

从业务视角来看**Workflow**:回答了“要做什么、如何完成” 对象是一个庞大的项目工程。

从结构视角来看**Graph**:回答了“这些步骤如何连接与流转”为实现Workflow提供了解决模板。

从思想视角来看**Loop**:回答了“什么时候重复执行”是保证工作流智能的核心思想。

继续沿用同一个“写文章并审核”的例子,那么三者的关系其实可以直接贴标签来看:

- 当我们说“先生成初稿,再审核,不达标就修改,直到达标后输出”,我们描述的是 **Workflow**。
- 当我们把 `生成节点-> 检查节点-> 修正节点-> 检查节点`画成节点与连线,并让它们共享同一份状态时,我们得到的是 **Graph**。
- 当我们规定“审核不通过就回到修改,直到评分达标或达到上限”为止,我们定义的就是 **Loop**。

所以这三者并不是三个并列的新名词,而是同一件事的三个观察角度:Workflow 关注任务目标,Graph 关注结构组织,Loop 关注回流控制。回到这篇文章的案例里,我们真正实现的始终只有一条流程,只是我们分别从业务、结构、控制三个层面在理解它。

---

## 六、工作流设计的分水岭:抽象能力
![](抽象对比.png)

上图可以看到高抽象工作流将四个判断节点抽象成一个判断节点: 评估是否达标。如果使用低抽象那么当我们需要减少/添加新的判断节点时需要花费时间去阅读源码寻找对应的节点, 由此我们可以得出: **一个好的工作流不是步骤堆得多,而是 Node / Edge / State 的抽象是否经得起复用与扩展。**

很多初学者设计工作流时,容易把每一步都写成具体动作,例如:调用模型生成文案; 检查标题长度; 检查语气是否合适; 判断是否需要补资料; 再调用模型修改。这样做短期可用,但流程会越来越碎,复用性也很差。更成熟的方式是把流程抽象到更稳定的结构层:

1. **Node** 抽象**职责边界**(在这个节点中产出的结果该是什么样子的, 必须出现哪些信息),而不是抽象这一次调了哪个 API。
2. **Edge** 抽象**流转规则**(在什么状态下允许去哪、何时结束),用条件边表达分支与循环,而不是在图外写满 if-else
3. **State** 抽象**推进任务时必须持久记住的信息**(工单快照、审核结论、重试次数、错误码等),让路径有据可依。

例如在“生成并审核文章”的场景里,与其设计十几个零散节点来检查文章标题符不符合题意, 文章字数是否满足要求,不如先抽象出几个更稳定的职责:

- `DraftNode`:负责产出当前版本内容。
- `ReviewNode`:负责评估当前结果是否达标。
- `ReviseNode`:负责根据反馈修正内容。
- `ExitNode`:负责在满足条件时输出最终结果。

---

## 七、设计工作流时的注意事项

真正把工作流落地时,问题往往不出在“图不会画”,而出在细节没有提前设计好。下面这些是实践里最常见的坑。

### 1. State 设计的粒度

- 太粗:所有东西都塞进一个大对象里,谁改了哪个字段不好查。
- 太细:字段拆得特别散,每个节点都要拼来拼去,容易出错。
- 建议:按业务含义分几块,例如「用户原始输入一块」「当前生成结果一块」「审核/评分结论一块」「流程控制用的一块(如当前步骤、重试次数)」

### 2. 循环终止条件(避免死循环)

不要只写“如果不满意就继续优化”,而要明确:

- 最大轮次是多少?
- 评分阈值是多少?
- 超时或成本超限时怎么办?
- 连续失败后是否要 fallback。

### 3. 错误处理与 fallback

AI 工作流不是只处理“成功路径”。工具异常、模型超时、格式校验失败、外部接口限流,都应在图上有**明确边**:重试、降级(例如跳过某工具)、转人工、或输出「当前最优 + 错误说明」,而不是只靠外围 `try-catch` 吞掉。

### 4. Token 消耗与成本控制

Loop 会自然放大 token 与延迟。设计时要提前思考:

- 哪些节点必须调用大模型,哪些可以用代码替代。
- 是否可以先粗筛,再精修。
- 是否需要在达到“足够好”时就提前结束,而不是追求“理论最优”。

### 5. 节点间数据传递格式

节点之间传什么、字段名怎么定义、结构化输出采用什么 schema,都应该尽早统一(例如统一用 JSON schema 或 Pydantic 模型)。否则图一旦复杂,调试成本会急剧上升。

---

## 八、总结

当我们开始用这套视角看问题时,工作流就不再只是一个可视化画布上的箭头图,而是一种工程建模能力。常见演进方向包括:

- **Agent 化**:节点从「固定脚本」变成「能自主选工具、拆子目标」的执行单元,但底层仍需要清晰的图与状态边界,否则难以观测与兜底。
- **多智能体协作**:多个角色分工、对话或委托;与 CrewAI、LangGraph 多子图等思路一致,难点往往在**共享 State 的权限**与**冲突解决**。
- **人机协同**:在关键节点插入人工审核、标注或纠偏,把 HITL(human-in-the-loop)当作一等公民写进图与状态机。
- **更长上下文与记忆**:工作流与 RAG、会话记忆结合时,要特别注意 State 里哪些该进向量库、哪些只该留在本轮任务上下文,避免成本和隐私失控。
- **Agent安全**: 工作流的出现将大模型生成的内容由不可控变为部分可控, 但是对于一些场景还是具有严重的安全问题, 毕竟我们知道AI发展日新月异谁也不能保证AI完全可控。

工作流框架会换代,但 **「图结构 + 状态 + 可控循环」** 这层抽象会持续存在, 所以我们需要深入思考这种思想摒弃框架思维。