Agent 系统组成

Planner、Executor、Memory、Tools 四大支柱如何协同,以及 Agent 循环与架构模式

18 min read Part of Agent · Ch. 2
← 上一层级:学习路径 · Part 03 · Agent 设计与工程

Agent 系统组成

flowchart TD
  U[用户目标] --> P[Planner
拆解目标 / 选策略]
  P --> X[Executor
执行当前步骤]
  X --> T[Tools
搜索 / API / 文件 / 浏览器]
  T --> X
  X --> M[Memory
任务状态 / 历史 / 长期信息]
  M --> P
  X --> R{完成或重规划?}
  R -->|重规划| P
  R -->|完成| O[最终结果]

上一篇讲的是概念:Agent 不是“更会聊天的模型”,而是一种围绕目标推进任务的系统。 这一篇往下走一步,讨论它到底是怎么“搭起来”的。

很多入门材料会把 Agent 简化成先看定义:一个 LLM + 一组工具。这个说法不算错,但太薄。它只能帮助你理解 Agent 为什么比普通 Chatbot 更像“执行者”,却不足以解释:为什么有些 Agent 能稳定做完任务,有些却会在第三步开始乱跑;为什么同样接了工具,有的系统可控,有的系统不可控;为什么很多问题并不出在模型本身,而是出在状态管理、工具设计和执行边界上。

从工程视角看,一个可用的 Agent,至少需要处理四类问题:

  1. 目标如何拆解
  2. 动作如何执行
  3. 上下文如何维持
  4. 外部能力如何接入

这四类问题,通常就对应四个核心部件:

  • Planner(规划器)
  • Executor(执行器)
  • Memory(记忆)
  • Tools(工具)

OpenAI 在面向开发者的 Agent 指南里强调,构建 Agent 的关键不只是模型调用,而是把模型、工具、状态和工作流组织成一个可反复运行的系统。Anthropic 也明确指出,许多成功的 Agent 系统并不复杂,但它们会非常认真地处理任务分解、工具定义和执行闭环,而不是把一切都丢给一个 prompt。(developers.openai.com) (OpenAI 开发者)

先给一个系统视角:Agent 不是“一个角色”,而是一条循环

如果只看静态结构,很容易误以为 Planner、Executor、Memory、Tools 是四个独立模块,像传统分层架构那样各管一摊。 但 Agent 更接近一个循环系统,不是一个静态页面。

它的典型运行方式是:

接收目标
 -> 判断当前状态
 -> 决定下一步
 -> 调用工具或执行动作
 -> 读取结果
 -> 更新状态
 -> 再决定下一步
 -> ...
 -> 结束 / 失败 / 请求人工确认

所以,理解 Agent 组成时最重要的一点是:

四个组件的意义,不是把功能机械分箱,而是共同支撑这个“观察—决策—执行—反馈”的闭环。

四大支柱:Planner、Executor、Memory、Tools

先看定义:Agent 系统通常由 Planner 负责“怎么走”、Executor 负责“怎么做”、Memory 负责“现在知道什么”、Tools 负责“能做什么”,而 LLM 经常在其中扮演推理和协调核心。

组件英文核心职责
规划器Planner理解目标、拆分步骤、决定策略、必要时重规划
执行器Executor落实动作、调用工具、处理返回结果和异常
记忆Memory维持上下文、任务状态和长期可复用信息
工具Tools提供检索、计算、读写、API 调用等外部能力

这四个概念里,Planner 和 Executor 更像逻辑角色,Memory 和 Tools 更像系统能力。 它们不一定都要做成单独服务,也不一定都要用不同模型实现,但在设计上最好把职责想清楚,否则系统一复杂就会互相污染。

Planner:决定“往哪里走”,而不只是“下一步调哪个工具”

规划器最容易被误解成“把大任务拆成几步清单”。这只是它最表层的工作。

一个成熟一点的 Planner,至少要处理四件事:

  1. 理解目标和约束
  2. 选择策略
  3. 决定步骤顺序
  4. 根据执行反馈修正计划

Planner 的真正职责:不是列计划,而是管理任务方向

当用户说:

“帮我分析这个报错,定位原因,并给出可验证的修复方案。”

Planner 真正要回答的不是“第一步、第二步、第三步是什么”这么简单,而是:

  • 这是一次性回答,还是多步任务?
  • 先查日志,还是先读代码?
  • 需不需要补充信息?
  • 是否要先验证环境?
  • 哪些步骤可以并行,哪些必须串行?
  • 执行失败时应该换路,还是停止?

