
做智能体开发这两年我最大的感触是提示词写得再花哨不如把能力拆得清楚。很多团队做 Agent一开始都靠堆提示词结果任务一复杂模型就犯迷糊——要么漏步骤要么调错工具。后来我们换了个思路不把 Agent 当成一个什么都懂的对话模型而是当成一个会调用技能的执行者。这个思路的核心就是 agent-skills把智能体的能力拆成一个个可复用、可组合、可验证的技能单元。这篇文章记录的是我在这套思路下的完整实践从技能怎么定义、怎么实现到怎么测试、怎么排查适合正在做 AI 应用的开发者也适合对智能体原理感兴趣、想从更高的视角理解 Agent 产品设计的读者。1. 先搞清楚agent-skills 到底在解决什么问题1.1 从提示词堆砌到技能化拆解我先说一个场景。假设你要做一个客服 Agent初期需求很简单回答常见问题。这时候一段精心编写的系统提示词确实够用。但产品跑起来之后需求开始膨胀要查订单状态、要处理退款、要识别用户情绪、要转接人工、要每天早上生成前一天的客服数据报表……如果你把这些需求全部写进一段提示词很快会超过上下文窗口而且模型会被互相冲突的指令搞糊涂。我见过最夸张的一次是团队把 40 多条业务规则塞进一个 system prompt 里结果线上 Agent 开始每句话都先复述一遍规则再回答用户问题。这就是典型的提示词过载。agent-skills 的思路完全不同它不把所有能力塞进提示词而是把每一个能力边界明确、输入输出清晰的技能独立出来让模型在需要时主动选择、动态加载。系统提示词只做一件事——告诉模型你手上有一批技能遇到对应任务就去调用。这样提示词永远简洁能力却可以无限扩展。1.2 技能、工具、插件与工作流的关系很多人一开始会问技能Skill和工具Tool、插件Plugin有什么区别我在实践里是这样区分的工具是最底层的原子能力比如执行一次 HTTP 请求读取某个文件调用某个 API。它本身不理解业务只负责把一件事做对。技能是建立在工具之上的、面向任务的能力单元它包含了对输入的解释、步骤的编排和对输出的约束。比如查询订单状态这个技能底层调用了订单 API但它还负责从用户的自然语言中提取订单号、判断订单状态枚举值、把结果格式化成用户能看懂的回复。这些逻辑不会全部写进提示词而是固化在技能内部。插件则是一种打包和分发形式一组技能加配套的配置、依赖、文档打成一个包方便别人安装使用。工作流更偏重流程编排多个技能按固定顺序或条件分支串起来完成一个复杂的端到端任务。我建议你记住一句话工具管手技能管方法工作流管路径插件管分发。把这层关系理清了后面设计技能体系就不会乱。1.3 技能化带来的三个实际收益为什么要折腾技能化我总结下来有三个实打实的收益一是可测试。技能是独立单元可以单独喂数据跑测试断言输出是否符合预期。相比整段提示词的黑盒表现技能出了 Bug 能精准定位到是参数解析的问题、逻辑分支的问题还是结果格式化的问题。二是可编排。同一个技能可以被不同的工作流复用。比如从文本中提取结构化数据这个技能既可以用在客服工单分类上也可以用在简历解析上。技能库越积累新需求开发的成本越低。三是可降级。某个技能依赖的第三方服务挂了Agent 可以快速切换到备用技能或者明确告诉用户这个功能暂时不可用而不是整个 Agent 变成一坨不可控的随机输出。这些收益不是理论推演是我们线上项目实实在在拿到的结果。后面我会一步步展示怎么落地。2. 技能体系的整体设计思路2.1 三层技能模型基础层、领域层、编排层我在实际项目中把技能分成三个层级这个分层帮我们解决了很多混乱的问题。基础层是跨领域通用的能力比如搜索网页读取链接内容提取文本中的日期和金额把长文本拆成摘要。这些技能不依赖具体业务像积木的基本块任何上层技能都能调用。领域层是面向特定业务场景的能力比如电商场景下的查订单申请退款计算运费金融场景下的查询汇率校验账户信息。领域技能的输入输出通常用领域内的语言定义比如参数叫order_id、refund_reason而不是通用的query。编排层比较特殊它本身也像个技能但内部逻辑是调度其他技能。比如处理用户退款请求这个编排技能内部会依次调用确认订单状态→检查退款政策→计算退款金额→发起退款申请→生成答复。编排层把业务流程固化成代码逻辑而不是让模型每次都在提示词里临时推理流程。这三层怎么协作基础层被领域层依赖领域层被编排层组合编排层直接对用户请求负责。每一层都只在需要时暴露给模型避免模型在同一刻面对过多选择。这一点非常重要模型可选的技能越多选错的概率越高分层就是为了压缩模型的选择空间。2.2 技能描述语言让模型看得懂技能技能要让大模型正确调用首先得让模型准确理解这个技能是干什么的、什么时候该用、参数怎么传。我见过很多失败案例都是技能描述写得含糊模型把它和另一个技能搞混了。一个合格的技能描述应该包含四部分名称要短一眼能读懂比如search_web、get_order_status。不要用process_data_v2这种看不出用途的名字。描述要用触发场景 具体功能 使用边界的格式。举个例子search_web的描述我一般写成当用户需要获取实时信息、查询新闻、查找事实性答案时使用。支持按关键词搜索互联网。如果用户问的是企业内部数据不要使用此技能应使用search_internal_kb。参数用 JSON Schema 定义并且每个字段都要写清楚类型、含义和示例值。特别是示例值最容易漏却是最关键的——模型参考示例生成参数时准确率能提升一大截。返回值同样要写清楚结构。模型拿到技能输出后需要知道怎么解读。如果返回体里既有content又有source你要在描述里明说content为正文source为来源域名可用于标注引用。我建议团队里所有技能描述由专人审阅标准就是一个不熟悉该技能的同事只看描述能不能正确判断何时调用、参数怎么填。如果能模型大概率也能。2.3 技能注册与发现机制技能多了之后注册和发现就成了基础设施层面的问题。我在项目里用了一个轻量的注册中心每个技能上线时要登记四类信息基本信息名称、版本、负责人、描述文档给模型看的、执行入口给运行时调用的函数、依赖列表依赖哪些基础技能或外部服务。模型侧做技能发现时通常有两种策略。一种是把所有技能描述一次性拼进上下文让模型自己选另一种是先做一个粗粒度的技能路由——给每个技能打上业务域标签模型先判断当前请求属于哪个域再只加载那个域下的技能描述。两种策略我都有使用。技能总数少于 30 个时一次性加载没问题超过 30 个我强烈建议做路由否则模型的选择准确率会明显下降。我们线上技能库已经超过 80 个技能没有做路由前选错技能的概率大约是 7%加上路由后降到了 1.2% 左右。这个差异在很多业务里就是好体验和坏体验的区别。3. 核心技能的类型拆解与实现要点3.1 信息检索型技能这是最常用的技能类型几乎所有 Agent 都需要。它解决的痛点是模型的知识有截止日期也不了解企业内部的私有数据。信息检索技能就是把外部知识接入 Agent 的桥梁。我实现这类技能时核心注意三点查询改写。用户的自然语言提问不能直接拿去检索要先把问句转成检索词。比如用户问上个月华南区的销售额达标了吗直接拿整句去搜索效果很差改写成语料库常见的华南区 上个月 销售额 达标这种关键词组合召回质量会好很多。改写动作我通常让模型完成但会给出检索词短语的要求约束。多路召回。只用一个检索源漏检风险很高。我常用的组合是向量数据库做语义召回 搜索引擎做关键词召回 企业内部知识库做规则召回然后再对三路结果做重排。很多团队在这里只做向量召回结果长尾问题答得很差。结果摘要。检回来的原始文档往往很长不能一股脑丢给模型。我会先做相关性截断再做摘要压缩最后只把最相关的 3-5 条结果传给模型。这既能省 token又能减少模型被无关信息干扰的概率。3.2 内容生成型技能内容生成技能不是请写一篇文章这么简单。我在项目里沉淀了生成类技能的通用模式模板 约束 校验三段式。模板负责搭骨架。比如生成周报的技能模板里规定了标题、本周完成事项、下周计划、风险与求助四个板块每块的字数范围和建议写法都写在模板里。这样做的好处是输出稳定不会出现有些段落特别长、有些段落空着的情况。约束负责管风格和边界。常见约束包括禁止使用夸张形容词、数字必须与数据源一致、不得编造未经验证的信息。约束要写清楚而且要和校验逻辑配套。校验负责兜底。生成结果出来以后程序化检查关键字段是否齐全、数字是否与输入一致、格式是否符合预期。不通过就重新生成连续多次不通过就降级为简单模板输出。这套校验机制把线上生成内容的合格率从 78% 提到了 95% 以上。3.3 工具调用型技能工具调用是 Agent 和外部世界交互的命脉。但很多开发者在第一步就踩坑直接把第三方 API 包一层暴露给模型结果参数五花八门返回结果混乱不堪。我的建议是工具调用型技能一定要做输入映射 输出规整。输入映射指把模型传入的通用参数映射成第三方 API 要求的字段格式比如把date转成yyyy-MM-dd把user_id转成带前缀的字符串。输出规整指把第三方 API 的返回体规整成技能定义好的标准结构同时过滤掉无用的字段。这两个步骤看似是多余的转化开销却极其重要。它隔离了外部 API 的变化对上层技能的影响——明天第三方接口改字段名你只需要改这一个技能的内部实现其他技能和模型逻辑都不用动。项目上线半年我们经历过三次外部 API 升级都是改完内部映射就完事了模型侧零改动。3.4 记忆管理型技能记忆管理是很多 Agent 产品体验的分水岭。没有记忆管理同一个用户重复提问Agent 每次都当新问题处理有了记忆管理Agent 才能实现记得你说过什么的连续服务体验。我在项目里把记忆拆成三种短期记忆存在于对话上下文中模型天然具备不用额外处理长期记忆是对用户画像和偏好的持久化存储比如用户偏好简明答复、用户最近关注的商品品类业务记忆则是在一个多步骤业务流程中的中间状态比如退款申请到一半、已经走到哪一步了。记忆管理技能要做的事情其实就是读写两个动作读是在对话开始时把该用户相关的长期记忆注入上下文让模型感知写是对话结束后抽取对话中值得留存的用户偏好信息异步写入存储。抽取这一步建议设计一个专门的技能来做而不是让主对话模型顺手完成。独立技能的 prompt 可以更聚焦抽取的准确率明显更高。我们上线独立记忆抽取技能后偏好抽取准确率从 68% 提升到 89%。4. 实操记录从零搭建一套技能库4.1 定义技能 Schema 与加载器先不讨论花哨的框架我把最基础的技能 Schema 定义放在 JSON 文件里方便维护和版本管理。一个技能的 Schema 长这样{ name: get_order_status, version: 1.2.0, description: 按订单号查询订单当前状态。当用户询问我的订单到哪了订单物流情况发货了吗时使用。只支持查询本平台订单不支持第三方平台订单。, parameters: { type: object, properties: { order_id: { type: string, description: 用户提供的订单号通常是纯数字串, example: 20250115123456 } }, required: [order_id] }, returns: { order_id: string订单号, status: string状态枚举pending/paid/shipped/completed/cancelled, tracking_info: object物流信息包含 carrier 和 tracking_number } }然后写一个技能加载器把目录下所有技能 Schema 读进内存作为模型的技能列表import json from pathlib import Path def load_skills(skill_dir: str) - dict: skills {} for file in Path(skill_dir).glob(*.json): schema json.loads(file.read_text(encodingutf-8)) skills[schema[name]] schema return skills skills load_skills(./skills)这个加载器的好处是新增技能 新增一个 JSON 文件 实现对应的执行函数不用改任何框架代码。团队协作时业务同学可以独立写 Schema开发同学专注写执行函数互不阻塞。4.2 搭一个技能调用的最小框架技能除了 Schema还得有执行入口。我习惯用一个注册表把技能名映射到执行函数然后提供一个通用的执行接口class SkillRegistry: def __init__(self): self._executors {} def register(self, name: str, executor): self._executors[name] executor def execute(self, name: str, params: dict): if name not in self._executors: raise KeyError(fskill {name} not registered) return self._executors[name].run(params) def run_agent(user_input: str, registry: SkillRegistry): # 1. 让模型根据 user_input 和技能列表选出要调用的技能 selected llm_select_skill(user_input, list_skill_descriptions()) # 2. 让模型填充技能参数 params llm_fill_params(selected, user_input) # 3. 执行技能 result registry.execute(selected[name], params) # 4. 让模型基于技能结果生成最终回答 return llm_generate_answer(user_input, result)这个框架虽然简单但跑通了一个完整的技能化 Agent 闭环。真实项目里我会再加入重试、降级、日志追踪和权限校验但核心骨架就是这个。我建议刚开始做的团队先跑通这个最小闭环再逐步加复杂度千万别一上来就上重型框架。4.3 技能测试与评估方法技能化带来的最大红利之一就是可测试。我给每个技能配了独立测试用例测试框架只做三件事输入样例集。每个技能维护 20-50 条代表性的输入样例覆盖正常情况、边界情况和异常情况。比如get_order_status的样例要覆盖正常订单号、不存在的订单号、含空格的订单号、订单号格式错误、用户同时查多个订单等。期望输出断言。对每个输入样例写明期望的技能输出是什么或者至少写明必须满足的断言条件。比如status 必须是枚举值之一tracking_info 为 null 时必须有 explain 字段。批量回归。技能升级后跑一遍全量测试用例跟上一版结果对比。凡是输出结构变化的都要求人工确认是有意调整还是回归故障。提示技能测试不要只看跑没跑通要重点验证结构化输出的字段是否严格符合 Schema。字段类型错误和缺失字段在线上会被模型放大最终导致用户看到不可理解的回复。5. 常见问题与排查技巧实录5.1 技能调用失败先看模型还是先看技能技能调用失败是线上最常遇到的问题。失败可以粗略分为两种技能没被选中和技能选中但执行报错。前者的根源往往在描述后者的根源往往在执行函数。我处理这类问题的排查顺序是先看调用日志确认模型是否选中了技能如果没选中检查技能描述是否触发场景覆盖不够或跟另一个技能描述有混淆。如果选中了但执行报错看报错堆栈定位是参数问题、外部服务问题还是内部逻辑问题。这里有一个经常会踩的坑把执行报错的锅完全甩给模型。有一次线上退款技能频繁失败团队一开始以为是模型参数传错了查了半天最后发现是退款接口在特定金额区间超时。所以排查要两条腿走路别先入为主。5.2 参数解析错误与 Schema 设计模型从自然语言里提取参数出错是很常见的。比如用户说帮我查明天到北京的航班模型可能把明天解析成当前日期字符串也可能解析成2026-02-08这取决于你的 Schema 怎么写。结果日期格式不一致下游接口直接报错。我在这方面的经验是尽量降低模型解析难度。参数描述里给出明确的示例值能用枚举就用枚举比如date_type: today/tomorrow/specific不要指望模型从一句含糊的话里提取精确日期。另外参数解析错误要做客户端校验一旦发现参数不合法先追问用户澄清信息而不是硬着头皮调用下游。5.3 技能冲突与选择漂移技能多了会出现一种有意思的现象同一个用户请求上午走技能 A下午走技能 B两个技能都能完成任务但结果风格完全不同。这就是选择漂移。漂移本身不致命但会让产品体验不稳定。我的应对办法是引入技能优先级和分流规则。如果多个技能都能处理同一类请求就在它们的描述里明确写清边界并给一个默认优先项。比如查物流和查配送进度两个技能高度重叠我直接保留一个把另一个下线或者明确写成用户询问配送进度时优先使用get_delivery_progress除非用户明确要求查看物流历史记录才使用get_logistics_history。5.4 安全边界与权限控制技能化提升了能力边界也放大了安全风险。一个技能如果在提示词里被注入恶意指令可能让 Agent 执行非预期操作。我在项目里强制落地了三条安全原则最小权限。每个技能只申请完成自身任务所需的最小权限。查询类技能只读、写入类技能必须二次确认、涉及资金操作的一定要人工复核。默认拒绝所有未显式开放的权限。输出审查。技能返回给模型的内容要经过敏感信息过滤器手机号、身份证号、银行卡号等字段在进入模型上下文前脱敏。很多团队只做输入过滤忽略了输出侧的数据泄漏风险。操作留痕。所有技能调用都要记录完整的入参和出参并支持追溯。一旦出现问题可以快速定位是哪个技能、哪个环节、在什么输入下触发的。这条看似简单出事的时候能救命。写在最后技能化的长期价值我实际用了大半年 agent-skills 这套思路之后最深的体会是它改变的不只是代码结构而是整个团队做 Agent 产品的方式。以前我们上线一个新能力要改提示词、反复测、担心影响其他功能现在新能力就是加一个技能写清 Schema、实现执行函数、跑一遍测试用例就完事了。这种确定性和可扩展性对长期维护一个 Agent 产品来说太重要了。最后再分享一个小技巧给你的技能库做一份技能地图文档画出每个技能的名称、所属层级、依赖关系和负责人。技能超过 30 个之后这份地图的价值会越来越大——它能帮你直观发现重复造轮子的技能、长期没被调用的僵尸技能以及依赖链过深的高风险技能。定期更新技能地图是维护技能体系健康度的起点。