ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI Agent技能系统搭建指南:从工具调用到生产级调度治理

AI Agent技能系统搭建指南:从工具调用到生产级调度治理 先把定义说清楚我这里讲的agent-skills是指一套为 AI Agent 设计的能力管理系统——把模型能做的事拆成一个个可注册、可复用、可独立验证的技能单元然后让智能体按需调度这些单元而不是把全部指令都塞进提示词里。这个概念看起来简单但实际落地的过程中牵扯到的架构取舍、调度策略、上下文管理和异常恢复都远比“写几个函数给 Agent 调用”要复杂也正是这些细节决定了 Agent 到底是个只能聊天的 Demo还是个能稳定干活的生产系统。这篇内容适合已经把 Agent 跑通、但发现任务一复杂就失控的开发者也适合刚接触 Agent 工程化、想搞清楚“技能系统和普通工具函数到底有什么区别”的同学。我会从技能的本质拆解出发讲透技能注册、调度、评估和治理的完整链路把我在实际搭建过程中踩过的坑和验证过的做法都放出来希望能省下你自己去试错的那些时间。1. 技能系统的本质为什么 Agent 不能只靠提示词先回答一个最容易被追问的问题Agent 不已经能调工具了吗为什么还要单独做一个技能层我的理解是工具解决的是“能不能调用”的问题技能解决的是“在什么场景下、以什么流程、按什么标准调用”的问题。两者差了整整一个工程化阶梯。1.1 技能不是函数也不是提示词很多人把技能理解成“给模型多注册几个函数”这是最常见的误区。函数本身只是技能的一个执行核真正让技能成立的是它外圈的那几层东西触发条件、参数契约、上下文使用规则、失败降级策略。举个例子。给 Agent 注册了一个call_search_api函数它只能保证“当模型决定搜索时这个 API 会被正确调用”。但技能系统要求你回答的是这几件事模型依靠什么判断该不该调用它搜索参数怎么从上下文里抽出来搜索结果返回后是原样丢给模型还是先做摘要再喂回去如果 API 超时或者返回空结果接下来走哪条分支这些逻辑写在提示词里会非常脆弱。因为你没法让模型稳定遵守一大段“如果遇到什么情况先做什么再做什么”的规则模型会忘会跳步会在没有依据的时候自作主张。而把这些规则固化在技能代码和描述里就是把可变的人为判断变成可测试的确定性逻辑。1.2 一个技能单元到底包含哪四层我在自己的 agent-skills 设计里每个技能都由四层组成缺一层就不能称之为完整的技能。第一层是元数据与触发条件。包括技能名称、所属分组、适用场景描述以及“什么时候不该用”的反向说明。这一层的作用对象不是代码而是大模型本身所以描述质量直接影响模型调度的准确率。后面我会单独讲怎么写这段描述。第二层是输入 Schema 与参数契约。定义技能需要哪些参数、每个参数的类型、范围、默认值以及哪些是必填哪些是选填。这里的核心不是为了好看而是为了在运行时拦截脏数据避免底层 API 收到奇怪的入参。第三层是执行逻辑。这是技能真正干活的代码——调 API、查数据库、执行计算或者操作文件系统。它应该保持纯粹和可测试不要夹带业务上下文然后把结果按统一结构返回。第四层是后处理与降级策略。包括如何从原始返回里提取有效信息、如何将结果格式化成模型更容易理解的文本、以及失败时的备用路径。很多团队做到第三层就停了但第四层才是决定 Agent 体验的上限因为真实环境中错误是常态不是例外。2. 技能、工具与工作流的分层逻辑在搭建 agent-skills 的时候有一个绕不开的架构决策技能系统应该处于什么位置它和你项目里的工具函数、业务流程编排是什么关系我最后采用的方案是三层结构技能作为中间的承上启下部分。2.1 三层能力模型怎么划分我把 Agent 的能力拆成三层原子工具、技能单元、工作流。原子工具是最底层的能力负责一件非常具体、没有状态的事比如“发送 HTTP 请求”“读写某个 key 的缓存”“调用某个模型的接口”。它们不感知业务语境参数由上层传入结果原样返回。技能单元是中间层也是 agent-skills 的核心。它把若干原子工具组合成一件有意义、可复用的能力。比如一个“网页内容理解”技能内部可能调用了请求工具、HTML 解析工具、文本抽取工具和摘要模型但对外只暴露一个统一入口。工作流是最高层的编排。它定义了完成一个业务目标的多步流程比如“追踪行业竞品动态”可能包括读取订阅列表、逐一抓取页面、生成差异摘要、推送通知。工作流确定走哪些技能技能确定怎么调用工具。这样的分层有一个直接好处每一层都可以独立测试和迭代。工具出问题只影响依赖它的技能技能的行为不合理重新编排工作流就行不用动底层代码。2.2 技能和工具的一个关键差异对模型的暴露程度原子工具可以直接暴露给模型但技能更推荐通过路由层间接暴露。原因在于上下文窗口是有限的如果系统里有几十个技能每个技能的描述加参数定义动辄上千字符全部塞给模型会让它在选择时明显变慢甚至出现选择错误。路由层的任务就是根据当前对话的目标从技能仓库里召回最相关的 topK 个技能再把召回结果注入模型的工具列表。这个召回可以用向量相似度可以用规则也可以混合两者。但无论用哪种方案核心原则是一致的模型能看到的技能必须是和当前任务强相关的数量尽量控制在十个以内。这里顺带提一个容易踩的坑如果招回的技能描述之间没有拉开差异比如五个技能描述里都有“分析用户需求”字样模型就会开始乱选。最好的做法是让每个技能的描述像产品定位一样清晰最好能包含具体的适用对象和边界让人不看代码也知道这个技能什么时候该登场。3. 从零搭一个技能网页理解技能的完整设计链理论说再多不如直接拆一个具体技能。这里选“网页内容理解”作为例子因为场景足够常见且牵扯的细节点非常全面从描述编写到参数校验再到错误处理都有代表性。下面这个设计过程就是我在 agent-skills 里沉淀同类技能的通用路径。3.1 技能描述决定模型“会不会用”的第一道关卡技能描述写入的不是给人看的文档是给 LLM 看的决策依据。写得太短模型不知道何时调用写得太泛模型会在无关场景下乱调。我最终稳定下来的格式大概是这样当用户提供网页链接并要求总结、提取关键信息、翻译或者对比内容时使用本技能。它会自动抓取页面、清洗无关注释并返回正文 Markdown。如果链接是 PDF、图片或需要登录的页面不要调用此技能改用文档解析技能。这段描述里包含了三个关键信息适用场景总结、提取、翻译、对比、能力边界自动抓取清洗返回 Markdown、排除条件PDF、图片、登录页不处理。前两个帮助模型正向选择第三个帮助它避免误用。描述之外技能名称也要用心。我习惯用group_action_object的格式比如web_fetch_and_summarize既有语义也保留了命名空间。名字太短会撞车名字太长模型容易截断控制在 3 到 5 个词比较合适。3.2 入口函数与 Schema 校验技能入口遵循简洁原则所有外部依赖都从参数传入。下面是这个技能在 Python 侧的入口实现def execute_web_understand( url: str, output_format: str markdown, max_length: int 3000, language: str zh, ) - dict: 执行网页理解技能的入口函数。 Args: url: 目标网页完整链接必须包含协议头。 output_format: 输出格式支持 markdown / text / json。 max_length: 返回内容的最大字符数防止上下文爆炸。 language: 摘要目标语言默认中文。 Returns: 统一技能返回结构格式为 resume。 validate_input(urlurl, output_formatoutput_format) html fetch_page(url) content extract_main_content(html) if len(content) max_length: content summarize_content(content, max_length, language) return build_result(statussuccess, datacontent)这段代码刻意没有写业务日志、也没有把页面内容直接返回给模型而是先经过抽取和摘要处理。原因很简单网页正文动辄一两万字如果原样拼进对话上下文几轮之后整个会话的上下文配置就已经被污染了。关于这一点后面专门展开讲。Schema 校验在入口处同步完成校验不通过直接抛异常不让错误流入内部逻辑。我在项目里使用的是 JSON Schema 描述参数结构既方便代码侧校验也可以直接转成 OpenAI/Claude 格式的 function definition。校验至少要覆盖必填参数是否缺失、URL 域名是否在允许列表、枚举参数是否为合法值。3.3 错误处理与降级策略真实环境中网页抓取最常遇到四类错误DNS 解析失败、目标站点超时、返回内容为空、编码解析乱码。技能层的职责不是回避这些错误而是把错误明确、结构化地暴露给模型让模型能够重新规划。超时控制很简单请求库设置连接超时和读取超时我常用的配置是 5 秒和 15 秒。超过这个阈值的请求直接放弃避免整个 Agent 被卡死在一个慢速页面上。编码乱码问题我之前吃过亏。网页没有声明 charset 或声明错误时用 requests 的apparent_encoding做兜底识别命中率不高。后来我换成了先按 Content-Type 头里的 charset 解析失败后再用字符频率检测这样遇到绝大多数中文站点都能正确拿到文本。如果抓取内容为空不要返回空字符串让模型干等。正确做法是返回一段带语义的错误信息比如“页面获取成功但未提取到正文可能是页面由 JavaScript 动态渲染建议获取渲染后的 DOM 或使用浏览器技能”。模型看到这种描述后会主动切换到其他技能而不是反复重试同一个失败的调用。4. 技能注册与调度的工程化实现技能写完只是第一步怎么注册进系统、怎么被模型发现和调用才是 agent-skills 真正工程化的地方。这一环节做得稳技能越多越不会乱做得随意十个技能就开始互相打架。4.1 技能仓库与注册清单所有技能都有自己独立的目录里面放三样东西SKILL.md技能描述文件、schema.json参数协议、可执行的入口脚本。目录命名和技能名保持一致方便自动扫描。注册中心启动时扫描技能仓库把技能元数据加载到内存缓存这样模型调度时就不用反复读磁盘。注册中心维护两张表。第一张是技能静态信息表记录技能 ID、版本号、入口信息、所需权限、当前状态第二张是技能路由索引用于召回阶段索引内容是技能描述和示例 query 的组合向量。静态表的作用主要是治理路由索引的作用主要是性能。两者分开是因为更新频率完全不同技能信息改版纪律性比较低但路由索引每次技能描述调整都需要重新构建混在一起管理很快就会乱。技能注册之后默认是disabled状态管理员手动开启才进入可用状态。这个机制看着多余实际帮我挡住过几次事故——技能代码会自动部署到一个新环境如果默认直接对模型可见没验证过的技能就会在真实请求里跑起来。默认禁用相当于给技能发布加了一个手动确认闸门。4.2 运行时调度逻辑与资源隔离当用户请求进入到 Agent 运行时调度流程大概是这样的意图识别Agent 主循环理解当前用户 query 的目标。技能召回路由层从技能仓库里返回 topK 个候选技能。注入工具列表候选技能被转成模型可识别的工具描述注入到 API 请求里。模型决策模型判断要不要调用某个技能以及传入什么参数。执行与回填技能代码被执行结果被回填进对话上下文供模型继续推导。这个流程里最容易被忽视的是第 5 步的结果处理。设计团队经常把大量精力花在前四步认为“技能执行成功就好了”但实际上执行成功不代表对模型“好用”。如果技能返回的结果过于冗长或者格式混乱模型在下一步推理时根本没法高效使用它。我在所有技能里统一规定了返回结构的格式大概是statusdatasummary三段式。status标明执行状态data放结构化结果summary放一段面向模型的精炼总结。模型看到summary就能快速决定是否继续追问细节不用自己去解析一大段 JSON。资源隔离方面技能执行统一放在带超时和内存限制的子任务中运行禁止技能代码直接操作 Agent 主线程的全局状态。单个技能的内存使用上限和最大执行时间都在注册信息里配置运行超限直接终止并返回错误信息。这个设计确保了一个技能失控不会拖垮整个 Agent 进程。5. 实测中发现的坑调用膨胀、技能冲突与上下文污染这一节是整篇里最值钱的部分全部来自真实运行环境里踩过的坑。每个问题都给出了完整的排查链路而不是只丢一个结论。你在自己的 agent-skills 系统里大概率也会遇到其中几个。5.1 问题一技能数量一多模型开始“选择困难”当我的技能仓库里技能数量超过 30 个时发现模型误调率显著上升具体表现为应该调用 A 技能的场景调用了 B 技能或者在两个相似技能之间反复切换把对话拖得很长。排查过程是从两个方向同时进行的。第一检查所有技能描述是否出现语义重叠结果发现有一组“内容改写”技能三个技能的描述里都包含“改进文本质量”字样模型根本没法从描述上区分它们。第二查看 token 统计发现全部技能描述注入后单纯工具描述部分就占了几千 token模型在长上下文里对工具描述的注意力被稀释了。修复方案分两个层面。在召回层把技能按“内容理解”“内容生成”“数据获取”“系统操作”等分组先由路由层根据意图选择技能组再在组内召回具体技能。在描述层对相似技能进行互斥定位明确各自专属场景比如“适合快速改写”“适合学术润色”“适合营销文案化”把差异在描述里单独强调出来。5.2 问题二技能内部再调技能上下文被重复改写有一次我观察到一个奇怪现象Agent 在连续处理问题时上下文里的“用户意图摘要”越积越多而且内容有大量重复。跟踪日志后发现原来是我设计了技能可以互相调用的机制一个技能执行完把结果描述写进了上下文另一个技能被调用时又把改写过的内容作为输入二次写入上下文不知不觉就把核心上下文挤爆了。排查链路很清晰先按 session ID 拉出完整对话记录发现同一份“用户目标描述”在上下文中出现了 6 次每次表述略有不同但语义相同。继续追根溯源发现其中三个技能的代码里都有“将当前理解写入主上下文”的逻辑而这些技能是链式调用的所以理解被反复追加。修复方案是给上下文写入增加了一个统一入口只有 Agent 主循环可以执行追加操作技能执行过程中产生的内容一律走隔离暂存在主循环决定采纳后才写入。同时给所有技能补了一条硬性规则任何技能不得自行修改主对话上下文。5.3 问题三参数透传的隐式约定导致的脏数据还有一次线上问题更隐蔽。某个技能调用另一个技能的参数时直接透传了上层传下来的大 JSON 对象而目标技能的 Schema 里根本没有定义这个字段校验逻辑把它忽略了结果执行时因为缺内部依赖字段而报错。这类问题的根因是技能之间“隐式约定”太多代码里总是有一些“恰好能用”的假设。我后来花了整整一个迭代周期把技能间调用的所有参数都显式声明在接口协议里调用方必须按 Schema 映射参数不允许把来源不明的大对象整包丢进来。过程很痛苦但完成之后技能之间的调用关系清楚了很多排错也容易了。6. 评估与迭代如何量化一个技能的真实质量很多团队搭建完技能系统就停在那里后续只是不断往里面加新技能却没有任何评估机制。没有评估就没有迭代依据也没有办法发现“某个技能其实很弱但没人知道”的沉默问题。6.1 四个核心指标我给每个上线的技能都建立了四个评估指标每一个都有明确定义和采集路径。命中率在所有应该调用该技能的测试场景中模型实际正确调用的比例。反映的是技能描述和路由召回的质量。成功率调用后技能执行成功、返回符合结构要求的比例。反映的是执行代码的健壮性。转化率技能返回结果被模型采纳并用于最终回答的比例。反映的是结果格式和摘要对模型是否友好。返修率当用户对最终答案不满意、要求重做或补充时涉及该技能结果的比例。返修率高意味着技能输出的抽象程度不够模型可能无法直接使用。四个指标里前两个大家通常都会建后两个容易被漏掉。但实际上“调用成功”只是中间指标最终指标是技能结果是否真正推进了任务完成也就是转化率和返修率。我每个评估周期会抽四个指标都异常的技能出来逐一分析日志看问题出在描述层还是执行层然后把结论反馈给开发迭代。这样技能不是一次性开发完就不管了而是像产品一样持续调整。6.2 技能回归集的作用技能系统的回归测试不能靠手工点几个例子就完事。我维护了一个技能回归集每个技能至少包含 10 个测试 case覆盖正常场景、边界输入和失败输入。每次技能描述或代码有改动跑一遍回归集确认没有引入新的失败 case 才允许发布。回归集里最容易被忽视的是负例测试。比如网页理解技能的负例是 PDF 链接、登录页链接、空页面。跑回归时这些 case 应该被正确拒绝并返回“不适用”的信息而不是被技能尝试处理。只有负例测试稳定技能才不会在真实环境里被模型乱用来处理它不擅长的问题。对于路由召回的质量我另建了一个小的检索评测集记录每个 query 对应应该召回的技能 ID。每次路由逻辑调整后跑一遍看召回精确率有没有低于 80%。这个方法帮我提前发现过一次向量化改造导致的召回全面退化差一点就漏到线上。7. 生产环境的技能治理规范技能系统在开发环境里跑得再好到了生产环境还是会遇到一堆新问题。这块的治理规范我觉得是决定一个 agent-skills 系统能不能长期运营的分水岭。以下是我沉淀下来的几条硬性规范。7.1 命名、权限和隔离的底线设计技能命名有一个我一直坚持的原则技能 ID 一旦发布不可变。原因是技能变更和日志分析都依赖一个稳定标识如果技能 ID 随着迭代变化历史日志就完全没法串联了。实在要用新名字就注册新技能、冻结旧技能而不是原地改名。为了保证这一原则落地我把技能 ID 允许变更的权限收口到管理员最高权限级别并且每次变更都会有审计记录。权限隔离方面技能运行环境分成了内部技能和外部技能两类。内部技能可以读取系统敏感配置权限较高外部技能只能访问能力受限的沙箱环境所有网络请求走独立出口配置文件里禁止写入硬编码密钥访问内部服务。这样做的好处是即使某个外部技能被利用也不会直接暴露内部系统的入口。7.2 灰度、审计和成本追踪新技能的发布流程我长期在用的是三阶段灰度影子模式、小流量模式和全量模式。影子模式下技能被调用时不返回真实结果只记录它“想返回什么”用于验证技能描述和路由是否符合预期。小流量模式放量 10%实时观察四个核心指标有没有缺口。全量模式只在指标通过之后开启。审计日志记录了每次技能调用的触发 query、当前技能版本、入参快照、执行耗时、token 消耗和返回状态。这个日志的意义体现在三个地方事故复盘时能快速定位责任环节评估指标计算时有真实数据支撑成本异常上涨时能直接看到哪些技能吃了最多资源。7.3 技能生命周期管理技能不是永生的。当某个技能连续两个评估周期命中率低于 50% 且没有明显改进路径时我会把它标记为“弃用”状态并移出默认召回范围。这样做的结果就是模型可选列表永远保持在精悍状态避免了一堆没人用的技能拉低整体调度准确率。还有一个值得注意的教训就是新增技能一定要克制。技能系统天然鼓励“能力下沉”任何任务做两遍就有人想把流程封装成技能。但实际上每一个新技能都在增加模型的选择空间和路由层的学习成本。不够通用的场景宁可继续用一段 Prompt 处理也不要草率封装成正式技能。把有限的系统预算留给真正高频、稳定的能力这个取舍在所有 Agent 项目里都成立。最后再分享一点我自己的感受agent-skills 这个方向目前并没有标准化答案各家都在摸索自己的边界。我见过只用十几个技能就把业务跑得很顺的团队也见过技能库里堆了两百个技能却到处失控的项目。这里面最关键的分水岭通常不是技术栈多先进而是团队有没有把技能当作一套需要持续治理的工程体系来对待。先从你最痛的三五个高频场景开始做技能跑出闭环再往仓库里加东西心态会稳很多系统的稳定性也会更好。
返回列表