这意味着,Planner 管的是任务推进策略,而不仅是一个待办列表。

Planner 常见的三种工作方式

方式特点适用情况
隐式规划不显式输出完整计划,每轮只决定下一步简单任务、低成本链路
显式规划先产出整体计划,再按计划执行复杂多步任务、需要全局视角
动态重规划执行中持续根据反馈修正原计划开放环境、工具结果不确定、任务容易偏航

很多教程会默认“先规划再执行”看起来更高级,但现实里不一定总是最优。 对于短任务,显式规划可能只是在增加 token 消耗和路径长度。Anthropic 对 Agent 实践的一个重要建议,就是优先使用简单、可组合的模式,而不是一开始就引入重型框架和复杂规划器。(anthropic.com) (Anthropic)

Planner 的失败模式

规划器常见问题不是“不会规划”,而是规划得看起来很合理,但对执行不友好。

典型失败包括:

  • 过度规划:对一个三步任务先写一大段宏观计划,徒增成本;
  • 伪规划:输出看似完整的步骤,其实没有实际约束意义;
  • 不重规划:明明执行结果已经改变前提,仍机械按原路线继续;
  • 规划脱离工具现实:计划里要求的动作,实际上系统根本没有对应工具;
  • 忽略终止条件:不断追加新步骤,任务越做越远。

所以,Planner 的价值不在于“写得像项目经理”,而在于它能否在有限工具和真实反馈下持续做出合适决策。

Executor:负责把“想法”变成“动作”

如果说 Planner 负责方向,Executor 负责的就是落地。

它通常要做这些事:

  • 接收当前步骤或子任务;
  • 选择或调用工具;
  • 传递参数;
  • 处理工具结果;
  • 捕获异常;
  • 将结果写回任务状态;
  • 决定是否能结束当前步骤。

先看定义:Executor 不是“简单调用 API 的胶水层”,而是把计划真正转化为系统行为的执行层。

Executor 处理的,不只是“调用成功”

一个很常见的误区是:认为执行器的职责就是“模型决定调用哪个工具,然后代码把工具跑一下”。

实际上,生产可用的 Executor 更关心的是:

  • 这个工具是否真的该现在调用?
  • 参数是否完整、格式是否正确?
  • 工具返回的是成功、失败,还是部分成功?
  • 返回值是否需要清洗、结构化、摘要化后再送回模型?
  • 这次失败是否可以重试?
  • 如果不能重试,是该降级、换路,还是请求人工确认?

这意味着,Executor 不是一个被动 adapter,而是一个带有动作控制与错误处理能力的运行时层。

Executor 为什么不能“完全听模型的”

如果执行器对模型输出无条件照做,会出现很多问题:

  • 模型参数拼错,直接导致工具报错;
  • 模型把上一步结果误读,连续执行错误动作;
  • 模型频繁调用同一个工具,陷入无效循环;
  • 模型在高风险场景下跳过确认直接执行;
  • 模型把模糊意图翻译成过于激进的操作。

因此,Executor 往往需要加一层非模型约束,例如:

  • 参数 schema 校验;
  • 工具白名单;
  • 高频调用限流;
  • 步数预算;
  • 高风险动作确认;
  • 幂等性检查;
  • 超时与取消机制。

这里的原则很简单:

Planner 可以相对开放,但 Executor 必须相对保守。

Memory:Agent 真正的“上下文底盘”

上一篇提到,Agent 和普通聊天系统的一个关键差异,是它要处理多步任务。 而只要有多步任务,就一定会有状态。

这个状态并不只来自“对话历史”,还包括:

  • 当前任务做到哪一步;
  • 已经拿到哪些中间结果;
  • 哪些工具已经调过;
  • 哪些约束仍然有效;
  • 哪些用户偏好应被继承;
  • 哪些信息只对当前会话有效,哪些应该长期保留。

这些东西合在一起,才构成 Agent 的 Memory。

记忆不只是一种,至少有三层

类型英文作用常见实现
短期记忆Short-term Memory当前对话和最近几步上下文上下文窗口、滑动历史
工作记忆Working Memory当前任务中的中间状态、计划、结果、待办状态对象、任务上下文、结构化变量
长期记忆Long-term Memory跨会话保留的偏好、历史、稳定知识数据库、向量库、用户档案、事件日志

为什么工作记忆比很多人想象中更重要

很多初学者一提到 Memory,就想到“长期记住用户偏好”或者“向量库召回历史”。 这些当然重要,但在实际 Agent 系统中,工作记忆往往更关键

因为任务执行中的大多数问题,都发生在当前会话内部:

  • 忘了上一步已经查过某个文件;
  • 忘了某个 API 返回过错误码;
  • 忘了用户刚刚否决过某种方案;
  • 忘了任务已经进入“等待确认”状态;
  • 重复执行已经完成的步骤。

这些都不是“长期记忆缺失”,而是“任务状态管理失败”。

所以,在很多系统里,真正该先设计好的不是记忆召回算法,而是:

  • 当前任务状态有哪些字段;
  • 哪些字段由工具更新;
  • 哪些字段由模型读写;
  • 哪些字段必须结构化;
  • 哪些字段会影响终止条件。

记忆的一个常见误区:把所有东西都塞进上下文

上下文窗口变大后,很容易出现一个错误直觉: “反正上下文很长,那就把所有历史都带上。”

这通常会带来三个问题:

  1. 成本上升:每一步都在重复消耗无关上下文;
  2. 注意力稀释:真正关键的状态被淹没;
  3. 行为漂移:模型抓住旧信息不放,忽略当前最新状态。

Anthropic 在 context engineering 的文章里反复强调,问题不在于上下文够不够长,而在于你是否把当前步骤真正需要的状态组织清楚。(anthropic.com) (Anthropic)

所以,Memory 设计的重点不是“存更多”,而是“让该出现的信息在该出现的时候出现”。

Tools:决定 Agent 到底能做什么,也决定它会怎么出错

工具是 Agent 与外部世界接触的接口。 没有工具,Agent 只能在文本里“思考”;有了工具,它才能真正检索、计算、读写、调用业务系统、产生状态变化。

MCP 的官方介绍把这件事说得很清楚:它的目标就是让 AI 应用能够连接外部数据源、工具和工作流,从而访问信息并执行任务。(modelcontextprotocol.io) (模型上下文协议)

工具的常见类型

类型作用例子
信息获取获取外部事实或状态搜索、数据库查询、读取文档、网页抓取
计算执行补足模型在精确计算和程序执行上的不足计算器、代码执行、SQL 执行
读写操作与文件或系统状态交互文件读写、对象存储、表单填写
业务动作触发真实业务流程发邮件、建工单、改订单、调内部 API

工具设计的质量,会直接决定 Agent 的质量

Anthropic 专门写过一篇关于工具设计的工程文章,核心观点非常直接:Agent 的效果,很大程度上取决于你提供给它的工具是否设计得足够好。(anthropic.com) (Anthropic)

一个好工具通常具备这些特征:

  • 名称明确,不和别的工具重叠;
  • 描述清楚,告诉模型它什么时候该用、什么时候不该用;
  • 参数 schema 明确,减少猜测;
  • 返回结果结构化,便于后续处理;
  • 失败语义清晰,模型能据此调整下一步;
  • 权限边界清楚,不把高风险能力随便暴露。

而一个坏工具会让 Agent 出现两类典型问题:

  1. 误用:明明不该调用,却频繁调用;
  2. 弃用:明明该用,却完全想不到去用。

很多所谓“模型不会用工具”的问题,本质上其实是工具设计得不适合被模型理解。

工具越多,不一定越强

这是 Agent 系统里一个很常见但不直观的 trade-off。

直觉上,好像工具越多,Agent 能力越强。 但实际情况往往是:

  • 选择空间过大,模型更难做对决策;
  • 工具之间语义重叠,容易误选;
  • 上下文中塞入太多工具描述,成本和噪声都上升;
  • 工具链变长,失败路径变复杂。

Anthropic 关于 MCP 和代码执行的文章甚至指出,随着工具数量增加,单纯把所有工具定义一股脑塞进上下文,会显著拖慢系统并推高成本。(anthropic.com) (Anthropic)

所以更稳妥的原则通常是:

  • 先给最小必要工具集;
  • 再按任务域分层暴露工具;
  • 能合并就别重复;
  • 工具要面向任务,而不是面向“展示能力”。

四个组件如何协同:Agent 循环

把四个组件放到一起,就能得到一个更完整的运行视图:

User Goal
  -> Planner 理解目标与约束
  -> Memory 提供当前状态与历史上下文
  -> Planner 决定下一步
  -> Executor 调用 Tools 执行动作
  -> Tools 返回结果
  -> Executor 处理结果并写入 Memory
  -> Planner 根据最新状态继续决策
  -> ...
  -> 输出结果 / 请求确认 / 终止

也可以把它抽象成更常见的循环:

