把 Agent 架构拆开:为什么它一定会有 Planner、Memory 和 Tool
这篇笔记只做一件事:把 Harness Agents / Agent 架构拆成核心部件、职责边界和执行链路,方便以后快速捡回整体认知。
这阵子大家聊 Agent,很容易一上来就聊模型能力、工具调用、提示词技巧。可一旦你真的开始做,或者哪怕只是认真看几套实现,很快就会发现:Agent 真正难的部分,根本不只在模型。
模型只是脑子的一部分。一个能连续工作的 Agent,还得知道现在要做什么、下一步该做什么、哪些结果要记住、哪些动作可以执行、出了错怎么收尾。少了这些,它更像一个会调工具的大号聊天机器人,不太像一个能跑完整任务的系统。
所以这篇我不讲某个框架,也不讲接入教程。我只想把 Agent 架构拆成几层,留一份以后自己回头看也能很快捡起来的笔记。
先记结论
LLM不等于Agent。模型只负责理解、判断和生成,不负责把整个任务跑完。- 真正把系统撑起来的,通常是
Planner、Executor、Memory、Tool这几层。 - 多 Agent、流式输出、并行、重试 这些看起来很高级的能力,说到底还是这条执行链路的组合和调度。
为什么只盯着模型和工具调用,理解会一直差一截
很多人第一次接触 Agent,脑子里会默认一个画面:
- 用户提问
- 模型判断要不要调工具
- 工具返回结果
- 模型生成答案
这个画面不算错,但它只够解释一个很短的 demo。
只要任务稍微复杂一点,问题马上就来了。比如:
- 这个任务要不要先拆步骤
- 拆完的步骤按什么顺序跑
- 中途某一步失败了,是重试、换路,还是直接停
- 前面查到的信息要不要记住,记多久
- 结果是不是要边执行边返回
- 多个子任务能不能并行
你会发现,这些都不是“模型会不会说话”的问题,而是系统怎么组织执行的问题。
我现在更愿意把 Agent 理解成一个带推理能力的执行系统。LLM 是这个系统里的判断器,但不是系统本身。把这个前提想清楚,后面的组件划分就自然了。
一个最小可用的 Agent,通常会拆成哪几层
如果你自己从零设计一套 Agent,大概率会拆出下面这几层:
Agent:统一入口,负责收请求、管流程、给外部一个稳定接口Planner:把目标翻译成步骤,或者在执行中途修正步骤Executor:真的去跑步骤,处理成功、失败、重试和结束条件Tool:和外部世界打交道,比如查数据、发请求、改文件、调 APIMemory:保存对话历史、任务上下文、阶段结果和长期记忆LLM:负责理解意图、选择动作、整理结果、生成回答Callback/Observer:补日志、监控、审计、限流、追踪这些横切能力
如果以后又忘了,我觉得可以先记一句最短的话:Planner 负责想步骤,Executor 负责跑步骤,Tool 负责碰外部世界,Memory 负责别忘事。
下面一层层拆。
Agent:入口和总协调
Agent 是你从外部看到的那一层。用户通常不会直接调用 Planner 或 Executor,而是把一句话、一个任务对象,或者一段上下文扔给 Agent。
它最重要的工作不是“亲自做事”,而是组织别的组件协作。说得直白一点,Agent 更像一个总控。
- 输入通常是用户请求、会话信息、运行配置
- 输出通常是最终回答、执行结果,或者中间流式事件
- 常见坑是把太多业务逻辑塞进
Agent,最后它变成一个什么都懂一点、但特别难扩展的巨型入口
如果这一层设计得太重,后面很多能力都会被绑死。比如你想加并行执行、换一套 Memory、或者把回答改成流式输出,最后都会变成拆入口。
Planner:把目标翻成步骤
Planner 的存在,是因为很多任务不能拿到一句话就直接开跑。
用户说“帮我看下这个订单为什么没同步成功”,系统真正要做的,可能是先读日志,再查数据库,再看消息队列状态,最后再整理结论。这里面不是一次工具调用,而是一串动作。
Planner 负责的,就是把“目标”翻成“可执行步骤”。有时它会先给出一份完整计划,有时只给出下一步,然后根据执行结果边走边改。
- 输入是目标、上下文、历史状态,有时还包括约束条件
- 输出是步骤列表、子任务、依赖关系,或者下一步建议
- 常见坑是把
Planner想得太聪明,什么都要提前规划完。现实里很多任务信息不全,计划往往只能先走一步看一步
我自己会把 Planner 理解成一个“把模糊目标压缩成清晰动作序列”的层。它决定的不是答案,而是路线。
Executor:把步骤真的跑起来
有了计划,不代表系统就真的动起来了。真正去执行步骤、串结果、判断是否继续的,是 Executor。
它解决的是行动问题,而不是理解问题。
比如某一步要求调用天气接口、某一步要求写文件、某一步要求等待前一步结果再继续,这些都是 Executor 的责任范围。它要处理步骤顺序、依赖、异常、重试、超时,有时还要决定哪些步骤可以并发。
- 输入是计划、当前步骤、工具集合、运行时状态
- 输出是步骤结果、状态变化、下一步执行信号
- 常见坑是把它做成纯粹的“顺序调用器”。一旦涉及重试、回滚、并行或中断,这种实现很快就不够用
很多 Agent demo 看起来已经能用了,其实只是 Executor 还没遇到脏活。
Tool:连接外部世界
没有 Tool,Agent 基本只能停在“说得像那么回事”。
Tool 的价值很简单:让系统不只会生成文本,还能真正接触外部世界。它可以去查数据库、调用搜索接口、读写文件、发 HTTP 请求、操作浏览器,或者触发别的业务能力。
- 输入是结构化参数,而不是一句自然语言废话
- 输出是结构化结果、状态码、错误信息,最好别只回一大段模糊文本
- 常见坑是工具描述写得太虚,参数定义太松,导致模型经常调错工具,或者把参数拼得乱七八糟
如果把 Tool 看成系统的“手”,那一个很现实的问题就是:手不能只有名字,还得知道能抓什么、怎么抓、抓错了会怎样。
Memory:保存上下文和状态
Memory 这层很容易被低估。很多人会把它理解成“保存聊天记录”,但真做任务时,它远不止这个作用。
一个稍微长一点的任务,系统至少要记住几类东西:
- 前面对话里已经确认过的事实
- 任务进行到了哪一步
- 某个工具刚刚返回了什么
- 哪些结果值得长期保留,供下次继续用
所以 Memory 既可能是短期会话状态,也可能是长期知识记忆。前者更像运行时上下文,后者更像可检索的经验库。两者经常会混在一起被提到,但它们解决的问题并不一样。
- 输入是对话、步骤结果、状态变更、外部检索内容
- 输出是当前上下文、补充事实、长期记忆命中结果
- 常见坑是把什么都往里塞,最后上下文又长又脏,真正有用的信息反而被淹掉
很多“Agent 越聊越笨”的问题,最后都不是模型突然不行了,而是记忆层已经乱了。
LLM:负责理解、选择和生成
LLM 当然还是核心,但它更像这个系统里的判断器和语言接口。
它至少负责几件事:
- 理解用户到底想做什么
- 结合上下文判断下一步动作
- 决定要不要调工具,调用哪个工具
- 把执行结果整理成用户能看懂的话
但有一点要分清:LLM 很强,不代表它应该直接接管全部流程。
只要一个系统把规划、执行、状态控制、外部调用全压在模型的一次输出里,稳定性通常就会越来越差。因为模型擅长的是推理和生成,不擅长替你充当整个运行时。
- 输入是提示词、上下文、工具定义、当前任务状态
- 输出是推理结果、动作选择、结构化调用,或者最终自然语言回答
- 常见坑是把所有控制逻辑都寄托在 prompt 上。这样短期看很省事,长期很难维护
Callback / Observer:把系统接到生产环境
前面几层决定系统能不能跑起来,Callback 或 Observer 决定系统跑起来之后能不能管得住。
这层经常被放到最后讲,但工程上其实很重要。你总得知道一次任务什么时候开始、调了哪些工具、哪一步耗时最长、哪里报错、是否触发了重试、有没有越权调用。
这些事情通常不该硬写在 Planner 和 Executor 里,否则主流程会越来越脏。更合理的做法是留出统一的事件钩子或观察点,让日志、监控、审计、追踪、限流、安全策略挂在旁边。
- 输入是执行事件、状态变化、工具调用记录、异常信息
- 输出是日志、指标、链路追踪、安全审计,或者拦截后的控制结果
- 常见坑是完全没有这层。demo 阶段感觉不到,进真实环境之后问题会一下全冒出来
一次任务从进来到返回,中间到底怎么走
如果只想记住一条主链路,我会记下面这句:
用户输入 → 读取上下文 → 判断任务类型 → 生成/修正计划 → 选择工具 → 执行步骤 → 写回状态 → 判断是否继续 → 生成最终回答
把它展开,大概是这样。
第一步:接收输入,先把上下文拼起来
系统先拿到用户输入,然后去 Memory 里把当前会话相关的信息补齐。这里的重点不是“把历史原样全塞给模型”,而是把当前任务真正需要的上下文整理出来。
如果这一步做得粗糙,后面几层都会跟着一起偏。
第二步:判断这是问答,还是任务
不是每个请求都值得走完整 Agent 链路。
有些问题直接回答就行,有些需要一步工具调用,有些则明显是多步骤任务。系统通常会先做一次分类,决定这次要走简单路径,还是走带规划的复杂路径。
这一步经常由 Agent 配合 LLM 完成。
第三步:生成计划,必要时只生成下一步
如果系统判断这不是一个一步就能结束的问题,就会进入 Planner。
这里未必要一次吐出完整计划。很多时候更稳的做法是先生成下一步,等拿到执行结果后再修正后续路线。任务越不确定,这种方式通常越实用。
第四步:选择工具并执行
到了这一层,Executor 会接手当前步骤,决定调用哪个 Tool,用什么参数调用,拿到结果之后怎么处理。
如果工具返回的是错误、空数据、超时,或者返回值和预期不一致,Executor 还要判断是否重试、换路,还是直接把失败状态抛回上层。
第五步:把结果写回状态,再决定要不要继续
每执行完一步,系统都要更新状态。
哪些结果要写入短期上下文,哪些要留作长期记忆,哪些只是临时中间产物,这里最好分清。否则跑几轮之后,Memory 很容易越堆越乱。
更新完状态之后,系统再判断任务是不是已经结束。如果没有结束,就回到规划或执行阶段继续下一轮。
第六步:整理成最终回答
当系统确认任务结束,或者已经拿到足够结果时,LLM 会把前面产生的信息整理成最终输出。
这一步看起来最像聊天,但它其实只是整条链路的最后一环。前面执行系统搭得好不好,直接决定这一步是“总结结果”,还是“用语言掩盖过程里的混乱”。
那些看起来很高级的能力,其实是怎么拼出来的
很多介绍 Agent 的文章喜欢把一些能力单独拎出来讲,搞得像额外长出来的超能力。其实拆回架构里看,会朴素很多。
多 Agent:把任务分给多个协调单元
所谓多 Agent,通常不是突然出现了什么神秘新物种,而是把原本一个总控要做的事情,拆给多个子 Agent。
比如一个负责检索,一个负责代码修改,一个负责验收。上层再有一个协调者,决定谁先做、谁接谁的结果。说到底,还是任务拆分和结果汇总。
流式输出:改的是返回方式,不是核心链路
流式输出最直接的变化,是系统不等到最后一步才统一返回。
它可以边思考边吐 token,也可以边执行边回传事件,比如“正在查日志”“工具调用成功”“开始生成结论”。底层主链路没变,变化的是结果组织和对外暴露方式。
重试:执行层的容错策略
重试通常不该由模型临场发挥,而应该是 Executor 或运行时策略的一部分。
什么错误可以重试、重试几次、间隔多久、是否换工具、是否需要降级,这些都更像执行框架的问题,而不是 prompt 灵感的问题。
并行:计划层或执行层的调度能力
如果两个子任务互不依赖,比如同时查两个系统状态,那它们理论上就可以并行。
这件事要么在 Planner 阶段就标出依赖关系,要么在 Executor 阶段根据当前步骤类型做调度。说穿了,它就是吞吐和时延优化,不是新的智能来源。
最后留一套以后还能捡起来的心智模型
如果哪天我又把这些东西忘了,我大概会先从这几句话往回想。
Agent是总入口,负责把整条链路串起来Planner负责决定先做什么,后做什么Executor负责把步骤真的执行掉Tool负责让系统接触外部世界Memory负责把上下文和状态留住LLM负责理解、判断、选择和表达Callback/Observer负责让系统可观测、可审计、可治理
再压缩一点,其实就是两层:
- 上半层解决“想什么”:理解任务、规划步骤、判断下一步
- 下半层解决“怎么做”:执行动作、保存状态、控制运行
我觉得理解 Agent 架构,最怕的一件事就是把它神秘化。
它当然有难度,但大多数难点并不玄。很多时候,它只是把推理、执行、状态、工具和工程治理放进了同一条链路里。拆开看,事情就清楚很多。