ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent技能体系实战:从工具调用到生产级应用

Agent技能体系实战:从工具调用到生产级应用 先说一下背景。我做AI应用层开发有几年了最近半年几乎全扑在Agent相关的项目上。从最早拿LangChain拼个Demo到后来在真实业务里落地带工具调用的Agent服务中间踩过的坑比写过的代码还多。这个过程中我意识到一个核心问题很多人做Agent功能Demo跑得通一上生产就废关键不在模型选得不好而在技能体系没搭对。这篇东西就是想把我在Agent技能设计、实现、评测上的一套经验完整写出来给准备做Agent应用或者正被Agent搞到头秃的朋友一个参考。agent-skills这个名字字面上看是Agent技能但它背后不是简单给模型接个API就完事而是一整套关于模型如何发现工具、如何决定调用、如何从错误中恢复、如何被评测验证的工程体系。我把这套体系拆开揉碎结合项目里的真实代码和踩坑记录尽量讲透。文章会覆盖几个核心问题技能到底是什么和普通API集成有什么本质区别一套可用的技能体系包含哪些模块每个模块怎么设计技能注册、调用、上下文管理的工程实现带可跑通的示例评测指标怎么定怎么让技能越跑越稳最后是一线实战里最隐蔽的几个坑。内容偏工程实践适合已经跑通过基本Agent流程、正在往能稳定干活方向推进的开发者。1. 先搞清楚Agent技能不是接个API那么简单很多朋友跟我聊Agent的时候开口就是我把GPT-4接上了工具也配了怎么还是干不了活。这种问题我听得太多了。他们所谓的配了工具往往就是在系统提示词里写了一句你可以调用天气API然后就没有然后了。模型确实知道有这个工具存在但不知道什么时候该用、参数怎么填、返回结果怎么处理、失败了怎么办——这就是典型的有接口没技能。我理解的Agent技能是一整套从意图识别到动作完成再到结果校验的闭环能力。它包含至少四个层次感知层模型能从用户输入中识别出需要调用某个技能的触发条件决策层在多个技能之间做选择甚至组合多个技能完成复杂任务执行层正确构造参数、发起调用、解析返回结果恢复层调用失败或结果不符合预期时能够自主纠错或向用户澄清。拿日常生活中的例子类比。你会用洗衣机这件事绝不只是知道有个洗衣机就够了。你得知道什么衣服该用什么模式、洗涤剂放多少、洗完怎么处理异常报警。跟模型一样给它一个工具接口只是给了它一台洗衣机真正让它会用得让它掌握完整的使用流程、边界条件和异常处理。在这个项目里我自己总结出一个公式后来一直用它衡量技能体系的完整度技能完整度 触发条件覆盖率 × 参数构造准确率 × 异常恢复成功率这三个值任何一个很低整体技能都是不可用的。只看模型有没有成功调用API这一个指标是典型的自我感动。2. 技能体系的第一层意图识别与技能发现机制技能体系的地基是让模型在合适的场景下想起这里有技能可以用。这听起来简单实际坑极多。2.1 技能发现的核心矛盾上下文长度与技能数量项目早期我天真地把所有技能的定义——包括名称、描述、参数Schema——全塞进系统提示词里。当时只有十几个技能上下文还扛得住。后来技能库扩大到四十几个问题立刻暴露Token占用爆炸。每个技能详细描述平均消耗150~300个Token四十个技能轻松吃掉8000多Token留给对话历史和中间推理的空间所剩无几。决策干扰加剧。模型在大量不相关技能描述中更容易选错工具。测试里出现过用户问天气、模型调了新闻接口的离谱情况。后来参考了业界一些成熟的方案我把技能发现从全量注入改成了检索召回。核心思路是为每个技能写一段高质量的技能索引描述比如查询天气技能就用天气、气温、降雨、空气质量这样的短句子用户请求进来后先做一次向量相似度检索只把Top 5~8个候选技能的定义注入上下文。这套方案上线后模型工具调用的准确率从76%提升到了91%。关键是Token占用从8000降到了1800左右单次请求成本直接砍半。如果你也遇到技能多了之后模型变笨的情况强烈建议先检查是不是上下文被工具定义塞爆了。2.2 索引描述怎么写别用功能描述用触发场景描述写技能索引描述的时候团队里一开始沿用了写API文档的思路比如某个技能写成提供股票行情数据查询服务。后来发现召回效果很差因为用户不会用服务提供这种词说话而是直接说帮我看看茅台今天涨了没。正确的做法是把索引描述写成用户在什么场景下可能触发这个技能的自然语言集合股票技能股价、行情、涨跌、K线、大盘、个股、买入卖出参考天气技能天气、下雨、气温、冷不冷、出门要不要带伞闹钟技能提醒、几点叫我、闹钟、定时这个调整看着不起眼却让技能召回的命中率显著提升。写技能索引描述时别站在开发者角度描述功能要站在用户角度描写需求场景这个经验后来写进了我们的工程规范。3. 技能体系的核心工具调用的工程实现有了技能发现层下一步就是工具调用的实际执行。这是Agent技能体系里最成熟、也最需要精细化处理的部分。3.1 从Function Calling到MCP协议我们的技术栈一开始用的是OpenAI的Function Calling机制后来为了兼容多模型和多工具服务逐步迁移到了MCPModel Context Protocol协议。这里给还没接触过的朋友做个快速梳理。Function Calling的核心是让模型输出一个结构化的JSON指定要调用的函数名和参数{ name: get_weather, arguments: {\location\: \北京\, \unit\: \celsius\} }这个机制本身很简单但生产环境里会有几个问题工具分散在不同服务里Agent需要为每个服务写不同的接入代码工具Schema和模型绑定换模型厂商就得重新适配无法标准化处理工具的认证、限流、审计等横切关注点。MCP解决的是工具服务标准化接入的问题。它定义了Client-Server架构Agent作为MCP Client各个工具服务实现MCP Server通过统一的协议基于JSON-RPC暴露工具能力。这样加一个新工具不需要改Agent代码部署一个新的MCP Server就行。3.2 一个能跑通的MCP工具实现示例我直接放一段我们在项目里用的精简版MCP Server代码用Python写的实现了一个查数据库表结构的技能import json from mcp.server import Server from mcp.server.stdio import stdio_server app Server(schema-server) app.list_tools() async def list_tools(): return [ { name: query_table_schema, description: 查询指定数据表的字段名、字段类型和注释, inputSchema: { type: object, properties: { table_name: { type: string, description: 表名如 user_order } }, required: [table_name] } } ] app.call_tool() async def call_tool(name: str, arguments: dict): if name query_table_schema: table arguments[table_name] # 真实的实现会去连接元数据库查询 schema [{col: id, type: bigint, comment: 主键}, {col: user_id, type: bigint, comment: 用户ID}] return {result: json.dumps(schema, ensure_asciiFalse)} async def main(): async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream)Agent侧通过MCP Client连接这个Server后工具描述会以标准格式暴露给模型模型就能像调用本地函数一样去使用查询表结构这个技能。实测下来这样一个技能的接入成本大约半天相比以前每个工具单独写适配代码效率提升非常明显。3.3 调参里容易被忽略的两个细节工具调用看起来就是模型输出JSON工程上却有两个细节很影响稳定性参数约束要严格别给模型自由发挥的空间。我在项目里见过模型把日期参数填成明天、把枚举值填成模糊描述的案例。解决办法是参数Schema里尽量用enum限定取值范围日期参数明确要求YYYY-MM-DD格式并在系统提示里给示例。返回结果要做结构化截断。有的工具返回结果很大比如查一张大表的全部数据Response里塞了10万行JSON模型上下文直接溢出。后来我们统一在工具返回值外面套了一层结果包装包含返回值摘要 完整结果存储位置 采样数据模型既能理解结果概要又需要详细数据时再按需二次获取。4. 技能不用多关键要有记忆这一节聊的技能不是调用外部工具而是让Agent能把多轮交互中的关键信息记住并复用。这是从能调用工具走向能用好工具的分水岭。4.1 显式记忆与隐式记忆的取舍我们的项目里用户经常在对话中透露一些长期稳定的偏好。比如以后查天气默认看杭州的我关心的股票列表是这几只。如果每次请求都让用户重申体验极差。但全部自动记住又会带来隐私和上下文污染问题。目前我们采用的是显式记忆为主、隐式记忆为辅的方案显式记忆用户明确说记住的信息比如以后都查杭州的天气会写入记忆存储在后续对话中被检索注入隐式记忆系统从对话中自动提取的偏好信号比如用户三次查询同一只股票会推测为关注标的但会在交互中跟用户确认后才正式存储。这里有个关键工程点记忆不是无限塞进上下文的。每条记忆打上时间戳和置信度注入时按相关性和时效性排序并且设置容量上限。测试中我们把记忆注入上限设为3条、150个Token以内既保留个性化能力又不干扰主体任务。4.2 记忆与工具调用的联动复杂一点的场景里记忆和工具调用是联动使用的。举一个实际case用户问我关注的股票今天表现怎么样Agent的处理流程是从记忆检索出用户关注的股票列表之前某轮对话中存储的调用股票行情接口参数传入记忆中的股票代码列表聚合结果生成简报。这套联动跑通后Agent的体验完全上了一个档次从每次问都像初次见面变得像真正的个人助理。不过也要提醒一下记忆功能涉及个人信息处理上线前一定要做合规评估该让用户授权的授权该提供删除入口的提供删除入口这不是能含糊的事。5. 多技能协作让Agent像团队一样分工单技能能力再强也只能应付简单任务。真实的用户需求往往需要多步操作比如帮我订一间明天下午的会议室并通知参会人这至少涉及查询空闲会议室创建预订发送消息通知三个技能。多技能协作的组织方式直接决定了Agent能处理的任务复杂度上限。5.1 技能编排的两种主流模式我在项目中对比过两种编排模式各有适用场景。一种是模型自主编排。模型根据用户请求自己决定调用顺序和组合方式。这种模式最灵活适合开放式任务但问题在于不可控模型可能在中间步骤上突发奇想。比如用户要订会议室模型先调用查询天气理由是看看明天是否下雨影响出行。从逻辑上说不算错但效率极低。另一种是预定工作流也就是把常用多步操作固化为固定流程模板用户意图: 订会议室通知参会人 技能序列: search_room - book_room - notify_users 兜底逻辑: search_room 返回空时询问用户是否需要调整时间段实际项目中我们是混合使用的有明确固定流程的业务场景用预定义工作流保证稳定开放式探索场景用模型自主编排保证灵活。比例大约7:3。这组数字是我们在客服场景和办公助手场景分别实测后调出来的。5.2 依赖冲突是编排里最容易炸的点多技能协作里的一个经典问题是依赖冲突。发生过一个印象很深的bugAgent执行订机票订酒店的流程订机票成功扣款了但订酒店因为时间冲突失败整体任务回滚不了用户被迫拿着没地方住的机票干瞪眼。这个问题的本质是技能之间存在状态依赖后续技能依赖前面技能的产出但前面技能的操作是不可逆的真实支付、真实下单。我的处理经验是所有可预见的会造成真实影响的操作尽量放到流程最后执行中间依赖查询类技能的结果时先做预演比如先查可订航班确认可行后再进入预订阶段关键节点引入人工确认比如即将为您下单确认请回复Y。别觉得这会降低体验对涉及钱和真实操作的事务它在挽回损失方面的价值远大于那一次点击的成本。6. 上下文窗口管理技能载荷的最优配比第三部分提过技能发现阶段通过检索召回规避上下文爆炸。但技能注入只是上下文的一部分生产环境中上下文窗口还装着系统提示词、对话历史、工具返回结果、Agent中间推理。怎么在有限的窗口里做合理配比是我花了很多时间调优的课题。我们最终在一个长期运行的Agent服务上固定了这样的配比策略以主流模型32K上下文为例内容类型Token预算说明系统提示词1.5K约4.7%角色设定、回复规范、安全规则技能定义2K约6.3%触发召回后的5~8个技能定义对话历史18K约56%滑动窗口按时间衰减裁剪工具返回结果8K25%超出部分截断或摘要化预留余量2.5K约7.8%模型推理与格式化输出空间这套配比不是拍脑袋定的是在连续几周的压测中慢慢调出来的。最关键的认知是对话历史不比工具定义更便宜。很多人为了给工具定义腾空间而压缩对话历史结果模型失忆用户重复说同样的话整体体验更差。另一个上下文管理技巧是滚动摘要。当对话历史超过预算时不是直接丢弃旧消息而是用一个较小模型或规则脚本把前面的对话压制成300字左右的阶段性摘要保留用户偏好、已办事项、待办事项这些关键信息。这个机制上线后长会话场景的用户满意度上升了大概30%。7. 评测是技能体系里最容易被偷懒的环节聊到评测我得说点不中听的。很多团队对Agent的验证停留在例子跑通几个就算完。但Agent技能的改动——不管是模型版本升级还是提示词微调——都可能让原来正常的能力悄悄退化。没有一套成体系的评测你根本不知道哪次改动把系统搞坏了。7.1 五维评测框架我在这个项目里建立了一套五维评测体系供你参考任务完成度用户目标是否达成由标注人员打分1~5分工具调用准确率实际调用出的工具是否是该场景最优选择参数正确率调用的参数是否与实际语义一致比如用户说杭州明天天气参数里的日期必须是明天、地点必须是杭州恢复成功率首次调用失败后Agent自主重试/更换策略/求助的成功率成本指标单次任务平均Token消耗、API调用次数、响应时延。这些维度缺一不可。早期我们只盯任务完成度出现过任务完成了但耗费了8次工具调用和巨量Token的情况成本完全失控。7.2 评测集的构建思路评测集是评测体系的地基。我们维护了三套评测数据回归集核心功能和历史bug复现用例每次改动必跑对抗集包含边界情况和恶意输入的用例集例如超长输入、歧义表达、多意图混合灰度集从真实用户日志中抽样的新场景定期补充进回归集。这里分享一个高效做法让Agent自己生产评测候选用例。我们定期从真实对话日志里把那些用户重复提问、Agent多次失败的会话提取出来经过人工改写后补进评测集。这样评测集随着系统上线时间越积越厚覆盖度持续上升而不是永远只有开头的几十条。8. 几个技能落地时最容易翻车的小细节最后这部分内容是从多个项目实战里踩出来的血泪经验。每个都很具体很难在官方文档里找到但都真实地影响过系统稳定性。8.1 技能描述越长模型越容易近视给技能写详细描述是本能但我在测试数据里发现一个反直觉现象技能描述超过一定长度后模型在长上下文中的工具选择准确率反而下降。推测原因是过长的描述稀释了模型对关键触发条件的注意力让它更难聚焦什么时候该用这个技能。我现在的实践是把技能描述控制在50~80字只写触发场景和核心限制。完整规则放到工具服务端做校验而不是让模型去阅读理解策略文档。模型负责判断要不要调用和初步构造参数服务端负责严格校验参数和业务规则各司其职。8.2 超时和重试必须有而且要快工具调用是有RPC特征的网络抖动、服务过载都可能导致超时。项目早期我们没有为工具调用设置超时结果一次对方服务响应慢Agent卡在等待中整个会话持续了3分钟无响应用户直接流失。后来我们为每个工具调用设置了统一的10秒超时超时后进入重试或降级逻辑。更关键的是在提示词里明确告诉模型如果工具调用超时直接告知用户稍后重试不要反复调用同一工具。这条小小的规则把因为超时引发的连环故障减少了七成。8.3 失败信息必须结构化不能只丢一句调用失败工具出错时如果只返回调用失败这四个字模型根本无从恢复。我们在所有工具调用里统一了错误返回格式{ error: { code: NO_PERMISSION, message: 当前账号无权访问该项目数据, suggestion: 请联系项目管理员开通访问权限 } }关键是suggestion这个字段。模型拿到错误后不需要自己揣测怎么修直接按照建议执行就可以了。这看起来像是在剥夺模型自主性但实际上大幅提升了故障恢复效率。因为Agent自主瞎猜的恢复方案往往会导致更复杂的连锁错误。8.4 系统提示词里要为无技能可用留退路模型面对超出能力范围的需求时如果没有预设退路就会强行编造一个工具调用凭空捏造的参数或者自信地给出错误答案。我们在系统提示词里明确加了一条当用户请求超出可用技能范围时明确告知用户当前无法完成并列出可以帮到他的相近功能。这条规则看着简单对用户体验的提升却很显著。它把模型从强行完成拉回到诚实沟通的轨道上避免了大量因幻觉产生的错误输出。9. 技能体系的一次完整上线复盘最后回到agent-skills这个项目本身聊一次完整上线过程中的关键节点和取舍这部分对准备落地类似系统的朋友应该最有参考价值。9.1 从全部自研到半自研的转变项目刚立项时团队倾向于所有技能模块都自己写理由很充分这样才能完全掌控。但开发到第二个月就发现进度严重滞后——技能注册、鉴权、协议适配、监控告警这些基础设施太占人力了。后来做了一个关键决策基础设施层尽量采用成熟开源方案比如MCP相关的SDK、向量检索直接用现成的团队精力集中在上层——技能定义、检索召回策略、编排逻辑、评测集这些能形成核心差异的部分。这个调整让上线时间提前了一个月。后来想想如果当初继续全自研很大概率会陷入无穷无尽的基础设施维护里。9.2 灰度发布先放10%流量盯三天技能系统上线时我们没有一次性全量切换。先放了10%的流量灰度观察重点盯着几个指标调用错误率、响应时延、用户反馈。当时确实盯出问题了——某个工具在高峰期出现了偶发超时因为灰度期间流量小这个问题如果直接全量上线很可能到第二天才能发现影响面会大得多。灰度第三天各项指标稳定后才逐步扩大到50%再到全量。整个过程大约用了一周。虽然看起来慢但换来的稳定性是值得的尤其是涉及多技能协作、真实操作的业务场景。9.3 上线后的持续迭代机制技能系统上线不是终点。我们现在保持着每周一个迭代周期的节奏新技能加入、旧技能优化、评测集扩充、回归测试。每个技能上线前必须跑完三件事通过评测集回归、灰度观察24小时指标、撰写使用文档。这套机制运行了几个月技能库从最初十几个扩展到了五十多个而稳定性指标——包括工具选择准确率、恢复成功率——都保持在小幅上升的曲线上。这说明技能体系确实是一个可以持续积累、越用越强的架构而不是上线即巅峰的一次性系统。我在这个项目里最深的体会是Agent技能的打磨没有银弹靠的是机制设计、数据积累和持续迭代。既需要技术层面的精细化实现也需要工程管理上的节奏控制。如果你也在做类似的事情希望这篇内容能帮你少踩几个坑。
返回列表