深入agent-skills:构建稳定可用的Agent技能体系
1. 先搞明白agent-skills 到底在解决什么问题这两年只要你在碰大模型应用应该绕不开一个词叫 Agent。很多人把 Agent 理解成“能自动调用工具的大模型”这个说法没错但真做起来你会发现光有模型和工具远远不够。我最早做 Agent 项目的时候就是简单地把十几个函数塞给模型去挑结果效果非常不稳定同一个问题今天答对明天答错换一种问法又答错。后来我慢慢意识到问题不在于模型笨而在于我没有给 Agent 建立一套成体系的“技能框架”——也就是 agent-skills 这个概念真正要解决的东西。agent-skills 简单来说就是给 Agent 定义、组织、调用、迭代一套可复用的能力单元。它不是单个提示词也不是单个工具函数而是把 Agent 能做的事情拆成模块化的“技能”让模型知道在什么场景下、用什么参数、按什么顺序去调用哪个技能。这套体系一旦搭好Agent 才能从“能跑”变成“稳定能跑”从“会回答”变成“会干活”。这篇文章适合谁看如果你是正在做 Agent 应用开发、想给 Agent 增加复杂工具调用能力、或者已经被“模型乱调用工具”折磨过的人那这内容应该能帮你省不少时间。我会讲清楚技能体系怎么设计、技能描述怎么写、技能库怎么落地以及我踩过的那些坑。1.1 我接触 Agent 之后踩过的第一个坑先讲个真实案例。我做过一个客户服务 Agent最开始只挂了三个工具查询订单、修改地址、申请退款。模型经常做的事情是用户说“我要退货”它就去调申请退款但实际正确的链路应该是先查询订单确认是否在退款周期内再判断走退货流程还是直接退款。为什么模型会犯错因为我只给它提供了一个个孤立的函数每个函数都有自己的 description但没有人告诉它这些函数之间的业务关系和调用顺序。我还犯过另一个错给模型的工具描述写得太简短。比如“查询订单”就一句话参数也写得模糊模型拿到用户的一句口语化表达根本不知道应该传什么参数进去经常传错字段或者干脆编造参数。后来我才意识到要让 Agent 真正稳定干活核心不是模型多聪明而是你给它的“技能说明书”有多清晰。1.2 技能四层框架从命令到技能的跃迁我后来把 Agent 的能力体系重新梳理了一遍发现可以分成四层。第一层叫“动作”是最小的原子操作比如调用一个 API、执行一段代码、查询一次数据库。这一层不需要模型做什么决策给它参数它就执行。第二层叫“任务”是把多个动作组合成一件完整的事比如“处理退款申请”就是一个任务它可能包含查询订单、校验资格、执行退款、发送通知这四个动作。第三层就是“技能”是相对完整的、可复用的能力封装。一个技能可以包含一个或多个任务同时包含触发条件、适用场景、输入输出约束、失败处理策略。例如“售后处理技能”下面可以有“退款处理”“换货处理”“物流异常处理”多个任务。第四层叫“策略”是 Agent 在面对开放性问题时选择技能的规则和流程比如先理解用户意图再判断该调哪个技能如果技能执行失败应该如何降级或转人工。这个框架的好处是每一层的职责清晰模型不需要在底层动作上做太多推理只需要在策略层做决策。你只要把 80% 的确定性逻辑下沉到技能封装里模型要做的事就变得非常简单准确率自然就上来了。1.3 一套通用技能分层体系该怎么设计前面说的是抽象框架落地的时候你还需要一套具体的设计规则。我自己的习惯是把技能按“通用”和“专用”两个维度去分类。通用技能是所有 Agent 都可能用到的比如信息检索、文本摘要、格式转换、数据提取。这类技能要做得足够通用不绑定任何业务逻辑接口设计也要追求稳定因为很多地方会复用。专用技能是绑定具体业务场景的比如“订单查询”“库存锁定”“优惠券核销”。这类技能要贴近业务团队的口径字段命名、状态枚举、权限控制都要和现有系统对齐。另外我还会给每个技能标记几个关键属性技能名称、技能描述、触发条件、输入参数、输出格式、依赖关系、超时时间、失败处理方式、权限级别。这些属性不是说一次性全部写完就完了而是需要随着 Agent 在真实场景里的表现不断迭代。2. 技能怎么描述才能让模型不“误解”很多人以为技能定义的重点是代码其实最重要的是描述文本。模型不像人它对字段含义、边界条件、隐含规则的理解完全依赖于你写的 description。写得太短模型就靠猜写得太长模型又会抓到无关信息。我自己的经验是一份好的技能描述要把握几个关键点。2.1 技能描述里必须写清楚的几个字段我给技能的描述文本设计了一套固定模板包含五块内容第一是“技能用途”用一两句话说明这个技能能做什么不能做什么。不能做什么非常关键因为模型经常会把不相干的问题硬套到某个技能上。第二是“触发场景”列出适合调用这个技能的情况最好给几个典型例子。比如“库存查询”技能的触发场景可以写用户询问商品是否有货、多个 SKU 的库存量、某个仓库的现货情况。第三是“输入参数说明”对每个参数都要解释含义、类型、取值范围、是否必填、默认值。别偷懒只写参数名模型需要知道这个参数在实际语境中怎么表达。第四是“输出说明”说清楚返回结构是什么、关键字段代表什么、有哪些可能的异常返回。第五是“使用约束”包括频率限制、前置条件、权限要求、与其他技能的顺序关系。比如“发起退款”就要注明必须先完成订单校验。这套模板我后来做成了一页纸团队里所有人都按这个标准写技能描述整体效果提升非常明显。2.2 描述写作的技巧少说废话给足约束写技能描述最忌讳的就是“什么都想说”结果模型不知道重点在哪。我举一个真实对比例子。之前团队成员给“查询天气”写的是“根据用户输入查询天气信息支持城市、日期查询返回天气数据。”这个描述太弱了模型无法知道该提取什么实体也不知道日期格式怎么处理。后来改成 “查询实时天气和历史天气。仅当用户明确提到某个具体城市或地区时调用否则不要主动调用。城市参数支持省市区格式如‘北京市海淀区’日期参数支持‘今天’‘明天’或 YYYY-MM-DD 格式缺省时默认今天。返回内容包括温度、天气现象、风力、湿度如果查询城市不在支持列表中返回错误码 C4001。”这么一写模型的判断准确率明显提升。核心原则就两条一是给出明确的触发边界二是把参数格式和约束写死。2.3 结构化技能的参数定义与校验描述写完之后参数还不能光停留在文本层面需要用结构化方式定义。近几年大模型工具调用已经标准化为 function calling你可以在 OpenAI、Claude 等平台的工具定义里写 JSON Schema也可以在本地用 Pydantic 之类的库做参数校验。我一般会为每个技能定义一个输入模型在运行时先校验参数格式再做真正的业务逻辑。这样做的好处是即使模型某个字段传错了系统也能在入口处拦下来而不是把错误参数打到下游接口造成脏数据。这里给一个例子技能名是“create_order”输入结构大概这样{ name: create_order, description: 创建订单。仅当用户确认购买商品并提供了有效收货地址时调用。, parameters: { type: object, properties: { user_id: {type: string, description: 用户唯一标识}, item_id: {type: string, description: 商品 ID}, quantity: {type: integer, minimum: 1, description: 购买数量默认为 1}, address: {type: string, description: 收货地址省市区详细地址} }, required: [user_id, item_id, quantity, address] } }模型端看到的 description 和 JSON Schema 要尽可能保持一致否则模型会困惑到底信哪一个。我一般都从 JSON Schema 自动生成描述文本而不是两边分开写。3. 技能库的工程化落地注册、加载、调度技能体系设计好了接下来就是工程问题。你在实际项目里可能会有几十甚至上百个技能怎么管理这些技能、怎么让 Agent 快速找到对的技能、怎么处理技能之间的依赖这些都需要一套机制来承载。3.1 技能注册中心与服务化我建议把技能看成一个独立的服务进行管理。每个技能在实现之后注册到一个中心化的“技能注册中心”注册信息包括技能名称、版本号、服务地址、输入输出协议、健康检查方式。注册中心不只是存一份清单还要能够做技能的上下线和灰度。比如你改了一个技能的实现逻辑不应该立刻全量替换而应该先注册一个新版本在测试环境验证后再切流量。这个思路跟微服务治理基本一样。我实际操作中用的方案比较轻量就是一张数据库表加一个简单的 API 网关。数据库表存技能的基本信息和当前状态API 网关负责把 Agent 的调用请求路由到对应的技能服务上。如果你团队规模不大完全没必要上一套复杂的服务网格。3.2 路由与调度机制技能多了之后模型在某个场景下可能会同时匹配到多个技能。比如用户说“我要改地址”可能同时触发“查询订单”“修改地址”“验证身份”三个技能。这时候就需要一个调度层来管理技能的调用顺序。调度策略有两种典型方式。一种是“显式编排”就是你在代码里硬编码工作流先调用身份验证再查询订单最后执行修改地址。这种方式稳定但灵活度低适合流程完全确定的场景。另一种是“模型自主规划”你把所有技能描述都给模型让模型自己选择调用顺序。这种方式灵活但需要技能描述写得很清晰同时也需要给模型一个“执行计划”的输出格式让它先输出计划再逐布执行。我目前做项目通常采用混合模式核心链路用显式编排分支场景用模型自主规划。这样既能保证关键业务的稳定又能让 Agent 应对一些意外情况。3.3 冷热技能与动态加载技能库大了以后不可能每次都把几百个技能的描述全部塞给模型。一方面 token 成本太高另一方面描述太多会把模型搞晕反而降低选择准确率。解决办法是给技能做“冷热分层”。热技能是当前对话最可能用到的技能数量控制在 10 到 20 个冷技能是那些偶尔才用的技能可以从索引中检索出来按需加载。我是怎么实现热技能选择的一是基于对话意图做一个预分类二是在每轮对话结束之后做一次技能命中率统计把高频技能放在候选列表最前面。还有一个更简单的方法很多 Agent 框架已经支持“只把前 N 个匹配技能描述注入到上下文”比如语义检索 top-k 技能。这种方式在实践里效果很好可以大幅减少 token 消耗。4. 从零实现一个技能模块代码级示例理论说了一大堆下面直接进入代码环节。我会用一个简单的“用户意图识别 订单查询”的例子展示一个技能模块的完整实现过程。这个例子虽然是简化版但结构上可以直接迁移到真实项目。4.1 技能实现的基本骨架我习惯了用 Python 写技能模块核心分三层参数校验层、业务逻辑层、输出格式化层。参数校验层用 Pydantic 定义输入和输出模型。业务逻辑层负责真正的数据查询和加工。输出格式化层统一输出结构方便 Agent 判断执行结果。下面实现一个 query_order 技能。from pydantic import BaseModel, Field from typing import Optional class QueryOrderInput(BaseModel): order_id: str Field(description订单编号必填) user_id: str Field(description用户 ID可选用于校验权限) include_items: bool Field( defaultTrue, description是否返回订单中包含的商品明细 ) class QueryOrderOutput(BaseModel): order_id: str status: str create_time: str total_amount: float items: Optional[list[dict]] def query_order(params: dict) - dict: # 1. 参数校验 inp QueryOrderInput(**params) # 2. 业务逻辑这里用 mock 数据代替真实数据库查询 data { order_id: inp.order_id, status: paid, create_time: 2025-01-12 10:30:00, total_amount: 299.00, items: [{name: 商品A, price: 199.00, count: 1}] } # 3. 输出格式化 out QueryOrderOutput(**data) return out.model_dump()这里有一个细节业务逻辑层尽量不要把数据库操作直接写死否则后面很难做缓存、限流和故障隔离。我一般会在业务层和数据库之间再包一层 repository 层。4.2 一次完整调用链路一个 Agent 在真实运行时调用技能的过程大概是这样的模型根据用户输入从技能列表中选择 query_order并生成参数Agent 框架把参数传给技能执行引擎引擎做参数校验失败则直接返回错误引擎调用业务逻辑层拿到数据输出结构化结果回到模型模型再组织语言交给用户。为了让你看得更清楚我写一段缩略的 Agent 调度代码skills { query_order: { description: 查询订单状态和详情, entry: query_order, input_model: QueryOrderInput, } } def run_agent(user_message: str): # 这里简化处理实际会调用大模型做意图识别和参数抽取 model_output llm_extract_skill_and_params(user_message, skills) skill_name model_output[skill] params model_output[params] skill skills.get(skill_name) if not skill: return 抱歉我暂时无法处理这个问题。 # 参数校验 try: parsed_params skill[input_model].model_validate(params).model_dump() except Exception as e: return f参数校验失败{e} # 执行技能 result skill[entry](parsed_params) return result这段代码里llm_extract_skill_and_params 是真正调用大模型的地方。我一般会让模型先输出技能名称和参数 JSON再进入执行阶段。这种模式的好处是模型只负责“决策”不负责“干活”干活全靠本地确定性代码。4.3 可观测性日志与追踪做 Agent 项目最容易忽略的就是可观测性。因为调用链路很长用户 → 模型 → 技能 → 下游 API → 数据库任何一环出问题都很难排查。我在项目里会为每一次技能调用生成一个 request_id然后把模型输入、模型输出、参数校验结果、业务执行时间、返回数据都打到结构化日志里。后面排查问题时直接按 request_id 拉全链路日志能省下很多时间。另外建议给每个技能都加一层简单的埋点命中次数、平均耗时、错误率、参数非法率。这些指标能直接反映技能描述是否清晰。如果某个技能的参数非法率特别高说明描述写得不够清楚模型理解不到就乱传参这时候需要优化的是描述而不是继续去调模型。5. 技能评测与持续迭代技能体系不是一次性搭完就完事的。随着业务变化和模型升级技能描述可能失效、参数结构可能变化、某些技能的使用频率可能下降。所以你需要一套评测和迭代机制让技能库保持在稳定健康的状态。5.1 技能评测维度我对技能的评测主要看四个维度。第一是准确率也就是在测试用例里模型能不能选对技能、传对参数。如果准确率低于 90%说明技能描述或数量配置还有问题。第二是覆盖率即面对同一类用户问题技能库能不能覆盖 95% 以上的场景。如果语义空间留了很大的空隙模型就会频繁出现“默认回答”或者幻觉这种情况下你需要新增技能而不是继续堆提示词。第三是稳定性也就是同一输入在不同时间、不同模型版本下是不是返回一致的技能调用结果。模型虽然有一定的随机性但技能选择和参数抽取应该尽量做到可复现。第四是效率包括技能调用时延和 token 消耗。如果你发现某个技能的调用时延特别长或者为了让它理解规则花了很多 token说明技能设计得不够简洁。5.2 评测集与回归做评测之前你得先有一套评测集。我一直用“人工整理 线上抽取”的方式做测试集一开始找业务同学帮忙写一批常见问题之后每周从线上日志中抽一批真实用户问题补充进去。测试集按场景分组每组至少 10 条左右这样才能看出某个技能在特定场景的准确率波动。每次修改技能之后都要把这一套测试集重新跑一遍这叫回归测试。没有测试集做回归改技能描述就会变成“拆东墙补西墙”。我有一次改了一个通用技能的描述自测没问题结果上线后发现另一个场景的准确率掉了 8 个百分点就是因为没有做完整回归测试。现在我的团队已经把这套回归流程固化进 CI/CD 里了。5.3 版本管理与发布技能的版本管理大家往往容易忽视。你千万不要直接在线上改技能描述每次修改都要生成一个新版本保留发布记录。这样做有两个好处一是可以随时回滚二是可以对比不同版本在同一评测集上的表现用数据决定留哪版。我一般把技能描述文件放到 Git 仓库里管理每次改动都走 code review 分支合并流程合并完成之后再通过 CI 触发评测评测分数达标才允许发布到线上。这样虽然流程重了一点但对生产环境的稳定性帮助非常大。6. 常见问题与排查技巧实录最后分享一些实操过程中高频踩坑的问题。这些问题我几乎在每次 Agent 项目沟通里都会遇到整理成一个排查表希望对你有用。6.1 问题排查表症状可能原因排查方向模型死活不调用某个技能技能描述写得不够清楚或者触发场景和用户表达差距太大重写描述补充典型触发例子模型调用技能但参数乱填参数说明不清晰缺少必填项说明和取值约束检查 JSON Schema补充字段描述技能调用成功但用户不满意技能的处理逻辑和用户预期不一致检查业务逻辑层的完整度同一问题两次结果不同模型随机采样或技能列表顺序调整拉日志看模型输出必要时固定 temperature技能数量太多导致选择困难高、低频技能混杂检索策略没做好实现冷热分层用语义检索缩小候选集技能描述改完效果更差缺少回归测试可能改动了别的场景依赖建评测集跑回归回溯版本对比6.2 关于 Prompt 与幻觉的几个细节有一类问题不是技能本身的问题而是模型幻觉。比如模型在描述技能时描述文本里出现了“根据 xx 格式”但实际实现里根本没有这个参数模型就会把话圆出来硬传一个空值或者根本不存在的字段。这里我的经验是技能描述里的每个字段代码实现里一定要有两边要严格同步。另一个很常见的坑是技能描述里用了太多否定句比如“如果没有返回数据不要尝试补全”。模型对否定句的理解不稳定经常会出现“听到不要补全结果自己加戏补全”的情况。我试过更稳妥的方式是给出明确的替代动作比如“如果返回空列表直接输出未找到相关订单”模型照做的概率高很多。6.3 避坑清单我再整理几条我们团队踩过坑之后的内部清单每一条都有实战依据。不要把所有工具一股脑塞给模型。上下文里的技能描述越多模型选错的概率越大要建立检索和筛选机制。不要在技能描述里写业务故事描述要简洁多用示例少用抽象概念。不要忽略参数校验。模型输出永远可能会有格式错误或非法值入口处的强校验是系统稳定性的第一道防线。不要高估模型的规划能力。复杂任务即使模型能规划也要在代码里兜底做超时控制和降级处理。不要跳过日志埋点。没有日志问题排查基本靠猜效率极低。不要忘了对技能做周期性的“肥瘦”检查。三个月没被调用的技能也该审查一下要么删除要么重写。7. 写在最后一点个人经验做了几个完整的 Agent 项目之后我越来越觉得 agent-skills 不是一种酷炫的技术而是一种偏工程、偏体系化的能力建设。你给 Agent 定义多少个技能、怎么描述技能、怎么管理技能的迭代直接决定了这个 Agent 是“玩具”还是“生产力工具”。我个人最喜欢的一个细节是技能描述不是写给用户看的而是写给模型看的。你得像带新人一样去“带模型”把边界条件、注意事项、异常处理全写在说明书里它才能真正帮你干活。最后再分享一个小技巧如果你想快速验证自己的技能描述写得好不好可以尝试在评测集里故意模拟一些极端表达比如用户的句子含错别字、有歧义、信息不全时看看模型会不会误报技能或硬编参数。如果它能扛住这些情况说明技能体系已经足够稳了。agent-skills 这条路没有终点但每完善一层你的 Agent 就会离“真正可用”更近一步。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →