Workflow 与 Agent Loop:确定性控制和动态决策怎样组合
# Workflow 与 Agent Loop:确定性控制和动态决策怎样组合
把一次 Tool Calling 扩展为有状态、有停止条件、能够暂停和恢复的任务闭环,并学会判断哪些步骤交给代码、哪些局部决策交给模型。
# 先记住一句话
Workflow 用代码保证关键路径可预测,Agent Loop 让模型在局部开放问题中动态决定下一步;生产系统通常不是二选一,而是 “外层 Workflow 控制边界,内层 Agent 处理不确定性”。
Workflow:接收任务 → 校验 → 检索 → 生成 → 评测 → 发布
└─ Agent Loop ─┘
决策 → 工具 → 观察 → 再决策
2
3
# Workflow 和 Agent Loop 的本质区别
| 对比项 | Workflow | Agent Loop |
|---|---|---|
| 下一步由谁决定 | 代码、状态机或规则 | 模型结合当前状态决定 |
| 适合任务 | 路径已知、规则明确、风险较高 | 路径难预先穷举、需要语义判断 |
| 优点 | 可预测、易测试、成本稳定 | 灵活,能根据观察结果调整 |
| 风险 | 新分支需要改代码 | 可能绕路、失控、成本波动 |
| 常见停止方式 | 流程到达结束节点 | 完成、失败、需确认或预算耗尽 |
能写成明确 if/else 的步骤,优先留给 Workflow。只有当下一步依赖开放式语义判断,并且分支很难穷举时,才值得交给模型。
# 一个可靠循环需要什么
# 1. 显式状态
消息历史不等于任务状态。宿主应用(后端或 Agent Runtime)至少应保存:
goal:目标和验收条件;phase:当前阶段;facts:已经验证的事实;artifacts:任务已经生成的产物,例如文件、草稿或引用;attempts:重试次数和失败原因;status:running、requires_action、completed、incomplete、failed。
# 2. 明确停止条件
停止不能只相信模型说 “完成了”。宿主应用要检查:
- 成功:业务验收条件是否真的满足;
- 暂停:是否缺少用户输入或敏感操作确认;
- 不完整:是否达到步数、时间或成本上限;
- 失败:错误是否不可恢复,或重试预算已经耗尽。
# 3. 每步都产生 observation
工具调用后必须把真实结果写回状态。成功返回结构化数据;失败返回可分类错误,而不是只抛一句模糊异常。
# 4. 预算和护栏
循环至少需要最大步数、总超时、Token 或费用预算、工具级超时、允许调用的工具集合,以及写操作审批。
# 框架无关的最小实现
type RunStatus = 'running' | 'requires_action' | 'completed' | 'incomplete'
type TaskState = {
goal: string
step: number
facts: string[]
status: RunStatus
pendingApproval?: { tool: string; args: unknown }
}
async function runAgent(initialState: TaskState) {
const state = initialState
// 宿主应用控制硬上限,模型不能自行取消这个限制。
while (state.step < 8 && state.status === 'running') {
const decision = await decideNextAction(state)
if (decision.type === 'final') {
// 模型声称完成后仍要由确定性规则验收。
state.status = await verifyGoal(decision.answer, state)
? 'completed'
: 'running'
continue
}
const tool = toolRegistry.get(decision.toolName)
const args = tool.inputSchema.parse(decision.arguments)
if (tool.risk === 'write') {
state.pendingApproval = { tool: tool.name, args }
state.status = 'requires_action'
break
}
const observation = await executeWithTimeout(tool, args)
state.facts.push(summarizeObservation(observation))
state.step += 1
}
// 达到预算并不等于前端只能显示 “失败”。保留已有成果和可恢复状态。
if (state.status === 'running') state.status = 'incomplete'
return state
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
达到最大轮数时,后端应返回结构化的 incomplete,同时携带已完成步骤、当前结果、未完成原因和恢复入口。前端再把它解释成 “本次未在预算内完成,可继续或调整目标”,而不是丢失进度后只弹失败。
# 重试、恢复和幂等
先理解幂等
幂等(Idempotency)是指同一个业务动作即使因为超时、重试或消息重复而被请求多次,最终也只产生一次预期副作用。例如,同一次 “保存薄弱知识点” 被提交三次,数据库中仍然只能有一条记录。
幂等键(Idempotency Key)是宿主应用为一次业务动作生成的唯一标识。第一次请求到达时,服务端以该键执行并保存结果;后续收到相同键时,直接返回第一次的结果,不再重复写入。新的业务动作必须使用新的幂等键,否则会被误认为旧请求。
可以记成一句话:同一个动作使用同一把钥匙;服务端见过这把钥匙,就返回旧结果,不再执行第二次。
重试只适合暂时性错误,例如网络抖动、限流和服务短暂不可用。参数错误、权限不足或业务冲突需要修正或人工处理,盲目重试只会重复失败。
写操作还需要幂等键:同一个任务因为超时重放时,不能重复发邮件、扣款或创建记录。幂等记录应配合数据库唯一约束或事务,防止两个并发请求同时发现 “没有执行过” 后都完成写入。长任务应在关键节点保存 checkpoint,恢复时从最近的可靠状态继续,而不是把整段对话重新跑一遍。
# 贯穿项目:笔记与面试助手
项目可以采用下面的混合流程:
- Workflow 校验问题、用户范围和预算;
- Agent 判断需要搜索哪些主题;
- Workflow 并行检索笔记和学习记录;
- Agent 根据证据生成答案或追问;
- Workflow 执行引用检查和答案评测;
- 写入薄弱点前暂停并等待用户确认;
- 保存结果、指标和完整 trace。
这里固定步骤由代码掌控,只有 “检索什么、是否需要补查、怎样追问” 交给模型。
# 高频面试题与回答
回答顺序:先说结论,再解释原因,最后补一个例子或工程边界。不要逐字背诵,记住这条表达主线即可。
1. 30 秒回答:Workflow 与 Agent 有什么区别参考答案
Workflow 是开发者提前把步骤和分支写好,优点是稳定、可预测、容易测试。Agent Loop 则让模型根据当前状态和工具结果决定下一步,更适合无法提前列完所有路径的任务,但成本和调试难度也更高。实际项目通常混合使用:外层 Workflow 管流程、权限和停止条件,局部 Agent 只负责开放式判断。
2. 项目回答:如何防止 Agent 无限循环参考答案
我不会只设置一个最大轮数,而是同时限制步数、总时间和费用。每轮都记录调用和状态变化,如果相同工具、相同参数或相同错误不断重复,就提前停止。模型说完成后还要用业务规则验收;达到上限则返回 incomplete 并保存 Checkpoint,让用户可以继续,而不是假装成功。
3. 为什么消息历史不能代替任务状态?参考答案
因为消息历史是给模型理解对话的文本,可能被裁剪、摘要,也很难准确查询。任务状态则是程序恢复和判断任务的依据,必须结构化保存目标、当前阶段、已验证事实、重试次数和审批状态。简单说,消息历史回答 “聊过什么”,状态回答 “任务现在走到哪里”。
4. 什么情况下不应该使用 Agent Loop?参考答案
如果步骤固定、规则可以写清楚,或者结果必须高度确定,就不应该让模型循环决定。例如付款主流程、数据库迁移和固定审批链,更适合普通代码或 Workflow。Agent 应该解决真正需要语义判断的问题,而不是把已经明确的流程变得不确定。
5. 达到最大轮数应该怎样返回?参考答案
后端应该明确返回 incomplete,并带上已经完成的步骤、当前结果、停止原因、能否恢复以及继续需要什么。前端再据此让用户选择继续、缩小目标或人工接管。不能只返回一句失败,也不能把达到上限包装成完成。
6. 为什么重试必须配合幂等?参考答案
因为请求超时只说明客户端没收到响应,不代表服务端没有执行。如果直接重试,可能重复发邮件、扣款或建单。幂等键给同一次业务动作一个唯一标识,服务端再次收到相同标识时复用第一次结果,从而做到 请求可以重复,业务效果只发生一次。
# 接下来学什么
下一篇学习 Agent 设计模式,重点理解怎样把循环中的动态决策整理成 Router、ReAct、Plan-and-Execute 等可选择、可组合的结构。