大模型Agent开发这两年从“新鲜词”变成了“绕不开的坎”。我身边不少做后端、做数据、甚至做硬件的朋友,都在问同一个问题:Agent到底是个啥,我能不能上手做?答案是可以,但前提是你得先搞清楚它和普通大模型调用之间的本质区别。很多人以为接个API、写个提示词就算Agent了,结果一跑就发现,模型该胡说还是胡说,该断片还是断片。问题不在模型本身,而在于你没有给它装上“手脚”和“记忆”。
这篇内容面向的是有一定编程基础、想从零搭出一个能跑通的大模型Agent的开发者。我会从Agent的核心构成讲起,把规划、记忆、工具调用这三块拆开揉碎,再给出一套可以直接抄的实操路径。中间会穿插我自己踩过的坑,比如工具描述写得太模糊导致模型乱调、记忆窗口爆掉之后响应直接崩掉这类问题。读完你至少能明白:一个能用的Agent需要哪些模块,每个模块的坑在哪,以及怎么用最小的成本把它跑起来。
1. 先搞清楚Agent和裸调大模型的本质区别
1.1 裸调大模型的天花板在哪
大部分人接触大模型的第一步,就是在对话框里输入问题、等回复。这种模式叫单轮或多轮对话,本质上是一次“输入-输出”的映射。你问“今天天气怎么样”,模型根据训练数据里的统计规律给你编一段话。它没有实时数据,没有执行能力,也不会主动去查任何东西。
这种模式的天花板非常明显。第一,知识截止问题,模型训练完之后发生的事情它不知道。第二,无法操作外部系统,你让它帮你发一封邮件、查一下数据库、调一个接口,它只能告诉你“你可以这样做”,但它自己动不了手。第三,多步推理容易断链,你让它先算A再算B最后得出C,中间任何一步出错,后面全崩,而且它不会自己回头检查。
我刚开始做Agent的时候,最大的误区就是觉得“模型够强就行了”。实测下来,GPT-4级别的模型在纯对话场景确实很强,但一旦涉及多步任务编排,没有外部框架支撑,错误率会指数级上升。这不是模型不行,是你让它干了他不擅长的事。
1.2 Agent多出来的三样东西
Agent和裸调大模型的核心差异,可以归纳为三个能力的叠加:规划、记忆、工具使用。
规划能力让Agent能把一个复杂任务拆成多个子步骤,并且决定先做哪一步、后做哪一步。比如你让它“帮我分析一下这份销售数据并生成报告”,它会自己拆成:读取文件、清洗数据、计算指标、生成图表、撰写文字、整合输出。这个拆解过程不是硬编码的,而是模型根据任务描述动态生成的。
记忆能力让Agent在多轮交互中保持上下文一致性。裸调模型也有上下文窗口,但那个窗口是线性的、有限的。Agent的记忆系统通常分为短期记忆和长期记忆:短期记忆存当前任务的中间状态,长期记忆存跨会话的知识和用户偏好。我见过太多项目因为没做好记忆管理,跑到第五轮对话的时候模型已经忘了第一轮说了什么。
工具使用能力是Agent最核心的差异化。模型本身只能生成文本,但通过工具调用,它可以执行代码、查询数据库、调用API、操作文件系统。工具调用的本质是模型输出一个结构化的调用请求,外部系统执行后把结果返回给模型,模型再基于结果继续推理。这个循环就是Agent的基本工作单元。
1.3 一个最小Agent的组成清单
如果你现在就想动手,一个能跑的最小Agent需要以下组件:
- 大模型接口:负责推理和决策,可以是云端API也可以是本地部署的模型
- 提示词模板:定义Agent的角色、可用工具、输出格式约束
- 工具注册表:列出所有可调用的工具及其参数描述
- 执行循环:接收模型输出、解析工具调用、执行工具、把结果喂回模型
- 记忆存储:至少要有对话历史的存储和截断策略
- 错误处理:工具调用失败、模型输出格式错误、超时等情况的兜底逻辑
这六个组件缺一不可。我见过有人只写了提示词和工具注册表就上线,结果模型返回了一个不存在的工具名,整个流程直接卡死。错误处理不是可选项,是必选项。
2. 规划模块:让模型学会“先想再做”
2.1 ReAct模式为什么成为主流
Agent规划最经典的范式是ReAct,也就是Reasoning加Acting。它的核心思想是让模型在每一步都先输出一段推理过程,再决定执行什么动作。推理过程是自然语言,动作是结构化的工具调用。
为什么这个模式好用?因为它把模型的“思考”和“行动”显式地分开了。裸调模型是直接给答案,ReAct是让模型先说我为什么要这么做,然后再做。这个“说”的过程本身就是一种自我校验。实测下来,加了ReAct的Agent在复杂任务上的成功率比直接让模型输出结果高出不少。
具体实现上,提示词里会要求模型按固定格式输出:
思考:我需要先查询当前库存 动作:query_inventory 参数:{"product_id": "12345"}然后外部系统解析这个输出,执行query_inventory,把结果拼回提示词,让模型继续下一轮思考。这个循环直到模型输出“最终答案”为止。
2.2 任务拆解的粒度控制
规划模块最容易出问题的地方是拆解粒度。拆得太粗,模型一步完不成,会卡住;拆得太细,步骤太多,token消耗爆炸,而且中间任何一步出错都会导致整个链条断裂。
我的经验是,单个子步骤的工作量控制在“一次工具调用能完成”的范围内。比如“分析销售数据”这个任务,不要拆成“读取文件、解析CSV、计算总和、计算均值、生成图表、写报告”这么细,而是拆成“读取并解析数据、计算关键指标、生成可视化、撰写报告”四个步骤。每个步骤对应一到两个工具调用,模型在每一步都有明确的完成标志。
另外,拆解的时候要给模型留出“回头”的余地。不要设计成线性流水线,而是允许模型在某个步骤失败后重新规划。比如读取文件失败,模型应该能决定“换个路径再试”或者“告诉用户文件不存在”,而不是直接崩溃。
2.3 规划失败的常见原因和修复
规划失败通常有三种表现:模型不拆解、拆解不合理、拆解后不执行。
模型不拆解,往往是因为提示词里没有明确要求它拆。你需要在系统提示词里写清楚:“对于复杂任务,你必须先列出步骤,再逐步执行。”同时给几个拆解的示例,模型会模仿。
拆解不合理,通常是模型对任务领域不熟悉。解决办法是在提示词里注入领域知识,或者提供类似任务的拆解模板。比如做数据分析的Agent,可以在提示词里写“数据分析任务通常包括:数据加载、数据清洗、指标计算、结果呈现四个阶段”。
拆解后不执行,多半是输出格式没约束好。模型输出了步骤列表,但没有输出结构化的动作指令。这时候需要在提示词里强制要求:“每一步必须包含动作和参数,格式如下...”
3. 记忆系统:别让Agent聊着聊着就失忆
3.1 短期记忆的窗口管理策略
短期记忆就是当前会话的上下文。大模型的上下文窗口是有限的,虽然现在动辄128K、200K,但token是要花钱的,而且窗口越长,推理越慢,模型对中间内容的注意力也会下降。
我常用的策略是滑动窗口加摘要。保留最近N轮完整对话,更早的对话压缩成一段摘要。摘要由模型自己生成,提示词大概是:“请用三句话总结以下对话的核心信息和结论。”这样既保留了关键信息,又控制了token消耗。
另一个策略是分层记忆。把对话分成“任务状态”和“闲聊内容”两层。任务状态是结构化的,比如当前进行到哪一步、已经获得了哪些数据、下一步要做什么。闲聊内容可以大胆截断。这样即使窗口满了,任务状态也不会丢。
注意:滑动窗口的截断点不要选在工具调用和工具返回之间。我踩过这个坑,模型发起了工具调用,结果历史被截断,工具返回的结果找不到对应的调用请求,整个流程直接报错。截断前一定要检查最近的对话是否成对出现。
3.2 长期记忆的存储和检索
长期记忆解决的是跨会话的知识保留问题。比如用户上次说“我偏好用柱状图展示数据”,这次再让Agent画图,它应该记得这个偏好。
实现长期记忆通常用向量数据库。把历史对话、用户偏好、领域知识都转成向量存起来,每次新会话开始时,根据当前任务检索最相关的记忆片段,注入到提示词里。检索的时机很关键,太早注入会干扰模型判断,太晚注入又来不及影响决策。我的做法是在任务开始时检索一次,在每次规划前再检索一次。
向量检索的坑在于相似度阈值。阈值太高,检索不到东西;阈值太低,检索出一堆无关内容,反而干扰模型。我一般从0.7开始试,根据实际效果调整。另外,检索结果要去重和排序,把最相关的放在最前面,因为模型对提示词开头的内容注意力最强。
3.3 记忆冲突的处理
记忆冲突是指新旧信息矛盾。比如用户上次说“我喜欢红色”,这次说“我讨厌红色”。如果两条记忆都检索出来了,模型会懵。
处理冲突的原则是“新信息优先”。在存储记忆的时候给每条记忆打上时间戳,检索时按时间倒序排列,并且在提示词里明确告诉模型:“如果记忆内容冲突,以时间较新的为准。”另外,对于用户明确表达的偏好变更,应该主动删除或标记旧记忆,而不是简单追加。
我见过一个更隐蔽的冲突:工具返回的结果和长期记忆里的知识矛盾。比如记忆里说“产品A的库存是100”,但工具查询返回“库存是50”。这时候应该以工具返回为准,因为工具是实时的。提示词里要写清楚优先级:实时工具结果 > 近期记忆 > 远期记忆。
4. 工具调用:Agent的手脚怎么装才稳
4.1 工具描述怎么写模型才不乱调
工具调用的第一步是让模型知道有哪些工具可用。工具描述写得好不好,直接决定了模型会不会乱调、错调、漏调。
一个好的工具描述包含四要素:工具名、功能说明、参数列表、使用示例。工具名要见名知意,用动词加名词的格式,比如query_inventory、send_email、calculate_metrics。功能说明要一句话讲清楚这个工具做什么、什么时候用、什么时候不用。参数列表要标明每个参数的类型、是否必填、取值范围。使用示例给一两个典型调用场景。
我踩过的坑是工具描述太模糊。比如有个工具叫process_data,描述写的是“处理数据”。模型完全不知道这个工具能处理什么数据、怎么处理,结果要么不用,要么乱传参数。后来改成“清洗和格式化CSV数据,输入文件路径,输出清洗后的DataFrame”,调用准确率立刻上来了。
另一个坑是工具太多。一个Agent挂二三十个工具,模型选择困难,错误率飙升。我的建议是单个Agent的工具数量控制在10个以内,超过就拆成多个Agent,用路由层分发。
4.2 参数校验和错误重试
模型输出的工具参数不一定符合预期。类型不对、必填项缺失、值超出范围,这些都很常见。所以工具执行前必须做参数校验。
校验失败后不要直接报错给用户,而是把错误信息返回给模型,让它重新生成参数。提示词里可以写:“如果工具调用失败,请根据错误信息修正参数后重试,最多重试三次。”这样模型有机会自我修复。
重试也要有策略。参数格式错误可以立即重试,但如果是网络超时或者外部服务不可用,重试前要加退避等待。我一般用指数退避,第一次等1秒,第二次等2秒,第三次等4秒。超过三次就放弃,把错误信息返回给模型,让它决定是换工具还是告诉用户。
4.3 工具调用的安全边界
工具调用是Agent最危险的部分,因为它真的能操作外部系统。一个没有安全边界的Agent,可能删掉你的数据库、给所有联系人发邮件、或者执行恶意代码。
安全边界的第一层是权限控制。每个工具都要有明确的权限范围,比如文件操作工具只能访问指定目录,数据库工具只能执行SELECT不能执行DELETE。第二层是参数白名单,对于关键参数,比如收件人地址、文件路径,要校验是否在允许列表内。第三层是人工确认,对于高风险操作,比如发送邮件、修改数据,先让Agent生成操作预览,用户确认后再执行。
提示:永远不要给Agent直接执行shell命令的权限。如果确实需要执行代码,用沙箱环境,限制网络访问和文件系统访问。我见过一个Agent因为能执行任意Python代码,被提示词注入攻击后把整个项目的环境变量都打印出来了。
5. 从零搭一个能跑的Agent:完整实操路径
5.1 环境准备和模型选型
先确定你的模型来源。如果追求效果,用云端API,GPT-4、Claude这些在工具调用和规划上表现更稳。如果考虑成本和数据隐私,本地部署开源模型,比如Qwen、Llama系列,7B到14B参数量的模型在简单Agent任务上已经能跑通。
本地部署的话,显存是硬约束。7B模型量化后大概需要6到8G显存,14B需要12到16G。如果显存不够,可以用CPU推理,但速度会慢很多。我的建议是先用云端API跑通流程,再考虑本地化。
开发环境用Python,核心依赖就几个:大模型SDK、向量数据库客户端、HTTP请求库。不需要一上来就上LangChain、AutoGPT这些重框架,先用原生代码把循环跑通,理解每一步在干什么,后面再用框架提效。
5.2 核心循环的代码实现
Agent的核心循环可以用不到100行代码实现。伪代码逻辑如下:
def agent_loop(task, tools, max_steps=10): messages = [{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": task}] for step in range(max_steps): response = call_llm(messages) if response.is_final_answer(): return response.content if response.is_tool_call(): tool_name = response.tool_name params = response.params if tool_name not in tools: messages.append({"role": "system", "content": f"工具{tool_name}不存在"}) continue try: result = tools[tool_name].execute(**params) messages.append({"role": "tool", "content": str(result)}) except Exception as e: messages.append({"role": "system", "content": f"工具执行失败:{str(e)}"}) return "达到最大步数限制,任务未完成"这个循环的关键点:最大步数限制防止死循环,工具不存在时返回错误让模型重新选择,工具执行异常时把错误信息喂回模型。每一步的messages都要完整保留,因为模型需要看到之前的推理过程。
5.3 提示词模板的编写要点
系统提示词是Agent的灵魂。我通常按以下结构写:
第一段定义角色:“你是一个数据分析助手,擅长处理销售数据并生成报告。”
第二段列出可用工具,每个工具一行,格式统一。
第三段规定输出格式:“你的每次回复必须包含思考过程和动作指令。如果需要调用工具,按以下JSON格式输出...”
第四段写约束条件:“不要编造工具返回结果。如果工具调用失败,根据错误信息修正后重试,最多三次。如果无法完成,明确告知用户原因。”
第五段给一两个完整示例,展示从任务到工具调用到最终答案的全过程。
提示词不要写太长,太长了模型会忽略中间内容。核心约束放在开头和结尾,因为模型对这两个位置的注意力最强。
5.4 跑通之后的调优方向
第一版跑通之后,你会发现效果可能不太稳定。调优的方向有几个:
工具描述的优化。把模型调用错误的case收集起来,分析是描述不清还是参数设计不合理,针对性修改。
提示词的迭代。把失败的对话拿出来,看模型在哪一步跑偏,在提示词里补充对应的约束或示例。
记忆策略的调整。如果发现模型经常忘记之前的步骤,检查记忆截断策略是不是太激进,或者检索的相关性不够。
模型参数的调整。温度调低一点,让输出更稳定;最大token数调大一点,防止推理过程被截断。
6. 并发场景下Agent的性能和稳定性
6.1 并发请求下的状态隔离
单用户跑通之后,下一步就是多用户并发。Agent和普通API不一样,它是有状态的。每个用户的对话历史、任务状态、记忆数据都是独立的,不能混在一起。
状态隔离的实现方式有两种:一种是每个会话一个独立的Agent实例,内存隔离,简单但资源消耗大;另一种是共享Agent实例,但用session_id区分状态存储,资源利用率高但实现复杂。我一般用第二种,用Redis存会话状态,每个请求带上session_id,Agent根据session_id读写对应的状态。
注意:并发场景下最容易出问题的是工具调用的副作用。比如两个用户同时让Agent写同一个文件,后写的会覆盖先写的。解决办法是给工具加锁,或者让每个会话有独立的文件空间。
6.2 超时和降级的处理
大模型推理本身就有延迟,加上工具调用,一个完整的Agent任务可能跑几十秒甚至几分钟。并发上来之后,超时是必然要面对的问题。
我的做法是分层超时:单次模型调用超时10秒,单次工具调用超时5秒,整个Agent任务超时60秒。任何一层超时,都返回当前已完成的部分结果,并告知用户任务未完成。不要无限等待,用户等不起,系统资源也耗不起。
降级策略也要准备好。如果模型API不可用,降级到规则引擎处理简单任务;如果向量数据库挂了,降级到只用短期记忆;如果某个工具不可用,从工具列表中临时移除,让模型用其他工具替代。
6.3 成本控制的实际手段
Agent的token消耗比普通对话高得多,因为每一轮都要把完整的历史和工具描述塞进提示词。并发上来之后,成本会快速上升。
控制成本的手段:第一,压缩提示词,去掉冗余描述,工具描述精简到必要信息。第二,限制历史长度,滑动窗口的N不要设太大,摘要要定期生成。第三,缓存常用结果,比如工具返回的静态数据,短时间内重复查询直接走缓存。第四,选择合适的模型,简单任务用便宜的小模型,复杂任务才用大模型。
我实测下来,一个中等复杂度的Agent任务,优化前大概消耗8000到10000 token,优化后能压到3000到4000。按这个量级估算,日活一千的用户,每天的成本是可控的。
7. 几个我踩过的坑和对应的解法
7.1 模型输出格式不稳定导致解析失败
模型有时候不按规定的JSON格式输出,多一个逗号、少一个引号、或者干脆用自然语言描述工具调用。解析失败后整个循环就卡住了。
解法是双重保障:提示词里强调格式要求,同时解析器要容错。解析器先尝试标准JSON解析,失败后尝试正则提取关键字段,再失败就把原始输出返回给模型,让它重新按格式输出。另外,可以在提示词里给一个格式错误的示例,告诉模型“不要这样输出”。
7.2 工具调用陷入死循环
模型反复调用同一个工具,参数几乎一样,结果也一样,但就是不输出最终答案。这种情况通常是模型陷入了“再试一次”的循环。
解法是加调用频率限制。同一个工具在连续三轮内被调用超过两次,就强制中断,把控制权交还给用户。或者在提示词里写:“如果同一个工具连续调用两次结果相同,请停止调用并基于已有信息给出答案。”
7.3 记忆检索引入无关信息干扰判断
向量检索的相似度匹配有时候会召回一些语义相似但实际无关的内容。比如用户问“怎么优化查询速度”,检索到了“查询语句怎么写”的记忆,虽然语义相关,但对当前任务没帮助。
解法是加一层重排序。先用向量检索召回Top 20,再用一个小的交叉编码器模型对这20条做精排,取Top 3注入提示词。交叉编码器比纯向量相似度准得多,虽然多了一步计算,但值得。
7.4 多轮对话中任务目标漂移
用户一开始说“帮我分析销售数据”,聊了几轮之后变成“顺便把库存也看一下”,再几轮之后变成“你觉得哪个产品该下架”。任务目标在不断变化,Agent如果一直沿用最初的规划,就会跑偏。
解法是在每轮对话开始时,让模型先判断“当前任务目标是否发生变化”。如果变了,重新规划;如果没变,继续执行。这个判断本身也由模型完成,提示词里写:“对比当前用户输入和之前的任务描述,如果目标发生变化,输出NEW_TASK并重新规划;否则输出CONTINUE。”
8. 从入门到进阶的学习路径建议
如果你已经跟着上面的内容跑通了一个最小Agent,下一步的进阶方向有几个。
第一个方向是多Agent协作。单个Agent的能力有上限,复杂任务可以拆给多个专职Agent,比如一个负责规划、一个负责执行、一个负责校验。Agent之间通过消息传递协调。这个方向的关键是通信协议的设计和冲突解决机制。
第二个方向是Agent安全。随着Agent能操作的系统越来越多,安全变得至关重要。提示词注入、工具滥用、数据泄露,这些都是真实存在的风险。建议研究一下Agent沙箱、权限最小化、操作审计这几个课题。
第三个方向是领域深度定制。通用Agent在特定领域的表现往往不如领域定制Agent。比如做金融分析的Agent,需要注入金融知识、接入金融数据源、定制分析工具。领域定制的投入产出比通常比通用Agent更高。
第四个方向是评估和可观测性。Agent跑起来之后,你怎么知道它好不好?需要建立评估体系,包括任务成功率、平均步数、工具调用准确率、用户满意度等指标。同时要有日志和追踪,每一步的输入输出都要记录,出问题能回溯。
我个人的体会是,Agent开发最难的不是写代码,而是理解模型的“脾气”。同样的提示词,换个模型效果可能天差地别;同样的工具描述,换个任务场景可能就不好使了。多跑、多看日志、多分析失败case,比看任何教程都管用。另外,不要追求一步到位,先让Agent能跑,再让它跑得稳,最后才让它跑得快。顺序反了,容易在细节里迷失方向。