本文概览:同样是"AI 参与业务流程",Workflow 和 Agent 的根本差别在于下一步由谁决定:前者写在代码里,后者由模型在运行时现场生成。Agent 之所以敢对"不确定的下一步"做决策,靠的是四个核心组件——大脑(大模型)负责决策,记忆提供连续性,工具提供行动力,规划把它们组织成循环。工具模块下有 Function Calling、MCP、Skill 三层机制,共同的设计哲学是"上下文是稀缺资源,按需加载";规划模块有 ReAct、Plan-and-Execute、Reflection、Multi-Agent 等架构,外加 Agent 之间的 A2A 协议——它们不是互斥选项,而是实践中叠加使用的积木。本文聚焦"是什么、为什么",所有搭建实现留待后续文章。


一、Workflow 与 Agent:差别到底在哪

先别看定义,看两段业务。

第一段:采购单。 一张采购单的生命周期是固定的:创建 → 提交 → 审批 → 完成,必须先创建才能提交——这是流程写死的规则,不是建议。请年假也一样:先找直属主管,主管批了再走 HR。每一步都是提前写死的流程控制,哪怕中间某个节点接入了大模型(比如让它帮忙生成审批意见),下一步走哪条分支依然由代码决定,和 switch 语句没有本质区别:上一步确定,下一步就必然确定。

第二段:一个开放需求。 "现在需要采购牙膏,请你根据现在的情况分析一个采购方案给我。"

这个问题工作流做不了,为什么?把它的决策链摊开看:

  • 先要确定供应商:哪些供应商支持牙膏这个商品?报价、账期、履约记录如何?
  • 再要看库存:仓库里这个 SKU 有没有积压?积压意味着存储成本在涨,可能根本不该补货;
  • 还要看销量:近期销量怎么样?未来一个季度的预测趋势怎么样?促销计划会不会拉高需求?
  • 最后综合以上所有信息,才能回答最初的问题:在哪个节点、采购多少量。

关键在于:这个链条没法提前画成流程图。下一步查什么,取决于上一步查到了什么——库存显示积压,重点就从"补多少"转向"要不要补、怎么清";供应商 A 不支持账期,就要临时追加一轮比价。步骤的集合本身是不确定的。

这就是两者的分水岭:

  • Workflow:确定性的下一步。 流程是人提前画好的,引擎只负责按图执行。
  • Agent:不确定的下一步。 流程是模型在运行时根据当前掌握的信息"走"出来的。

这个分法也不是本文独创——Anthropic 在《Building Effective Agents》里就把 LLM 系统分成两类:workflow 是"LLM 和工具通过预定义代码路径编排"的系统,agent 则是"LLM 动态主导自身流程与工具使用"的系统。名字不同,内核相同:控制流在谁手里。

没有优劣,只有场景

需要立刻澄清一点:确定性不是低级形态。恰恰相反,越是强规则的场景,越必须用 Workflow。

金融就是最典型的例子。一笔大额支付,必须经过双人复核、限额校验、账务借贷平衡检查;清算结算的每一步都有监管依据。这些流程不能出现不确定的下一步——如果让模型现场决定"要不要跳过复核",那不是智能,是事故。不确定性在这里直接等价于资损和合规风险。

所以正确的结论是:场景决定选型,而不是先进程度决定选型。

两者也不互斥

实践中二者经常互相嵌套。一方面,Workflow 的骨架里可以嵌 Agent 节点(固定审批流的"生成审批意见"这一步就可以交给模型推理);另一方面,当 Agent 被组织成图结构时(比如 LangGraph 这类框架),它的某些节点、某些边同样是确定的——从"开始"到"理解需求"这一步不会跑偏,只有中间的检索与决策环节是开放的。

Workflow 与 Agent 互相嵌套:左图为审批流中嵌入 Agent 节点,右图为 Agent 图结构中保留确定性边

