1. 为什么我盯上了“技能化”这条路先说说这个 agent-skills 项目是怎么来的。做 AI Agent 应用开发做了两年多我最大的感受是大部分团队做智能体做着做着就变成了“给大模型套壳”核心逻辑就是写一大段 system prompt 塞进去然后接两三个 API跑通一个 demo 就算完事。但一旦进入真实业务场景你会发现这种“一次性提示词”的打法立刻见底——业务方今天要一个日报生成助手明天要一个合同审查工具后天要一个舆情分析机器人每一个需求都从零开始写提示词、调参数、排错误。代码仓库里堆满了prompt_utils.py、agent_v2.py、agent_final_v3.py维护成本直线飙升。当时我手里同时压着三个项目一个是给运营部门做的活动效果分析助手一个是给客服团队做的工单分类与话术推荐工具还有一个是给研发团队做的代码评审助手。这三个项目看起来八竿子打不着但抽出来看它们有超过 40% 的基础能力是重叠的——都要做意图识别都要做信息抽取都要做结果格式化输出都要有兜底回复逻辑。区别只在于业务规则和知识范围。于是我开始认真思考一个问题能不能把这些“能力”沉淀成一套可以被任意 Agent 复用和组合的技能库这就是 agent-skills 这个项目的起点。它要做的事情很简单——把零散的提示词工程、工具调用逻辑、参数校验规则、错误处理策略封装成一个个标准化的“技能模块”让 Agent 按需加载、灵活组合而不是每次从一张白纸开始。我后来在复盘文档里写过一句话“提示词是能力技能是产品”。提示词解决的是“模型能不能做”技能解决的是“业务怎么用”。这个项目本质上就是在两者之间搭一座桥。如果你现在的 Agent 开发也遇到了“每次都要重写提示词”“能力无法跨项目复用”“工具调用老是出错”这类问题那这篇文章里的思路和实践应该能给你一些直接能上手的参考。2. 整体设计思路给 Agent 装一套“可插拔的技能模组”agent-skills 设计的第一个原则是搞清楚“技能”和“工具”到底有什么区别。很多人会把这两者混为一谈实际差别非常大。我见过不少团队的 Agent 设计基本上长这样定义几个函数比如search_database()、send_email()、get_weather()然后把函数说明塞进 tools 参数里模型需要的时候自己选。这种做法的好处是简单直接但弊端很快会显现工具只是能力的“接口层”它不包含“什么时候该用”“用的时候先做什么后做什么”“结果出来之后如何处理”这些业务逻辑。换句话说工具知道怎么执行但不知道为什么要执行。技能则不同。技能 工具的调用规则 业务场景判断 步骤流程 参数规范 结果处理策略。它是一个完整的“能力单元”可以独立使用也可以互相组合。举个例子我在 agent-skills 里定义了一个report_generation技能它内部并不直接调用某个单一工具而是编排了三个步骤先调用data_fetcher获取结构化数据再调用metric_calculator计算关键指标最后调用template_renderer生成 Markdown 报告。在传统工具模式下你需要让模型一步步自己决定“该先调谁后调谁”错了就重来而在技能模式下这整条链路被打包成一个整体模型只需要判断“用户需要报表吗需要那这把report_generation技能推上去就行”。2.1 技能分组把相似能力收拢到同一屋顶下第一个设计决策是给技能做分组。我参考了插件系统里常见的“体系分层”思想没有任何犹豫地把技能分成了三层basic基础技能、business业务技能、meta元技能。基础技能是一般化、跨领域通用的能力比如text_summarize文本摘要、data_extract结构化抽取、format_convert格式转换、web_search联网检索。这些技能与具体业务无关任何 Agent 都可以挂载。业务技能则绑定特定领域比如customer_ticket_classify客服工单分类、contract_risk_scan合同风险点扫描、code_review_violation代码违规检查。元技能是用于管理其他技能本身的技能比如skill_router技能路由、skill_fallback技能兜底、skill_merge技能结果融合。这个分层的收益非常直接。新项目启动时团队不用从零讨论“这个 Agent 需要什么能力”而是先看基础技能里有哪些直接能挂再看业务技能里有没有同领域的沉淀最后只针对业务盲区开发新技能。我在内部做过一个粗略统计接入 agent-skills 之后三个并行项目的重复开发量下降了差不多 60%主要省下的就是“基础能力重写”这部分。2.2 注册制管理技能不是堆在仓库里而是靠配置暴露第二个设计决策是技能注册机制。我没有把技能做成“硬编码类”而是选择了“注册制 配置文件”的模式。每个技能由一个目录承载目录里包含三样东西skill.yaml技能元信息、prompt.md技能执行提示词、examples/示例样本。skill.yaml长这样name: contract_risk_scan version: 1.2.0 group: business description: 对合同文本进行风险条款扫描输出风险等级和具体条款编号。 triggers: - 合同审查 - 合同风险 - 帮我看看这份合同 - 条款有没有坑 steps: - load_document - clause_segment - risk_predict - report_generate params: doc_type: [pdf, docx, txt] output_format: [markdown, json] risk_levels: [high, medium, low] fallback: general_qa模型在运行时会先读取所有已注册的skill.yaml将其中的name、description、triggers预置到上下文中形成一个“技能清单”。当用户输入到来时模型基于这个清单判断该激活哪个技能。这个机制的核心在于技能暴露给模型的是“元信息”而非完整逻辑。真正详细的执行步骤和提示词存在prompt.md里等技能被选中时才注入上下文。这样做的原因很简单上下文窗口再大也是稀缺资源。如果每个技能都把自己几千字的完整提示词塞进 system prompt上下文能爆得飞快。注册制把上下文占用压缩到了“最少描述”用的时候再把详细内容放进来这是整个项目性能表现稳定最重要的一个架构决策。2.3 为什么不用纯 Function Calling而要自己干一套聊到这里有人会问直接用 OpenAI/Anthropic 的 Function Calling把每一步都定义成函数不是也能实现类似效果吗非要自己造轮子吗我的回答是Function Calling 适合“工具”场景不适合“技能”场景。 Function Calling 本身不解决业务步骤编排的问题它只是帮你做函数选择的接口。即便你把合同审查拆成五个函数模型还是要自己决定先调哪个后调哪个中间夹杂着大量失败重试和调用顺序混乱。而技能是一个已经编排好的“流程单元”它把步骤、判断、异常处理都固化了模型做选择题的难度就从“选哪个函数”简化成了“选哪个技能包”。这不是说 Function Calling 没用——实际上我底层还是用 Function Calling 去触发技能只是技能内部的步骤编排逻辑不再依赖模型临场决策而是由技能自己的执行器executor去逐步驱动。这套混合架构在后续项目里反复验证过精度更高、调用次数平均下降了 35% 左右用户体验提升非常明显。3. 技能定义的核心细节与实操要点聊完了宏观设计我来拆解一个技能模块内部该怎么写。这是 agent-skills 项目里最花功夫的部分也是决定最终效果上限的地方。很多人做智能体犯的最大错误就是把技能脚本当成“给模型看的说明书”写完 description 就结束了。实际操作下来至少需要从五个维度来打磨。3.1 技能描述让模型在“模糊意图”下也能匹配对技能描述的第一原则是从用户视角描述而非从功能视角描述。什么叫用户视角用户不会说“请帮我调用一个文本摘要工具”他会说“给我概括一下这篇东西的核心观点”。所以text_summarize这个技能的 description 我写的是对用户提供的长文本进行信息压缩与要点提炼保留关键数据、结论与逻辑主线去除冗余描述和背景铺垫。适用于用户提到“总结”“概括”“太长不看”“看重点”等表达的场景。注意这里我没有写任何技术术语全是口语化的场景描述。为什么因为模型对自然语言触发的理解远远强于对功能标签的理解贴合用户表达习惯的描述能让意图匹配的准确率明显提升。triggers字段则可以理解为“关键词触发器”但不是简单字符串匹配而是给模型看的“语义路标”。我一般会列 4 到 8 个最常见的高频触发表达同时会主动说明“不要把 triggers 当唯一判断标准语义相似就激活”。3.2 技能步骤定义“执行路径”而不是“任务清单”我的prompt.md里会包含一个步骤执行区但写法非常讲究。拿code_review_violation这个技能举例第一個版本我写的是1. 读取代码 2. 检查语法 3. 检查规范 4. 输出结果后来发现模型压根不买账给的输出非常空泛。问题出在“步骤描述抽象度太高”模型不知道该关注什么。后来我把步骤改成了决策式引导1. 读取用户提供的源代码文件判断语言类型Python/Java/Go/JS 等。 2. 先做静态扫描检查是否存在语法错误、命名不规范、重复代码块。 3. 再审查业务逻辑重点排查空指针隐患、未处理的异常路径、不安全的输入输出。 4. 对发现的每个问题给出文件位置、问题类型、严重级别、修改建议。 5. 全部检查完毕后按“严重问题 一般问题 建议优化”的顺序输出报告。核心变化在于每个步骤都加上了“判断维度”和“产出要求”。模型看到的不再是一个泛泛的任务而是一条可执行的路径。这个技巧后来被我用到了所有技能里效果立竿见影。3.3 参数规范宁可前置过滤不要后置补救一个很容易被忽视的点是技能参数的校验。模型调用技能时给的参数经常格式漂浮比如有的传fileName有的传文件名有的直接不传。如果技能执行器不做参数归一化下游函数必然报错。我的做法是在每个技能里定义一个参数预处理规则执行器在拿到模型输出后先做一轮清洗和校验包括字段名映射兼容英文、中文、下划线、驼峰等多种写法类型强制转换123转成123yes转成true等必填校验必填字段缺失时不直接报错而是通过追问补全这个设计让我避免了一整类“模型乱传参数”的问题。后来我又在规则里加了一条如果参数缺失且无法补齐技能可以降级为“询问模式”而不是硬着头皮用错误参数跑结果。3.4 示例注入给模型一张“标准答卷”每个技能目录下的examples/文件夹放的是这个技能的输入输出示例。示例文件通常是两三组结构化的案例比如{ input: 帮我总结一下这份交通事故责任认定书的要点。, activated_skill: text_summarize, output: 已提炼责任认定书核心信息事故时间、地点、涉及方、责任归属、赔偿建议。 }示例的真正价值不在“给模型背答案”而在约束输出格式。有一次我在report_generation技能里加了一个示例展示 Markdown 表格格式的报告长什么样之后模型生成的报告格式稳定性立刻大幅提升。3.5 技能命名简单直接不搞抽象最后讲一个很不起眼但很关键的细节——命名规范。技能名我要求用小写字母加下划线避免大小写混用因为模型对大小写敏感命名不一致会导致技能检索失败。同时名称要直观data_extract比information_extraction_helper要好得多命名越短模型记忆和复用的成本就越低。这条建议虽然简单但确实是我踩了坑之后总结出来的。4. 实操过程从零搭建一套可运行的技能调度链路如果你看到这里说明你已经接受“技能化”这个思路了。那我们就动手落地我带你完整过一遍 agent-skills 的搭建流程。这套流程不需要特别高级的硬件一个能跑大模型 API 的服务器就够了整个链路的核心逻辑都可以用 Python 实现。4.1 目录结构设计一个技能一个家我的技能仓库根目录是这样组织的agent-skills/ ├── skills/ │ ├── basic/ │ │ ├── text_summarize/ │ │ │ ├── skill.yaml │ │ │ ├── prompt.md │ │ │ └── examples/ │ │ └── data_extract/ │ ├── business/ │ │ ├── contract_risk_scan/ │ │ └── customer_ticket_classify/ │ └── meta/ │ ├── skill_router/ │ └── skill_fallback/ ├── executor/ │ ├── registry.py │ ├── loader.py │ ├── router.py │ └── runner.py ├── config/ │ └── default.yaml └── main.pyexecutor目录是整个运行时的核心每个文件职责单一。registry.py负责扫描全部技能目录并建立索引loader.py负责按需加载指定技能的prompt.mdrouter.py负责意图匹配和技能选择runner.py负责执行技能内的固定步骤链。四个模块各干各的互不依赖后期维护成本很低。4.2 技能注册与加载遍历目录生成技能清单注册逻辑比较直白核心就是遍历skills/目录下所有skill.yaml解析后放入内存字典。代码大概长这样import yaml from pathlib import Path class SkillRegistry: def __init__(self, skills_root: str skills): self.skills_root Path(skills_root) self.skills {} def scan_all(self): for yaml_file in self.skills_root.rglob(skill.yaml): skill_dir yaml_file.parent with open(yaml_file, r, encodingutf-8) as f: meta yaml.safe_load(f) # 自动补全关键路径字段 meta[prompt_path] str(skill_dir / prompt.md) meta[examples_path] str(skill_dir / examples) self.skills[meta[name]] meta return self.skills def generate_manifest(self) - str: 生成一段供大模型读取的『技能清单』文本。 lines [] for name, meta in self.skills.items(): lines.append( f- {name}{meta[group]}: {meta[description]} f触发场景: {, .join(meta[triggers][:4])} ) return \n.join(lines)generate_manifest()输出的这段文本最终会被拼到 system prompt 里。它是模型选择技能的唯一依据所以描述要简洁、信息密一眼能看出每个技能是干什么的。4.3 意图路由让模型先选技能再执行内容技能路由我使用的是轻量级方案——让模型先针对用户输入输出一个技能选择决策而不是直接去生成最终回答。我给路由环节设计了一个单独的小提示词模板ROUTING_PROMPT 你是智能体技能路由模块。你的任务是根据用户输入从技能清单中选出最匹配的一个技能。 要求 1. 如果存在明确匹配输出技能名格式如: SKILL: contract_risk_scan 2. 如果没有匹配输出 SKILL: none 3. 一次只选一个技能不要输出解释不要输出多余内容。 可用技能清单 {manifest} 用户输入{user_input} 为什么不直接生成回答而要单独做一次路由因为我把“决策”和“生成”拆开了。先压缩成一个选择问题模型确定选了哪个技能后再把该技能的完整 prompt 注入第二次请求。这个做法的好处是前一轮的上下文非常干净不会因为塞入大量无关技能说明而干扰模型的判断。4.4 技能执行注入详细提示词按步骤跑链路路由环节确定技能后runner.py开始干活。它的职责是读取技能的prompt.md将其注入到一个新的会话上下文里同时准备技能需要的输入参数、外部工具句柄最后调用模型生成输出。class SkillRunner: def __init__(self, llm_client, registry: SkillRegistry): self.llm llm_client self.registry registry def run(self, skill_name: str, user_input: str, params: dict None): meta self.registry.skills.get(skill_name) if not meta: raise SkillNotFoundError(skill_name) with open(meta[prompt_path], r, encodingutf-8) as f: skill_prompt f.read() messages [ {role: system, content: skill_prompt}, {role: user, content: user_input}, ] # 这里可以插入外部工具注入、历史对话回填等扩展逻辑 response self.llm.chat(messages, toolsmeta.get(tools, [])) return response实际生产环境中runner内部还会处理多轮对话、工具结果回填、上下文裁剪这些事情。核心思路就是系统提示词按技能动态变化同时保留用户意图的连续语义。4.5 一个最小可运行主流程把上面几个模块拼起来主流程极其简洁from executor.registry import SkillRegistry from executor.router import SkillRouter from executor.runner import SkillRunner registry SkillRegistry(skills) registry.scan_all() router SkillRouter(llm_client, registry) runner SkillRunner(llm_client, registry) def handle_message(user_input: str): skill_name router.route(user_input) if skill_name none: return 当前没有匹配的技能请更换描述方式再试。 return runner.run(skill_name, user_input)先注册、后路由、再执行三步走一个能复用技能库的最小智能体就跑起来了。这套代码我后来抽成了模板新项目接入 agent-skills 时只需要配置 LLM client 和技能目录其余全部复用。5. 技能运行中的常见问题与排查技巧实录任何系统跑起来都会遇到问题agent-skills 也不例外。我在这套机制上踩过不少坑其中有些属于设计缺陷有些属于模型特性导致的“不可抗力”。我把最典型的四类问题整理出来附带排查思路和最终解法你可以直接拿去做参考。5.1 技能冲突多个技能都匹配模型到底选哪个这是接业务需求后暴露的第一类问题。比如用户说“帮我分析一下这份数据”数据分析技能觉得该激活报表生成技能觉得自己也能干最后模型随机选了一个效果完全看运气。我的解法是给skill.yaml加一个priority字段在 manifest 里按照优先级从高到低排序。同时调整描述写法让每个技能的适用边界更清晰别都写“适用于用户需要分析数据的场景”而是写清楚“适用于用户提供了结构化数据文件Excel/CSV/JSON且需要做统计分析的场景”。边界清楚了模型的选择自然就准了。另外我在路由阶段增加了“重问机制”——当模型对多个技能的置信度都低于阈值时反问用户一句“你是想生成报表还是做数据洞察”把选择权交还给用户而不是让模型盲目猜。5.2 上下文被撑爆技能越多Token 消耗越高技能数量上了 20 个以后manifest 本身会占用不少上下文空间再加历史对话和工具返回结果很快就逼近上下文窗口上限。这个问题在长会话场景下尤其致命。我的处理策略是给 manifest 做“动态裁剪”只把高优先级的 10 个技能完整展示其余技能只保留name和一句话简介。同时把所有技能的详细 prompt 改为按需加载避免一次性全量注入。另外还做了查询缓存——同一个用户在同一个技能下的多轮交互技能 prompt 只注入一次后续轮次复用同一份上下文。5.3 技能内部步骤断裂第一句正常第二句开始胡言乱语有一次contract_risk_scan技能跑出了非常离奇的结果——它读取了文档、切分了条款但到了“风险预测”这一步竟然自己编造了一份“标准合同文本来对照”。排查后发现原因是技能执行链中的某一步超出了模型自身的知识边界让它产生了幻觉。这个问题靠提示词已经解决不了我在两个层面做了修复第一给技能步骤加上“依赖外部工具”的标记如果某一步需要真实数据支撑那就强制调用检索接口模型没有权限自主生成第二给prompt.md增加了“禁止行为”段落明确列出模型不能做的事比如“不得编造条款内容”“不得对未提供的合同文本进行假设”。这一步是整个项目里对结果质量提升最大的一次修改从此技能输出的可控性上了一个台阶。5.4 跨场景迁移失效换了一个业务技能就不灵了最后一个坑属于“成长的代价”。我把text_summarize技能从运营场景迁移到研发场景时发现它输出的摘要风格完全不对——运营要的是数据和结论导向研发要的是变更点和影响范围导向。迁移之后生成的内容两头不靠。研究之后我把方案改成了“技能模板 领域参数”的模式。text_summarize里增加一个summary_focus参数默认值是“通用”在运营场景里配置为focus_on_business_metrics在研发场景里配置为focus_on_tech_changes。技能逻辑不变参数变了输出风格随即跟着变。这套思路后来演化成了我所有技能设计的标准操作能通过参数适配的就不要新开一个技能。6. 进阶玩法技能编排、降级兜底与自动生成当你把基础技能跑通之后一定会产生一个更进一步的念头能不能让 Agent 自己编排技能解决一个之前从没定义过的复合问题这个方向我在 agent-skills 的后续迭代里尝试了一部分有些成果有些还在改进。6.1 技能编排让几个技能按顺序协同完成复合任务最直接的编排方式是“链式调用”。用户说“帮我分析这个网页的内容然后结合我的历史行为数据生成一份今日运营简报”——这里至少涉及三个技能web_page_reader读取网页内容、data_analyst分析行为数据、report_generation生成日报。我的做法是定义一个pipeline类型的技能它内部把其他技能当作“步骤”来调用。Pipeline 的prompt.md只描述整体流程而每个具体步骤仍然由对应的技能执行器来跑。pipeline的好处是模块边界保持清晰单个技能仍然可以独立复用。6.2 技能降级主技能挂了备选方案自动顶上Agent 总会遇到主技能走不通的情况比如合同审查时文档格式不支持或者联网搜索服务临时不可用。我在路由层增加了一个降级策略表每个技能可以配置fallback技能。主技能失败时降级到备用技能保证用户的诉求至少有个回应。举个例子web_search失败时降级到knowledge_base_query虽然不能拿到最新实时信息但至少能从内部知识库给用户一个基于历史资料的参考回答。这条策略让 agent-skills 的可用性有了显著提升线上兜底回复占比从原来的 12% 降到不足 3%。6.3 技能自动生成离“AGI”最近的一次尝试最后一个进阶方向有点实验性质——我尝试让模型基于一段业务需求描述自动生成一个新的skill.yaml和prompt.md草案。原理不复杂把“技能开发指南”作为系统提示词让模型先分析业务场景再起草技能定义最后由人审核修正。做出来的效果还不够完美但已经能生成 60% 可用的初稿。负责新技能开发的同事反馈说以前一个技能从需求对齐到写提示词、调整参数至少需要半天有了自动生成辅助半天能跑通两三个技能的初版。这个方向我还在持续改进目前重点关注的是“自动生成的提示词与手写提示词之间依然存在质量差距”这个问题。7. 从项目实践里沉淀的三条体会项目做到这个阶段有些话我觉得值得单独拿出来说一下算是我个人视角的经验沉淀。第一技能化重的东西是“抽象层次”不是“功能数量”。很多团队一上来就追求技能数量多做了几百个技能最后发现维护成本爆炸。少而精、覆盖面广、边界清晰的技能库效果远好于一堆细碎的小技能。第二技能描述的投资回报率极高。你在skill.yaml的description和triggers上多花的每一分钟都会在后续无数次调用中持续回报。与其频繁调试模型参数不如先把每个技能的描述打磨到“读者一看就知道该不该选它”的程度。第三Agent 开发的壁垒不在模型有多强而在流程有多稳。同样一个模型有人拿它做出来的东西三天两头出错有人拿来就能稳稳支撑业务。差别不在 prompt 长短而在有没有把执行路径、异常处理、降级策略这些东西都固化下来。agent-skills 给我的最大启发是AI 应用的竞争力正在从“调用模型的能力”转向“定义流程的能力”。如果你也在做 Agent 类应用不妨从今天开始试着把项目里最常用的几个能力抽成标准的技能模块哪怕不用全自动路由先做一套手动触发的技能库我也相信你会在三个月后明显感受到沉淀带来的复利。