先说结论Agent实现方式这条技术路线上单轮调用、多Agent协作从来不是非此即彼而是层层递进的进化关系。很多刚接触Agent开发的人一说Agent就想到LangGraph、CrewAI这类框架恨不得上来就搭一个多角色协作系统结果连最基础的单轮调用和工具调用都没理清楚写出来的东西既不好维护成本还高得吓人。这篇理论篇就是帮你把这条路走明白从一次Prompt进一次结果出的单轮调用讲起一步步理解多轮会话、工具调用、ReAct推理循环再到Plan-and-Execute和多Agent协作把每种实现方式的原理、适用场景、演进动机一次讲透。不管你是在准备Agent开发面试、做技术选型还是想系统梳理自己的Agent知识体系这篇文章都适合读。我会尽量用大白话解释背后的设计逻辑偶尔给点示意代码但重点始终放在为什么上这也是理论篇区别于实操篇最大的价值。1. 动手之前先分清楚工具调用、Agent框架和执行环境1.1 Agent的三个关键词自主感知、自主决策、自主行动市面上关于Agent的定义五花八门但从业者普遍认可的核心能力无非三个自主感知、自主决策、自主行动。所谓感知是Agent能接收环境反馈比如用户的输入、工具返回的数据、上一轮任务的结果决策是模型根据当前状态判断下一步该做什么行动是真正调用工具、修改状态、产生输出。这个三角关系决定了Agent与普通API调用的本质区别。你写一个函数输入身份证号输出年龄这是确定性程序分支全由程序员写死Agent则相反控制流掌握在模型手里同一个Prompt在不同场景下走出的路径完全不同。就好比一个实习生你给他一个目标他自主决定先做什么后做什么遇到阻碍自己想办法绕过。这个自主两个字就是Agent的灵魂也是理解后面所有实现方式的钥匙。1.2 Harness、框架与Agent本体三层不同的东西在讨论Agent实现时有个特别容易混淆的点Agent本体的决策逻辑、Agent框架、以及Agent运行时的Harness到底各自扮演什么角色我习惯用一个比喻来解释Agent本体是大脑框架是骨架Harness是身体。大脑决定怎么想骨架决定各部分怎么拼接身体提供呼吸、供血这些底层支撑。具体来说Agent本体是模型与Prompt、工具、记忆等元素的组合逻辑框架比如LangGraph、CrewAI、AutoGen这类编排引擎负责把循环、状态转移、多Agent通信这些通用机制封装好而Harness更偏运行时环境负责沙箱隔离、工具执行、上下文注入、安全策略这类与编排无关但必不可少的脏活累活。为什么要刻意区分这三层因为日常讨论中大量鸡同鸭讲本质上就是把层级搞混了。有人问Agent怎么扛并发这主要还是Harness和执行层的规划问题有人问怎么让两个Agent协作这是编排框架的职责有人问怎么设计Prompt让Agent不乱来这又回到Agent本体。理论篇的意义正在于此先建立一个清晰的分类框架后面讨论任何具体方案都不会跑偏。1.3 从单轮到多Agent协作三层控制逻辑的叠加把Agent实现方式的演进拆开看你会发现它其实是在不断叠加控制逻辑每一层都在解决上一层暴露出的新问题。单轮调用是第一层模型接收到Prompt然后返回结果控制流极简一次调用走完就结束。但它天然有记忆缺失的问题于是有了第二层多轮会话通过维护消息列表让Agent能引用历史信息。然而多轮会话只解决了记住没解决做事所以第三层加入工具调用让Agent能够查询数据库、操作API、读写文件。工具调用之后模型一次只能走一步遇到复合任务时容易迷失方向于是第四层来了ReAct循环让模型能推理-行动-观察-再推理循环推进直到完成目标。ReAct足够强但它在超长任务上会出现规划和执行缠绕不清的问题所以被Plan-and-Execute演进拆分。到了多步、多角色、多目标并行的时候单个Agent的上下文窗口和职责边界已经扛不住于是进入第五层多Agent协作。记住这条主线你会少走很多弯路。很多人问我Agent开发学习路线应该怎么规划我都会说先把这条主线上的每一层都亲手实现一遍哪怕是最朴素的版本也比直接套框架强得多。2. 单轮调用所有Agent实现的地基2.1 最原始的形态一次Prompt一次输出单轮调用的形态极其简单你用一个System Prompt设定角色塞一条用户输入模型返回一段回答调用结束。它之所以重要是因为它是所有复杂Agent实现的最小原子单位——后面的多轮、工具、ReAct、多Agent无一例外都是基于这种单次调用能力循环和扩展出来的。很多框架文档里爱说的LLM-as-a-Service本质上描述的就是这一层。它不涉及状态管理不需要记忆系统也不关心工具执行代码往往短得可怜。但这不意味着单轮容易做好恰恰相反单轮调用里最大的学问在于如何设计System Prompt。同样的模型一个思路清晰的System Prompt和一个含糊的System Prompt输出质量可能会差一个数量级。比如让模型做意图分类你要在Prompt里明确输出格式、枚举所有可能的分类、给出示例模型才能稳定地给你结构化结果。2.2 快速跑通第一个单轮Agent理论上不想堆太多代码但一个可运行的最小示例能帮你理解什么叫决策逻辑完全在调用方。用兼容OpenAI接口的Python SDK单轮调用大概长这样from openai import OpenAI client OpenAI() response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是一个严谨的代码评审专家只输出评审结论不含客套话。}, {role: user, content: 请评审这段代码def add(a, b): return a - b} ], temperature0.3 ) print(response.choices[0].message.content)你看整个流程确实就是一个请求一个响应。但值得注意的细节藏在参数里temperature调到了0.3因为代码评审这种任务要求低随机性System Prompt里声明了只输出评审结论这是在做输出约束。真实项目里单轮调用会花大量精力在输入构造和输出解析上比如让模型输出JSON然后程序解析或者用Pydantic定义响应的Schema。2.3 单轮调用的边界能用在哪卡在哪单轮调用适合什么场景一句话总结单次决策、无状态依赖的任务。典型的就是文本分类、情感分析、实体抽取、JSON格式化、单轮问答、标题生成、短文本翻译。这些任务不需要上下文记忆不做多步操作调用一次返回结果效率和成本都最可控。它的局限性同样一目了然。首先是记忆缺失你连续问它我上一条说了什么它一概不知其次是无工具能力它只能基于模型参数里的知识说话没法查实时天气、查数据库、操作文件最后是推理链断裂一步到位的结果往往在数学计算、多跳推理这类任务上表现不稳定。理解边界很重要因为很多入门者会指望一个单轮调用解决复杂业务结果模型给不出好答案就怀疑模型不行其实应该怀疑的是实现方式。就好比你买了一把螺丝刀却非要拿它开啤酒瓶不是螺丝刀的问题是工具和场景不匹配。3. 多轮调用与状态管理Agent开始有记忆3.1 从无状态到有状态Messages消息协议单轮调用最大的痛点是模型不记得你刚才说过什么解决办法也简单把历史上的对话一起交给模型。这就是多轮会话的本质不是说模型真的有记忆而是你把记忆数据从调用方塞回给模型。聊天模型的消息协议一般包含几种角色system表示全局指令user表示用户输入assistant表示模型或Agent的回复tool表示工具执行结果。多轮调用只是不断往messages里追加新消息每次请求把完整列表带上。你可以把messages理解为一份聊天记录每次发送都给对方看全部记录对方自然能顺着上下文接话。这里有个容易误解的点模型侧最新版本的API大多不是简单字符拼接而是按消息序列理解上下文。每条消息的角色交替、内容结构都会影响模型对当前是谁在说话的判断。所以在组装消息时如果出现连续两条user消息很多模型会感到混乱正确做法是让Agent先以assistant角色输出一个中间状态或者用工具消息把多源输入包裹起来。3.2 上下文窗口有限的现实Token预算与历史压缩多轮会话实现起来很简单却有一个绕不过去的墙上下文窗口是有限的。窗口再大也有塞满的一天而且token越长单次调用的延迟和费用越高。处理超长对话工程上最直接的策略是滑动窗口只保留最近N轮消息更早的内容直接丢弃。丢文件会丢失信息所以衍生出压缩策略如果对话太长先把旧对话丢给模型做摘要再把摘要作为一条system消息注入。我见过一个朴素的实现当消息列表超过30轮时调一次模型把前20轮压缩成200字摘要然后带着摘要继续后续对话。这个做法有效且成本低缺点是有信息损耗关键细节可能在压缩时丢失。进阶一点的做法是让历史消息分场景存储哪些是用户偏好、哪些是业务上下文、哪些只是闲聊分开管理每次只注入与该轮任务相关的一部分。这其实已经跨入记忆系统设计的领域但它的出发点还是多轮会话的上下文管理属于同一个问题的不同解法。3.3 从短期记忆到长期记忆为什么最终要接检索多轮会话天然只覆盖短期记忆消息列表一清理超出窗口的内容就忘了。要做真正的长期记忆行业里通用的路子是外部存储加检索。最经典的实现是向量记忆把用户的历史消息切块用Embedding模型转成向量存入向量数据库每次新的问题进来先做相似度检索把最相关的历史片段拿出来注入上下文。这就是Agent记忆系统里最常见的RAG式记忆方案你不一定需要把每一次对话都直接塞给模型而是按需回忆。理论篇里我只点明一个关键点短期记忆是上下文窗口的直接管理长期记忆是检索增强的外置系统两者的信息通路完全不同。理解了这一点你再去看各种Agent框架里的memory模块就会明白它们各自解决的是哪个环节的问题。4. 工具调用Agent真正开始伸手做事4.1 没有工具的Agent只是个聊天机器人在很多人的朴素理解里Agent就是比ChatGPT多一点Prompt技巧。但如果你只能让模型说而不能让它做那它更接近聊天机器人而不是智能体。模型本身不会访问外部世界它的知识截止于训练数据它不会算精确的账不会查询你的数据库更没法替你发邮件、改文件。要让Agent产生实际作用你必须给它工具。工具调用机制在LLM时代其实不算新鲜底层思路是模型不直接执行任何外部操作而是输出一个结构化的意图比如调用get_weather参数是北京然后由你的程序去执行这个意图把结果再交还给模型。这个程序负责执行、模型负责决策的分工既利用了模型的理解能力又避免让模型直接接触危险操作安全边界清晰。4.2 Function Calling完整链路剖析一次完整的工具调用通常包含五个环节。第一步声明工具。你需要用JSON Schema描述工具的用途、函数名、参数结构和必填项。第二步把工具声明和当前消息一起发给模型模型根据用户语义判断哪个工具合适。第三步模型返回的不是普通回答而是tool_calls字段里面包含函数名和参数。第四步你的程序解析这段调用意图执行本地函数。第五步把执行结果包装成tool角色消息回传给模型模型基于结果生成对用户的最终回复。示意代码如下import json def get_weather(city: str) - dict: # 实际项目里这里会查真实的天气API return {city: city, temp: 25, condition: 晴} tools [ { type: function, function: { name: get_weather, description: 查询指定城市的当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名如北京、上海} }, required: [city] } } } ] messages [ {role: system, content: 你是一个生活助手可以使用天气查询工具。}, {role: user, content: 北京今天适合出门吗} ] response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools ) msg response.choices[0].message if msg.tool_calls: tool_call msg.tool_calls[0] args json.loads(tool_call.function.arguments) result get_weather(args[city]) messages.append(msg) messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result) }) final client.chat.completions.create(modelgpt-4o-mini, messagesmessages) print(final.choices[0].message.content)你在很多框架里看到的agent底层的工具调用链路基本都是这个形状。区别只在于框架帮你封装了工具注册、参数校验、结果回传这些样板代码。4.3 工具调用的三个设计要点工具声明、schema设计、错误处理这是我在实际项目里踩坑最多的三块。第一工具描述要写得像给新同事的交接文档。模型选择工具时主要靠description判断语义匹配写得模糊它就会选错。比如一个函数既能查气温也能查湿度你只在description里写查询天气那用户问要不要带伞时模型可能就不会去调用因为描述里没提到降水信息。第二参数Schema尽量严。能约束成enum的不要用自由字符串能用integer不要用number能限制min/max就限制。参数解析错误是工具调用里最高频的故障源模型偶尔会生成越界或错误类型的参数程序端必须做严格校验。第三工具的异常结果也要作为正常输入回传给模型。工具返回错误并不是世界末日你可以把错误信息说清楚让模型换一种方式完成任务。比如数据库连不上就把Connection refused传给模型它可能会建议降级方案或重新调用其他工具。这一点对构建鲁棒Agent极其关键。5. ReAct循环从问一次答一次到分解动作推进目标5.1 ReAct的核心思想推理与行动交替有了工具调用Agent能做单步操作了但问题来了一个任务如果涉及多个步骤比如帮我查一下本周项目排期然后给每个任务的负责人发一封提醒邮件单次工具调用完不成模型需要自己分解步骤、决定顺序、判断是否完成。ReAct模式就是为了解决这个问题。ReAct即Reasoning Acting核心思想是让模型在推理、行动、观察之间循环先根据当前状态思考下一步该做什么Reason调用工具执行Act观察工具结果Observation再进入下一轮推理。它不是一次性规划完整个任务而是走一步看一步这和外科医生做手术类似不可能术前把所有动作写死必须根据开刀后的实时情况不断调整。这套机制的高明之处在于它把最终目标交给了模型掌控而把每一步执行过程的正确性交给环境反馈。模型不需要预知所有细节只要在每个决策点上做出局部最优选择路径可以动态调整。5.2 一次最小可用的ReAct循环长什么样ReAct循环的实现骨架非常简洁本质上是一个while循环。MAX_STEPS 10 step 0 while step MAX_STEPS: response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools ) msg response.choices[0].message messages.append(msg) if not msg.tool_calls: # 模型没有要求调用工具说明它认为任务已完成 print(msg.content) break for tool_call in msg.tool_calls: result execute_tool(tool_call) messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result) }) step 1这个循环的逻辑链条很清晰不管模型要调用几个工具都执行完再把结果一并回传然后让模型继续推理。循环唯一的出口是模型不再请求工具调用也就是它给出了最终回答。之所以强调MAX_STEPS是因为模型在复杂任务里偶尔会陷入循环比如一个工具老报错它就一直重试。设置最大轮次可以让Agent在失控时主动停止而不是无限烧token。5.3 循环设计中的终止条件与防失控ReAct循环看着简单工程上要打磨的细节却不少我把实际踩过的坑整理成几个关键原则。终止条件必须显式设计。理论上模型会在不需要工具时直接给答案但实践中模型偶尔会兜圈子尤其在你设计的Prompt里没给它明确何时结束的指令时。在System Prompt里加一句当任务目标已达成或无法继续推进时直接给出结论不要继续调用工具循环的收敛性会好很多。观察结果要控制长度。工具返回的原始数据常常很大比如搜索引擎返回20条结果、数据库返回100行记录全部塞进上下文既费token又干扰模型判断。通常的做法是做截断或者摘要只保留最有价值的信息。很多人把这个细节放在记忆管理里讲但本质上它也是循环设计的必要环节。要警惕幻觉式调用。模型偶尔会虚构一个不存在的工具名或者生成不符合Schema的参数框架层必须对tool_calls做白名单校验只放行已注册的工具否则你的Agent会在逃跑。6. Plan-and-Execute复杂任务的规划与执行分离6.1 ReAct处理长任务时的两个痛点ReAct模式虽然灵活但在超长任务上会暴露两个越来越明显的问题。第一个是决策疲劳每一步都把规划和执行混在一起模型每走一步都要重新理解全局目标轮次一多局部决策往往会偏离最初意图。第二个是上下文污染每一轮的工具结果都堆进上下文到后面模型光读历史就消耗了大量token留给当前决策的空间越来越少。打个比方ReAct像是你每走一步才问一次路Plan-and-Execute则是先掏出地图把全程路线看好再按路线走走一段确认一下位置必要时重新看地图。把规划动作前置执行过程就能轻装上阵。6.2 规划器和执行器的架构拆解Plan-and-Execute模式里有两个核心角色规划器和执行器。规划器接收用户目标分解成有序的任务列表输出一份Plan执行器拿到Plan逐个完成子任务每个子任务可以是独立的ReAct循环或工具调用。实现上通常分三个阶段。第一阶段规划器生成任务列表比如写一篇文章被拆成调研主题背景、列出提纲、撰写初稿、修改润色。第二阶段执行器按顺序执行这些子任务步骤间可能需要传递中间产物。第三阶段全部子任务完成后可以由规划器或一个专门的汇总器把结果合并成最终输出。这里有一个细节值得展开Plan需不需要在执行过程中动态更新简单任务可以一次性规划但真实场景里执行器某个子任务失败是常有的事这就需要一个动态更新机制。比如一个任务拆了三步第二步做失败了是跳过、重试还是重新规划整个Plan常见做法是在每一步执行后把执行结果反馈给规划器让它判断下一步是维持原计划还是修正计划。这个执行-反馈-再规划的闭环其实就是Plan-and-Execute和静态任务清单最大的区别。6.3 任务分解的两种组织思路规划器把目标拆成子任务时存在两种组织思路一种是自上而下的层级分解一种是自下而上的信息汇聚。自上而下是大拆小子任务之间往往有先后依赖比如研究-写作-校对适合线性推进的任务。自下而上是小聚大先并行收集资料、生成片段再统一汇总适合调研报告、竞品分析这类信息密集型任务。两种思路对应到工程实现上差异很大线性任务只要一串顺序执行器即可信息汇聚型任务则需要引入并行执行和结果合并逻辑对框架的调度能力要求更高。这也是为什么有些Agent框架强调图结构编排本质就是为了同时表达依赖关系和并行分支。7. 多Agent协作从单兵作战到团队分工7.1 为什么需要多个Agent而不是一个万能Agent单Agent经过ReAct和Plan-and-Execute的加持已经可以处理不少任务了但它有一个天花板单个Agent的权重和能力不可能在方方面面都顶尖。让它写代码可能要强推理模型让它做内容创意可能要更强的语言风格让这个Agent同时扮演多个专业角色会严重分散模型的行为空间结果就是每个方向都平庸。多Agent协作的价值正在于此把一个超级Agent的垂类问题转化为一个团队分工问题。每个Agent专注于一个角色拥有特定的System Prompt、工具集和判断标准比如一个Agent只负责搜索整理资料另一个只负责撰写正文还有一个专门做事实核查。专业分工带来两个优势一是每个Agent的Prompt和工具可以极致定制化二是多Agent之间的信息互通可以管理得更清晰把单一Agent模型大而全的上下文压力拆成一个组织松散的网络。7.2 三种典型协作拓扑编排、流水线、辩论多Agent协作的拓扑结构我总结下来无非三种。第一种是编排模式也叫主管-工作模式。一个Orchestrator Agent作为任务的总体调度者负责把任务分解并分发给若干SubAgent每个子Agent执行完返回结果由编排者汇总或继续下派新任务。这种模式适合任务类型复杂但子任务边界清晰的情况比如你做一个市场调研一个Agent查政策一个Agent查竞品一个Agent查用户评价编排者最后统一写报告。第二种是流水线模式也叫管道模式。Agent A的输出直接作为Agent B的输入像工厂流水线一样逐级处理。比如一个Agent负责生成图片描述下一个Agent根据描述生成图片再下一个Agent做质量检查。流水线模式逻辑最简单性能瓶颈在于每个Agent都必须依赖上一步的结果无法并行。第三种是辩论模式多个Agent针对同一个问题分别独立给出答案然后互相质疑、评审最终达成共识或投票选出最佳答案。这种模式适合高风险的判断型任务比如代码评审、内容审核、策略推演。辩论能显著提升结论质量但成本成倍增加且需要设计好谁来最终裁决的机制。理论篇里我不展开教某个具体框架的用法因为所有框架在顶层设计上都是围绕这三种拓扑做文章。把你的任务需求对号入座去找框架会比反过来从框架出发做适配高效得多。7.3 多Agent协作落地时绕不过去的工程问题多Agent协作看着美好的背后有几个工程问题是一定要面对的这里挑影响最大的三个说。通信协议要结构化。多个Agent之间传递的不能是零散的自然语言文本而应该是结构化消息比如{task: report, data: {...}}。否则一个Agent的输出解析不出来下游Agent就会咽下脏数据。并发与成本是双刃剑。多Agent天然适合并行执行但并行也意味着token消耗成倍上涨。怎么扛并发不是说把所有Agent同时发起而是在划分依赖拓扑时尽量把可以并行的分支并行把必须串行的依赖保留同时做好token预算。实测里一个三个Agent的协作任务token消耗往往比单Agent高一个数量级这是所有多Agent方案都必须正视的现实。共享记忆怎么设计。Agent之间如果共享同一个知识库要设计好写入和读取的边界防止一个Agent的错误输出污染整个团队。如果你做了一个Agent团队一定要在开局就把每个Agent的消息只能被谁访问这个权限模型设计好一旦记忆混乱排查问题会让人头皮发麻。8. 理论篇总结如何选择你的Agent实现路径8.1 五层实现方式的选型对照表写到这里主线已经清楚选型时最核心的判断就是你的任务复杂度和成本容忍度匹配哪一层实现方式。为了方便你对号入座我把五层实现方式的关键特征整理成一张表。实现层级核心能力典型场景主要成本适合人群单轮调用一次问答分类、抽取、翻译、格式化极低所有Agent初学阶段的必经起点多轮会话上下文记忆客服对话、多轮问答随token数增长需要连续交互的业务工具调用外部世界交互查天气、查库、调API、操作文件中等需要让模型实际做事的场景ReAct循环多步自主推理多跳信息检索、按步骤完成复杂任务轮次越多成本越高已理解工具调用追求自主性的开发者Plan-and-Execute规划分解与执行长文档写作、深度调研、复杂项目执行规划执行双层开销任务链条长、步骤清晰的场景多Agent协作角色分工与团队协作大型项目、高风险决策、多领域综合任务成倍上升需预算管控已充分理解前五层且业务复杂度需要这张表最大的作用是帮你抵制一步到位的诱惑。我见过很多团队一上来就叠多Agent框架结果单Agent都调不好的场景最后debug成本远超开发成本。8.2 我踩过的坑和给你的建议文章最后分享一些我的真实经验算不上什么高深理论但能帮第一次搭建Agent的人规避一些常见坑。第一先跑通单轮再谈Agent。很多面试者会背多Agent框架的使用方法但让他解释一次ReAct循环里tool_calls和tool角色消息的组装关系他就支支吾吾。底层链路没吃透框架对你来说只是黑盒出了问题完全无从下手。第二Prompt、工具、上下文三者要分开治理。我之前犯过的错误是把所有逻辑全塞进一个巨长的System Prompt结果模型越到后面越混乱。正确做法是System Prompt只负责角色和目标工具声明放工具位历史消息在上下文中管理三者各司其职。第三成本护栏要提前设置。Agent跑的轮次不可控一个失控循环可能把一天的token预算几分钟烧完。尤其多Agent场景开销根本不是线性叠加而是指数叠加。早期一定要设置硬性约束最大轮次、单轮超时、工具调用白名单、预算上限。如果你现在正站在Agent开发学习路线的起点我的建议很朴素这一篇理论篇里的每一层都值得你亲手用最朴素的代码实现一遍。等你能不看框架文档写出一个能跑通ReAct循环的Agent之后再去看LangGraph或CrewAI的源码会发现一切都似曾相识。到那时候你用框架还是自己写就真正是工程取舍问题了而不是因为大家都在用的理由。