判断标准始终是那一条:这一步的下一步,是代码决定的,还是模型决定的。

那么问题来了:让模型对"不确定的下一步"做决策,凭什么它是靠谱的?它需要哪些能力兜底?这就是 Agent 的核心组成。


二、核心组成:从缺陷到设计

上一篇讲过大模型的四个天生缺陷:无状态、知识冻结、与世隔绝、输出不可控。Agent 的核心组件不是凭空发明的,每一块都是针对某个缺陷设计的"补法":

大模型的缺陷 补它的组件 怎么补
无状态,调用结束一切归零 记忆 把历史对话重新塞回上下文
知识冻结在训练截止日 记忆(长期)/ RAG 需要时从外部检索新信息
与世隔绝,碰不了外部世界 工具 Function Calling / MCP 让它能执行操作
输出不可控,格式随意 工具 + 规划 结构化输出(FC 的 JSON 规范)+ 规划的受控流程

四个缺陷,补出三块静态组件:大脑、记忆、工具。但表里还藏着一个缺口——单次调用只能产出一小步,而真实任务是几十步,这不是任何一个静态组件能独立解决的,它需要第四块:把所有组件组织起来流转的规划,也是本文的重头戏。

于是得到 Agent 的完整四件套:

  • 大脑(大模型):实现决策的基础,全文唯一"会思考"的部分;
  • 记忆:多轮对话的基础,让 Agent 知道此前发生了什么;
  • 工具:与外部世界交互的前提,让 Agent 从"能说"变成"能做";
  • 规划:规定这些组件怎么组合、怎么流转,本质是一个循环驱动。

Agent 四件套:规划是最外层的循环骨架,循环每转一圈经过记忆、大脑、工具三站

下面按"大脑 → 记忆 → 工具 → 规划"的顺序展开。前三块是静态组件,最后单独看它们怎么被规划"转"起来。


三、大脑:大模型

大脑就是大模型本身,没什么可包装的——但它的地位怎么强调都不过分:Agent 决策的正确率,绝大部分由模型能力决定。

同一个编排框架、同一套工具,接强模型和接弱模型,任务成功率是数量级的差距。因为拆解需求、判断何时该调工具、发现上一步走错了及时纠偏——这些全是模型在"思考"环节完成的,外面的组件只能提供素材和执行,替代不了判断。工程上有一种普遍的现象:框架调了无数遍,最后发现真正的杠杆是换了个模型。

模型在 Agent 里的工作方式可以概括为一句话:给它一段上下文,它输出一段内容——这段内容要么是给用户的话,要么是"我要调用某个工具"的意图。 理解、推理、决策、生成,全部发生在这一次次的输入输出里。

选型时值得关注的四个维度:

  • 推理能力:拆解与纠错的地基;
  • 上下文窗口长度:直接决定记忆模块的发挥空间;
  • Function Calling 支持度:不是所有模型都擅长稳定输出结构化的工具调用;
  • 成本与延迟:Agent 是循环调用,单次便宜一点,一轮任务下来差距就很大。

大脑能思考,但它思考时手里得有素材——此前的对话、此前的操作结果从哪来?这就是记忆模块。


四、记忆模块:多轮对话的基础

短期记忆:本质是"重新塞回去"

先说一个容易想歪的点:模型并没有"记住"你说过什么。上一篇讲过,它是个无状态的函数,调用结束一切归零。

那多轮对话是怎么实现的?说出来毫无神秘感:把上一轮你和它的对话,连同新的问题一起,重新塞给大模型。

1
2
第 1 轮发给模型:  [用户: 我叫张三]
第 2 轮发给模型: [用户: 我叫张三] [模型: 你好张三] [用户: 我叫什么?]

第 2 轮模型能答出"张三",不是因为它记性好,而是因为"我叫张三"这句话又被你亲手递给了它一遍。所谓短期记忆,就是不断增长的上下文窗口本身。

多轮对话的真相:历史消息每轮被完整重新提交

