把 Agent 架构拆开:为什么它一定会有 Planner、Memory 和 Tool
· 10 分钟阅读

把 Agent 架构拆开:为什么它一定会有 Planner、Memory 和 Tool

这篇笔记只做一件事:把 Harness Agents / Agent 架构拆成核心部件、职责边界和执行链路,方便以后快速捡回整体认知。

这阵子大家聊 Agent,很容易一上来就聊模型能力、工具调用、提示词技巧。可一旦你真的开始做,或者哪怕只是认真看几套实现,很快就会发现:Agent 真正难的部分,根本不只在模型。

模型只是脑子的一部分。一个能连续工作的 Agent,还得知道现在要做什么、下一步该做什么、哪些结果要记住、哪些动作可以执行、出了错怎么收尾。少了这些,它更像一个会调工具的大号聊天机器人,不太像一个能跑完整任务的系统。

所以这篇我不讲某个框架,也不讲接入教程。我只想把 Agent 架构拆成几层,留一份以后自己回头看也能很快捡起来的笔记。

先记结论

  • LLM 不等于 Agent。模型只负责理解、判断和生成,不负责把整个任务跑完。
  • 真正把系统撑起来的,通常是 PlannerExecutorMemoryTool 这几层。
  • 多 Agent、流式输出、并行、重试 这些看起来很高级的能力,说到底还是这条执行链路的组合和调度。

为什么只盯着模型和工具调用,理解会一直差一截

很多人第一次接触 Agent,脑子里会默认一个画面:

  1. 用户提问
  2. 模型判断要不要调工具
  3. 工具返回结果
  4. 模型生成答案

这个画面不算错,但它只够解释一个很短的 demo。

只要任务稍微复杂一点,问题马上就来了。比如:

  • 这个任务要不要先拆步骤
  • 拆完的步骤按什么顺序跑
  • 中途某一步失败了,是重试、换路,还是直接停
  • 前面查到的信息要不要记住,记多久
  • 结果是不是要边执行边返回
  • 多个子任务能不能并行

你会发现,这些都不是“模型会不会说话”的问题,而是系统怎么组织执行的问题。

我现在更愿意把 Agent 理解成一个带推理能力的执行系统。LLM 是这个系统里的判断器,但不是系统本身。把这个前提想清楚,后面的组件划分就自然了。

一个最小可用的 Agent,通常会拆成哪几层

如果你自己从零设计一套 Agent,大概率会拆出下面这几层:

  • Agent:统一入口,负责收请求、管流程、给外部一个稳定接口
  • Planner:把目标翻译成步骤,或者在执行中途修正步骤
  • Executor:真的去跑步骤,处理成功、失败、重试和结束条件
  • Tool:和外部世界打交道,比如查数据、发请求、改文件、调 API
  • Memory:保存对话历史、任务上下文、阶段结果和长期记忆
  • LLM:负责理解意图、选择动作、整理结果、生成回答
  • Callback / Observer:补日志、监控、审计、限流、追踪这些横切能力

如果以后又忘了,我觉得可以先记一句最短的话:Planner 负责想步骤,Executor 负责跑步骤,Tool 负责碰外部世界,Memory 负责别忘事。

下面一层层拆。

Agent:入口和总协调

Agent 是你从外部看到的那一层。用户通常不会直接调用 PlannerExecutor,而是把一句话、一个任务对象,或者一段上下文扔给 Agent

它最重要的工作不是“亲自做事”,而是组织别的组件协作。说得直白一点,Agent 更像一个总控。

  • 输入通常是用户请求、会话信息、运行配置
  • 输出通常是最终回答、执行结果,或者中间流式事件
  • 常见坑是把太多业务逻辑塞进 Agent,最后它变成一个什么都懂一点、但特别难扩展的巨型入口

如果这一层设计得太重,后面很多能力都会被绑死。比如你想加并行执行、换一套 Memory、或者把回答改成流式输出,最后都会变成拆入口。

Planner:把目标翻成步骤

Planner 的存在,是因为很多任务不能拿到一句话就直接开跑。

用户说“帮我看下这个订单为什么没同步成功”,系统真正要做的,可能是先读日志,再查数据库,再看消息队列状态,最后再整理结论。这里面不是一次工具调用,而是一串动作。

Planner 负责的,就是把“目标”翻成“可执行步骤”。有时它会先给出一份完整计划,有时只给出下一步,然后根据执行结果边走边改。

  • 输入是目标、上下文、历史状态,有时还包括约束条件
  • 输出是步骤列表、子任务、依赖关系,或者下一步建议
  • 常见坑是把 Planner 想得太聪明,什么都要提前规划完。现实里很多任务信息不全,计划往往只能先走一步看一步

