Agent 的工作模式
把模型接上工具,并不意味着系统就有了完整的 Agent 能力。真正决定 Agent 怎样工作的,是模型推理、工具执行、状态更新和下一次模型请求之间的控制关系。
目前常见的 Agent 架构包括 ReAct、Plan-and-Execute、Multi-Agent、Reflective Agent、Tool-Augmented、Memory-Augmented、RAG Agent 和 Autonomous Loop 等。
其中 ReAct、Plan-and-Execute 和 Multi-Agent 描述的是控制流,Tool、Memory 和 RAG 描述能力来源,Autonomous Loop 描述系统能自主运行到什么程度。真实项目往往把多种模式组合起来。
例如 Copilot 会先规划任务,再逐个完成。

这一章先建立主流 Agent 模式的整体认识,再结合 Codex、OpenCode 和 Microsoft Agent Framework 的源码,观察这些模式落在工程中的哪一层。
ReAct
ReAct 来自 Reasoning and Acting,让模型 “看见结果再决定”。框架不要求它一次给出完整答案,而是允许它先调用工具、看到 Observation 后再继续判断。
messages = [用户任务, ...工具定义]
for step in 1..MAX_STEPS:
reply = model(messages)
if reply 没有工具调用:
return reply.最终结果
observation = 宿主执行(reply.工具调用)
messages += [工具调用, observation] # 结果写回历史,进入下一轮
这里的 Observation 不只是搜索结果或文件内容,也可以是命令失败、权限拒绝、测试输出或另一个 Agent 的回复。模型每次看到新的 Observation 都可能改变判断,所以 ReAct 很适合代码探索、故障排查这类路径无法预先确定的任务。
它简单而灵活,问题也来自灵活性:模型可能反复读同一文件、在长任务里偏离目标;随着工具结果进入历史,上下文越来越大,成本和噪声随之上升。工程实现通常还要加最大步数、重复调用检测、审批和上下文压缩。
Autonomous Loop
Autonomous Loop 不是和 ReAct 对立的算法,而是对 “能自主运行到哪种程度” 的描述:当 Agent 能自行决定是否继续、自动调用工具、处理错误并跑到完成时,它就在执行自主循环。ReAct 常是循环内部的决策方式,Plan-and-Execute 和 Multi-Agent 同样可以自主运行。
while 可以继续:
合并用户输入与当前状态
模型推理 + 执行工具
如果 上下文接近上限: 压缩历史
如果 任务完成: break
难的不是一个 while 语句,而是循环怎样安全地暂停和恢复:上下文到上限要先压缩,危险操作要等审批,用户追加指令要送进下一轮,还要处理中断、重试和任务取消。
Plan-and-Execute
ReAct 走一步看一步,Plan-and-Execute 则是 “先建全局路径再一步步走”。它通常把任务拆成两个角色:Planner 生成并更新计划,Executor 执行当前步骤。计划不是一段展示用的文字,而是运行时会读取的状态。
大多数 AI 工具的默认 Plan、Build 两种模式,就属于 Plan-and-Execute。