问题:窗口塞不下,也塞不起

窗口有上限,而且 token 越多、单轮成本越高、模型注意力越容易被无关内容干扰。于是出现了压缩:把较早的对话总结成一段简短的摘要,替换掉原始记录,腾出窗口空间。

但压缩立刻带来一个新问题:摘要是有损的。 如果用户问"我上周发你的数据库连接串是什么?"——这种细节在摘要里必然丢失。压缩之后,怎么查回原始历史?

压缩后查历史:窗口放摘要,窗口外放原文

思路是把"记忆"拆成两层,各管各的:

  • 窗口内:只保留压缩后的摘要,粗粒度,常驻,负责"让模型知道大概发生过什么";
  • 窗口外:完整原始记录落盘——文件、数据库、加向量索引,细粒度,负责"细节随时可查"。

当模型发现摘要里信息不够时(比如上面那个连接串的问题),就拿着关键词去外部存储检索原始记录,把命中的片段重新塞回窗口,再作答。

1
2
3
4
5
窗口内:  [摘要: 用户提供了项目背景、数据库配置等信息, 讨论了表结构...]
↓ 检索"数据库连接串"
外部存储: [完整历史: ... "MySQL 地址 10.0.3.7:3306, 账号 app_rw" ...]
↓ 命中片段塞回窗口
窗口内: [摘要 + 原文片段] → 模型据此回答

记忆分层:窗口内摘要常驻,窗口外原文按需检索回填

这套"摘要常驻 + 原文检索回填"的机制,往小了说是压缩后的补救方案,往大了说就是长期记忆的雏形:跨会话的用户偏好("这个项目负责人喜欢最小改动,不要整文件重写")、事实性知识,都放在窗口外的存储里,用到时才检索进来。你会发现 RAG 在这里换了个身份——它不只是"给模型补知识",本质上就是一种检索式记忆。

记忆让模型"知道得更多"了。但知道归知道,它依然只能产出文本——查不了库存,发不了请求,改不了文件。行动力从哪来?工具。


五、工具模块:从"能说"到"能做"

工具是 Agent 操作外部内容的基础:编辑文件、查天气、发请求、读数据库。这个模块下面有三层概念,一层套一层地解决不同的问题:Function Calling 定义"模型怎么表达调用意图",MCP 解决"这套调用代码谁来写、谁来维护",Skill 解决"能力怎么组织进上下文才不挤爆"。

5.1 Function Calling:一套交互规范

先破除一个误解:大模型自己什么都执行不了。 它没有手,Function Calling(FC)只是规定了它"表达想调用工具"的方式——一套模型与外部程序交互的规范。

完整闭环是这样五步:

  1. 注册:调用模型时,把可用工具的清单(名称、描述、参数 schema)随请求发给模型;
  2. 表达意图:模型思考后判断需要调用工具,此时它返回的不是给用户的话,而是一个结构化的 tool call JSON;
  3. 捕获:本地程序从响应里识别出这个 JSON;
  4. 执行:把 JSON 转化为真正的程序运行——发 HTTP 请求、查数据库等;
  5. 回填:执行结果(同样是 JSON)塞回上下文,模型基于它继续生成。

拿查天气举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 第 2 步:模型返回的调用意图(节选)
{
"tool_calls": [
{ "name": "get_weather", "arguments": { "city": "北京" } }
]
}

// 第 4 步:本地程序真正去请求天气 API,第 5 步把结果塞回上下文
{
"role": "tool",
"tool_name": "get_weather",
"content": { "temp": 18, "condition": "多云", "aqi": 55 }
}

模型自始至终只输出了两段 JSON:第一段说"我想查北京",第二段是别人替它查完递回来的结果,它再据此组织人话。意图与执行分离,执行永远发生在模型外面——理解这一点,后面 MCP 的一切设计你都能秒懂。

Function Calling 闭环:模型只表达意图,执行永远在运行时侧

