1. 先搞清楚:智能体(Agent)到底是什么
1.1 用“带实习生”类比理解Agent
我第一次听说“AI原生架构”这个概念时,脑子里冒出来的画面是:一堆AI Agent在服务器里互相发消息、调用工具、商量着把活儿干完。听起来很科幻,但落地到写代码的层面,其实就是一句话——智能体是以大模型为“大脑”,能自己规划、调用工具、观察结果并不断调整直到完成目标的一段程序。
打个比方。你带过一个实习生就会秒懂:
- 你给他一个目标:“去把这份合同打印三份,装订好放我桌上。”这是任务目标。
- 他自己会想:先找打印机,还是先看合同页数?装订用订书机还是夹子?这是规划(Planning)。
- 他走到打印机前按打印按钮,发现没纸了,于是去柜子拿纸。这是调用工具(Tools)。
- 他装订完检查了一下页码对不对,发现第二页顺序错了,重新打一页替换。这是观察结果并反思(Reflection)。
传统程序则完全不同。传统程序像一条流水线的传送带:你给一个明确输入,代码走固定分支,输出固定结果。你要是让传送带“自己决定要不要换个方式把箱子搬过去”,它会直接宕机。
所以理解Agent的第一把钥匙是:它不是一套写死的if-else,而是把“怎么做”的决策权交给了大模型。你要做的是定义好“目标、边界、工具、评价标准”,剩下的事让模型自己推理和尝试。
1.2 Agent和传统API调用、工作流有什么区别
很多刚接触AI开发的朋友会把Agent和“调用一次大模型API”搞混,也会把Agent和“预设好的工作流”搞混。我直接画个对比:
| 形态 | 决策方式 | 适用场景 | 典型代码量 |
|---|---|---|---|
| 单次API调用 | 一问一答,无记忆无工具 | 翻译、摘要、分类 | 几十行 |
| 预设工作流 | 人工写死步骤,节点固定 | 固定流程、流水线处理 | 百到千行 |
| 智能体Agent | 模型自主规划,动态调整 | 开放任务、多步骤、跨系统 | 中等复杂度 |
| 多智能体系统 | 多个Agent协作拆分任务 | 大型复杂目标、并行子任务 | 较高复杂度 |
以一次API调用来说,你传入了prompt,拿到一个输出,隧道就关闭了。它没有状态,没有工具,不会因为“这个结果不对”而主动重试。
而工作流呢?比如你做一个“文章审核流水线”:先调用模型A做敏感词过滤,再调用模型B做风格检查,最后调用模型C生成结论。这些节点的顺序是人工写死的,任何一个节点出问题,流程就断了,你只能手动修复。
Agent的本质区别在于:它在步骤之间有一个“循环”——模型看当前状态,决定下一步干什么,执行动作,看结果,再决定下一步。同一套代码,给不同的任务,它走出的路径可能完全不同。
1.3 Agent的核心四件套:模型、规划、记忆、工具
我习惯把Agent拆成四块,写代码前先在纸上把这四块列清楚,后面基本不会跑偏。
第一块:模型(Model)。这是大脑,负责推理和决策。选模型时重点看三个能力:指令遵循能力(听话不跑偏)、函数调用能力(能不能按JSON格式调工具)、长上下文能力(能不能记住足够长的对话历史)。这三项决定了一个Agent的上限。
第二块:规划(Planning)。这是把大目标任务拆成小步骤的能力。最简单的规划是让模型“一步一步思考”并在每一步结束时输出下一步计划;进阶的做法是让模型输出一个结构化任务列表,或者用独立的规划模块去拆解。规划质量直接决定Agent会不会像无头苍蝇一样乱撞。
第三块:记忆(Memory)。Agent必须记得“我正在干什么”“我已经做了什么”“用户刚才说过什么”。短期的对话上下文、中期的任务状态、长期的用户偏好和领域知识,这三层记忆怎么组织,是Agent工程里最考验架构能力的地方。
第四块:工具(Tools)。这是手和脚。搜索、计算、调数据库、执行代码、发HTTP请求——Agent通过“函数调用”机制使用这些能力。没有工具的Agent只能输出文字,有了工具的Agent才能真正改变世界状态。
一句话总结:模型是智商,规划是方法,记忆是经历,工具是四肢。任何一块缺失或配合不好,这个Agent都会变成“会说话但不会干活”的花架子。
2. AI原生架构下的单智能体设计
2.1 Agent的运转循环:感知-决策-行动-反思
写Agent的代码时,脑子里要一直有一根循环的弦。不管你用什么框架,核心循环逃不开这五步:
- 感知(Perceive):把当前环境状态打包给模型。包括用户目标、历史消息、工具返回结果、系统时间等。
- 决策(Decide):模型根据这些信息,决定下一步动作。动作的类型可能是“继续思考”“调用某个工具”“给出最终答案”。
- 行动(Act):执行决策。如果是调用工具,就在代码里真正执行那个函数。
- 观察(Observe):拿到工具的执行结果,把结果重新喂给模型。
- 反思(Reflect):模型判断当前行动是否达成目标,未达成则回到第2步,继续迭代。
我在实际项目里写过一个最小实现,核心循环长这样(用伪代码表示,方便理解,接入任意大模型API都能用):
def run_agent(user_goal): # 初始化上下文 messages = [{"role": "user", "content": user_goal}] # 最大迭代次数,防止死循环 MAX_STEPS = 20 step = 0 while step < MAX_STEPS: step += 1 # 让模型决策下一步动作 response = llm(messages) # 情况1:模型觉得任务完成了 if response.is_final_answer: return response.content # 情况2:模型要调用工具 if response.tool_call: tool_name = response.tool_call.name tool_args = response.tool_call.args # 执行工具 result = execute_tool(tool_name, tool_args) # 把工具结果追加进上下文 messages.append({ "role": "tool", "tool_name": tool_name, "result": result }) continue # 超过最大步数,返回当前最佳结果 return "抱歉,我没有在限定步数内完成任务。"这段代码看起来简单,但它是所有Agent系统的地基。我见过很多所谓的Agent框架,剥完壳发现核心就是这样一个while循环。理解了它,你就理解了Agent的命脉。
关键点在于role: tool的返回消息。它告诉模型“你刚才调的那个工具有结果了”,模型基于这个结果继续决策。如果工具返回的信息格式不清晰,模型就会懵,所以工具函数的结果一定要设计成“机器能看懂、模型也能看懂”的结构化文本。
2.2 提示词与角色边界设计
很多新手Agent跑起来效果差,第一锅甩给模型不好,其实八成是提示词没写好。Agent的提示词和做普通Chat问答的提示词完全不同,它更像一份“员工手册”,要讲清楚四件事:
- 身份与职责:你是谁、你负责什么、你不负责什么。
- 可用工具清单:你有什么工具,每把工具什么时候用、怎么用。
- 任务流程约束:先做什么后做什么,哪些事必须确认后才能做。
- 边界与禁忌:遇到什么情形要停止、要上报、要给用户明确反馈。
我举个例子,一个“代码辅助Agent”的系统提示词开头可以这么写:
你是某团队的代码助手Agent。 你的职责:帮助开发者诊断代码问题、生成补丁、解释代码片段。 你的工具:search_code(搜索项目代码)、run_test(运行测试用例)、fetch_docs(查阅技术文档)。 你的工作规范: 1. 先用search_code定位问题相关代码,必须拿到实际代码内容再判断,禁止凭空猜测。 2. 当你怀疑是测试失败导致的问题时,必须调用run_test复现,复现结果作为判断依据。 3. 涉及删除代码的操作,必须向用户说明影响范围并等待确认,禁止直接执行。 4. 如果你无法确定根因,明确回复“当前信息不足以定位问题”,并建议用户补充日志。注意这里面的“禁止凭空猜测”“必须拿到实际代码再判断”——这就是边界约束。没有边界的Agent特别喜欢一本正经地胡说八道,会为了完成任务自圆其说。这一点我在项目里吃过大亏,后来每写一个Agent都把边界写死。
还要强调一个技巧:工具的说明书就是提示词的一部分。模型决定要不要调用工具,完全看你对工具的描述。你写“计算两个数的和”和写“当你需要聚合统计一组数值的总和时使用,输入为数字列表”,后者的调用准确率会高出很多。因为描述里包含了“什么时候用”和“输入长什么样”。
2.3 记忆体系:短期、长期、工作记忆怎么落地
Agent最常见的翻车现场之一是“失忆”。你说“把刚才那个文件改一下”,它问你“哪个文件?”——因为它的上下文里没有留存足够信息。所以记忆设计是Agent能否“好用”的分水岭。
我习惯把记忆分三层:
短期记忆:就是存在上下文窗口(context window)里的对话内容。这个最简单,但也有坑:模型上下文窗口是有限度的,比如可能只有几十K Token。你要能处理窗口过长的场景,最粗暴也最有效的办法是“压缩+截断”:把历史消息里已经完成的步骤压缩成一行摘要,只保留最近的对话原文。
工作记忆:指当前任务的执行状态。比如Agent正在写一篇长文,已经写了开头和中间,还剩结尾——这些状态可以维护在内存里,甚至在每轮循环中都显式地写一段“当前任务状态”给模型看。我用过一个很土但好用的方法:维护一个JSON文件作为“任务便签”,Agent每完成一个步骤就把进度写进去,下一轮决策时读出来放在上下文最前面。
task_state = { "goal": "写一篇关于多智能体系统的技术博客", "step": 3, "completed_sections": ["概念解析", "单Agent设计"], "next_step": "多智能体架构讲解", "data": {"key_points": ["协作模式", "通信协议", "状态管理"]} }长期记忆:指跨会话的知识沉淀,比如用户的偏好、上次交互中确认过的事实。落地方式一般两种:一种是外部数据库(比如向量数据库)——把重要信息向量化存储,每次开启新会话时做相似度检索;另一种是结构化存储——把重要事实存进关系型数据库,需要时精确查询。对小白程序员,我建议先不用搞向量数据库,直接从“把重要信息写进一个JSON或SQLite”开始,够用而且好调试。
记忆这块有一个容易被忽视的问题:记忆污染。存进去的信息可能是模型脑补的错误信息,下一轮它把这些错误信息当事实继续推理,错误就会滚雪球。所以要建立“事实准入机制”——只有经过工具返回结果、官方接口数据或用户明确确认过的信息,才允许写入长期记忆。凡是模型自己推测出来的内容,最多放短期记忆里用一下。
2.4 工具调用的3个关键细节
工具调用是Agent从“聊天机器人”变成“干活机器”的关键跳板。但我发现新手写工具时特别容易踩三个坑:
坑一:工具返回结果没有格式化。你的search_code函数如果返回的是Python字典,请把它转成清晰的自然语言描述或JSON后再塞回给模型。原因很简单:模型擅长读文本,不擅长解析深层嵌套结构。我一般会在工具内部做一次“结果摘要”,把十万行日志提炼成“共发现3个错误,核心错误发生在第412行:空指针异常……”。
坑二:工具调用参数校验不到位。模型传入的参数和工具期望的格式不符,是最常见的报错。比如模型传来的是字符串"123",你的函数却期望整数。我给每个工具都包了一层校验器:类型不对就强转,强转不了就返回“参数错误,请重新提供整数格式的ID”,而不是直接抛异常。记住:工具异常要转成模型能理解的错误信息,而不是Python的traceback。
坑三:工具调用没有超时和失败重试机制。一个HTTP请求可能卡60秒,一个数据库查询可能锁表。在Agent场景里,工具执行必须有超时上限(我通常设10-15秒),超时后返回“调用超时,请重试或换一个替代方案”。有些可控的失败(网络抖动、临时服务不可用)还可以自动重试两到三次,但指数退避必须有,不然并发一高你的下游接口直接被重试打到崩溃。
还有一个我自己家的私货习惯:给每个工具加一个“成本预估”字段。比如“执行SQL查询”这个工具,模型调用前我会提示它“此操作可能耗时5-10秒,请务必确认查询条件”。这样能显著减少模型随手执行危险/昂贵操作的概率。
3. 多智能体(Multi-Agent)系统设计核心
3.1 什么时候该上多智能体
单Agent写得好好的,为什么要引入多Agent?我听到很多人说“多Agent很酷”,结果架构搭得很华丽,业务一点没跑快——这就是典型为了技术而技术。
我的判断标准很简单:当一个任务的完成需要多种角色视角、需要并行处理多条线、或者单Agent的上下文已经装不下完整业务链条时,才考虑多Agent。
举几个真实场景:
- 客服系统:用户问题可能是“订单没收到”,你既要查物流、又要查订单状态、还要查支付记录。让一个Agent把所有子系统都串一遍,上下文很容易爆,而且一旦某一步出错会影响后面所有步骤。拆成“订单Agent”“物流Agent”“售后Agent”,各自调各自的系统,再加一个主管Agent分流,清晰得多。
- 代码审查:让一个Agent既写代码又做审查,它会自带滤镜,很难发现自己写的bug。拆成“编码Agent”和“审查Agent”,让审查Agent独立行动,能发现很多单Agent状态下漏掉的问题。
- 大型内容生产:调研、写作、校对是三个完全不同的能力需求。一个Agent干三件事,通常某一步会拉胯。拆开后,每个Agent只专注一件事,质量提升非常明显。
反过来,如果你的任务两步就能完成、也不需要额外视角,那单Agent就够了。多一个Agent多一层通信开销,多一份调试麻烦。能用单车解决的问题,别硬上火车。
3.2 4种主流协作模式
我梳理一下多Agent系统最常见的四种协作模式,本质上它们解决的是“Agent之间怎么配合”的问题。
模式一:主管-工人模式(Orchestrator-Workers)
一个主管Agent负责接收总任务、拆解子任务、分发给多个工人Agent,工人完成后把结果交回主管收拢。主管是唯一的大脑,工人是纯粹的执行者。优势是结构简单、可控性强;劣势是容易“过度集权”——主管Agent的决策质量一旦下降,整个系统拉胯。适用于任务边界清晰、子任务可独立完成的情况。
模式二:流水线模式(Pipeline)
Agent A的输出是Agent B的输入,B的输出是C的输入,像流水线一样。典型场景是“内容生产流水线”:选题Agent→大纲Agent→初稿Agent→审校Agent。优势是职责清晰、每个环节可以单独优化;劣势是链路延迟累积且任何一个节点出错,下游全部白干。适用于处理流程稳定、顺序固定的任务。
模式三:辩论评审模式(Debate/Critic)
多个Agent对同一个问题各自提出方案,或者一个Agent提方案、另一个Agent负责挑刺,来回多轮。这种模式能提升决策质量,因为方案经过了对抗性检验。代价是Token消耗翻倍甚至更多,而且如果没有终止条件,很容易陷入无限互喷。适用于高风险决策、需要极致质量的任务。
模式四:市场/黑板模式(Blackboard)
没有集中的主管,多个Agent共享一块“黑板”(公共消息区或共享状态库),各自取走自己能处理的任务,干完后再把结果写回黑板。灵活度和并行度最高,但也是调试地狱——任务可能被多个Agent重复处理,或者所有人都以为有人处理了某件事。我是到了后期才敢在生产环境用这种模式的。
新手入门我给一个路线建议:先照着主管-工人模式做一版demo,把整个链路跑通,再考虑是不是要加流水线或者辩论环节。上来就搞黑板模式,大概率你会被并发和竞态问题折磨到怀疑人生。
3.3 消息协议与共享状态的设计要点
多Agent系统里最核心的设计决策就两个:他们之间传什么、状态放哪里。
先说传什么。Agent之间的消息我统一建议用结构化的JSON,不要用自由文本。理由告诉你:
- 自由文本的语义模糊,Agent生成的消息会随着上下文漂移,下游Agent解析靠猜。
- 结构化消息方便追踪和回放,出了问题可以定位到具体某一个Agent说了什么。
我会定义一个通用的消息信封:
{ "event_type": "task_request", "from": "orchestrator", "to": "search_agent", "task_id": "20241015_001", "payload": { "query": "多智能体系统的定义", "max_results": 5, "deadline": "2024-10-15T18:00:00Z" }, "created_at": "2024-10-15T17:30:00Z" }关键字段是task_id和event_type。task_id用于串联整个流程,一个总任务下的所有消息都携带同一个ID,方便追踪和做会话隔离。event_type标识消息类型(任务请求、任务结果、状态汇报、错误上报等),不同类型走不同的处理逻辑。
再说状态放哪里。很多公司项目里遇到过一个问题:多Agent共享同一个全局状态字典,结果A Agent写入的数据被B Agent覆盖,查bug查到崩溃。我给一个折中方案——用独立的“状态仓库”而非共享大字典。
具体做法是:维护一个键值存储(内存KV或Redis都行),键是task_id + agent_name + 数据域,比如20241015_001_orchestrator_subtask_list。每个Agent只允许读写自己那个agent_name前缀下的键,主管Agent可以读所有Agent的状态但不能直接改子Agent的执行数据。这样既保证信息互通,又避免互相踩脚。
另外建议为每个消息加一个ttl(有效期),超时的消息自动丢弃,防止历史上堆积的旧消息被模型误读为当前状态。这个细节在联调时帮过大忙。
4. 从零撸一套多智能体Demo
4.1 选型与架构
理论说了这么多,来点实际的。我带大家从零写一个“技术文章写作工坊”多智能体Demo。之所以选这个场景,是因为它不需要外部系统依赖,新手理解起来最轻松,却能完整展现多Agent协作的全部核心环节。
系统三个Agent角色:
- 主编Agent(Orchestrator):接收用户给定的选题,拆解文章结构,分配写作任务,最后收拢审校意见。
- 写作Agent(Writer):根据主编下发的章节大纲,独立完成一个章节的初稿。
- 审校Agent(Reviewer):对写作Agent的产出做挑剔式检查,输出修改意见。
技术选型上我特意不依赖任何重型Agent框架,用最纯粹的Python + 大模型接口把骨架写清楚。为什么?因为框架的封装太厚,新人容易只见森林不见树木。等你理解了核心逻辑,再看框架源码会事半功倍。大模型接口只要支持“对话补全”和“函数调用”就行,我一般用一个统一的llm()函数封装,替换任意厂商API时只改这一处。
4.2 代码实现:主管分发逻辑
先定义每个Agent的“大脑”——其实就是把各自的系统提示词和上下文管理封装成函数。
def llm(messages, tools=None): """统一的大模型调用入口;不同厂商API差异封装在这里""" # 不同厂商API差异封装在这里 pass def orchestrate(topic): """主编Agent:拆解任务并分发""" orchestrator_prompt = """ 你是技术文章主编Agent。你的任务是把用户给出的技术主题拆解为一篇文章的结构。 要求:输出一个JSON数组,每个元素包含section_title(章节标题)和section_points(该章节3-5个核心要点)。 注意:不要写正文,只要大纲。 """ messages = [ {"role": "system", "content": orchestrator_prompt}, {"role": "user", "content": f"请为选题《{topic}》设计文章大纲。"} ] response = llm(messages) sections = parse_json(response) # 把模型输出解析成结构 # 把每个章节作为任务分发给写作Agent subtasks = [] for i, section in enumerate(sections): subtask = { "task_id": f"task_{i}", "section_title": section["section_title"], "section_points": section["section_points"], } subtasks.append(subtask) return subtasks注意我要求模型输出严格JSON并进行parse_json校验。这一步不能省,模型偶尔会输出带Markdown代码块包裹的JSON,解析前要先清理。我见过太多人在这上面翻车,白花一整天调试。清理代码简单粗暴:
def parse_json(text): # 去掉可能的代码块包裹 if text.startswith("```json"): text = text.replace("```json", "").replace("```", "") return json.loads(text.strip())临时写demo够用,生产环境建议用带自动纠错的JSON解析库。
4.3 代码实现:子Agent协作响应
接下来写写作Agent和审校Agent。写作Agent要“看到”主编下发的章节和要点,然后产出一段正文;审校Agent要针对这段正文输出“修改意见”和“评分”。
def write_section(subtask, context): """写作Agent:根据章节信息写正文""" writer_prompt = """ 你是资深技术作者Agent。你的风格是:通俗易懂、逻辑清晰、多给实操建议。 你只负责写你被分配到的章节,不要越权写其他章节。 你需要根据给定的章节标题和要点,输出至少500字的中文正文。 """ messages = [ {"role": "system", "content": writer_prompt}, {"role": "user", "content": ( f"请撰写章节:{subtask['section_title']}\n" f"必须覆盖以下要点:{subtask['section_points']}\n" f"全文已提供的上下文:{context}" )} ] content = llm(messages) return {"task_id": subtask["task_id"], "content": content} def review_section(article): """审校Agent:对正文做检查和提出修改意见""" reviewer_prompt = """ 你是严格的编辑Agent。你要从三个维度检查文章:逻辑连贯性、表达准确性、实操可落地性。 请输出JSON格式:{"score": 0-100, "issues": ["问题1", "问题2"], "suggestions": ["建议1", "建议2"]} 如果分数不低于85,可以输出"approved": true。 """ messages = [ {"role": "system", "content": reviewer_prompt}, {"role": "user", "content": f"请审查以下文章:{article['content']}"} ] review = llm(messages) return {**article, "review": parse_json(review)}核心协作逻辑是主编把大纲下发,写作Agent各自写各自的章节,写完塞回给主编,主编收集全部初稿后统一交给审校Agent逐章审核。这是主管-工人模式和流水线模式的结合体。为了效率,写作Agent之间的调用互不依赖,完全可以用ThreadPoolExecutor并行跑。
def run_article_workshop(topic): # 1. 主编拆解大纲 subtasks = orchestrate(topic) # 2. 并行写作 from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=5) as executor: drafts = list(executor.map( lambda sub: write_section(sub, context="本文是AI原生架构系列"), subtasks )) # 3. 审校 reviewed = [] for draft in drafts: result = review_section(draft) reviewed.append(result) # 分数不够的可以触发重写循环(这里先不做,后面避坑部分会讲) # 4. 汇总最终文章 assembly = assemble_final_article(topic, reviewed) return assembly这套骨架跑通以后,你能清楚地看到每一个Agent的输入输出之间是怎么衔接的。我给内部做过培训,学员普遍反馈“手写这么一版,再看考试题目就完全不慌了”。
4.4 运行效果与调试
跑这个Demo时,我建议你要刻意去观察三样东西:
- 每个Agent的输入是否完整:主编拆出来的大纲有没有丢要点?写作Agent的prompt里是否带上了足够的上下文?
- 输出是否符合约定格式:JSON有没有解析失败?评分字段是不是数值?
- 消息流转是否顺畅:
task_id有没有贯穿始终?每个Agent是不是拿到了自己那份任务?
调试技巧上,第一件事是“开日志”。我习惯在每个Agent的入口和出口打一行结构化日志,记录入参、出参、耗时。别小看这个习惯,多Agent系统跑起来信息量爆炸,没有日志你根本不知道是哪一步歪了。
第二件事是“单点试跑”。多Agent系统调优时不要从头到尾跑全链路,那样慢且难定位。我会先单独调主编Agent,把拆解结果打印出来人工看;确认没问题了,再单独调写作Agent,用假的大纲喂进去看产出质量。每个环节都通过后,才把整条流水线串起来。这叫分而治之,是调试多Agent系统的第一方法论。
5. 常见问题与避坑实录
5.1 Agent陷入死循环怎么办
这是所有Agent开发者都会遇到的“鬼打墙”。表现为:模型反复调用同一个工具,拿到的结果一样,然后再次调用,循环往复,直到上下文塞满或步数耗尽。
根因通常是两个:一是模型没有从工具结果里获得新信息,导致决策不变;二是任务目标本身定义模糊,模型不知道“什么时候算完成”。
我的三板斧:
- 设置硬性步数上限(比如单次任务最多20步),超限强制Terminate并汇总现有结果返回给用户。
- 在系统提示词里写清“终止条件”:“当你连续两次得到相同结果时,认定该方案不可行,必须停止并换一个方案或上报用户。”
- 循环模式变异检测:在循环里记录工具+参数的哈希值,如果相同的调用出现了3次以上,主动中断并提示模型“检测到重复行动,请换一条路径”。
我还会在循环里加一个“计数提示”:每过5步,就往上下文里塞一条系统消息——“你已经执行了5步仍未完成任务,请回顾已完成工作,判断是否需要改变策略。”实测这个操作能显著降低无效循环。这个优化思路是某次做线上客服机器人的时候踩坑踩出来的,省下来的Token费用相当可观。
5.2 Token成本失控
多Agent系统的Token消耗是单Agent的几何倍数。我见过一个团队,多Agent版本跑了一个demo,月底账单出来傻眼了——成本翻了好几倍。
控制成本的措施,按优先级排:
- 限制子Agent的上下文长度:子Agent并不需要知道全量历史,只给它当前任务相关的信息。
- 输出裁剪:要求审校Agent返回结构化短文本(JSON),而不是大段评论。
- 记忆压缩:超过N轮后,将历史消息抽象成摘要,替换原文。
- 引入“预算字段”:在Agent的状态里写一个
token_budget,每轮估计消耗并扣减,预算不足时自动切换为“快思考模式”,要求模型用更短的回答完成收尾。
另外特别提醒:多Agent并行很爽,但每一路并行都要花钱。刚开始做实验时,串行跑通功能再上并发,别一上来就开满并行度。
5.3 工具调用不稳定
模型选错工具、传入错误参数、甚至幻觉出根本不存在的工具,这类问题在Agent项目里能占到调试时间的四成。
我的经验是三层防线:
- 工具名和描述写极端清晰,每把工具在描述里写清楚“适用场景”和“不适用的场景”。
- 函数调用失败时给出“自救指引”:工具内部把常见错误转成“上次的调用原因是XXX,请尝试XXX方式解决”的消息返回给模型,让它能自我修复。
- 顶层加一层“工具白名单校验”:模型说要调用的工具如果不在白名单里,直接返回“该工具不可用,这是可用的工具列表……”
最后一点容易被忽略:不要依赖模型的“创造力”。工具列表越精简越好。我见过一个项目把十几个弱相关的工具全部挂上去,模型选择困难症犯了,调用准确率反而下降。工具数量控制在5个以内,效果通常最好。
5.4 多Agent互相推诿和重复劳动
多Agent系统最坑的行为是:A Agent以为B Agent会处理某件事,B Agent以为A Agent已经做完了,结果这事谁也没干。或者更常见的是,两个Agent都把同一件事干了,浪费资源不说,结果还可能互相矛盾。
我给的根治方案有三条:
- 明确每个Agent的“职责声明”:系统提示词里写清“你负责什么、你不负责什么、遇到不属于你职责范围的事项务必转交给谁”。
- 引入“确认消息”机制:当A Agent把任务转给B Agent时,B Agent必须先回执“我已收到并开始处理”,才视为交接成功。没有回执的任务,A Agent有义务继续跟进。
- 共享状态里维护“任务认领表”:结构就是
{task_id: agent_name, status: pending/processing/done}。任何Agent处理任务前,先查这张表,如果任务已被认领且状态不是done,就必须跳过,避免重复劳动。
这套“认领表”模式是从分布式任务队列学来的,搬到多Agent场景里依然好用。成本很低,就是用一张字典维护状态,但收益巨大。
5.5 给新手的演练建议
最后按我的经验,给你一条从零到实战的学习路径,避免一头扎进复杂框架被劝退。
第一周:把单Agent玩透。写一个能调用3个工具的小Agent(比如搜索、计算、时间查询),重点理解循环、上下文、工具返回格式。这一周的目标是让它稳定完成“查天气→计算温差→给出穿衣建议”这种简单的复合任务。
第二周:加记忆和反思。在单Agent上加上长期记忆(存SQLite)、加上“连续失败两次就换策略”的反思逻辑,让它学会从错误中恢复。
第三周:上多Agent协作。把第4章的写作工坊Demo手敲一遍,自己加一个新的Agent角色(比如“配图Agent”),理解任务分发和消息往来的细节。
第四周:引入健壮性和成本控制。把第5章的避坑措施逐一实现,做压力测试,观察Token消耗曲线,培养“架构感”。
我见过不少同学学了一遍基础概念就想直接啃复杂的Agent框架源码,被一堆抽象层劝退;也见过老老实实从手写循环开始、把每个环节吃透的同学,后面看任何框架都毫不费力。小白学Agent,最忌讳的就是“跑通即懂”。把每一步为什么这么写、出了问题怎么排查搞懂,AI原生架构的底子才算真正打牢。这条路不轻松,但走完之后你回头看,绝对值得。