Agent技能体系实战:从工具堆砌到规范化调用

发布时间:2026/10/13 11:09:38
Agent技能体系实战:从工具堆砌到规范化调用 最近几个月一直在调各种Agent应用说实话模型本身带来的惊喜已经越来越少真正让我头疼的是“技能”这件事。你可能也遇到过这种情况一开始只是给Agent塞了三五个函数它表现得还不错等工具数量上了两位数Prompt越来越长模型开始频繁调用错函数、填错参数甚至干脆不理工具自己瞎编。我后来把这一整套东西抽出来重新设计名字就叫 agent-skills。你可以把它理解成一套“给Agent用的技能管理体系”核心解决三个问题技能怎么定义、怎么注册、怎么被可靠地调用。这篇文章我就把这个项目的完整思路、落地细节和踩过的坑展开聊聊适合正在做Agent应用开发、或者想把工具调用做规范化的工程师参考。1. 为什么Agent需要一套独立的“技能体系”1.1 从“堆提示词”到“技能封装”的三个阶段我最早做Agent的时候采用的是最粗暴的方式把所有工具函数一股脑写进系统提示词里后面附上“如果你需要使用工具请按以下格式输出”。效果怎么样呢初期能用后期一塌糊涂。你可以把这种方式想象成让一个新员工上岗不吃培训手册直接塞给他一摞单据让他自己悟。单据少的时候没问题单据一多他就开始分不清哪张是报销单、哪张是申请单填错栏位是必然的。模型也是这样当函数列表超过一定规模它对函数意图的区分能力会明显下降最典型的表现就是——明明该调用A函数它调了B而且参数还是从上下文里猜的。后来我进入第二个阶段用现成的Function Calling能力把工具以JSON Schema的格式传给模型。这个阶段解决了“格式混乱”的问题模型知道该输出结构化的调用请求了但新的问题又来了——我依然要把所有工具都塞到一次请求里不管这个技能在本轮对话中是否用得上。结果是Prompt越来越长响应越来越慢模型在无关工具之间反复犹豫。走到这一步我才意识到真正需要的是第三个阶段把每个工具从“函数的零散集合”升级为“独立的技能包”每个技能包自带说明、参数协议、执行逻辑和测试用例。Agent侧只需要维护一个技能注册表按需加载动态调用。这就是 agent-skills 这个项目最初的动机。1.2 技能体系的三根支柱注册表、调用协议、生命周期一套完整的技能体系我拆成了三块缺一块后面都会出问题。第一根支柱是注册表。它解决的是“Agent到底有哪些能力可用”的问题。所有技能在上线之前都要登记登记的内容包括技能名、用途描述、入参出参定义、版本号、负责人信息。这就像酒店的前台住客不会直接冲进每个房间找人而是先通过前台的房态表知道哪里有空房、能提供什么服务。没有注册表Agent就是一个黑箱子连开发者自己都不知道它现在能干什么。第二根支柱是调用协议。模型输出一段结构化的调用意图运行时把它翻译成真实的函数调用再把结果按统一格式回传给模型。协议要足够严格否则就会出现参数类型错乱、返回结构不统一的问题。在我的设计里协议统一走JSON Schema入口出口都是JSON不许有“特殊通道”。第三根支柱是生命周期。一个技能不是写完就结束的它会经历开发、测试、上线、迭代、下线这几个阶段。如果没有生命周期管理最典型的问题是你改了技能A的参数结构但忘了告诉模型侧结果模型用旧参数去调用全链路报错。所以每一次变更都要有版本记录最好还能支持灰度。1.3 什么算技能什么不算这里我想多说一句界定问题。很多人会把“技能”等同于“函数”但它们其实是两个层次的抽象。函数是最小的执行单元而技能是面向Agent的完整能力打包。举个例子“获取天气”可以是一个函数但“根据用户所在城市获取未来三天的天气并给出穿衣建议”才是一个技能。技能的粒度要控制在模型看到描述之后能在一次调用中完成目标不需要多次来回试探。如果你把一个技能设计成“先查城市编码再查天气再算温差”那就太细了模型会晕调用链也会变得脆弱。反过来如果技能描述里塞了一堆不相干的功能比如“这个技能既能查天气又能订机票还能算汇率”那也不行。模型分不清边界迟早会拿错参数。我自己的判断标准就一条一个技能应该能用一句话说清楚它帮Agent完成了什么。说不清楚说明粒度不对要么继续拆要么合并掉。2. 技能设计实战目录、参数和描述这三个地方别偷懒2.1 标准目录结构让每个技能开箱即用我先给出我最终采用的目录结构你可以直接抄skills/ image-resizer/ SKILL.md schema.json handler.py tests/ test_handler.py fixtures/ sample_input.json每个技能包固定四类文件描述文件、参数协议、执行代码、测试文件。看起来简单但每一步都有讲究。SKILL.md 是用自然语言写给模型“看”的说明文档。它描述的是这个技能在什么场景下应该被调用需要注意什么边界。很多人不重视这个文件觉得模型反正读JSON Schema就够了实际上恰恰相反模型理解技能的语义主要靠这段描述。写得好的SKILL.md能让模型在相似技能之间做出正确选择写不好就等着模型乱调用。schema.json 是参数协议我用JSON Schema严格定义每个字段的类型、必填性、取值范围和默认值。handler.py 是真正的执行逻辑它接收经过校验的参数执行外部操作然后返回统一结构的JSON。tests 目录放单元测试和模拟输入每次改动都要能跑通。有一点我特别想强调handler.py 内部不要写“聪明”逻辑只做参数到结果的映射。所有条件判断、状态管理都放到Agent层去处理。为什么因为技能是要被模型编排的你没法预料它会以什么顺序、什么参数来调用你保持技能本身的纯粹后面才不容易出Bug。2.2 参数定义最容易翻车的地方整个技能体系里参数定义是翻车率最高的地方没有之一。我调过的Agent应用里至少有三分之一的问题出在参数上模型传了不存在的字段、类型对不上、枚举值超出范围、必填项被忽略。JSON Schema能解决大部分问题。下面是我常用的一个示例{ name: resize_image, description: 调整图片尺寸并返回处理后的图片URL, parameters: { type: object, properties: { image_url: { type: string, format: uri }, width: { type: integer, minimum: 16, maximum: 4096 }, height: { type: integer, minimum: 16, maximum: 4096 }, mode: { type: string, enum: [fit, fill, stretch], default: fit } }, required: [image_url, width, height] } }这里有三个细节值得展开说说。第一枚举字段必须用 enum 把选项限定死。比如美颜等级、排序方向这类字段如果不限定枚举模型会凭感觉给出一个听起来合理的值比如“big”或者“美丽模式”然后你就得在handler里做兼容兼容来兼容去代码就烂了。用enum之后模型只能在给定选项里选错误率直接归零。第二数值字段要给范围和语义单位。宽度、高度这类参数如果只写integer模型可能传负数或者一个离谱的大数。我在描述里还会补一句单位说明比如“width表示输出图片的像素宽度超过4096会自动截断”这样模型在推理时有个度不会放飞自我。第三required字段要克制。只把真正执行功能必需的东西设为必填可选项不要放进required。我见过有人把十个字段全设为必填结果模型为了凑齐参数开始编造内容那比缺参数更可怕。参数缺了可以补对话编造了没法补救。2.3 技能描述怎么写模型才不瞎调用参数定义之外技能的名称和描述是另一个关键点。我把调模型比作和实习生合作你交给他的每个工具都要有清晰的标签和使用说明他才知道什么时候该用哪个。首先是命名。技能名要符合模型对语义的直觉不要搞缩写不要用“Util_01”这种编号。我一开始为了省事把一个查库存的技能命名成inv_q结果模型在需要查库存的时候完全没把它和“库存”这个词关联起来。改成query_inventory之后调用准确率肉眼可见地上来了。其次是描述。描述里至少包含三个维度触发场景、功能范围、典型示例。我常用的模板是当用户需要调整图片尺寸时使用此技能。支持按宽高等比缩放、裁剪填充和拉伸三种模式。示例用户说“把这张图缩小到800像素宽”调用resize_imageimage_url为原图地址width为800height按原图比例自动调整mode传fit。这个描述的作用是给模型建立“什么情况下该用我”的决策边界。遇到相似技能比如图片压缩、图片裁剪、图片加水印描述写得越清楚模型选错的概率越低。我在实测中发现加了典型示例之后模型调用准确率能提升至少15个百分点这个数字我测过多次稳定有效。3. 把技能挂到Agent上注册、调用、回传的三条链路3.1 注册与发现启动扫描还是运行时注册技能设计好之后接下来就是怎么让Agent知道它们的存在。这里有两套方案各有取舍。方案A是启动时扫描目录。Agent启动时遍历skills目录读取每个子目录下的schema.json构建技能注册表生成一个全部技能的索引。优点是实现简单、逻辑清晰增删技能只要动文件系统就行。缺点是每次新增技能都要重启服务不适合需要动态扩能力的场景。方案B是运行时动态注册。通过注册中心或数据库登记技能Agent在每次对话前按需拉取相关技能的清单。优点是灵活技能可以热更新缺点是复杂度上来了还要考虑注册中心的可用性和一致性。我在项目里先实现了方案A跑通之后再扩展方案B。有一件事要特别注意注册表里要维护技能名和handler的映射关系技能名重复是绝对不允许的。我遇到过两个技能都叫search结果Agent每次调用都路由到先注册的那个另一个完全被无视。后来我在注册阶段加了一步冲突检测发现重名直接报错并终止启动宁可启动失败也不能带病运行。另外注册表里建议记录技能版本号。我在最开始没记录版本上线一个新版本时模型缓存里还留着旧参数的描述导致调用参数和实际handler不一致。加了版本号之后至少可以通过日志快速定位是哪一版本的调用出了错。3.2 调用分发从模型输出到真实执行的完整通道技能注册完成之后核心环节就是调用分发。整个链路我理成了一条单向通道模型输出结构化调用意图解析器将其解析为调用请求路由器找到对应handler执行器带着校验过的参数去执行最后把结果按统一格式压缩后回传模型。用代码示意如下# 调用分发核心逻辑简化版 def dispatch(model_output, registry): # 1. 解析模型输出中的调用意图 call_intent parse_function_call(model_output) if not call_intent: return None # 2. 根据技能名查找handler skill registry.get(call_intent.name) if skill is None: return build_error_response(unknown_skill) # 3. 参数校验 validated_params validate_params( call_intent.arguments, skill.schema ) if validated_params.errors: return build_error_response(invalid_params, validated_params.errors) # 4. 执行技能带超时控制 result execute_with_timeout( skill.handler, validated_params.data, timeoutskill.config.timeout ) # 5. 回传统一格式 return build_success_response(result)这里每一步都有坑。解析模型输出时我最初直接用正则去抠函数名后来发现模型有时候会输出Markdown包裹的JSON有时候会带多余逗号纯正则根本扛不住。换成一个容错性强的JSON解析器之后解析失败率从5%降到了0.5%左右。路由查找方面要注意大小写归一化。模型的输出稳定性其实比你想象的低同一个技能有时候输出snake_case有时候又变成CamelCase。我在注册和路由两层都强制小写统一才能避免这种低级问题。执行环节最需要注意的是超时。技能里如果涉及外部API调用你要预设超时时间否则一个卡住的HTTP请求能把整个Agent的响应时间拖到用户无法接受。超时之后怎么办我采用的策略是超时错误包装成一种“技能执行超时”的错误结果回传给模型让模型判断是否需要重试或者换一种方式。你可能会想为什么不直接自动重试因为有些操作不是幂等的自动重试可能造成重复扣费或者重复写入。3.3 结果回传与错误处理别让一次失败毁掉一次对话调用成功之后结果的回传质量直接决定了模型的下一步推理质量。如果返回结果是超大篇幅的文本模型的注意力会被无关细节淹没后续思考质量下降。所以我在回传前增加了一个“结果压缩”步骤把非结构化的冗长文本截断、把无关字段去掉只保留模型推理需要的最小信息集。我做了一个简单的压缩规则结果超过预设阈值通常是2000字符时自动截断并在末尾追加“结果过长已截断如需完整信息请说明”。这样做的好处是模型不会被淹没同时它也知道信息不完整可以主动追问。错误处理这块我设计了一套错误码体系。错误分成三类RetryableError网络抖动、服务临时不可用模型可以重试InvalidInputError参数错误、内容不存在重试也没用模型需要修改输入或调整方案UnknownError未知异常统一按“暂时无法完成”处理错误码设计的意义在于给模型传递可行动的信号。如果什么错误都笼统地返回“调用失败”模型不知道是该换参数、换技能还是直接放弃。有了明确的错误分类模型的应对策略会清晰很多整个对话的容错能力也会明显提升。我还额外给每个技能配了一个“失败兜底文案”。技能可能因为各种原因执行不了但Agent不能对着用户干瞪眼。兜底文案会让Agent说一句“当前暂时无法完成你可以稍后再试”而不是直接把一堆堆栈信息抛给用户那体验太糟糕了。4. 实测中的问题排查与避坑记录4.1 模型填错参数堪称最高频的事故参数填错是我在实际运行中遇到的最高频事故没有之一。不是说有了JSON Schema就能完全避免模型在不理解业务语义的时候依然会填出结构合法但语义错误的值。举个例子你的技能要求传入时间范围模型给你传了一个“last year”幸好JSON Schema的type约束为string它不会报错但你handler里一解析就崩了。针对这个问题我的经验是在参数协议之外增加一层“语义校验”。时间类字段必须符合ISO 8601格式URL字段必须能解析出有效域名和协议枚举字段必须精确匹配。任何一层校验失败都会被捕获为InvalidInputError而不会让错误语法渗透进handler内部。这层语义校验看着麻烦但它是把模型的不确定性挡在系统外部的一道关键防线。我还对参数来源加了记录。每次调用的参数都会以日志形式存储包括模型最初输出的原始JSON、校验后的最终参数、执行结果。这个日志帮我发现了不少规律比如某个模型在日期推算上特别容易错那么针对这个模型我会在技能描述里给出更明确的时间格式示例相当于做了一层针对性的防御。4.2 并发与状态多个技能同时调用怎么不乱Agent有时候会在一次回复里同时调用多个技能。比如用户问“对比这两款手机的相机参数和价格”Agent可能同时调用参数查询、价格查询、对比分析三个技能。并发调用的顺序和结果归属如果不处理好返回给模型的内容就是一团乱麻。我采取的方案是给每个调用请求生成一个唯一的 call_id。模型在发起多个调用时每个调用都带着自己的call_id执行完成后按call_id把结果装配回对话上下文。这有点像快递单号包裹再多单号一对就能定位。并发还需要考虑共享状态的问题。技能本身要尽量设计成无状态的所有输入输出都通过参数传递。如果一个技能内部维护了全局变量两个调用同时进来互相污染几乎是必然的。为了让技能可并发我会强制要求handler内部不写全局可变状态所有临时状态用局部变量需要跨调用保存的数据统一交给外部存储。如果你的技能涉及外部存储的写入还要注意冲突处理。比如两个调用同时写同一个文件用“读改写”的方式很容易丢数据。我在底层存储层加了按key粒度的锁虽然牺牲了一点性能但换来了确定性做Agent场景值得。4.3 技能更新之后老对话怎么办版本管理是个容易被忽略但实际很致命的问题。Agent的对话是有状态的一段长对话可能已经持续了十几轮。如果在这期间技能A从V1升级到了V2参数结构变了那么模型在后续轮次里可能会用新技能定义发起调用但对话历史里还残留着旧调用的记录。这会导致一种很尴尬的情况前后调用使用的协议不一致上下文里的中间结果没法对齐。我的处理策略是每次技能升级版本号递增并在注册表里保留最近两个可兼容版本的协议。新发起的调用统一走新版本协议但历史会话中已经产生的技能调用结果继续以“快照”形式存在上下文里不被新版本影响。这样可以避免升级导致的长对话中断。快照机制还有个好处当技能执行涉及外部状态变化时比如调用了支付、修改了数据库记录模型后续推理需要依赖那个操作的结果。如果因为版本升级导致快照丢失模型就会基于错误前提继续推理。所以我会在回传结果时同时写入一个快照字段这个字段在后续轮次中对模型可见保证了长对话的连贯性。4.4 快速排查清单从现象到根因最后整理一份排查清单遇到问题可以按顺序过一遍现象可能原因排查动作模型从不调用某个技能描述不清晰或触发场景没写明白检查SKILL.md是否包含触发场景和典型示例重写描述模型调用了错误技能技能名称歧义、相似技能太多在描述里增加区分性说明必要时合并技能参数频繁报错语义校验缺失或参数描述不足补充JSON Schema约束增加语义校验逻辑调用超时外部API响应慢或超时设置过短检查外部服务耗时调整超时参数并发结果错乱缺少call_id或请求隔离给每次调用分配唯一ID校验回传归属升级后老对话出错版本协议不一致保留兼容协议对历史结果做快照技能执行成功但模型胡编结果未回传或回传被截断确认回传链路完整检查结果压缩策略这套排查清单看起来简单但它是我把几十个线上异常案例归类汇总之后的结果。你先定位现象再沿着对应行去找可能原因基本能覆盖绝大多数问题。说到这我有点感慨。当初做 agent-skills我以为自己是在写一套工具框架后来才发现它更像是在给Agent设计一套“职业培训体系”——每个技能都是岗位说明书加操作手册注册表是人事档案调用协议是跨部门协作的标准接口错误码是应急预案。把这些东西做扎实Agent才真正算是一个可靠的执行者而不是一本什么都会一点、但什么都不精的百科全书。如果你也在做Agent相关的事情我建议你哪怕不搞这么重的框架也从目录结构、参数校验、调用日志这三件事做起。先把最容易翻车的地方守住再逐步补上生命周期和协议规范。技能体系的搭建不是一天能完成的但每多做一层后面省下的排查时间都是成倍的。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询