plan = planner.generate(任务)
while 未完成:
step = select_next_step(plan, 进度)
result = executor.execute(step)
进度.record(result)
if 计划已失效:
plan = planner.replan(任务, 进度)
严格的 Plan-and-Execute 至少有三个特征:
- 计划被保存为独立状态,而不是只存在于一条模型回复中。
- 运行时根据计划选择下一步或下一位执行者。
- 执行结果偏离预期时,系统存在明确的 Replan 路径。
有些 Agent 提供 update_plan 或 todo 工具,让模型维护一份可见清单。这有助于模型和用户了解进度,但如果主循环从不读取清单来决定下一步,它就不是严格的 Plan-and-Execute。实际系统中的 Executor 也常常是一个 ReAct Agent,所以两种模式可以嵌套使用:外层计划控制方向,内层 ReAct 处理每一步的不确定性。
Reflective Agent
Reflective Agent 关心的不是任务怎么拆分,而是如何评价已经产生的输出。它在一轮工作后让 Evaluator 或 Judge 检查结果,不满足就给出反馈,再做一轮改进。
for round in 1..MAX_ITERATIONS:
output = agent.produce(任务)
feedback = evaluator.evaluate(output)
if feedback.通过: return output
任务 += feedback # 下一轮带着反馈改进
Evaluator 可以是同一个模型配合另一份提示词,也可以是专门的规则、测试程序或另一个 Agent。代码 Agent 运行测试后根据失败信息继续修改,也带有反思性质。不过,严格的 Reflective Agent 会把“是否继续”和“下一轮反馈”明确交给评估器,而不是只依赖执行 Agent 自己判断。
反思循环适合文档审校、代码生成和安全检查等质量敏感任务。代价是额外的模型调用,而且 Judge 本身也可能判断错误。因此必须设置最大迭代次数,并尽量使用测试、Schema 和静态分析等可验证信号,而不是只让两个模型互相评价。
Multi-Agent
Multi-Agent 把任务分给多个不同职责的 Agent,每个 Agent 可用不同的系统提示词、模型、工具和权限。价值不只是 “多开几个模型”,而是隔离上下文和职责,让每个 Agent 只处理自己擅长的部分。
常见协作方式:
- 顺序编排:框架预先确定路由,前一个 Agent 的结果交给下一个,例如先分析需求、再实现、最后评审。
- 并行编排:框架把同一任务同时发给多个 Agent,再由聚合节点合并结果。
- Handoff:当前 Agent 决定下一步交给谁,框架把“移交给某 Agent”包装成工具,运行时切换执行主体,适合客服分流这类职责边界明确的任务。
- Group Chat:共享同一对话历史,由 Manager 选择下一位发言者(模型或轮询等确定性策略)。适合多轮讨论,但共享历史耗上下文,还容易“讨论很多、进展很少”。
- 层次化委派:父 Agent 创建子 Agent,子 Agent 持独立会话、在自己的循环里完成研究或实现,再把结果返回父 Agent。
threads = [subagent(role, 任务) for role in roles]
results = gather_all(threads) # 可并行派发,也可逐个等待
return summarize(results)
层次化委派不会永久转移控制权,这点与 Handoff 不同。父 Agent 可以并行派发多个任务,也能等某个子 Agent 完成后再继续。独立会话能减少主上下文噪声,但父 Agent 只看得到子 Agent 返回的摘要,所以任务描述和结果格式必须写清楚。
多 Agent 会增加调用次数、状态同步和失败处理成本。只有当任务能被拆成相对独立的专业工作,或不同角色确实需要不同权限时,它才比单 Agent 更合适。
Tool-Augmented
Tool-Augmented 不是独立的控制流,而是叠加在 ReAct、Plan-and-Execute 或 Multi-Agent 之上的能力增强:通过工具影响外部环境。模型只负责选工具、生成参数,真正的文件读取、命令执行、数据库查询和 API 请求由宿主程序完成,工具结果再作为 Observation 回到循环。
tool_call = model.choose_tool_and_args()
result = 宿主执行(tool_call) # 真正的读写/执行在宿主发生
messages += [tool_call, result]
工具扩大了行动范围,也扩大了风险。工程上要同时考虑参数校验、超时、输出截断、并行能力、沙箱和审批。工具定义本身还会占输入 Token,所以工具很多时,要按 Agent 权限裁剪工具集,或用工具搜索延迟加载。
Memory-Augmented
Memory-Augmented 也是能力增强,它用外部状态弥补上下文窗口和会话生命周期的限制。常见记忆分三类:
- 工作记忆:当前任务的消息、工具结果、计划和临时状态。
- 情景记忆:过去任务的经历,例如某次修改用了什么方案。
- 语义记忆:相对稳定的知识,例如项目规范、用户偏好、领域事实。
# 写入:当前 turn 产生的关键信息
工作记忆.update(本轮消息、工具结果、计划)
if 是显著事件: 情景记忆.append(片段)
if 是稳定事实: 语义记忆.upsert(事实)
# 读取:构造模型输入时按需取
context += 按需检索(工作记忆, 情景记忆, 语义记忆, 当前问题)
对话历史并不天然等于有效记忆。未经处理地塞入全部历史会带来大量噪声;成熟实现会压缩旧消息、提取稳定事实、记录来源和时间,并在需要时检索相关内容。
RAG Agent
RAG Agent 是 Memory-Augmented 的常见实现:从文档库、代码库或业务数据中检索相关内容,再把结果注入模型上下文。检索可以作为每轮自动执行的前置步骤,也可以注册成工具,由模型按需调用。
# 自动:每轮请求模型前先检索
context += retrievers.retrieve(当前问题)
# 按需:把“检索”注册成工具
docs = tool("search", 当前问题) # 模型按需触发
context += docs
自动检索保证每轮都有知识支持,但可能把无关内容放进上下文;工具检索更灵活,模型可根据中间结果改写查询,但可能因过度自信而不检索。两种方式可以组合使用。RAG 负责提供知识,真正推进任务的仍然是外层 Agent 循环。
Codex:自主 ReAct 与层次化子 Agent
Codex 的主干是会话级 Autonomous ReAct Loop。普通任务从 RegularTask::run 进入 run_turn,核心代码位于:
codex-rs/core/src/tasks/regular.rs
codex-rs/core/src/session/turn.rs
codex-rs/core/src/stream_events_utils.rs
run_turn 每轮从会话历史构造模型输入,再调用 run_sampling_request。模型返回工具调用时,Codex 会路由并执行工具。当前响应流结束后,运行时等待尚未完成的工具,将结果写回历史,再根据 needs_follow_up 决定是否继续请求模型。
loop {
let result = run_sampling_request(...).await;
let needs_follow_up = result.needs_follow_up || has_pending_input;
if needs_follow_up && token_limit_reached {
run_auto_compact(...).await;
continue;
}
if !needs_follow_up {
let stop_outcome = run_turn_stop_hooks(...).await;
if stop_outcome.should_block {
continue;
}
break;
}
}
Codex 的循环不只由工具调用推进。Responses API 可以明确要求不要结束当前 turn,用户可以在运行时追加输入,子 Agent 可以向当前会话投递消息,stop hook 也可以阻止结束并注入新的提示。上下文达到限制时,run_auto_compact 会选择合适的压缩路径,压缩后继续当前任务。
工具审批和沙箱没有直接堆在 run_turn 中,而是位于工具调度链。ToolOrchestrator 会处理审批要求、沙箱策略、权限钩子和必要的升级重试。工具是否能够并行执行,也由各自的运行时能力决定。
Codex 有 update_plan 工具,但它只发送 EventMsg::PlanUpdate 并返回 Plan updated。run_turn 不会读取这份计划来选择下一步,所以它是可见的任务清单,不是 Plan-and-Execute 编排器。Plan mode 使用另一套 proposed plan 解析逻辑,也不能与 update_plan 混为一谈。
Codex 还提供 spawn_agent、等待和消息发送等多 Agent 工具。子 Agent 拥有独立线程和 Session,并运行自己的 run_turn。父 Agent 可以等待结果,也可以向已有子 Agent 发送后续任务。这是层次化 Multi-Agent,不是由中央 Manager 驱动的 Group Chat。
因此,Codex 的模式可以概括为:以自主 ReAct 为主干,叠加 Tool-Augmented、上下文压缩、Hooks、审批和层次化子 Agent。计划帮助展示进度,但不会接管主循环。
OpenCode:持久化 ReAct 与 task 委派
OpenCode 当前实际执行链仍由 packages/opencode 中的 SessionPrompt 驱动:
SessionPrompt.prompt
-> SessionPrompt.loop
-> SessionPrompt.runLoop
-> SessionProcessor.process
-> LLM.stream
SessionPrompt.runLoop 每轮重新读取过滤压缩后的消息,找到最近的用户消息、Assistant 消息和待处理任务。只有 Assistant 已经完成,并且没有未处理的工具调用时,循环才退出。
AI SDK 负责一次 LLM.stream 中的工具执行和流事件,SessionProcessor 把工具参数、状态和结果持久化为消息 Part。一次模型请求结束后,外层 runLoop 重新读取历史,把工具调用和结果转换为模型消息,再发起下一次请求。OpenCode 的跨请求 ReAct continuation 因此位于 Session 循环,而不是完全封装在 AI SDK 内部。
当上下文溢出时,循环会创建 compaction 任务。隐藏的 compaction Agent 生成摘要,并根据上下文预算保留最近的 turn。压缩结束后,系统可以注入 synthetic user message,让主 Agent 从摘要状态继续工作。这使 OpenCode 的自主循环能够持续运行较长时间。
OpenCode 的 Multi-Agent 入口是 packages/opencode/src/tool/task.ts。父 Agent 调用 task 后,工具检查当前子 Agent 深度和权限,再创建带有 parentID 的子 Session:
const nextSession = yield* sessions.create({
parentID: ctx.sessionID,
title: params.description + ` (@${next.name} subagent)`,
agent: next.name,
permission: childPermission,
})
之后,ops.prompt 使用子 Session ID 启动另一条 SessionPrompt 循环。子 Agent 有独立的历史、模型和权限。前台任务等待子 Agent 完成,并把结果作为工具输出返回。实验性的后台模式立即返回运行状态,完成后再向父 Session 注入 synthetic user message。传入 task_id 还可以复用已有子 Session。
OpenCode 也有 plan Agent。它主要通过权限限制编辑,只允许写计划文件。当前可以核验的切换工具是 plan_exit:用户确认后,工具在同一个 Session 中写入一条 Agent 为 build 的 synthetic user message,下一轮由 runLoop 继续执行。
运行时不会读取计划文件自动选择下一步,用户也可以直接使用 build 或拒绝切换。因此,OpenCode 的 plan 模式是可选的计划阶段和权限切换机制,不是严格的 Plan-and-Execute。
OpenCode 可以概括为:持久化的自主 ReAct 循环,加上权限控制、上下文压缩和通过 task 实现的层次化 Multi-Agent。
Microsoft Agent Framework:组合多种模式
MAF 与 Codex、OpenCode 最大的区别,是没有把所有控制逻辑放进一个 Session 主循环。它把单 Agent 调用、工具循环、反思循环和多 Agent 工作流拆成不同层次。
ChatClientAgent.RunCoreAsync 会准备消息、会话和运行选项,然后只调用一次 IChatClient.GetResponseAsync:
ChatResponse chatResponse = await chatClient.GetResponseAsync(
inputMessagesForChatClient,
chatOptions,
cancellationToken);
一次 IChatClient 调用不代表底层只请求模型一次。ChatClientExtensions.WithDefaultAgentMiddleware 默认插入 FunctionInvokingChatClient。它负责识别函数调用、执行函数、写入函数结果并继续请求模型。因此,MAF 的 ReAct 工具循环位于 Microsoft.Extensions.AI 的聊天客户端装饰器,而不是 ChatClientAgent 自己的 while 循环。
ChatClientAgent
-> FunctionInvokingChatClient
-> 实际模型客户端
MAF 的 Reflective Agent 由另一层 LoopAgent 实现。LoopAgent 包装一个 AIAgent,每轮结束后调用 LoopEvaluator。AIJudgeLoopEvaluator 可以使用模型评估结果,CompletionMarkerLoopEvaluator 可以检查完成标记。Evaluator 要求继续时,反馈会进入下一轮;全部 Evaluator 都不要求继续,或者达到最大迭代次数时,循环结束。
多 Agent 和 Plan-and-Execute 则位于 Microsoft.Agents.AI.Workflows。Workflow 使用 Executor 表示节点,使用 Edge 路由消息,TurnToken 通知 Agent 节点处理已经累积的消息。当前提供的主要编排包括:
| 编排 | 对应的工作方式 |
|---|---|
SequentialWorkflowBuilder | 多 Agent 顺序执行 |
ConcurrentWorkflowBuilder | 扇出执行后聚合结果 |
HandoffWorkflowBuilder | 当前 Agent 选择目标 Agent |
GroupChatWorkflowBuilder | Manager 选择下一位发言者 |
MagenticWorkflowBuilder | 计划、进度账本和动态执行者选择 |
Handoff 会注入形如 handoff_to_1 的工具,再通过内部映射找到目标 Agent。Group Chat 的 Manager 不一定是模型,内置的 RoundRobinGroupChatManager 使用确定性的轮询策略。
Magentic 是 MAF 中最接近严格 Plan-and-Execute 的实现。管理器首先生成事实和计划,保存到 Task Ledger。之后每轮更新 Progress Ledger,其中包含任务是否完成、是否陷入循环、是否仍有进展、下一位发言者和给该发言者的指令。
await this._manager.UpdateProgressLedgerAsync(taskContext, context, cancellationToken);
if (taskContext.ProgressLedger.IsRequestSatisfied)
{
await this.PrepareFinalAnswerAsync(taskContext, context, cancellationToken);
return;
}
if (taskContext.ProgressLedger.IsInLoop ||
!taskContext.ProgressLedger.IsProgressBeingMade)
{
taskContext.TaskCounters.StallCount++;
}
达到停滞条件后,ResetAndReplanAsync 会重置协调状态并重新规划。正常情况下,编排器根据 NextSpeaker 找到 Agent,再发送具体指令和 TurnToken。所以 Magentic 不是按固定清单机械执行,而是使用计划约束方向,使用进度账本动态协调多个 Agent。
三个项目的模式可以用下面这张表对齐:
| 维度 | Codex | OpenCode | MAF |
|---|---|---|---|
| ReAct 循环位置 | run_turn | SessionPrompt.runLoop | FunctionInvokingChatClient |
| Autonomous Loop | 原生主干 | 原生主干 | 由装饰器或 Workflow 组合 |
| Plan-and-Execute | update_plan 不驱动执行 | plan 模式不自动驱动执行 | Magentic 会读取计划和进度账本 |
| Reflective Agent | Hooks 和运行结果可要求继续 | 根据工具结果和状态继续 | LoopAgent 加 LoopEvaluator |
| Multi-Agent | 独立 Agent Session | task 创建子 Session | Workflow、Handoff、Group Chat、Magentic |
| Tool-Augmented | 内置工具、MCP 和延迟工具 | 工具注册表、插件和 MCP | IChatClient 工具与上下文提供者 |
| Memory 与 RAG | 会话历史、压缩和外部工具 | 持久化消息、压缩和检索工具 | Session、历史提供者和 AIContextProvider |
从这三种实现可以看到,模式没有唯一的落点。Codex 和 OpenCode 把自主循环放在 Session 层,再通过工具派生子 Agent;MAF 把函数循环、反思循环和多 Agent 编排分散到不同的可组合层。选择架构时,应先确认任务由谁决定下一步、计划是否真的参与调度、是否需要隔离多个 Agent 的上下文,再决定叠加哪些工具、记忆和 RAG 能力。