Observe -> Think -> Plan -> Act -> Observe -> ...
阶段发生什么
Observe读取用户输入、环境状态、工具结果、已有记忆
Think判断当前任务状态,理解问题所在
Plan决定下一步执行策略或更新计划
Act调用工具、执行动作、产出中间结果
Observe读取执行反馈,决定是否继续循环

这里有一个非常重要的工程理解:

Agent 不是“先想完再做”,而是“边做边更新状态,边更新状态边调整路径”。

这也是它与传统脚本式工作流的根本差异。

LLM 在系统里到底扮演什么角色

很多人会说“LLM 是 Agent 的大脑”。这个比喻有帮助,但也容易过度人格化。

更准确一点说,LLM 在系统里通常扮演的是三个角色:

  1. 解释器:理解用户目标和工具返回结果;
  2. 决策器:决定下一步行动;
  3. 生成器:生成计划、参数、答案或中间总结。

这意味着,LLM 更像一个通用推理控制器,而不是独立承担所有系统功能的“人格核心”。

单模型与多模型

在实现上,LLM 可能有两种常见组织方式:

模式特点适用情况
单模型同一个模型同时负责规划和执行决策简单系统、实现快、调试路径短
多模型强模型负责规划,快模型负责执行或摘要复杂任务、成本敏感、需要分层控制

OpenAI 的 Agent 指南也强调,模型选择应和任务复杂度匹配:不是所有步骤都值得用最强模型,很多场景更适合把高成本推理留给关键步骤,把普通执行交给更便宜、更快的模型。(developers.openai.com) (OpenAI 开发者)

别把“Planner = 一个模型”“Executor = 另一个模型”当成硬规则

这是一个非常常见的误解。

Planner、Executor 是逻辑职责,不一定非要映射成两个独立模型。 在很多系统里:

  • Planner 和 Executor 的决策都由同一个模型完成;
  • 只是通过不同 prompt、不同状态输入、不同控制逻辑来区分角色;
  • 真正的执行动作由程序代码和工具运行时承担。

所以,“分角色”是设计方法,不是部署规定。

常见架构模式

不同任务复杂度,对应的 Agent 架构也会不同。 不是所有系统都需要完整分层,也不是所有系统都适合单循环。

1. 单循环架构

这是最常见的最小形态:

观察 -> 决策 -> 调工具 -> 读取结果 -> 再决策

优点是简单、实现快、路径短。 缺点是复杂任务容易“走一步看一步”,缺少全局视野。

适合:

  • 步骤较少的任务;
  • 工具数量有限;
  • 用户容忍一定程度的互动补充;
  • 原型验证阶段。

2. Plan-and-Execute 分层架构

先由 Planner 生成一个粗计划,再由 Executor 逐步执行,每步执行后必要时回写状态并触发重规划。

优点:

  • 更适合长任务;
  • 更容易表达顺序依赖;
  • 更利于插入检查点和人工确认。

缺点:

  • 计划可能一开始就错;
  • 维护计划本身有成本;
  • 如果环境变化快,原计划很快失效。

适合:

  • 明确多步任务;
  • 步骤间依赖强;
  • 需要更强可解释性。

3. 模块化架构

除了 Planner、Executor、Memory、Tools,还引入更多可替换角色,例如:

  • Critic / Reviewer
  • Router
  • Safety Guard
  • Summarizer
  • Retriever

优点是灵活、可实验、可替换。 缺点是复杂度增长快,调试难度上升。

适合:

  • 平台型系统;
  • 高定制场景;
  • 需要多种执行策略共存;
  • 后续可能发展成多 Agent 系统。

模式选择建议

任务特征更适合的模式
步骤少、路径短、工具少单循环
步骤多、依赖强、需要计划可见性Plan-and-Execute
工具多、场景复杂、需要治理与可替换性模块化

关键不是“哪种更高级”,而是复杂度是否真的值得引入

和传统软件架构相比,Agent 架构难在哪

Agent 系统之所以难,并不是因为它组件更多,而是因为它的控制流不再完全预定义。

维度传统软件Agent 系统
控制流开发时写死运行时部分由模型决定
状态推进规则明确、分支固定依赖上下文和执行反馈
错误处理显式分支和异常捕获还要处理模型误判和工具误用
测试方式输入输出相对稳定非确定性更强,需要行为评测
扩展方式改代码、加模块既要改代码,也要改工具、上下文、提示和评测

这意味着,做 Agent 时你不能只像写传统后端那样思考“接口和数据库”;也不能只像写 prompt 那样思考“语气和格式”。 它要求你同时处理:

  • 运行时决策;
  • 工具能力暴露;
  • 状态组织;
  • 非确定性测试;
  • 安全和治理。