我自己会把 Planner 理解成一个“把模糊目标压缩成清晰动作序列”的层。它决定的不是答案,而是路线。

Executor:把步骤真的跑起来

有了计划,不代表系统就真的动起来了。真正去执行步骤、串结果、判断是否继续的,是 Executor

它解决的是行动问题,而不是理解问题。

比如某一步要求调用天气接口、某一步要求写文件、某一步要求等待前一步结果再继续,这些都是 Executor 的责任范围。它要处理步骤顺序、依赖、异常、重试、超时,有时还要决定哪些步骤可以并发。

  • 输入是计划、当前步骤、工具集合、运行时状态
  • 输出是步骤结果、状态变化、下一步执行信号
  • 常见坑是把它做成纯粹的“顺序调用器”。一旦涉及重试、回滚、并行或中断,这种实现很快就不够用

很多 Agent demo 看起来已经能用了,其实只是 Executor 还没遇到脏活。

Tool:连接外部世界

没有 ToolAgent 基本只能停在“说得像那么回事”。

Tool 的价值很简单:让系统不只会生成文本,还能真正接触外部世界。它可以去查数据库、调用搜索接口、读写文件、发 HTTP 请求、操作浏览器,或者触发别的业务能力。

  • 输入是结构化参数,而不是一句自然语言废话
  • 输出是结构化结果、状态码、错误信息,最好别只回一大段模糊文本
  • 常见坑是工具描述写得太虚,参数定义太松,导致模型经常调错工具,或者把参数拼得乱七八糟

如果把 Tool 看成系统的“手”,那一个很现实的问题就是:手不能只有名字,还得知道能抓什么、怎么抓、抓错了会怎样。

Memory:保存上下文和状态

Memory 这层很容易被低估。很多人会把它理解成“保存聊天记录”,但真做任务时,它远不止这个作用。

一个稍微长一点的任务,系统至少要记住几类东西:

  • 前面对话里已经确认过的事实
  • 任务进行到了哪一步
  • 某个工具刚刚返回了什么
  • 哪些结果值得长期保留,供下次继续用

所以 Memory 既可能是短期会话状态,也可能是长期知识记忆。前者更像运行时上下文,后者更像可检索的经验库。两者经常会混在一起被提到,但它们解决的问题并不一样。

  • 输入是对话、步骤结果、状态变更、外部检索内容
  • 输出是当前上下文、补充事实、长期记忆命中结果
  • 常见坑是把什么都往里塞,最后上下文又长又脏,真正有用的信息反而被淹掉

很多“Agent 越聊越笨”的问题,最后都不是模型突然不行了,而是记忆层已经乱了。

LLM:负责理解、选择和生成

LLM 当然还是核心,但它更像这个系统里的判断器和语言接口。

它至少负责几件事:

  • 理解用户到底想做什么
  • 结合上下文判断下一步动作
  • 决定要不要调工具,调用哪个工具
  • 把执行结果整理成用户能看懂的话

但有一点要分清:LLM 很强,不代表它应该直接接管全部流程。

只要一个系统把规划、执行、状态控制、外部调用全压在模型的一次输出里,稳定性通常就会越来越差。因为模型擅长的是推理和生成,不擅长替你充当整个运行时。

  • 输入是提示词、上下文、工具定义、当前任务状态
  • 输出是推理结果、动作选择、结构化调用,或者最终自然语言回答
  • 常见坑是把所有控制逻辑都寄托在 prompt 上。这样短期看很省事,长期很难维护

Callback / Observer:把系统接到生产环境

前面几层决定系统能不能跑起来,CallbackObserver 决定系统跑起来之后能不能管得住。

这层经常被放到最后讲,但工程上其实很重要。你总得知道一次任务什么时候开始、调了哪些工具、哪一步耗时最长、哪里报错、是否触发了重试、有没有越权调用。

这些事情通常不该硬写在 PlannerExecutor 里,否则主流程会越来越脏。更合理的做法是留出统一的事件钩子或观察点,让日志、监控、审计、追踪、限流、安全策略挂在旁边。

  • 输入是执行事件、状态变化、工具调用记录、异常信息
  • 输出是日志、指标、链路追踪、安全审计,或者拦截后的控制结果
  • 常见坑是完全没有这层。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 架构,最怕的一件事就是把它神秘化。

它当然有难度,但大多数难点并不玄。很多时候,它只是把推理、执行、状态、工具和工程治理放进了同一条链路里。拆开看,事情就清楚很多。

评论