刚开始接触大模型Agent开发时,我的心态是有些矛盾的。当时想着,只要把大模型接上几个API,写好提示词,一个看起来挺唬人的智能体不就跑起来了吗?实际动手之后才明白,一个能稳定工作的Agent,需要考虑的问题远比一个聊天机器人多得多:模型到底该选多强的,工具接口该怎么设计模型才用得顺,护栏要建到什么程度才既安全又不会误伤正常请求。这三个问题没有标准答案,但如果一开始就有一套清晰的路线,后面能少踩很多坑。这篇文章把我从零做Agent的经历整理成了完整攻略,围绕模型选择、工具设计、护栏搭建三条主线展开,希望能让刚起步的同学少走弯路,也能给已经做了Demo但始终上不了生产的团队一些灵感。
1. 内容整体设计与思路拆解
1.1 Agent和普通对话机器人到底差在哪
很多人把Agent理解为“AI助手”,但“助手”这个词太宽泛。普通对话机器人是“你说一句,它答一句”,即使内部有再复杂的RAG链路,它本质上仍然是在做信息处理和文本生成,最终输出的还是文字。Agent则不太一样,它不仅能生成答案,还能根据任务内容决定“自己去做什么”,比如调用一个天气查询接口、创建一个日历事件、把一组数据写入数据库,甚至发起一笔交易。这个“决策 — 行动 — 观察 — 再决策”的闭环,是Agent和普通对话机器人之间最本质的区别。
举个方便理解的例子。你问普通对话机器人“帮我看看明天的天气”,它可能会告诉你“由于我无法访问实时信息,请您手动查询天气应用”。而一个正常的Agent会说“正在为你查询,请稍等”,然后真的去调用天气API,拿到数据后整理成答案返回给你。这个过程看起来简单,但背后有一整套机制在运转:大模型需要理解用户的意图,把这个意图映射到一个预先定义好的工具调用上,取出参数,然后执行并总结结果。每一步都有出错的概率,这也是Agent开发比普通对话机器人开发复杂得多的原因。
在实际做Agent之前,我会建议先想清楚一件事:你的场景里用户需求真的需要“自动执行动作”吗?很多业务场景,比如一些简单的知识问答、政策解读,其实只需要返回准确的文字信息就够了,没必要为了“智能化”而强行接入工具、配置复杂流程。硬给不需要行动能力的系统套上Agent外壳,不仅增加开发成本,还会因为工具调用失败或链路太长而降低整体稳定性。想清楚场景是否需要Agent,是整体设计的第一步,也是很多人忽视的一步。
1.2 模型选择、工具设计、护栏搭建三者如何构成完整闭环
如果把一个Agent当作一个团队来看,模型是大脑,负责理解和规划;工具是四肢,负责落地执行;护栏是安全员,负责拦截越界行为。三个部分不是独立的,而是互相制约的三角关系。
先说模型和工具的关系。模型能力越强,能驾驭的工具复杂度就越高。比如一个推理能力较弱的模型,你给它提供一个包含五个可选参数、每个参数还有依赖关系的复杂工具,它大概率会漏填参数、填错类型,甚至把工具名都拼错。反过来,模型很强工具却很粗糙,也不行。工具的描述模糊、参数命名混乱,模型再聪明也猜不透你真正想要的输入格式,最后只能靠“猜”。所以模型选型决定工具设计的复杂度上限,工具设计质量又反过来影响模型能不能高效使用这些能力。
再说护栏如何影响前两者。护栏不是上线前才临时加的安全锁,而是从选型阶段就要参与的约束条件。比如你选择的模型上下文窗口只有几万Token,但业务场景里可能传入超长工具说明和几十轮对话历史,这时候护栏就要设计成“强制截断”的策略,不能让上下文爆掉。又比如你希望Agent能自动访问用户邮箱发送邮件,这个动作风险很高,如果护栏里没有“发信前必须经过用户二次确认”的规则,模型一旦受到诱导就可能做出越权操作。护栏划定了系统允许的边界,模型和工具必须在这个边界内工作,三者彼此约束,少一块都会出问题。
我的做法是在项目最开始就画一张简单的关系图:用户输入进来之后先经过什么判断、模型如何决策、工具怎么执行、结果回传给模型之后如何校验输出再返回给用户。不需要画得很正式,但必须把各个环节的负责人标清楚。这张图在后续开发中会不断修正,但一开始画出来的价值非常大,能让你在写第一行代码之前,就对系统的行为边界和风险点有整体认知。
2. 模型选择:不看参数大小,看这五个维度
2.1 不同任务类型对模型能力的要求
很多同学选模型时第一句话就是“哪个模型参数最大?”其实参数不是唯一指标。对Agent开发来说,模型的选择更该从任务类型倒推,关键看几个相对独立的维度:
- 意图理解能力:用户说的话是否足够直白?还是经常有口语化表达、指代不明、隐含上下文?意图理解弱的模型,在第一步就容易把方向带偏。
- 指令遵循能力:你是否要求模型严格按JSON格式输出工具调用参数?是否能遵守“如果结果为空,必须回复无法查询”这种条件性指令?这个维度直接关系到工具调用的稳定性。
- 规划与推理能力:任务是否依赖多步推理?比如“找出最近一个月订单里金额最高的三笔,并汇总给每个用户发送通知”,这需要拆解子任务并合理安排顺序。
- 上下文利用能力:你的业务是否需要在很长的对话中保持关键信息不丢失?模型是否会在几十轮之后忘记早期提到的约束条件?
以我的经验,上述四个维度并不等价。如果你做的是客服类Agent,指令遵循能力比规划推理能力更重要,因为大部分用户请求都是单一、明确的,真正复杂的长链路任务占比很低。但如果你做的是数据分析助手或自动化运营Agent,规划推理能力就成了核心竞争力。先把自己的场景需求按这几个维度打一遍分,再回头对比模型,会清晰很多。
我自己在选型时会准备一组固定的测试集,大约二三十条真实业务请求,覆盖常见意图、模糊表达、长上下文、复杂工具调用等典型场景。每换一个模型,就跑一遍这套测试集,用通过率决定哪些模型进入候选池。这个做法的好处是客观,比“我觉得它在很多新任务上表现不错”靠谱得多。建议每个认真做Agent的人,都要建立自己的评测集而不是只看评测榜单。
2.2 主流模型路线与适用场景
现在市面上的模型可以分为几个路线,各有各的特点,说不上绝对好坏。
第一类是带函数调用能力的主流商业模型。这类模型的优势是综合能力强、工具调用接口成熟,开发者只需要按照标准格式声明工具,模型就能自动生成调用请求,不需要额外训练或微调。它们通常也是新功能最先落地的平台。缺点是成本较高,对于频繁调用工具、用户量大的场景,Token费用可能压得人喘不过气。数据隐私敏感的项目也需要额外评估,很多时候商业API不允许数据出域,这会直接卡掉一部分业务。
第二类是开源的通用基座模型,配合外部的工具调用框架一起使用。这类模型的好处是可以私有化部署,数据不出内网,成本可控;缺点是它们对工具调用的理解能力和格式遵循程度通常弱一些,需要开发者自己在提示词或微调层面做很多修补工作。如果你的团队有较好的大模型调优经验,这条路很值得走;如果团队只想快速做出Demo,那选用商业模型会更省心。
第三类是轻量级高性价比模型,适合简单的单工具场景。比如一个只负责“查天气、算个税、查快递”的小助手,不需要很强的推理能力,轻量模型就能胜任。这类模型的延迟更低、成本更便宜,还能在边缘设备或低配服务器上跑。但一旦你的工具列表变长、参数变复杂,这些模型就很容易“犯迷糊”,这也是在项目过程中需要反复测试打磨的地方。
我个人的经验是:立项目初期先用能力最强的模型把整个链路跑通,确认业务逻辑没有硬伤,然后再用低成本模型替换并进行系统评测。如果低成本模型的通过率能超过业务要求的阈值,就用它;达不到就用回强模型,同时想办法减少调用次数或缓存中间结果。用最强模型保底,用低成本模型优化毛利,这个思路比一开始就纠结“用哪个模型好”要实用得多。
2.3 成本、延迟与实际可用性的权衡
模型选择不仅要看能力评分,还要算成本账和延迟账。
成本方面,Agent的Token消耗通常是普通聊天的数倍。一次工具调用背后可能有几轮模型请求,每轮都会把历史记录、工具定义、中间结果拼进上下文。哪怕用户只问了一句“帮我查订单”,中间可能要经历“意图理解 — 工具调用参数生成 — 工具结果总结”三轮请求。如果工具定义本身很长,那每一轮都要把几十个工具的描述传进去,Token开销累计起来非常可观。做预算时要按照“每次会话平均Token数 × 预估会话量”来估算,而不是按一句用户请求的Token数来算。
延迟方面要区分首Token延迟和完整返回时间。对于工具调用场景,模型需要先把完整参数Json生成出来,这通常要等全部输出完成,所以Agent感觉上的响应速度,取决于完整返回时间而不是首Token。尤其当你使用开源模型在本地方跑时,推理速度不足会导致每次工具调用都要等好几秒,整体体验会非常拖沓。这里有个实用经验:过大的模型并不总是最优选择,应当在满足业务要求的前提下选择推理速度较快的模型,因为Agent的交互链路长,任何一环拖慢都会放大最终体验的延迟。
可用性也是很多人忽略的维度。商业模型接口可能发生限流、超时、临时不可用;自建模型服务器则要考虑并发能力、显存规划、扩容方案。建议至少设计两套模型方案:主模型 + 备模型,一旦主模型接口抖动,就自动切换到备用的另一条模型线路继续提供服务,并在日志中标记出来,避免一次上游故障导致整个Agent全线瘫痪。
3. 工具设计:Agent的四肢决定它能走多远
3.1 工具的本质:把系统能力翻译成模型能理解的语言
工具设计是Agent开发里最容易被低估的环节。表面上,工具就是给模型写一份接口文档,但真实难度在于:这份文档不是给人看的,而是给模型看的。模型不会像工程师那样去理解代码逻辑,它只能根据工具名、描述、参数说明来推测这个工具是干什么的、什么时候该用、参数该怎么填。
一个常见的错误是把工具接口按工程师的习惯命名。比如你写了一个查询库存的方法,内部叫getAStockList,在工具定义里也直接用这个名字,只写一句“获取库存列表”。模型看到这个工具时,并不知道“A”代表什么,不知道需要传哪个仓库、哪个时间范围、返回值是什么,更不知道它和另一个叫getBStockList的工具区别在哪。结果就是模型经常选错工具、填错参数。
所以工具设计的核心原则是:站在模型视角去写描述,把系统能力用最直白、最有区分度的语言翻译出来。每个工具都应该回答清楚这几个问题:这个工具负责做什么?在什么条件下应该调用它?每个参数的含义、格式、取值范围是什么?调用之后会返回什么结果?有什么需要注意的边界?把这些写清楚,模型的工具调用成功率一定会有质的提升。
我另一个体会是工具数量要克制。很多业务一开始就想把系统所有能力都暴露给模型,工具列表动辄三四十个。这么做的结果是:模型在选择工具时出现严重混淆,准确率明显下降,同时每轮请求都要把所有工具描述塞进上下文,Token成本直接飙高。一个合理的方案是先将工具分发分组,让上游意图分类器先决定“这一轮路由到哪一组工具”,只把这一组工具的少量描述传给模型,这样既照顾了覆盖面,又控制了单轮上下文长度和决策复杂度。
3.2 从函数到JSON Schema,工具声明的标准写法
目前主流的大模型Agent框架,都遵循“工具描述即Schema”的思路。开发者用一套结构化声明来描述工具,模型基于这套声明生成调用请求。一个标准的工具声明通常包括以下几个核心字段:
name:工具唯一标识,建议用英文小写加下划线,简洁明确。description:描述工具职责和调用时机,越具体越好。parameters:定义每个参数的名称、类型、是否必填、含义范围。required:必填参数列表。
下面是一个简化但完整的例子。假设你有一个查询股票价格的工具:
{ "type": "function", "function": { "name": "get_stock_price", "description": "查询指定股票的最新价格。当用户询问某只股票的当前价格时调用此工具。如果用户提到的是基金或指数,请使用其他工具,不要调用本工具。", "parameters": { "type": "object", "properties": { "symbol": { "type": "string", "description": "股票代码,例如600519或000858,必须是6位数字代码。" }, "market": { "type": "string", "enum": ["CN", "HK", "US"], "description": "股票所在市场,CN为内地市场,HK为港股,US为美股。" } }, "required": ["symbol", "market"] } } }这个例子看似简单,细节却很多。description里我明确写了“如果用户提到的是基金或指数,请使用其他工具,不要调用本工具”,这种负面约束能有效防止模型因为信息不足而误调用。symbol字段里给出了示例和数字长度的约束,模型就能根据用户输入“茅台现在多少钱”自动映射到600519,而不会去搜“茅台股份”。market字段用枚举值限定取值范围,从源头杜绝了“Chinese”“A股”这类不规范等价词的出现。
工具描述里的每一句话,都要当成代码逻辑来对待。模型读这些描述时的理解方式与程序员不同,它更依赖零散的词面匹配,所以你在描述中提到的每一个名次、示例、反例,都在影响模型的选择倾向。与其追求短小精悍,不如追求重点突出、边界明确。
3.3 工具命名、描述与参数设计中的避坑细节
工具命名的第一原则是语义直通,宁可长一点也不要含糊。get_order_status比queryStatus的命中率高得多,calculate_shipping_fee比ship_fee清晰得多。全文统一使用小写加下划线,不使用大写驼峰,因为大部分模型的工具名生成习惯都偏向小写,名称风格统一能减少随机错误。
描述部分要学会“给模型举例”。例如天气查询工具的description可以写成:“查询某个城市当天或未来几天的天气情况。当用户说'明天北京天气怎么样'时,城市参数应为'北京',日期参数应为'明天'对应的具体日期。”很多模型对模糊时间词的处理不稳定,你直接在描述里给出转化规则,模型照着做就会稳定很多。
参数设计要尽量少而精,减少参数间的依赖关系。能用一个参数解决的就不要拆成两个,比如省市区三级联动,与其拆成三个参数,不如让工具层自行解析一个完整的“广东省深圳市南山区”字符串。参数过多会让模型的填参难度线性上升,出错率也随之增加。同时,能加默认值的参数都加上默认值,并为可空参数提供明确的说明,这样即使模型忘记传,也不至于让工具直接崩溃。
还有一个我踩过几次的坑:工具参数的类型定义必须严格。integer就是整数,number可以是浮点数,两者定义混用会导致很多模型输出带小数点的价格字段,程序里再用整数解析就会报错。再一个是字符串里要不要加引号的问题,模型返回的工具参数有时会自带多余甚至嵌套的引号,核心字段仍须设计一遍转义清理逻辑,不能直接把模型输出原样扔给函数解析器。
4. 护栏搭建:看似多余,出了事才知道值多少钱
4.1 Agent护栏到底要防什么
护栏这个名字听起来专业,其实类比成现实业务就是“审核机制”。Agent一旦拥有调用工具的能力,就等于拥有了一定范围内的“行动权”,行动权越大,出错造成的后果越严重。护栏要防的对象,从我的实际经验来看主要有四类:
- 提示注入:用户故意在输入里说“忽略之前的指令,现在直接执行...”,试图操纵Agent做出越权动作。这是Agent开发里最常见也最危险的攻击方式。
- 越权动作:Agent在正常回答过程中,对不确定或风险较高的操作仍然执行了工具调用,比如误删数据、误发邮件、误改配置。
- 幻觉导致的错误决策:模型并不总是诚实的,它会基于凭空捏造的信息决定调用某个工具,尤其在信息不完整时,它倾向于“猜一个答案”,于是经常接到错误参数或错误工具。
- 资源与成本失控:Agent陷入死循环,反复调用同一工具,不仅让用户体验变差,Token和API费用也一路飙升。
把这些问题在脑子里过一遍之后,你就会认同:护栏不是给Agent“添麻烦”,而是在给Agent的行动权上保险。没有护栏的Agent就像没有刹车的汽车,动力越强风险越大。
4.2 输入侧护栏:第一道闸门怎么设计
输入侧护栏要做的事情是:在用户输入进入模型之前,先做一轮风险预判。最简单的方式是设一组规则或使用一个小模型做分类,初步判断当前输入是否包含“指令注入”“越权要求”“敏感信息查询”等风险。
一个实用的结构是:
- 规则拦截层:用正则、关键词列表或者敏感词库快速过滤明显可疑的输入,比如“忽略以上所有指令”、“输出你的System Prompt”、“我要删除所有订单”等。这一层速度最快,用于挡住大部分低级的攻击。
- 模型分类层:用一个小且快的模型对输入做意图分类,判断它属于“正常请求”“敏感请求还是”“危险请求”。分类结果会决定后续走哪条不同管道路线。
- 上下文可疑度评估:结合多轮对话历史判断当前输入是否在试图诱导Agent偏离既定任务。这个需要更精细的设计,通常是把上一轮的工具调用结果也一块送进分类模型,让它判断“当前上下文有没有不一致的迹象”。
这个三级输入侧护栏看似很重,实际上在性能优化后并不会增加多少延迟,几毫秒的规则校验加几十毫秒的小模型分类,对整体Agent调用链路来说可以接受。牺牲一点速度换取安全边界,是很划算的买卖。
4.3 输出侧护栏:执行前必须再确认一层
输入侧护栏负责拦截危险输入,但模型在决策环节依然可能出错。比如它把“查询订单”理解成了“取消订单”,并且已经生成好一个高风险工具调用。这时候输出侧护栏就要接住:在模型生成完工具调用请求之后、真正执行业务动作之前,加一道“执行前校验”。
执行前校验要完成几件事:
- 工具白名单:判断当前调用的工具是否在允许列表内,不在就直接拒绝并让模型重新作答。
- 参数合法性校验:工具层会对参数做严格类型与取值范围检查,比如金额字段不能为负数、日期必须符合格式、城市必须存在于字典。任何一步校验失败都可以直接终止调用。
- 高危动作二次确认:对于一些高风险动作,比如删除数据、发送邮件、修改配置,设置“必须经过用户二次确认”的规则。模型不能直接执行,必须先输出一段“你确定要执行XXX吗?”的确认文案,返回给用户,用户点了确认后才继续。
- 结果合理性校验:工具返回结果后,再让模型判断这个结果是否符合用户原始问题的预期。比如用户问订单状态,工具返回“订单不存在”,这时模型就不应该强行编造一个状态,而是如实告知用户。
输出侧护栏里还有一个容易忽略的点:要统一记录“模型生成了什么调用、工具返回了什么结果、最终文案怎么写的”这个完整链路。一旦线上出了问题,可以回看当时的模型输出,定位问题出在哪一层,而不是靠用户截图来猜。给Agent装上“黑匣子”是一件非常值得做的事。
4.4 运行时护栏:超时、限流、人审与回滚
除了输入输出两侧,Agent在运行时还需要一套像微服务一样的基础保护机制。
- 最大步骤数:每个Agent任务设置最大调用轮数,通常建议控制在10轮以内。一旦超过轮数,强制终止并把当前已获取的信息整理成中间结果反馈给用户,避免死循环造成无限成本消耗。
- 单次调用超时:模型接口和工具接口都要配置超时时间。模型接口建议10~20秒,工具接口则根据业务本身特性设置,但最多不要超过30秒。超过就按失败处理,不要无限等待。
- 全局限流与预算控制:给每个用户设置每分钟、每天最大调用次数。给整个服务设置每日Token消耗上限,一旦到达预算上限,自动切换到备用模型或触发人工干预。
- 人工审批流:对真实业务影响极大的操作,比如批量删除、大额资金流转,应当设立“人机协同”机制。Agent先把拟执行的操作生成审批单推给管理员,管理员同意后执行。虽然这样做降低了“全自动”的程度,但能最大程度避免不可逆损失。
- 回滚机制:如果工具调用产生了写操作,要考虑事后回滚。比如Agent自动改了某个文件、某一条配置,最好生成操作快照,必要时可以通过一个带鉴权的接口一键还原。
上面这些运行时护栏做起来不难,难的是要在第一版就纳入设计。我见过不少项目把Agent调得流畅准确后再想“要不要加护栏”,结果一加就发现之前的工具调用参数根本没考虑幂等性,重试执行会重复创建订单,不得不回头重构很多接口。护栏和业务接口,应该是同一时间设计的两面,互相知道对方的约束,而不是等到最后才拼装。
5. 实操复盘:从0到1搭一个Agent的完整流程
5.1 场景定义与“最小可用Agent”的设定
我自己的习惯是先挑一个边界清楚、用户需求具体的场景作为首个试水项目,而不是一上来就做全能助手。比如我做过一个“企业内部排障助手”,它能读取系统状态接口、查询日志、根据知识库给出排查建议。这个场景的好处是工具数量少、风险可控、用户问题比较集中。
确定场景后,定义“最小可用Agent”是什么样子。它必须具备这样几个能力:能识别常见用户问题、能通过工具调用获取真实数据、能基于数据给出准确结论、以及在拿不到数据时能如实承认。先把这个最小闭环跑通,再往里面加更复杂的子任务。
这个阶段的Tips是:忍住不要加太多功能。每次只增加一个工具、一个护栏规则或一种新的用户意图,然后充分测试。功能堆叠得越快,后面排查问题就越难。一次只改一个变量,是Agent开发里最重要的调试原则。
5.2 模型与工具的联调细节
联调阶段最重要的事,是观察模型“原始输出”。不要只盯着Agent最终回复的对不对,要看模型在中间环节到底生成了什么工具调用请求,格式是否规范,参数是否准确。很多工具调用错误在这个阶段就能暴露出来,而不是等到生产环境用户反馈。
调试时建议开启一个Debug开关,把每个环节的原始数据都打印出来:用户输入、输入侧护栏的判定、模型生成的完整回复、工具调用的原始Json、工具的原始返回、输出侧护栏的校验结论。这些数据放在日志里,排查问题时会轻松很多。
一个我反复用到的策略是先手动构造几组固定输入,分别测试“成功调用工具”“工具返回空结果”“工具调用参数缺少必填字段”“模型拒绝调用工具”这几条典型路径。每条路径都要有对应的处理逻辑和兜底文案。把这些路径测完,核心链路的稳定性才算初步过关。
如果发现模型频繁漏参数或选错工具,先不急着换模型。回头按前面提到的工具设计原则优化一遍工具描述:加更多示例、更明确的条件说明、更严格的类型约束。很多时候,问题不只在模型,而在于工具描述写得不够准。工具描述改好后,再让模型重试,往往是立竿见影的。
5.3 一个可运行的参考配置
以下是一个极简但完整的Agent工具调用参考实现,示意如何把模型生成的结果与工具执行衔接起来:
import json def execute_tool(tool_name: str, args: dict) -> str: if tool_name == "get_stock_price": symbol = args.get("symbol") market = args.get("market") # 业务侧实现真实查询 result = fake_stock_query(symbol, market) return json.dumps(result, ensure_ascii=False) raise ValueError(f"unknown tool: {tool_name}") def parse_model_call(raw_output: str) -> dict: # 模型输出可能带前后缀,尽量提取合法的JSON片段 start = raw_output.find("{") end = raw_output.rfind("}") if start == -1 or end == -1: raise ValueError("no json found in model output") return json.loads(raw_output[start:end + 1]) def run_agent(user_input: str) -> str: # 第一步:输入侧护栏 if is_risky_input(user_input): return "抱歉,这个请求我无法处理,请确认后重试。" # 第二步:调用模型生成回复,这里简化处理 model_raw = call_llm_with_tools(user_input) # 第三步:解析模型输出,判断是否需要调用工具 call_info = parse_model_call(model_raw) if call_info.get("type") == "tool_call": tool_name = call_info["tool_name"] args = call_info["arguments"] # 第四步:输出侧护栏,校验参数合法性 if not validate_tool_call(tool_name, args): return "我无法执行这个操作,请检查参数是否正确。" # 第五步:执行工具 tool_result = execute_tool(tool_name, args) # 第六步:把结果交由模型总结后返回 return final_summarize(tool_result) # 不需要调工具时,直接返回模型回答 return model_raw实际项目里,call_llm_with_tools会请求外部模型接口,传入工具定义列表和对话历史;is_risky_input会包含多级输入侧校验;final_summarize也是一次新的模型调用。看起来代码很简单,但理解了这条主链路,后面不管接多复杂的框架,本质都一样:模型是决策中枢,工具是执行器,护栏是各类校验规则的总称。
6. 常见问题与排查技巧实录
6.1 工具调用总是不稳定,时好时坏怎么排查
这是出现频率最高的问题,几乎每个Agent项目都会遇到。排查时我的思路是“从下往上、从原始到抽象”。
首先看原始模型输出,确认它本次到底返回了什么。是输出里夹带了多余文本导致解析失败?还是工具名被篡改成相似词?还是参数类型不对?把原始输出截图或者日志翻出来,往往原因一目了然。
其次看是不是上下文太长导致模型注意力分散。工具定义整个塞进上下文时,如果前面还有其他大段提示词,模型后半段可能会忽略细节,尤其模型上下文接近上限时,误调用概率显著上升。解决办法是把工具分组、裁减或调整上下文结构,把最新、最关心的信息放在靠近输出的位置。
再看是不是提示词与工具描述冲突。比如系统提示词里说“如果需要查询天气,请调用天气插件”,但工具名实际叫get_weather_info,模型看到“天气插件”会不知所措。保持系统提示词里提到的每一个能力名称与工具声明的名称完全一致,是最容易忽略又最容易致错的地方。
最后再考虑升级模型的必要性。如果工具描述已经写得清晰规范,模型依然反复出错,那就说明当前模型确实驾驭不了这个工具组合。此时换更强模型是可选路径,但记得先跑一遍前面提到的测试集,对比确认提升幅度再决定。
6.2 Agent陷入死循环或反复横跳怎么办
死循环的典型表现是:模型反复调用同一个工具,拿到结果后不满意,换个参数再试一次,又拿到结果,还是不认可,继续循环。这种现象的根源,通常不在于循环本身,而在于模型缺乏“自己已经尝试过某方案”的记忆。
排查与修复可以从几个方向入手:
- 加上步数上限和全局熔断:哪怕不解决本质,也必须先止血。超过最大步数立即终止,返回当前最佳的中间结论,并告诉用户“问题比较复杂,建议换一种问法”。
- 给模型提供上一步的摘要:在每轮工具返回之后,用一小段话把“我们刚刚试了什么方法,结果如何”写进上下文。模型看到历史摘要后,就能避免重复执行相同动作。
- 对工具结果做轻量评估:在工具返回后加一层校验,判断结果是否符合预期。如果连续两次返回结果类型相同,就主动判断“继续尝试无意义”,终止该分支,让模型换一条思路或者如实告知用户。
- 提供更多可选的合理动作:有时候模型反复试同一个工具,是因为它根本不知道怎么用其它工具完成当前目标。这时应增加一个“咨询类”的工具,比如
ask_human_for_clarification,让模型懂得在不确定时停下来问用户,而不是盲目尝试。
反复横跳的问题与死循环类似,通常是模型在多个工具之间摇摆,拿不定主意。这时会加大工具描述的区分度,并且增加一条规则:“优先选择第一个最可能正确的工具,不要反复比较”。把决策标准的优先顺序写清楚,模型就不会在那里反复权衡。
6.3 护栏误伤正常请求怎么办
护栏规则设置得越严格,误伤正常请求的概率就越大。最典型的是关键词拦截。比如你写了一个拦截“删除”的规则,结果用户只是问“怎么删除好友”,请求被拦截了,这显然不合理。
处理误伤的核心思路是“分级处理,而不是一刀切”。
- 把高危规则分成“绝对禁止”和“需要确认”两档。绝对禁止的规则很少,比如涉及身份证号、银行卡号、密码等直接拦截。需要确认的规则,则先让用户读一遍“您是要执行XX操作吗?”,避免误伤正常询问。
- 关键词拦截结果不直接打回用户,而是交由一个轻量模型结合上下文判断“这个词在当前语境中是否真的具有危险动作含义”。这样既能拦下真正的恶意请求,又能让“怎么删除好友”这类普通问题顺利通过。
- 建立“误伤反馈闭环”。遇到用户投诉或运营反馈“这条正常请求被拦了”,直接把案例加入测试集。后续每改动一条护栏规则,都用测试集回归一遍,看误伤率有没有上升。护栏规则的维护,应该像代码一样有版本记录。
还要提醒的是,护栏误伤不只影响体验,还可能直接影响业务数据。一个高风险动作校验如果在参数校验时因为变量类型问题误判,就可能把用户一个合法的下单动作拦成“非法操作”。在护栏规则中,一定要区分“业务校验失败”和“安全拦截”,两者走不同的提示文案,前者告诉用户参数错了,后者告诉用户操作不被允许,否则用户会非常困惑。
总结
每次回看自己的Agent开发过程,最深的体会都是:技术本身并不难,难的是始终把“系统边界”放在心上。模型选型不是越强越好,而是匹配场景后的最优解;工具设计不是功能越多越好,而是让模型用得越顺手越好;护栏搭建不是越严越好,而是让安全与体验达到平衡。我踩过的最大的坑,就是在初期把精力全放在“让Agent答得更聪明”上,等发现工具调用不可控、用户反馈体验差时才回头补护栏,结果付出了一倍以上的返工成本。
最后分享一个小细节:在项目的每个阶段,都要用笔记下“当前系统最怕发生什么”。这个记录会引导你决定下一步该优化模型能力、完善工具定义、还是加固护栏环节。Agent开发不是一次性工程,而是一轮又一轮“能力增强 + 边界加固”的迭代过程。保持这个节奏,你的Agent自然会从“能跑”变成“好用”。