最近这半年圈子里聊 Agent 的人越来越多但真正上手做过完整项目的都知道Agent 能不能落地关键在于那层手和脚——也就是技能体系。GitHub 上这个叫agent-skills的项目本质上就是一套教你怎么给大模型 Agent 构建可复用技能的方法论和实现参考。我自己拿它重构过一个内部工具链节省的 Prompt 调试时间远超预期今天把整个过程和踩过的坑整理出来。这个项目适合谁看只要是正在做 AI Agent、工作流自动化或者已经在用 Function Calling 但总觉得效果不稳定的人都值得花十分钟过一遍。它想解决的问题很具体怎么把一次性的指令调用沉淀成可复用、可组合、可测试的技能单元。下面我按实际落地的顺序把设计思路、实操细节、工程实现和排查经验完整讲一遍。1. 技能体系设计为什么 Agent 需要一套手和脚1.1 从调模型到用技能的思维转变很多人刚接触 Agent 时习惯把逻辑全写在 System Prompt 里觉得模型什么都能干只要指令写得够细就行。这个思路在小 demo 里没问题项目一旦复杂就会失控。我自己踩过最痛的坑一个文档处理 Agent 的 System Prompt 写到 4000 字结果模型在 A 场景表现很好换到 B 场景就开始自作主张今天给它加了新指令明天就把旧指令忘了——Prompt 之间互相打架。agent-skills 这个项目的核心思路是把让模型自由发挥改成让模型在技能库中做选择。技能的本质是一个带描述、带参数、带调用逻辑的函数单元模型要做的只是根据当前任务从技能库里挑一个最合适的。这样一来逻辑的主控权从模型手里回收到了开发者手里可控性立刻上了一个台阶。我把这种设计叫做手脚分离大模型是大脑负责理解和决策技能库是手脚负责执行和反馈。两者之间用一套标准化的协议通信不互相嵌入。这种做法最大的好处是技能可以单独测试、单独迭代不需要每次改动都拉着整个 Agent 陪跑。1.2 技能拆分的两种维度能力维度和场景维度设计技能库时最容易犯的错误是把技能拆得太碎或太粗。太碎比如把一个写报告拆成 20 个原子步骤模型光做路径规划就烦了太粗比如把处理所有 Excel 事务当作一个技能参数复杂到模型根本记不住。我参考 agent-skills 里的思路把技能拆分成两个层次能力型技能和场景型技能。能力型技能是通用的动作比如文件读写、网页请求、数据库查询、消息推送这类技能跨项目复用定义时要保证接口稳定场景型技能是特定的流程比如根据 Excel 生成月度报表把 Markdown 批量转为 PDF这类技能聚合多个能力型技能面向具体业务可以在不同 Agent 间共享。这种分层的价值在于能力型技能追求稳定和通用场景型技能追求灵活和业务匹配。两者分开管理改动互不影响。比如业务方说报表格式要加一列我只需要改场景型技能的编排逻辑底层读写 Excel 的能力型技能完全不用动回归测试范围一下就缩小了。1.3 协议先行定义输入输出的接口契约技能库能不能做起来最关键的不是写多少技能而是定义一套严格的接口协议。大模型是典型的对格式敏感、对暧昧内容不友好的运行环境技能描述写得模糊模型就会瞎猜参数定义得不严谨模型就会传出诡异的类型。我在实际项目中做了一套技能描述模板每个技能必须包含六个字段技能名称英文标识符、功能描述两到三句话说明能做什么、不能做什么、输入参数JSON Schema 格式带类型和描述、输出格式明确的返回结构、使用示例一到两个典型调用案例、错误信息可能抛出的异常和含义。这个模板看起来琐碎但每个字段都有它的意义。举个例子功能描述里必须明确写不能做什么。比如一个发送邮件技能如果只写了发送邮件模型可能拿它去发短信。我会在描述里补一句本技能仅支持 SMTP 发送文本邮件不支持附件和 HTML 格式。这样模型就不会乱用。参数部分我强制使用 JSON Schema而不是简单的参数字符串这种模糊写法因为模型对类型错误的容忍度极低string和array传错下游函数大概率直接报错。2. 技能实现细节从自然语言指令到可执行函数2.1 技能描述怎么写模型才不视而不见很多人以为技能描述随便写两句就行反正模型能理解。实测下来完全不是这样。大模型对技能描述的处理本质上是一种隐式的信息检索它会在上下文里搜索与当前任务最匹配的描述。如果你把信息埋在一大堆废话里模型就很可能漏看。我总结了一套写描述的经验。第一句式要固定。所有技能描述都采用该技能用于动词宾语约束条件的结构模型对这种排比句式很敏感检索命中率明显提升。第二关键词要贴近用户表达。比如用户说帮我查下今天的天气技能描述里如果写的全是获取气象数据模型可能匹配不上写成查询天气/气温/降水/风力效果就好得多。第三否定信息单独成句。不要写不适用于以下情况时会怎样这种长句直接单列一行注意不支持批量操作模型更容易捕捉。我在 agent-skills 的实践里还发现一个规律描述越短调用准确率越高。一个技能描述超过 80 个英文单词模型的准确率就开始明显下降。后来我给自己定了个硬指标每个技能的描述控制在 50 个单词以内用词精准优先语法完整次之。描述越短模型越容易看到这算是用 token 预算换鲁棒性。2.2 参数设计的少而精原则参数设计是技能库里最容易被低估的环节。多数人会把参数设计得很多觉得所有情况都覆盖到更安全。这个想法放在传统软件开发里没问题但放到大模型场景就是一个巨大的坑——参数越多模型要做的选择就越多出错的概率也就越大。我看了 agent-skills 仓库里的实例几乎所有参数精简的技能调用成功率都更高。所以我实践时定了一条规则一个技能最多不超过五个参数能合并的合并能省略的省略。比如一个生成图片技能初始版本有 7 个参数尺寸、风格、光线、角度、色彩、画质、迭代步数准确率惨不忍睹。我把参数压缩成三个提示词、尺寸、风格其余全部用默认值。模型的选择少了任务一下就清晰了。参数默认值的设置也有讲究。默认值不应该随便填而是填在大多数情况下最适合的值。比如图片尺寸的默认值我设成 1024x1024因为 Agent 最常见的需求是方形配图特殊需求用户会在参数里单独传。这样设计既降低了模型的决策成本又不牺牲灵活性。2.3 技能实现的语言与框架选择选实现语言时主流是 Python 和 TypeScript两者我都试过。Python 的优势在于生态丰富几乎所有的数据处理、机器学习、办公自动化库都优先支持TypeScript 的优势在于调试工具链完善类型系统能提前发现问题尤其在参数校验时特别有用。我的建议是如果是做企业级 Agent 平台团队有前端基础选 TypeScript如果是做个人项目、数据分析类 Agent无脑选 Python生态省下来的时间太值了。框架层面LangChain 和 LlamaIndex 都提供了 Function Calling 的基础设施但我不建议一上来就全上框架。原因很简单框架屏蔽太多实现细节出了问题排查起来特别费劲。我个人做法是协议层自己写JSON Schema 校验 错误处理工具调用层用框架自带的 Function Calling 能力编排层自己实现。这样既享受了框架的能力又保留了对核心逻辑的掌控。3. 从零搭建一个技能库完整实操记录3.1 第一步盘点需求圈定技能边界动手写代码之前我建议先花半天时间做一次彻底的能力盘点。列出当前业务场景里 Agent 会被要求做的所有事情逐条归类。比如我当时的业务是文档自动化处理 Agent盘点后发现高频任务集中在文件读取PDF/Word/Excel、内容提取摘要/关键词、格式转换MD/HTML/PDF、内容生成报告/邮件/总结。这些就是技能库的第一批成员。盘点完需求接下来要画边界哪些能力做成内部技能哪些能力改造现有 API哪些能力直接用开源库。我的标准是代码量少于 50 行、自己能完全掌控的做成内部技能需要调用成熟服务的比如 OCR、翻译包一层 API 封装周期长、需求不确定的先不做。画好边界后再动手能省掉后面大量返工。3.2 第二步实现核心技能用 JSON Schema 定义参数代码实现层面我先写了一个统一的技术基座包含两个核心部分参数校验器和错误处理器。参数校验器负责检查模型传入的参数是否符合 JSON Schema不符合就返回明确错误信息错误处理器负责把底层异常转成模型能理解的文本描述。这一步不能省原因后面排查章节会说。实际代码如下from jsonschema import validate, ValidationError def validate_params(schema, params): try: validate(instanceparams, schemaschema) return None except ValidationError as e: return f参数校验失败: {e.message}请按 schema 要求重新传参参数校验的返回信息要尽量友好因为读取它的不是人而是大模型。你返回的错误信息越明确模型越容易自我纠正。比如返回参数校验失败: width 必须是整数, 当前是字符串模型看到后就会把类型改对再试一次。错误处理器我写成一个装饰器所有技能统一复用避免重复代码。核心逻辑是捕获所有异常包括网络超时、文件不存在、权限不足转成结构化 JSON 返回同时打印详细日志供开发者排查。这样模型拿到的永远是可读的、可决策的信息而不是一坨堆栈报错。3.3 第三步把技能注册进 Agent 的可用列表技能写好后需要注册到 Agent 的可用列表让模型知道你有这些工具可以用。注册的关键是模板渲染。大多数框架OpenAI SDK、LangChain都支持从函数定义自动生成工具描述但自动生成的描述太机械模型识别效果一般。我建议手动维护一份技能描述清单把上一步写好的精美描述填进去。注册信息最终会拼接进模型输入的 tools 字段里结构大致如下{ type: function, function: { name: read_pdf, description: 读取 PDF 文件内容返回纯文本。注意不支持扫描件 OCR仅支持文本型 PDF。, parameters: { type: object, properties: { file_path: {type: string, description: PDF 文件的完整路径} }, required: [file_path] } } }注意 description 里那句注意不支持扫描件 OCR这是给模型划定的边界。没有这句模型遇到扫描件也会硬调用然后拿到一堆乱码有了这句模型会直接告诉用户该文件是扫描件当前技能不支持体验完全不一样。3.4 第四步测试与迭代用真实场景跑通全流程技能库上线的最后一关是测试。很多人的习惯是写完技能随便调几个 demo 就上线这个习惯在传统开发里勉强能用在 Agent 场景里是致命的。因为大模型有随机性同一个技能同一个参数每次调用可能输出不同的结果。必须在测试阶段覆盖足够多的真实场景。我给自己的项目做了一套回归测试脚本准备 50 条真实业务请求逐条执行记录调用成功率、参数错误率、结果满意度三类指标。每轮测试后针对失败案例更新技能描述或参数设计然后重新跑全部用例保证没有回归。这个流程跑下来每次迭代的改动都有的放矢不是拍脑袋乱试。4. 常见问题与排查技巧实录把那些坑摊开来讲4.1 模型总是选错技能怎么办选错技能是 Agent 落地最高频的故障。表现是明明有查询订单技能用户说帮我看看订单到哪了模型却调了查询商品技能。这类问题九成出在描述上——描述里的关键词和用户口语表达不匹配。排查时先看模型实际选择的技能名再看这个技能的描述重点检查是否包含用户常见表达词。如果描述没问题还是选错那大概率是两个技能的边界模糊。比如生成日报和生成周报两个技能描述里如果都写到汇总数据、生成报告模型就会纠结。解决方案不是把描述写得更多更细而是合并技能把生成日报生成周报合并成生成报表加一个频率参数让模型在做一次选择时同时确定报表类型。多个相似技能合并成一个参数化技能是解决选择困难症的终极大招。还有一种少见情况模型在多次调用时状态错乱第二次调用用了第一次的技能记忆。这种要检查上下文长度和消息历史看是否封装了不必要的历史信息。我的做法是每次工具调用只保留最近两轮上下文更早的历史直接截断降低模型的信息负担。4.2 参数传错从根源上减少而不是反复纠正参数传错比选错技能更难排查因为它往往不是明显错误而是类型对但值不对。比如模型把date参数传成2024-1-5而你的函数预期 ISO 格式2024-01-05这类问题连校验器都难发现。我的经验是与其事后纠正不如从根源上减少模型乱传参数的空间。具体做法是所有可以枚举的参数都用 Enum 或 const 限制比如风格参数直接规定只能传[写实, 卡通, 水墨]之一模型没有自由发挥的余地。日期、时间这类参数在描述里直接写明必须是 ISO 8601 格式如 2024-01-05并配一个示例。示例非常管用模型会模仿示例的格式进行传参比抽象描述直观得多。如果项目已经上线参数问题频发我建议加一层参数后处理在函数内部做一次容错转换比如把裸数字转成整数、把日期字符串解析后重新格式化。这个兜底逻辑虽然不优雅但在生产环境里能救下很多差一点就成功的调用。4.3 一次技能调用耗太久Agent 整体响应迟钝这个问题很多人会忽略但生产环境非常致命。原因通常是技能内部有阻塞的同步请求比如一次 HTTP 调用等待 10 秒模型就卡在那里 10 秒用户体验极差。排查时先给每个技能加耗时日志看哪些技能的平均耗时超过 3 秒针对性优化。优化手段有三板斧第一能并行的请求用并发比如同时获取多个数据源用asyncio.gather合并等待第二能走缓存的走缓存比如高频调用的信息天气、汇率、订单状态设 30 秒到 60 秒的 TTL 缓存避免重复请求第三实在慢的操作把同步返回结果改成先返回受理成功再异步通知结果让 Agent 先向用户反馈后台继续执行。其中第三点特别适合生成类技能。比如生成一份 100 页的行业报告直接同步等待可能要几分钟改成提交任务后返回任务 IDAgent 告诉用户报告正在生成稍后我通知你体验完全不同。这套异步换交互的设计我从一个电商客服 Agent 里验证过用户满意度有显著提升。4.4 技能调用链路太长中间失败如何优雅降级一个复杂的场景型技能往往要串多个能力型技能。比如生成季度财务分析报告流程是读 Excel → 算汇总 → 调图表库画图 → 调模板渲染文档 → 导出 PDF。任何一个环节失败整条链路就断了。我在早期版本里不做任何降级处理一断就返回秃秃的报错用户和模型都很沮丧。后来我给每个技能链路加了两层保障。第一层是重试机制对网络类、临时类错误自动重试两次间隔 1 秒、3 秒对业务类错误不重试直接向上抛。第二层是降级方案如果一个能力型技能失败了模型会尝试用替代技能完成。比如图片生成服务挂了技能库会自动切换到调用本地模板合成图片的备用技能确保主流程不被阻断。实现降级的关键是在场景型技能的描述里预先声明备用路径。比如本技能支持自动降级若 A 服务不可用将使用 B 服务返回结果。模型读到这个信息后遇到失败就不会直接摆烂而是按描述里的降级路径继续执行。这个技巧对复杂 Agent 的稳定性提升非常明显。5. 探索 agent-skills 的进阶玩法从能用到好用5.1 技能组合把多个技能编排成一个流水线技能库积累到一定规模后你会发现很多任务是固定模式下多个技能的组合。比如自动整理会议纪要本质是录音转文字 文本摘要 要点提取 邮件发送四个技能的无缝衔接。与其让模型每次动态规划步骤不如预先把常见的组合封装成流水线技能。封装流水线的价值在于两点一是缩短模型的推理路径它不再需要一步步想下一步做什么直接调用流水线入口链路内部的编排是确定的二是提高任务完成的确定性动态规划在简单场景没问题但复杂任务在多轮调用后容易迷路流水线避免了这种偏差。我在 agent-skills 基础上扩展了一套工作流描述语法用一个 YAML 文件定义流水线的步骤、每个步骤调用哪个技能、参数如何传递、失败如何处理。这样业务方不需要接触代码改 YAML 就能调整流程大大降低协作门槛。5.2 技能共享团队级技能库的版本管理在团队协作场景中技能库天然需要版本管理和共享机制。我给几个项目团队搭了一套基于 Git 的技能库管理流程每个技能一个独立目录包含源码、描述文档、测试用例通过 Git 分支管理开发和上线。变更时走 PR 审查合并后通过 CI 自动跑一遍全量回归测试。技能共享带来的一个意外好处是不同项目的 Agent 可以复用同一套能力型技能。比如发送企业微信消息查询内部数据库生成 Excel 报表这类通用技能在三个项目里完全一致只维护一份。修一个 bug 四处受益节省的重复开发时间非常可观。5.3 技能评估上线之后还要持续盯数据最后提醒一句技能库不是上线就完事了它是一个活的系统。模型版本升级、用户表达习惯变化、业务规则调整都会影响技能的调用表现。我的做法是建一个简单可观测仪表盘统计每日调用次数、成功率、平均耗时、最常见错误类型每周过一遍数据发现指标滑坡就回溯排查最近的变更。这段数据化运营的经验也是我从 agent-skills 项目里学到最受益的一点。最开始我以为技能库是一个写一次用终身的东西实际做下来才发现它更像是养盆栽需要持续观察、修剪和调整。好的技能库是慢慢长出来的而不是一蹴而就造出来的。每次折腾完一轮技能迭代看到 Agent 的调用成功率往上走那感觉比写一堆 fancy 的功能代码踏实得多。