5.2 MCP:当 N 个系统撞上 M 个服务

FC 本身没问题,问题出在规模化。

假设路线规划这个能力(比如请求高德)是很多系统都要的:IDE 里的 Agent 要、办公系统里的 Agent 要、客服系统要、移动端也要。按 FC 的方式,每个系统都得自己封装一套 Tool:写 schema、写捕获逻辑、发请求、解析返回、塞回上下文。四个系统 × 高德一个服务,就是四套几乎一样的代码。这还只是一个高德,再来 GitHub、再来数据库、再来地图以外的服务,就是 N×M 遍。

但停下来观察一下:这 N 套代码的本质逻辑完全相同——捕获 tool call、发请求、把结果拼回上下文。真正不同的只有"请求发给谁、参数怎么填"这几个细节。同样的胶水,粘了 N 遍。

MCP(Model Context Protocol)就是把这遍胶水标准化,它把角色拆成三个:

  • Host:你本来的系统(IDE、办公系统、客服系统……);
  • MCP Client:运行在 Host 里的请求发送器,负责"捕获、组包、发送、回填"这套通用逻辑;
  • MCP Server:服务真正运行的地方,由高德、GitHub 这些能力方提供。

工作方式对比一下就很清楚:

  • 原来:模型返回 tool call → 你系统里手写的 N 套代码各自捕获、各自发请求;
  • 现在:tool call 换成 MCP call → Host 里的 Client 按协议把 JSON 填进对应请求 → 发给 Server → 结果原路拼回上下文。

你本地那些工具调用的封装代码,全部被收进了 MCP Client 这个通用件里。于是"写 N 遍代码"变成了"部署一个 Client"——MCP 这一层只需要写一次,这是重点,后续的维护同样是只维护一次:高德的接口变了,改的是高德自己的 MCP Server,协议挡住了变化,所有 Host 和 Client 一行都不用动。原来改 N 处,现在改 1 处。

从 N×M 网状适配到 N+M 星型接入

内部系统同理:多个系统共用一套数据库,各自的 Agent 里就都有重复的查询 tool。做一个数据库的 MCP Server,大家直接引用即可。

MCP 的协议细节(tools/list、resources、prompts 这些原语)和 Spring AI Alibaba 的实战,在 MCP协议完全指南 里有完整展开,本节只讲它在 Agent 工具层里解决的那一个问题。

5.3 Skill 与渐进式披露

FC 和 MCP 解决了"能不能调用",还有一个问题没解决:怎么做对。

很多能力不是一两个函数,而是一整套知识:流程、规范、判断标准、输出格式。比如"小说章节审查"这个能力,它包含多维度审查清单、问题分级标准、报告格式——这些塞进一两个 tool 的 description 里根本装不下。Skill 就是把"能力 + 知识 + 流程"打包在一起的封装单元:一个目录,一份说明书(SKILL.md),外加可选的参考资料和脚本。

但新的矛盾马上出现:系统里装了 20 个 Skill,每个正文几千 token,全量注册进上下文就是几万 token——窗口塞不下,也塞不起,还干扰模型注意力。

解法就是渐进式披露(Progressive Disclosure):不是一次性把一切都给模型看,而是分三级,按需加载:

级别 加载内容 体量 加载时机
目录级 Skill 名称 + 一句话描述 几十 token 常驻上下文
正文级 SKILL.md 主文件 几千 token 任务命中该 Skill 时
资源级 references / 脚本 不定 正文里明确引用到时

实际效果:平时上下文里只有 20 行"名称+描述"的清单;你说"审查小说第 12 章",模型从清单里命中 novel-review,这时才把它的正文读进窗口,按流程执行。没命中的 19 个 Skill,一个 token 都不占。

渐进式披露:三级加载,命中才展开

回头看,FC、MCP、Skill 三层共享同一个设计哲学:上下文是稀缺资源,按需加载。 模型不需要"带着全部世界"思考,只需要在正确的时刻拿到正确的片段。

