有的朋友跟我说Agent这个概念看了十几篇文章还是觉得有点虚。我觉得不怪你因为市面上的教程要么一上来就堆LangChain、AutoGen这种框架要么只会拿“给大模型加个记忆和工具”一笔带过。真正把实现路径讲清楚的文章很少。这篇我打算从根上捋一遍从一次最简单的单轮调用开始一步一步走到多Agent协作。不是给某个框架的API文档背书而是把背后的设计逻辑和取舍讲透。适合那种已经能调通大模型API、但不知道下一步该搭建什么的人也适合准备搞多Agent系统、但还没想清楚模块边界的人。先说结论Agent不是“一个会对话的大模型”它是一个包含状态、工具、调用策略的完整系统。多Agent协作也不是把Agent复制几份而是任务拆解、通信、状态同步、终止条件这些硬功夫的组合。下面从头讲。1. 单轮调用Agent实现的地基也是最初的那道天花板1.1 一次请求走完的闭环输入、推理、输出单轮调用听起来很简单把用户的一句话丢给大模型拿到返回结果。但它是所有Agent实现的最小单元很多人在这个环节就踩了坑。我拿最常见的OpenAI接口风格举个例子from openai import OpenAI client OpenAI( api_keyyour-api-key, base_urlyour-base-url ) response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是一个乐于助人的中文助手。}, {role: user, content: 帮我写一段周报开头的描述项目是智能客服系统的集成测试。} ] ) print(response.choices[0].message.content)这里的关键不是代码本身而是理解一次调用的本质你给模型一个静态的世界切片system messages模型基于这个切片做一次前向推理返回一段文本。它不保留调用之间的任何状态第二次调用时模型“忘了”第一次说过什么。很多Agent的雏形就这么写出来了。但你会发现只要任务稍微复杂一点比如用户追了一句“注意语气要正式一些”“刚才那段再缩短一半”系统就接不住了。这就是单轮调用的第一个天花板。1.2 单轮的三大局限无状态、无工具、无分工单轮调用的问题可以用三个词概括无状态、无工具、无分工。无状态模型不记得之前对话。解决思路是让外部系统接管记忆用变量、数据库或历史消息数组把状态存起来每次请求时重新组装。无工具模型只能输出文本不能查数据库、不能发请求、不能算数学。解决思路是工具调用也就是把函数暴露给模型。无分工一个模型从头到尾包办所有思考。复杂任务比如“调研三个竞品并生成对比表”单靠一个模型一次推理很难做好因为每一步都需要不同的知识或能力。先说清楚这三个局限后面所有东西都是在补这些窟窿。1.3 顺带澄清一个高频疑问harness 和 Agent 到底分在哪近期讨论“Agent”话题时harness 这个词频繁出现很多人问“harness 和 agent 区别”。我的理解很简单Agent是那个“会思考、会决策的执行单元”它接收输入、调用大模型、决定下一步动作harness是“承载Agent运行的框架层”负责消息传递、状态管理、工具注册、循环控制这些结构化的编排逻辑。你可以把Agent类比成“员工”harness类比成“公司的流程制度”。员工再能干也得靠制度告诉他什么时候开会、事情办完找谁审批。很多代码库里的Agent其实只是“大模型调用封装”真正的调度循环在harness里。单轮调用就是harness里的最小执行粒度一次请求一次响应。到了多Agent协作harness要管的事情会成倍增加这一点等讲到第五章再展开。2. 状态与上下文从一句一问到能“记住你说过什么”2.1 无状态意味着什么一个“健忘”的对话者如果你做过客服机器人一定遇到过这种情况用户问“你们有什么套餐”你说了一堆用户接着问“哪个最便宜”模型回了一句“我没有前面对话的信息咱们从哪开始”。这就是无状态带来的尴尬。解决方案不是让大模型自己长脑子而是在外部维护状态然后每次请求时把状态塞回prompt里。最常见的做法是维护一个消息历史列表history [ {role: system, content: 你是一个客服助手。}, {role: user, content: 你们有什么套餐}, {role: assistant, content: 我们有基础版、专业版和企业版...}, ] # 用户追加新问题 history.append({role: user, content: 哪个最便宜}) response client.chat.completions.create( modelgpt-4o-mini, messageshistory )从实现上来讲就是把history变量存到内存或数据库里等用户下一轮发消息时再把它读出来拼上。整个过程没有增加模型的能力而是用代码帮模型“装上了记忆”。2.2 最常见的三种上下文管理手段处理上下文不能无脑全塞因为模型有上下文窗口限制而且超过一定长度后中间的细节会被注意力“稀释”。我整理了我常用的三种手段手段做法适用场景滑动窗口截断只保留最近的N轮对话聊天机器人、一般客服关键信息提炼把历史抽象成摘要/结构化字段长任务、项目型对话向量检索召回把历史切片存入向量库按相关性召回知识库问答、长期记忆滑动窗口是最easy的设置history长度上限超出就丢最老的。它的问题是可能丢掉关键信息。关键信息提炼更实用。比如用户三天前说过“我在上海出差”三天后问“我住哪个酒店”滑动窗口早就把那句丢掉了。但如果你在每次对话后跑一个轻量“状态抽取”把“用户当前城市上海、出差状态进行中”存进结构化对象下次请求时把这个对象放在system里模型就能答上来。向量检索适合做百科型Agent把历史消息切块、embedding、存库检索时按向量相似度取TOP-K个片段拼进上下文而不是把全部历史都带上。2.3 上下文不是越多越好注意力资源是稀缺的这个坑我踩过。早期做会话型Agent时为了“保险”把所有历史都塞进去结果模型输出质量反而下降。原因在于大模型处理长上下文时注意力会呈“中间遗忘”趋势——开头和结尾的内容影响力最大中间的内容容易被忽略。所以我现在的做法是system里放最核心的角色定义和硬性要求。历史消息放在中间尽量精简。用户当前问题放在最后确保它是离模型输出最近的内容。如果历史太长先用摘要压缩再把摘要放中间详细记录放外部存储。上下文管理是Agent实现里最“润物细无声”的部分做好了用户感觉不到做差了模型整天胡言乱语。3. 工具调用让Agent从“动嘴皮子”变成“能动手”3.1 Function Calling模型帮你在JSON里做决定大模型只输出文本要让它操作外部系统得靠Function Calling函数调用。机制很简单你把可用的函数以JSON Schema的形式告诉模型模型在推理时判断“该调用哪个函数、传什么参”输出一个结构化结果框架帮你真正执行函数把返回值再喂给模型。代码示例大概长这样tools [ { type: function, function: { name: search_flight, description: 查询符合条件的航班信息, parameters: { type: object, properties: { from: {type: string, description: 出发城市}, to: {type: string, description: 到达城市}, date: {type: string, description: 出发日期} }, required: [from, to, date] } } } ] # 第一次请求模型可能返回一个 tool_call res client.chat.completions.create( modelgpt-4o, messages[{role: user, content: 帮我查明天北京到上海的航班}], toolstools ) print(res.choices[0].message.tool_calls)模型的输出不是你想要的最终答案而是一个“意图”它说要调用search_flight参数是北京、上海、明天。你的代码去执行这个函数拿到航班列表后再把结果拼成一条Assistant消息给模型让模型基于结果生成面向用户的语言回答。这个“执行函数-回填结果-再生成”的循环是Agent能实际操作系统的核心机制。3.2 ReAct循环思考-行动-观察把单步串成多步Function Calling解决的是单步工具调用。但Agent要做的事往往是多步的比如“对比三个云服务商的价格并给出推荐”。这个任务需要先搜索A、再搜索B、再搜索C还要做对比计算。多步工具调用最经典的范式是ReActReasoning and Acting。循环结构是这样的Thought思考模型分析当前状态决定下一步该做什么。Action行动模型输出要调用的工具和参数。Observation观察系统执行工具后返回结果。然后回到Thought继续循环直到模型认为任务完成输出最终答案。一个简化版循环max_rounds 5 for step in range(max_rounds): # 1. 把历史信息 tool result 发给模型 res client.chat.completions.create(model..., messages..., toolstools) msg res.choices[0].message # 2. 如果没有工具调用说明模型准备好输出最终答案了 if not msg.tool_calls: print(msg.content) break # 3. 有工具调用就执行工具并把结果回填 for tool_call in msg.tool_calls: result execute_tool(tool_call.function.name, tool_call.function.arguments) messages.append({ role: tool, tool_call_id: tool_call.id, content: result })这个循环里最值得注意的地方是模型每跑一轮都要把之前的Observation全部放进上下文所以Multi-step ReAct对token消耗很大迭代轮数要设上限不能让它无限循环下去。3.3 接工具的三次翻车现场与修复方法我把自己调工具Call时翻过的车总结一下都是常规文档里不会写的细节。第一次翻车工具参数设计得像数据库字段表字段名太长、嵌套太深。模型经常传错参数。修复方式是把参数名改得接近自然语言比如user_from_city_code改成from_city并在description里给例子。第二次翻车工具返回的结果太大。模型拿到一个几百KB的JSON直接“看晕”回答泛泛而谈。修复方式是在工具内部做裁剪只回传关键字段或先对结果做一次摘要再回填。第三次翻车工具执行抛异常时我直接传了一个“ERROR”字符串给模型。结果模型一本正经地说“抱歉我暂时无法回答”。修复方式是把异常类型、可能原因、建议重试的信息一并回传让模型有机会修正参数重试一次。有了工具调用和ReAct循环单个Agent已经能独立完成不少任务。但任务一旦复杂到需要不同领域的知识、十几个步骤的串联单Agent还是会走到尽头——这就该聊聊多Agent协作了。4. 多Agent协作任务拆解与三种基础范式4.1 单个Agent撞上的能力边界单Agent不是不能做复杂任务而是代价越来越高。第一个边界是上下文窗口。一长串工具调用历史很快就会撑爆窗口。第二个边界是“角色冲突”。你让一个Agent既当规划者又当执行者又当审查者它很难切换好而且自己审自己的结果往往发现不了问题。第三个边界是工具集合的复杂度。一个Agent带几十个工具工具描述和参数Schema占用的token就非常可观。多Agent协作解决的不是“模型变聪明了”而是“系统结构变清晰了”把一个大而全的Agent拆成多个小而专、并且能相互协作的Agent。4.2 范式一编排者-执行者垂直分工这种模式最常用也叫Planner-Executor / Orchestrator-Workers。有一个中心Agent编排者负责接收用户任务、拆解子任务、分配子任务给执行Agent最后汇总结果。典型的流程用户输入分析2025年云服务器市场趋势并生成PPT大纲。编排者拆解任务A市场数据收集、任务B趋势分析、任务CPPT大纲生成。执行者各干各的数据Agent访问数据库/搜索分析Agent总结趋势大纲Agent整理结构。编排者汇总把三个结果合并、校验输出最终答案。这种模式最大的优点是逻辑清晰、每个Agent职责单一。最大的缺点是中心节点容易成为瓶颈编排者一旦拆错任务后面全跟着错。4.3 范式二流水线水平分工流水线模式适合流程相对固定的场景比如内容生产链路选题Agent → 大纲Agent → 写作Agent → 校对Agent。前一个Agent的输出就是后一个Agent的输入数据单向流动没有中心调度器。流水线的优势是简单、可预测性强。缺点是中间环节出错不好回溯越到后面的Agent越容易把前面的错误当既定事实。解法是在关键节点之间加校验Agent或者让每个Agent输出时附带“本步骤依据的数据源”。4.4 范式三辩论/评审对等协作当任务带有主观判断色彩时让多个Agent“吵一架”往往比单个Agent给结论靠谱得多。典型做法是两个Agent分别独立给出方案第三个Agent当评审指出各自优缺点最后再合成一版。我见过一个技术选型讨论场景三个Agent分别代表“用现成框架”“自研轻量框架”“混合方案”三个立场各写一份论据然后由一个中立Agent综合。结果是比单个Agent输出的选型建议要细致得多因为它迫使学生用不同视角审视同一个问题。辩论模式的缺点是token消耗大、节奏慢不适合实时性要求高的场景。4.5 多个Agent与多个并发请求并不是一回事这是很多人混淆的关键概念。把同一个任务并发发给5个Agent、取最好结果那不叫多Agent协作那叫并发采样。多Agent协作的本质是不同Agent承担不同职责它们之间存在信息流动和相互影响。判断标准很简单如果你的系统里所有Agent的prompt、角色、工具都一样那就不是多Agent系统只是“大模型调用池”。真正多Agent系统的Agent之间至少有一个维度是不同的角色指令不同或工具集不同或上下文不同。5. 编排与通信让多个Agent有序工作的骨架5.1 静态编排图与动态决策确定了范式之后下一步是设计编排逻辑。编排就是“决定下一步让哪个Agent做什么”的机制。最直观的是静态图流程提前写死。比如流水线模式A完成后必然走BB完成后必走C。静态图适合业务规则明确的场景实现简单、可测试性强。动态决策则让一个控制器Agent或策略函数在每一步决定下一步是谁。比如“用户问天气→走天气Agent用户问路线→走地图Agent用户问都涉及→两个都调”。动态决策灵活性高但多了不可预测性机制上要设好保底路径防止决策Agent做出离谱选择。我在动态决策Agent上翻过车它把“如何做红烧肉”归到了“数据分析Agent”因为没有别的Agent跟它讲清楚边界。后来我改了策略控制器不自己理解任务而是先做“任务标签分类”再按标签路由准确率立刻提升。5.2 三种通信模式对比直连、消息总线、共享黑板多Agent之间的通信方式决定了系统的松耦合程度和扩展性。我用过三种模式各有各的适用场景。通信模式核心思路优点缺点适用场景直连调用Agent A直接调Agent B的接口实现简单、延迟低耦合度高、调用链固定流程固定的流水线消息总线Agent之间通过消息队列通信松耦合、好扩展增加中间件运维成本有多个不确定路径的系统共享黑板所有Agent读写同一个共享存储区信息共享透明度高并发写冲突难处理大任务逐步推进的场景共享黑板模式在复杂任务里很管用。它相当于给所有Agent挂了一块公用的白板每个Agent把阶段性结果往上写后面的Agent从中取需要的部分。常见实现是一个Redis存储键是任务ID值是阶段性产物。隐患是不同Agent同时改同一个key容易互相覆盖。后面第六章细讲。5.3 终止条件与防“死循环”设计多Agent系统最大的风险是“跑不停”。两个Agent互相纠错可以无限循环下去一个Agent等另一个Agent的结果另一个又在等它形成死锁。我设计多Agent流程时会硬性加三道保险第一道是最大轮数限制。整个协作流程最多执行N轮超了直接返回当前已有结果并加一句“任务未完全完成”。第二道是置信度门槛。每个Agent最终输出时附带一个置信度评分比如0到1。低于阈值的Agent结果强制进入“补充处理”或“重做”。第三道是超时控制。给每个Agent调用环节设定超时时间超时就用兜底方案不让整个系统卡死。这三道保险听着很基础但没有它们你的多Agent系统早晚在生产环境里给你表演一次“挂起一整夜”。5.4 harness的职责边界再谈回到第一个章节提到的harness概念。到了多Agent编排层面harness的职责就非常清晰了维护共享状态如任务进度、Agent输出缓存、全局配置。控制调用循环决定当前轮到哪个Agent什么时候结束。消息路由负责把A Agent的输出正确送达B Agent。异常处理Agent挂了怎么办、结果不合法怎么办、要不要重试。把这几项从Agent代码里抽出去Agent本身才能保持“纯粹逻辑”的状态。我在项目里对Agent成员的要求只有一条接收输入、处理、输出结果其他一律不管都由harness处理。这条纪律让整个团队协作开发多Agent系统时每个人的模块都能独立测试。6. 记忆、共享与一致性多Agent协作里的隐形陷阱6.1 记忆分级短期、长期与外部存储多Agent场景下的记忆管理比单Agent复杂得多。我习惯把记忆分成三级短期记忆当前任务内的对话上下文存在内存或请求级变量里。任务结束就丢。长期记忆用户或项目的持久偏好存在数据库或KV存储里。跨会话复用。外部存储工具结果、知识库片段、历史报告存在对象存储或向量库里。按需检索。分工的核心原则是谁需要什么记忆就给谁什么记忆不给每个Agent都塞全量记忆。如果每个Agent都带全套长期记忆那还没开始干活token就已经爆炸了。6.2 上下文广播的信息通胀多Agent系统最常见的管理错误之一是“上下文广播”。也就是每个Agent运行时把之前所有Agent的输出全部塞进自己的上下文。这种做法的结果是信息通胀上下文越来越长模型越来越抓不住重点。实测下来同样的任务只用与当前步骤相关的上下文片段时输出质量提升了不止一个档次。我把这个做法叫“按需组装上下文”Agent运行时由harness从全局状态里拉取与当前任务标签匹配的片段而不是全量转发。6.3 幻觉放大与校验机制单个Agent会幻觉这个大家都认。但多Agent系统里的幻觉会“放大”A生成了一个中间结论B不校验就直接往下一环节传C基于被污染的信息再做推理最后出来的结果可能离事实十万八千里。这点在信息检索类任务里特别明显。早期我是靠人工抽检来兜底后来换了机制每个Agent输出时除了结论还要附一段“证据块”说明这个结论是从哪个工具结果或哪份文档里来的。下游Agent拿到结论后先做一步“证据检查”发现自己依赖的数据不存在时可以主动拒绝处理并发起重查。有了校验机制多Agent系统才算从“看起来很聪明”进化到“真的可靠”。6.4 共享状态的一致性问题当多个Agent同时读写共享状态时会碰上一类经典并发问题。最常见的场景是Agent A在任务进展里写入“阶段1完成”Agent B在同一毫秒内因为旧数据也写入“阶段0进行中”然后整个系统的状态就乱了。我的处理经验是两条一是部分场合指定单一写入者。一块状态只允许特定Agent写其他Agent只读。二是版本号或乐观锁。写入前检查版本号发现版本不对就重新拉取最新状态再写。这两个方案加起来基本上能覆盖多Agent共享状态的大部分坑。7. 从理论到选型真到了自己动手什么时候该用多Agent7.1 一张决策表帮你判断很多人一听多Agent很时髦就想把现有系统改造成多Agent。我的建议是先用一张决策表冷静判断。判断维度适合单Agent工具适合多Agent协作任务步骤少于5步逻辑线性超过5步多分支并行角色需求单一视角足够需要多种专业视角交叉验证工具数量少于10个工具工具类型差异很大质量要求一般问答、内容生成高可靠性、需要校验与评审实时性秒级响应要求分钟级可接受预算对token敏感能接受较高token消耗我判断的核心原则只有一条先看“单Agent 记忆 工具”这套组合能不能扛住扛不住再上多Agent。多Agent带来的不只是能力提升还有成本、延迟和不确定性这三样都必须纳入评估。7.2 单Agent增强路线先别急着拆分多数实际业务场景优先走“单Agent增强”路线就够了。具体做法是把状态和记忆做扎实让Agent记得住。把工具调用做扎实让Agent能真正执行操作。加一层校验和纠错机制提高输出稳定性。再不行加一个“重试Agent”在主Agent结果质量差时重新处理。我见过很多号称“多Agent系统”的项目拆开一看其实就是一套单Agent逻辑只是按流程写了好几个步骤罢了。这种系统用单Agent增强路线去重写效果反而更好代码也更简单。7.3 从最小角色梯度升级Planner → Worker → Critic如果真的确定要多Agent我强烈建议从一个最小的角色梯度开始不要一步到位搞出一个完整“Agent王国”。最小可行的多Agent结构是三个角色Planner拆解任务生成子任务清单。Worker负责执行某个具体子任务。Critic检查Worker的输出发现问题退回重做。以“生成产品方案”为例Planner把方案拆成市场分析、技术评估、路线图三个子任务Worker分别执行Critic把三个结果合并检查逻辑一致性跳回有问题的部分重做。这个三角色模型能覆盖多数场景而且每个角色都能独立测试。跑通之后再根据具体需要加角色。比如加一个“检索员”专门负责知识检索或者加一个“执行员”专门负责调用业务API。每次加角色都遵循一个原则新增角色必须承担一种现有角色承担不了或做不好的职责否则就是冗余。7.4 并发与成本想清楚再“扛”热搜词里有个说法是“ai agent怎么扛并发”。从团队内部看并发你需要先弄清楚是哪一层并发单Agent系统扛并发要看模型API的速率限制、工具服务的负载能力以及上下文状态的存储压力。这三个能扛住单Agent就足够扛业务流量。多Agent系统扛并发问题会放大好几倍每个Agent都会额外消耗token协作链路长时一次用户请求可能要触发十几次模型调用。真到了需要大并发的时候我更建议做“无状态化处理”把每个Agent调用拆成可独立运行的任务单元放入消息队列由多个worker并行执行同一个用户的任务链路仍然串行不同用户的任务链路并行。这样架构上既控制了单个链路的成本又扩展了整体吞吐。记住多Agent的并发瓶颈通常不在模型本身而在状态管理和编排中心的调度能力。我最后给你个最诚恳的建议如果你刚入门先踏踏实实把单轮调用、状态管理、工具调用这三件事吃透再考虑多Agent协作。我自己就是单Agent跑了大半年因为一个需要同时处理数据搜索、方案撰写、风险审查的任务被逼着上了多Agent才真正理解编排和通信设计的份量。纸上得来终觉浅Agent这东西手写一遍ReAct循环比看十篇框架文档都管用。你可以先把最小的“Planner-Worker-Critic”三角色搭出来跑一遍跑的过程里遇到的那些“这不合理啊”瞬间才是你真正入门的时刻。