这也是为什么很多看起来“只是接几个 API”的 Agent,最后会演变成一个完整系统工程。

实践中最容易踩的坑

1. 过早模块化

还没验证任务路径,就先拆 Planner、Executor、Critic、Router、Memory Manager、Tool Broker 六七个模块。 结果是系统看起来很完整,但没有一个链路真正稳定。

更稳妥的顺序通常是:

  • 先做最小闭环;
  • 识别真正痛点;
  • 再把痛点位置模块化。

2. 把 Memory 当日志堆栈

什么都记、什么都传、什么都不淘汰。 最后模型在庞杂上下文里抓不住重点,成本还一路上升。

3. 工具设计面向开发者,而不是面向模型

接口从人类程序员视角看很优雅,但模型很难判断何时使用、如何组合。 结果就是“看起来工具很多,实际上 Agent 不会用”。

4. 缺少终止条件

这是最典型的 Agent 工程事故来源之一。 如果没有明确的:

  • 最大步数;
  • 总超时;
  • 预算上限;
  • 完成判定;
  • 人工接管点;

系统就会在“不太确定但还想再试一下”的状态里不断循环。

5. Executor 过于放权

让模型自由发起任何动作,而没有参数校验、权限约束和高风险确认。 这类系统可能在 demo 里很惊艳,但离生产往往还很远。

一个最小 Agent 的实现骨架

如果只追求“最小可运行”,一个 Agent 可以非常简单:

  1. 接收用户目标;
  2. 把当前上下文发给模型;
  3. 模型返回“回答”或“工具调用请求”;
  4. 如果是工具调用,则执行工具;
  5. 把工具结果放回上下文;
  6. 继续循环,直到模型给出最终结果或达到终止条件。

可以抽象成这样:

while not done:
    state = read_context_and_memory()
    decision = model(state)
    if decision == tool_call:
        result = executor.run(tool, args)
        memory.update(result)
    else:
        return final_output

这就是很多“Hello World Agent”的真实骨架。 后续所谓更复杂的能力——ReAct、Plan & Execute、Reflection、Multi-Agent——本质上都是在这个最小闭环上增加控制、分层和治理。

设计建议:从“能跑”到“能管”

如果你要开始搭一个 Agent,下面这些建议通常比“先选最强框架”更重要:

1. 先确定状态模型,再写 prompt

很多 Agent 问题看起来像 prompt 问题,其实是状态模型不清楚。 先定义:

  • 当前任务有哪些状态字段;
  • 哪些状态由工具更新;
  • 哪些状态由模型解释;
  • 完成和失败如何表示。

2. 工具少一点,但定义好一点

比起一开始暴露二十个工具,更好的做法通常是先暴露三个真正必要、语义清晰、返回结构稳定的工具。

3. 给 Planner 自由,给 Executor 护栏

规划层可以更灵活;执行层要更保守。 把风险控制压在 Executor 和工具运行时,而不是完全寄希望于模型“自己懂”。

4. 把工作记忆结构化

与其在上下文里堆自然语言历史,不如显式维护:

  • 当前计划
  • 已完成步骤
  • 待确认事项
  • 最近工具结果
  • 失败原因
  • 预算与步数

这会显著提高系统的可控性。

5. 一开始就设计终止条件

最大步数、总超时、预算上限、人工中断、高风险确认点,这些都不是后期“补一下”的细节,而是 Agent 运行时的基本生命体征。

核心概念速查

概念一句话
Planner负责理解目标、选择策略、拆解步骤并在必要时重规划
Executor负责把计划转成动作,调用工具并处理执行结果
Memory负责维持短期上下文、任务状态和长期可复用信息
Tools提供 Agent 与外部世界交互的能力接口
Agent Loop观察、决策、执行、反馈的持续闭环
Working Memory当前任务中的中间状态,比长期记忆更直接影响执行质量
Plan-and-Execute先做整体计划,再逐步执行的分层架构模式

小结

Agent 系统的核心,不是“模型会不会思考”,而是 Planner、Executor、Memory、Tools 这四类能力能否形成一个稳定闭环。

Planner 决定方向,Executor 负责落地,Memory 维持状态,Tools 连接外部世界。LLM 往往承担其中的解释、决策和生成角色,但它不是全部;真正决定系统是否可用的,往往是状态如何组织、工具如何设计、执行层如何加护栏、以及整个循环何时结束。

理解了这四个组成部分,下一步就可以进入 Agent 最经典的模式之一: 为什么“思考—行动—观察”这种交替循环,会成为很多 Agent 实现的基础。