到这里,四件套的三块静态组件讲完了。但还有个问题没碰:工具让 Agent 能走出一步,可"牙膏采购方案"这种任务是几十步——一步一个决策容易,怎么让几十步连起来不跑偏? 这就是规划模块,Agent 真正的心脏。


六、规划模块:循环驱动

Agent 与"一次调用"的本质区别,在于它是一个循环:决策 → 行动 → 观察结果 → 再决策。 规划模块回答的问题是:这个循环按什么策略组织。业界给出了几种经典架构,逐个看。

6.1 ReAct:边想边做

ReAct = Reasoning + Acting。模型每一步先输出一段思考(Thought),再决定一个动作(Action),拿到观察结果(Observation)后进入下一轮思考。

拿一个简单任务看它的完整推理链——"今天北京适合户外跑步吗?":

轮次 Thought(模型在想) Action(模型在做) Observation(世界回话)
1 需要先知道北京当前天气 get_weather(city=北京) 18°C,多云,AQI 55
2 空气不错;但跑步还怕下雨,查下预报 get_forecast(city=北京) 今日无降水
3 温度、空气、降水都确认了,可以下结论 输出最终回答 —

ReAct 循环:Thought → Action → Observation 反复推进

注意第 2 轮:下一步做什么,是基于第 1 轮的观察结果现场决定的——这正是第一节说的"不确定的下一步"在微观层面的样子。

  • 优点:灵活,每一步都能根据最新情况纠偏;
  • 代价:没有全局视野,走一步看一步,复杂任务容易绕路甚至跑偏(在第 20 轮突然回头做第 3 轮已经做过的事)。

6.2 Plan-and-Execute:先谋后动

反过来:先让模型产出完整计划,再逐项执行。

还是写竞品分析报告,规划器先输出:

1
2
3
4
5
6
1. 确定竞品清单
2. 分别收集各产品公开资料
3. 对比功能与定价
4. 分析市场份额与趋势
5. 汇总结论
6. 成文

执行器按清单逐条推进,全部完成后再汇总。如果某一步的产出推翻了假设(比如步骤 2 发现清单里漏了一个关键玩家),就触发重规划(replan),修订剩余步骤。

  • 优点:全局性好,进度可见,长任务不容易迷失方向;
  • 代价:计划赶不上变化,重规划有额外成本;计划本身也可能一本正经地错。

ReAct 与 Plan-and-Execute 对比:走一步看一步 vs 先全局后执行

6.3 Reflection:做完再挑刺

在循环里加一个"自我审查"环节:产出结果后,模型换一顶批判的帽子重新审视它,对照需求找问题,再迭代修正。

例子:需求是"生成用户管理接口",第一轮代码产出后,反思环节对照需求清单检查,发现手机号返回时没有脱敏——补上;再检查,通过,交付。

  • 优点:直接提升产出质量,尤其在有明确验收标准的任务上;
  • 代价:每轮反思都是额外的 token 和时间,收益递减。

6.4 实践中:这些是积木,不是单选题

到这里必须把最关键的一层窗户纸捅破:ReAct、Plan-and-Execute、Reflection 不是互斥的架构选项,而是可以叠加的积木。 观察真实产品级的 Agent(Claude Code、Manus 这类),它们的运行形态普遍是三者的组合:

  • 接到复杂任务,先做一轮规划,生成 todo 列表(Plan-and-Execute 的骨架);
  • 执行每个 todo 项时,走 ReAct 循环——调工具、看结果、再决策(灵活的血肉);
  • 产出后或报错时触发反思,对照需求检查、纠偏(质量的保险)。

所以选型问题从来不是"用 ReAct 还是用 Plan-and-Execute",而是"这个任务里,哪些环节值得提前规划,哪些环节必须边走边看,哪些产出需要回头审查"。

6.5 Multi-Agent:一个脑子不够,就多几个

