ARTICLE DETAIL

资讯详情

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

Agent技能库设计:从工具函数到结构化执行策略的完整实践

Agent技能库设计:从工具函数到结构化执行策略的完整实践 最近在做 Agent 项目的时候我踩了一个很典型的坑业务方提的需求越来越多Agent 需要调用的工具从十几个涨到上百个模型开始频繁选错工具上下文里被无关的工具描述塞得满满当当响应速度也肉眼可见地变慢。后来我把思路从“一个个堆工具函数”转成“按技能组织能力”也就是给 Agent 做了一套“技能库”这个问题才真正得到解决。这个库就是我这段时间一直在迭代的 agent-skills 体系的雏形。这篇文章会把我在设计和落地 agent-skills 过程中的完整思考、代码结构、踩坑记录和复盘经验都整理出来。如果你正在做基于大模型的 Agent 开发或者你维护的 Agent 工具数量已经开始失控这篇文章会给你一套可以直接参考的落地方案。1. 为什么单个工具函数不够用从“工具”到“技能”的进阶思路先聊清楚一个基本问题既然大模型本身就支持 function calling那直接给模型传几十个工具函数不就行了为什么还要额外做一层“技能”抽象1.1 工具函数的天花板当你有一百个工具时会发生什么我最早的项目很简单Agent 只需要处理查天气、设提醒、发邮件这三件事三大模型厂商的 function calling 都能轻松搞定。但业务一旦做起来事情就完全变了要查内部 CRM 的客户信息、要对 Excel 报表做聚合统计、要调用审批流接口发起流程、要从知识库检索文档、要基于搜索结果生成周报……功能越加越多我一次性把几十个工具函数的 JSON Schema 全部塞给模型结果出现了几个非常典型的问题上下文被大量工具描述占据模型真正能用来推理的 token 空间被压缩。工具描述相似度过高比如“查询客户信息”和“查询客户联系人”模型经常选错。工具之间的调用顺序完全靠模型临场发挥经常出现参数缺失、中间结果没有正确传递的情况。函数内部如果抛出业务异常模型的处理策略很初级基本就是报个错让用户重试。这就像你给一个新同事发了一份五十页的操作手册他确实能看到所有操作步骤但真遇到问题的时候他并不知道“什么场景该用哪一章”。1.2 技能是什么把工具、触发条件、执行策略和容错规则打包agent-skills 的核心思路是大模型的思维并不是越跳越好而是给它一个结构化的“约束支架”。我把一个技能定义成下面这五个要素的集合触发条件什么类型的用户请求应该走这个技能技能描述里要写清楚适用边界。所需参数技能对外暴露的入参结构以及从用户请求中抽取这些参数的规则。执行步骤技能内部要调用哪些工具按什么顺序调前一步输出怎么给后一步用。工具清单实际执行时允许调用的底层工具函数这些函数可以是 API、数据库操作、脚本。容错规则某一步失败了怎么办是重试、降级还是终止并给用户解释。换句话说工具是最小执行单元技能是“什么时候用哪些工具、按什么顺序用、出错怎么办”的完整策略集合。1.3 agent-skills 的定位在模型和底层工具之间加一层注册表在我的体系里agent-skills 更像一个位于模型和底层工具之间的“注册中心 编排框架”。它不是让模型自己去遍历海量工具而是让模型先去匹配“哪个技能最符合当前用户意图”拿到技能后再按照技能内部预定义的步骤去执行。这样做带来的直接好处有三个模型每次只需要看当前技能相关的工具描述上下文压力小很多。技能内部的执行顺序是确定性的不需要模型每一步都做高难度决策。容错和重试策略是写死在技能逻辑里的业务行为稳定可控。下面我具体展开讲技能结构怎么设计。2. 技能的结构设计与注册机制从 JSON Schema 到可执行策略技能的底层结构是整个 agent-skills 体系最基础的部分。结构没设计好后面加技能、改技能都会很痛苦。2.1 技能描述文件一份技能该包含哪些字段我用 JSON 来表达每个技能的定义文件核心字段如下{ name: customer_analysis, version: 2.1.0, description: 对客户数据进行聚合分析支持按时间、地域、行业维度统计销售额、客户数、转化率。适用于销售周报、月度复盘、业务异常排查场景。, trigger_hint: 包含客户数据、销售额、转化率、周报对比等信息时优先匹配, inputs: [ { name: time_range, type: string, description: 时间范围格式如 2025-01-01~2025-01-31 }, { name: group_by, type: string, enum: [region, industry, time] } ], steps: [ { tool: crm_query_client_data, input_mapping: { start_date: input.time_range.split(~)[0], end_date: input.time_range.split(~)[1] }, output_key: raw_client_data }, { tool: data_aggregator, input_mapping: { data_source: raw_client_data, dimension: input.group_by }, output_key: aggregation_result }, { tool: report_formatter, input_mapping: { data: aggregation_result, format: markdown }, output_key: final_report } ], recovery: { on_tool_failure: retry, max_retries: 2, fallback_tool: data_aggregator_lite } }字段设计时我重点考虑了三个点description 和 trigger_hint 是给模型看的。description 描述技能的完整能力trigger_hint 则给出典型触发场景的关键词组合方便模型做意图匹配。input 部分必须给出明确的类型、取值范围和示例这样模型在抽取参数时不容易出偏差。steps 部分定义了固定的执行序列通过 input_mapping 把上游输出和当前工具入参连接起来而不是让模型临时去理解“上一步返回了什么”。2.2 技能仓库的加载与校验新增技能足够简单有了技能描述文件接下来要考虑怎么让 Agent 运行时加载这些定义。我的做法是技能仓库模式每个技能是一个目录包含 skill.json上述定义文件和 executable/实际的可执行脚本或函数包。启动时Agent 会扫描整个 skills 根目录逐个加载 skill.json做格式校验和依赖检查。校验内容包括字段完整性、steps 中引用的工具是否已注册、inputs 参数命名是否冲突。校验通过后技能会被注册到内存中的技能索引表里供意图路由模块查询。技能仓库的目录结构大概是这样的skills/ customer_analysis/ skill.json executable/ main.py document_summary/ skill.json executable/ main.py这样设计后新增一个技能只需要新建目录、写好 skill.json 和执行代码重新加载配置即可不需要改动 Agent 主体逻辑。2.3 技能间的依赖与组合把技能体系做成能力编排技能不是完全孤立的实际业务经常需要多个技能协作。比如“生成销售周报”这个技能内部可能需要先调用“客户分析”技能拿到聚合数据再调用“文档生成”技能把数据转成周报。为了支持这种组合我在技能定义里增加了一个 depends_on 字段允许某个技能声明自己依赖哪些其他技能。执行时运行时先递归加载依赖技能并按照依赖顺序执行。这种设计让我可以在高复用性和易维护性之间取得平衡底层技能保持原子化高层技能只做编排。做数据分析的需求方永远是同一个“聚合分析”技能而不是每个业务都自己写一遍逻辑。3. 意图路由与执行链路从用户的一句话到跑完整套技能技能定义好之后关键问题就变成了模型怎么知道用户请求应该匹配哪个技能这个环节我把它称为“意图路由”。3.1 意图路由的两种方案对比提前分类和运行时匹配最开始我尝试过让模型直接看所有技能描述然后自己选。技能一多超过 30 个的时候就明显了模型的选择准确率下降很厉害。后来改成两段式第一段用较小的模型对所有技能描述做一个粗筛选给每个技能算一个相关度分数把最相关的 3 到 5 个技能选出来。第二段把候选技能的详细定义包括 steps 和 inputs注入主模型让主模型做最终选择并填充参数。这样整个上下文窗口占用很小主模型做决策的信息量却足够精准。在实际测试里技能数量从 20 个扩展到 80 个以后路由准确率基本保持稳定。这个思路和检索增强生成很像不用把整个知识库都读一遍先检索再阅读效率和准确率都能兼顾。3.2 参数绑定模型抽取和代码校验的配合用户说的话往往是不规则的比如“帮我看看华东区这个月销售额怎么样”与技能定义里的 time_range、group_by 不完全一致。参数绑定阶段要做的工作就是主模型根据用户的会话上下文按技能的 inputs 定义抽取结构化入参。抽取完成后进入代码校验环节类型不符、枚举值非法、缺失必填项都会在这里被拦截然后返回给模型要求重新提取。这个“模型抽取 代码校验”的循环机制极大降低了因为参数不合法导致的执行失败。之前不校验直接执行时失败率大概 15% 左右加了校验以后降到 3% 以内。3.3 执行编排与轨迹记录让每一步都有迹可循技能执行的编排逻辑我是用 Python 写的核心是一个 executor负责按 steps 定义顺序调用底层工具并记录每一步的输入、输出、耗时和状态。执行轨迹有两个重要作用一是出了问题可以精确定位到具体某一步而不是只知道整个技能失败。二是轨迹可以作为数据闭环用来分析哪些技能调用频繁、哪些步骤耗时异常、哪些工具经常报错。我基于轨迹数据做过一次优化发现超过一半的执行失败发生在某个报表格式化工具上后来把这个工具替换成并行版本整个技能的完成时间缩短了 40%。3.4 多级降级策略失败时不要直接摆烂在技能执行过程中工具报错是家常便饭。我的降级设计分三层第一层重试。对于超时、网络抖动类的错误按 recovery.max_retries 自动重试。第二层降级。如果重试仍然失败检查 recovery 里是否有 fallback_tool有的话就用备选工具替代。第三层局部结果。如果某一步失败且没有备选工具就把当前已经拿到的部分结果封装后返回给用户并明确告知缺少哪些内容。这套降级设计在真实场景里非常重要。用户要的是能拿到一部分能用可用的结果不是每次都必须完美数据否则就报错让他自己想办法。4. 生产环境里的真实踩坑清单五个必须注意的细节这一节把我在这段时间迭代 agent-skills 过程中踩过的坑、排查过的坑整理成一份简单直接的经验列表。4.1 技能描述膨胀导致检索失效技能越加越多description 越来越长很多描述开始互相包含粗筛选阶段的相关度分数拉不开差距。比如“客户分析”和“渠道分析”这两个技能都包含“分析”“数据”“销售”这些词。我的解法是给 description 加约束只能写“这个技能能做什么、不能做什么”典型场景用 trigger_hint 单独维护并且定期用测试集检查每个技能描述的“可区分度”如果两个技能余弦相似度太高果断改成互补描述。4.2 循环调用和自锁死风险技能之间的依赖如果形成环就会导致死循环。比如技能 A 依赖技能 B技能 B 又依赖技能 A加载阶段如果没做检测执行时就会无限递归。我加了一个 DAG 依赖检测在技能注册阶段就把全部依赖关系构建成有向图检查是否存在环只要发现环就拒绝加载并及时在启动阶段报错。这个检查多花不到 10 毫秒却能省下大量排查死循环的精力。4.3 大段工具输出污染对话上下文这个坑是我踩得最惨的坑。技能内部的工具经常返回几千字的原始 JSON如果直接把这份数据全部塞回主模型读 Token 也会被消耗殆尽模型的重点也会被带偏。后面我彻底改掉了传递逻辑工具原始输出只保留在内部状态里不进入模型上下文。需要模型决策时只给它经过摘要处理后的结果或者把结构化关键字段提出来。最终回复用户时只把 final_report 传入模型做表达润色或格式调整。改造之后单次技能执行的平均 Token 消耗降了大概六成。如果你的技能工具也有类似大块输出建议直接踩我的方案。4.4 并行技能冲突共享资源竞争问题多个技能同时执行时很容易碰到共享资源竞争问题比如两个技能同时写同一个临时文件或者同时调用同一个下游 API 导致限流。我引入了简单的两级处理技能在定义里声明自己属于全局独占型还是并行安全型全局独占型技能通过全局锁串行执行并行安全型可以自由并发。另外给所有写资源操作都加上了独立的工作目录避免写冲突导致彼此影响。4.5 升级技能版本导致线上行为突变技能逻辑改了之后旧会话还在继续走但新会话已经开始按新技能执行结果同一类请求在不同时间点得到的输出结构和格式完全不同用户反馈“Agent 怎么突然不会干活了”。现在我给所有技能强制加了版本号Agent 的会话状态里记录当前会话正在使用的技能版本。如果技能升级只对新建会话生效执行中的旧会话继续沿用旧版本逻辑。等旧会话自然结束之后再清理旧版本整个线上切换过程平滑很多。5. 技能库的持续迭代与管理让技能系统能在业务演进中存活agent-skills 不是一个做完就固定下来的东西业务一发展技能就得跟着扩张和调整所以这层机制的管理和维护方式非常重要。5.1 把高频手动操作沉淀为新技能从日常对话里挖需求我的习惯是每周拉取一次 Agent 的执行轨迹看哪些用户请求没有命中任何技能最后是模型硬聊或者反复请求通用工具完成的。只要某个模式在一周内出现三次以上我就会考虑把它沉淀成新技能。举个例子运营同学经常说“把这个表整理一下按转化率排个序然后生成一个摘要。”最初这个需求没有任何技能能覆盖只能让模型硬拆解效果一般。后来我提炼了一个“表格清洗排序摘要”技能把它变成工具链的一段确定性流程次周相关请求的处理成功率直接从 60% 升到了 90%。5.2 技能灰度上线新技能不直接对全量流量开放新增技能时我不会立刻让它对全量用户可见。做法是分两个阶段内部验证先用测试集跑一遍确认技能路由准确率和执行成功率都达标。灰度放量先在技能描述里加一个触发权重比如让模型只在部分请求中选择这个技能观察轨迹质量。灰度阶段我能看到的指标包括路由准确率、参数抽取成功率、工具平均失败率、用户是否中途重试等。只有所有指标都稳定之后才会把技能设为全量可用。5.3 技能的跨业务复用社区化共享思路最终agent-skills 还应该往“可共享”的方向走。我在自己的团队内部做了一个技能包中心各个业务线可以把验证过的技能上传成共享包其他团队直接打包复用。为了让技能包跨业务可用我在技能定义里增加了 scopes 字段声明技能适用的业务范围和环境要求。比如某个支付风控技能声明自己只适用于交易类业务另一个团队如果拉取它用于内容推荐加载的时候就会收到不匹配的警告。现在的技能体系已经从最开始十几个工具函数发展到八十多个可复用技能Agent 的响应质量反而比最初那版稳定得多。追溯起来最大的转折点就是引入了“技能”这一层抽象把决策从逐个工具调用的试探变成了一套有策略、有套路、有兜底的结构化执行方案。如果你也在被工具数量膨胀、模型选错功能、上下文爆炸这些问题困扰我建议你先别继续堆工具函数试试从最小的一两个技能开始重构你的 Agent 能力层。
返回列表