单 Agent 再会叠积木,也有一个物理极限:它的上下文随任务增长而膨胀。 查了 30 个网页的 Agent,窗口里塞满了别人的原始资料,轮到写报告时,这些资料既挤占空间又干扰判断;同时扮演"检索员"和"撰稿人"两个角色,prompt 也会互相打架。

Multi-Agent 的解法是拆:多个各司其职的 Agent 协作,最常见的形态是 orchestrator-worker——主 Agent 负责拆解与分派,子 Agent 各领一块任务。

还是"产出调研报告":

  • 主 Agent 把任务拆成三个方向,分派给 3 个检索子 Agent 并行执行;
  • 每个子 Agent 的窗口里只有自己那一段任务的上下文,查完只交回一份浓缩结论,原始网页资料留在自己肚子里、用完即弃;
  • 最后汇总 Agent(或主 Agent 自己)拿着三份结论整合成报告。

Multi-Agent orchestrator-worker:拆解、并行、只回传浓缩结果

拆开之后得到三样东西:上下文隔离(各自的窗口各自干净)、分工(检索 Agent 和撰稿 Agent 用完全不同的 prompt 与工具集)、并行(三个子 Agent 同时跑,墙钟时间大幅缩短)。

6.6 A2A:Agent 之间也需要标准协议

Multi-Agent 还带来一个新问题:Agent 怎么和 Agent 说话?

这里可以借一个对仗来定位:MCP 标准化的是"模型 ↔ 工具",A2A(Agent2Agent Protocol)标准化的是"Agent ↔ Agent"。 工具不会自己干活,A2A 面对的是另一个有自主决策能力的对等方——需要任务委托、进度流转、结果回传这套协作协议,而且双方可能来自不同厂商、不同团队。

例子:你的行程规划 Agent 要把"订机票"这一步委托出去。它不需要内置任何航司的工具,而是通过 A2A 发现航司团队提供的订票 Agent,把任务(日期、航线、预算)委托过去,对方按自己的能力自主执行(它内部可能自己就是一套 ReAct 循环),再把结果回传。双方只约定协议,互相不暴露内部实现。

MCP 连工具,A2A 连 Agent:两层协议各管一段

本节所有架构的具体编排实现(代码层面怎么搭 ReAct、怎么接 Multi-Agent),就是上一篇文末承诺过的续篇内容,这里不展开。


七、总结

回到开头的问题:Agent 凭什么敢做"不确定的下一步"的决策?现在可以给出完整的回答:

  • 大脑(LLM) 提供决策能力,它的强弱直接决定 Agent 的上限;
  • 记忆 提供连续性——短期靠把历史塞回窗口,长期靠"窗口放摘要、窗口外放原文、按需检索回填";
  • 工具 提供行动力——Function Calling 定义调用规范,MCP 让这套规范一次实现、处处复用,Skill 用渐进式披露把能力组织进有限的上下文;
  • 规划 提供组织方式——ReAct、Plan-and-Execute、Reflection 叠加成循环,Multi-Agent 把循环复制成多份并行,A2A 让循环之间能互相委托。

三层机制、四种架构,背后其实都是同两句话:上下文是稀缺资源,按需加载;确定性交给代码,不确定性交给模型。

最后留一张实用的选型对照表:

判断维度 倾向 Workflow 倾向 Agent
步骤能否提前穷举 能(采购单状态机) 不能(开放调研)
错误容忍度 低(金融、清算,错一步是事故) 高(方案可以迭代重来)
规则强度 强监管、强合规 弱规则、重结果
典型形态 固定流水线 + LLM 填节点 循环 + 四件套

以及一句务实的提醒:真实系统里两者几乎总是混用的——用工作流管住必须确定的部分,把真正开放的那一段交给 Agent。分清"哪些下一步必须确定、哪些下一步值得交给模型",比纠结"要不要上 Agent"重要得多。