公司动态

AI Agent技能化:从“能聊”到“能用”的工程化实践

📅 2026/8/28 10:39:39
AI Agent技能化:从“能聊”到“能用”的工程化实践
读 addyosmani/agent-skills为什么你的 Agent 总是“能聊不能用”如果你最近在折腾 AI Agent大概率经历过这个怪圈Demo 里聊得挺好一旦接入真实业务模型就开始“自由发挥”。让它总结一份周报它把格式写成了散文让它调用内部工具它把参数传得乱七八糟好不容易把 Prompt 调到能用了换一个模型版本效果立刻衰减。于是很多人给出结论Agent 不行模型不够聪明。但更接近真相的判断是问题不在模型而在你根本没有把 Agent 的“能力”组织起来。你给它的是几段 Prompt 加上一堆散装函数它当然只能给出散装的结果。这正是 addyosmani/agent-skills 这类项目试图解决的核心问题——把 Agent 的能力按“技能Skill”的方式建模、注册、复用和验证。这篇文章不会只翻译 README。我会从工程实践的角度拆解为什么技能化是 Agent 开发的必经之路一个技能包应该包含哪些部分如何实现技能注册与调用以及实际项目中容易踩的坑。无论你最终选择哪个 Agent 框架这套方法论都通用。1. Agent 技能化要解决的真实痛点先说一个容易被忽略的事实传统软件开发里代码组织是基本功。你会把公共逻辑抽成函数把模块拆成服务把配置放到配置中心。但到了 Agent 开发很多人直接退回了“把所有逻辑塞进一个超长 Prompt”的原始状态。这带来的问题是非常具体的第一能力无法沉淀。你在项目 A 里费了大力气调好的“文档分类”逻辑到了项目 B 里只能复制粘贴一段 Prompt然后重新调试。Prompt 不是代码它没有接口没有版本没有测试用例别人根本不知道它接受什么输入、输出什么格式。第二上下文快速失控。一个复杂的 Agent 任务往往需要多个工具配合。如果所有技能说明都塞进系统 Prompt模型的上下文窗口很快就被占满。更要命的是技能描述之间可能互相冲突模型在关键决策时采用哪一条完全不可控。第三工具调用不稳定。很多人把“技能”等同于“写一个 Python 函数”这是另一个误解。函数本身不会告诉模型什么时候该调用它、参数怎么填、输出怎么处理。你必须在每次请求时把这些信息喂给模型而这一过程如果不结构化模型就会反复出错。第四效果无法验证。普通代码有单元测试有 CI/CD有线上监控。Agent 的技能逻辑呢大多数团队没有。改了 Prompt 不知道影响谁上线之后不知道哪个环节退化只能靠人工点几个 Case 感受一下。所以Agent 技能化的本质是把模型能力从“靠感觉调试”变成“可组合、可测试、可运维的工程资产”。这不是为了赶时髦而是当你的 Agent 从玩具走向生产环境时绕不开的一步。2. 什么是 Agent Skill概念与边界要理解 skill先要搞清楚它和几个容易混淆的概念之间的关系。先给一个通俗定义Agent Skill技能是 Agent 可调用、可复用、可验证的能力单元。它约定了“这个能力是干什么的”“接收什么输入”“产出什么输出”“模型应该怎么使用它”。它和 Tool工具的区别是很多人的第一个困惑点。概念核心定位典型形态关键差异Prompt指导模型行为的文本系统提示词无结构依赖模型理解Tool / Function可执行的代码函数Python/TypeScript 函数有输入输出但缺少使用说明Function Calling模型与函数之间的调用协议结构化 JSON schema是机制不是能力单元Skill能力封装单元manifest 指令 实现自带元数据、用法说明、验证逻辑用一个人力部门的类比来理解Tool 相当于“会干活的人”Function Calling 是“他和别人沟通的标准话术”而 Skill 是一个完整的“岗位说明书 培训文档 考核标准”。光有人和话术还不够你还得告诉他岗位职责是什么、什么情况下该做什么、完成得怎么样算合格。Skill 在 Agent 运行流程中的位置可以用一个简化的四阶段来描述理解Comprehension模型解析用户意图。规划Planning模型判断需要调用哪些技能以什么顺序调用。执行Execution系统解析技能参数调用技能实现拿到结果。验证Verification检查技能输出是否符合预期失败则重试或上报。Skill 主要作用在后三个阶段。它给规划阶段提供“能力清单”给执行阶段提供“参数契约”给验证阶段提供“判断标准”。所以如果你只是写了一个函数却没有告诉模型“什么时候用它、怎么填参数、结果怎么解读”那你做的还不是 Skill。你只是造了一个没人看得懂的工具。3. 技能包的结构设计一个 Skill 应该包含什么在 agent-skills 这类项目的设计思路里技能不是一个裸函数而是一个“包Package”。一个完整的技能包通常包含五个部分3.1 元数据Metadata元数据是技能包的“身份证”。它告诉系统这个技能叫什么、版本是多少、属于什么分类、作者是谁。元数据最重要的两个字段是name和version它们是技能调用的唯一标识。{ name: weekly_report_analyzer, version: 0.1.0, description: 解析周报内容提炼关键进展、风险和下一步计划, tags: [report, analysis, office], author: team-platform, license: internal }3.2 输入输出契约Schema这是技能包最容易偷懒、也最不应该偷懒的部分。一个技能必须明确声明接收什么类型的参数、哪些字段必填、输出什么结构。这样做有两个直接好处第一模型在 Function Calling 时有了严格的参数约束不再乱填第二执行端可以做参数校验而不是等模型报错。{ input_schema: { type: object, properties: { report_text: { type: string, description: 周报的原始文本内容 }, focus_areas: { type: array, items: { type: string }, description: 需要重点关注的方向可选 } }, required: [report_text] }, output_schema: { type: object, properties: { summary: { type: string, description: 本周核心进展摘要 }, risks: { type: array, items: { type: string } }, next_steps: { type: array, items: { type: string } } } } }3.3 使用指令Instruction / Prompt Template这是“告诉模型怎么用这个技能”的部分。很多实现把它写成一段自然语言模板在调用时动态填充参数再交给模型。你是一个周报分析助手。请根据用户提供的周报原始内容输出结构化分析结果。 分析要求 1. 提取本周最重要的 3-5 条关键进展 2. 识别可能阻碍项目推进的风险点 3. 给出可执行的下一步计划 输出要求 - summary 不超过 80 字 - risks 和 next_steps 每条不超过 30 字 - 禁止编造原文没有出现的信息 原文 {{report_text}}使用指令的核心价值是它把“模型如何理解任务”这件事从全局 Prompt 中剥离出来只在使用该技能时注入。这从机制上避免了上下文污染。3.4 执行实现Implementation这是真正跑代码的部分。它可以是一段本地 Python 函数、一个 HTTP API 调用、数据库查询甚至是另一组 Agent 的编排逻辑。关键要求是实现必须符合输入输出契约并且有清晰的错误处理。3.5 验证用例Validation Cases技能包还应该包含一组“冒烟测试用例”。至少要有一个正常输入的样例、一个边界输入的样例、一个错误输入的样例。这组用例不仅在开发时有用在 Agent 上线后的回归测试里更关键。4. 技能注册与调用核心机制实现理解了技能包的结构之后下一步就是把它接入 Agent 运行流程。这里我给出一套通用的注册-发现-选择-执行机制不依赖某个特定框架你可以照着移植到自己的代码里。4.1 技能注册Registry注册中心是技能包的统一入口。它负责装载技能、校验技能格式、提供查询和调用接口。# 文件路径skill_registry.py from typing import Dict, List, Optional, Callable class DuplicateSkillError(Exception): 技能重名异常 class SkillNotFoundError(Exception): 技能不存在异常 class BaseSkill: 技能基类所有技能都需要继承并实现以下接口 name: str description: str def validate_input(self, payload: dict) - dict: 校验输入参数返回清洗后的参数。校验失败时抛出 ValueError return payload def execute(self, payload: dict, context: dict) - dict: 执行技能主体逻辑返回符合输出契约的结果 raise NotImplementedError class SkillRegistry: 技能注册中心注册、发现、调用 def __init__(self): self._skills: Dict[str, BaseSkill] {} def register(self, skill: BaseSkill) - None: if not skill.name: raise ValueError(Skill name cannot be empty) if skill.name in self._skills: raise DuplicateSkillError(fSkill {skill.name} already registered) self._skills[skill.name] skill def list_skills(self) - List[dict]: 返回全部技能的摘要信息供 Agent 规划阶段使用 return [ { name: skill.name, description: skill.description, } for skill in self._skills.values() ] def discover(self, query: str, limit: int 5) - List[dict]: 按关键词发现技能。生产环境可以替换为向量检索。 query query.lower() matched [] for skill in self._skills.values(): haystack f{skill.name} {skill.description}.lower() if query in haystack: matched.append({name: skill.name, description: skill.description}) return matched[:limit] def execute(self, skill_name: str, payload: dict, context: dict) - dict: if skill_name not in self._skills: raise SkillNotFoundError(fSkill {skill_name} not found) skill self._skills[skill_name] cleaned_payload skill.validate_input(payload) return skill.execute(cleaned_payload, context)这段代码的逻辑很直白register负责装载discover给 Agent 提供技能清单execute统一执行入口。核心设计是BaseSkill基类它强制每个技能实现validate_input和execute从结构上保证“契约优先”。4.2 从 Manifest 加载技能现实项目中技能通常不是一个个手动 register而是从配置目录自动加载。目录下每个技能包都有一个 manifest.json加载器负责扫描目录、解析 manifest、实例化技能类。# 文件路径skill_loader.py import json from pathlib import Path def load_skills_from_directory(skills_dir: str, registry) - int: 扫描 skills_dir 下的所有子目录 读取 manifest.json动态加载技能模块并注册到 registry。 base_dir Path(skills_dir) loaded_count 0 for manifest_path in base_dir.glob(*/manifest.json): with open(manifest_path, r, encodingutf-8) as fp: manifest json.load(fp) skill_name manifest.get(name) main_module manifest.get(main, skill.py) module_path manifest_path.parent / main_module # 这里用 importlib 动态导入技能实现 import importlib.util spec importlib.util.spec_from_file_location( fskill_{skill_name}, module_path ) module importlib.util.module_from_spec(spec) spec.loader.exec_module(module) skill_class getattr(module, Skill, None) if skill_class is None: raise ValueError(fSkill {skill_name} must define a Skill class) skill_instance skill_class() registry.register(skill_instance) loaded_count 1 return loaded_count这套设计的好处是技能团队可以独立开发一个技能包丢进目录就能被加载互不干扰。本质上它把“Agent 能力模块化”做成了类似插件系统的机制。5. 完整示例从定义到调用跑通一个技能下面用“周报分析”这个典型场景完整演示从技能定义到 Agent 调用的全过程。这个场景足够简单一眼能看懂又包含输入校验、模型调用、结构化输出三个关键环节。5.1 定义输入输出 Schema沿用前面给出的manifest.json我在这里把它整理成完整结构包含prompt_template字段。{ name: weekly_report_analyzer, version: 0.1.0, description: 解析周报内容提炼关键进展、风险和下一步计划, tags: [report, analysis, office], main: skill.py, input_schema: { type: object, properties: { report_text: { type: string, description: 周报的原始文本内容 } }, required: [report_text] }, output_schema: { type: object, properties: { summary: { type: string }, risks: { type: array, items: { type: string } }, next_steps: { type: array, items: { type: string } } } }, prompt_template: 你是一个周报分析助手。\n请根据用户提供的周报原始内容输出结构化分析结果。\n\n要求\n1. 提取最重要的 3-5 条关键进展\n2. 识别可能阻碍项目推进的风险点\n3. 给出可执行的下一步计划\n\n输出要求\n- summary 不超过 80 字\n- risks 和 next_steps 每条不超过 30 字\n- 禁止编造原文没有出现的信息\n\n原文\n{{report_text}}\n }5.2 实现技能类技能类继承第 4 节的BaseSkill实现校验和执行逻辑。# 文件路径skills/weekly_report_analyzer/skill.py import json import os from skill_registry import BaseSkill # 这里假设你的项目接入了某个大模型 SDK按实际项目替换 from llm_client import chat_completion class Skill(BaseSkill): name weekly_report_analyzer description 解析周报内容提炼关键进展、风险和下一步计划 def validate_input(self, payload: dict) - dict: if report_text not in payload: raise ValueError(missing required field: report_text) report_text payload[report_text].strip() if len(report_text) 10: raise ValueError(report_text is too short) return {report_text: report_text} def execute(self, payload: dict, context: dict) - dict: template self._load_prompt_template() prompt template.replace({{report_text}}, payload[report_text]) response chat_completion( modelcontext.get(model, default-model), messages[ {role: system, content: 你只输出合法的 JSON不输出任何其他内容。}, {role: user, content: prompt}, ], response_format{type: json_object}, ) content response[choices][0][message][content] result json.loads(content) # 对输出做二次校验保证结构存在 return { summary: result.get(summary, ), risks: result.get(risks, []), next_steps: result.get(next_steps, []), } staticmethod def _load_prompt_template() - str: manifest_path os.path.join(os.path.dirname(__file__), manifest.json) with open(manifest_path, r, encodingutf-8) as fp: manifest json.load(fp) return manifest[prompt_template]这里的要点有两个第一validate_input在调用模型之前就把不合法输入挡掉了避免浪费 token第二execute里使用response_format强制模型输出 JSON并做二次解析。实际项目中模型输出可能不是合法 JSON这一层必须要做容错。5.3 在 Agent 主流程中调用接下来把这些串联到一个最小的 Agent 主流程里。这里的重点是Agent 先通过list_skills获取技能清单再在规划阶段决定调用哪个技能最后通过execute执行。# 文件路径agent_main.py from skill_registry import SkillRegistry, SkillNotFoundError from skill_loader import load_skills_from_directory from skills.weekly_report_analyzer.skill import Skill as WeeklyReportSkill def main(): registry SkillRegistry() # 方式一手动注册技能 registry.register(WeeklyReportSkill()) # 方式二从目录自动加载技能二选一即可 # load_skills_from_directory(skills, registry) # 1. Agent 获取技能清单 print(可用技能:) for skill_info in registry.list_skills(): print(f - {skill_info[name]}: {skill_info[description]}) # 2. 模拟大模型规划阶段选择技能 user_input 帮我分析这周的周报看看有没有风险 matched_skills registry.discover(周报 分析) print(f\n根据用户意图匹配到的技能: {matched_skills}) # 3. 执行技能 test_report 本周完成了订单模块的重构接口响应时间从 800ms 降到 300ms。 同事离职手头订单导出功能交接进度滞后可能影响月底对账。 下周计划推进优惠券系统设计需要产品部确认规则。 try: result registry.execute(weekly_report_analyzer, {report_text: test_report}, {}) print(\n技能执行结果:) print(f摘要: {result[summary]}) print(f风险: {result[risks]}) print(f下一步: {result[next_steps]}) except SkillNotFoundError as e: print(f技能调用失败: {e}) if __name__ __main__: main()5.4 运行与预期输出python agent_main.py预期输出类似这样可用技能: - weekly_report_analyzer: 解析周报内容提炼关键进展、风险和下一步计划 根据用户意图匹配到的技能: [{name: weekly_report_analyzer, description: 解析周报内容提炼关键进展、风险和下一步计划}] 技能执行结果: 摘要: 完成订单模块重构性能明显提升但导出功能交接滞后存在风险。 风险: [导出功能交接进度滞后可能影响月底对账] 下一步: [推进优惠券系统设计, 与产品部确认规则]这里要强调一点实际输出取决于底层模型。上面的输出是理想情况。判断运行成功的关键不是看输出文案多漂亮而是看三条链路是否都完成了技能清单能否正常列出技能发现能否根据查询词匹配到技能技能执行能否返回符合output_schema的 JSON 结构如果失败优先排查顺序是先看registry是否正确注册再看validate_input是否通过最后看模型返回内容能否被json.loads解析。这三个环节是这类系统 90% 问题的发生地。6. 技能化改造前后效果对比为了让你更直观地理解技能化改造的价值我从四个维度做了对比。对比维度传统 Prompt 函数方式Skill 技能化方式能力复用复制粘贴 Prompt每次重新调技能包目录化一次开发多处引用参数稳定性模型自由发挥经常漏参、错参参数契约约束校验层兜底上下文开销所有技能说明都堆在系统 Prompt按需注入上下文精简回归测试无改一处可能影响多处每个技能自带用例可单独验证团队协作改 Prompt 靠口头同步技能包版本化管理变更可追溯需要特别说明的是技能化不是灵丹妙药。它解决的是工程组织问题而不是模型推理能力问题。如果模型本身没有能力完成某类任务技能化也不能无中生有。但反过来说如果模型能力已经达标只是你的调用方式太随意技能化的收益会非常明显。从这类项目的设计思路看更稳妥的判断是对于已经跑通 Demo、准备进入生产环境的 Agent 项目现阶段最值得投入的不是继续调 Prompt而是把能力做一次“结构化重组”。重组之后后续的调试、评测、迭代效率会指数级提升。7. 常见问题与排查思路技能化落地过程中下面几个问题出现频率最高。我把它们整理成排查表遇到问题时可以直接对照。问题现象可能原因排查方式解决方案技能注册时报重名两个技能包使用了相同 name检查 manifest.json 的 name 唯一性建立技能命名规范注册中心增加名称校验模型调用时参数乱填schema 不够严格或模型未感知 schema查看请求日志中的 function call 参数在系统提示中明确“严格按 schema 填写”必要时由代码层兜底清洗技能输出频繁 JSON 解析失败模型返回了非 JSON 内容打印模型原始 response 日志启用 response_formatjson_object增加重试与解析容错上下文仍然很长所有技能描述都注入系统提示统计每次请求的 token 用量按用户意图动态检索技能只注入相关技能说明换模型后技能效果退化指令模板对模型风格敏感用同一组测试用例对比两个模型指令模板尽可能使用通用表达建立多模型回归测试技能加载失败但无明确报错manifest 路径或模块名拼写错误打印加载过程中的异常堆栈在 loader 中为每个技能包增加独立 try-except失败不阻塞整体加载改技能后线上效果不可控技能变更未走版本管理记录技能版本与调用日志的关联引入技能包版本和灰度策略先小流量验证再全量这些问题的共同根因绝大多数不是模型不行而是技能包的定义不规范、注册链路不透明、输出不可观测。先修工程问题再去怪模型。8. 最佳实践与工程建议最后这部分是我认为真正有价值的工程经验沉淀。如果你决定把 Agent 技能化落地到真实项目中下面七条建议可以直接拿来用。8.1 技能粒度宁小勿大一个技能只做一件事。周报分析就只做周报分析不要把“数据分析 邮件发送 日历提醒”塞进同一个技能。技能粒度太粗会导致复用率低、测试困难、模型选择混乱。好的技能粒度应该像 Unix 命令每个命令简单、明确复杂任务通过编排组合完成。8.2 输入 schema 越严格越好不要为了省事把输入定义成一个庞大的自由文本字段。每多一个结构化的字段模型在 Function Calling 时就有了多一分约束。字段描述也要写清楚例如description: 周报原始文本不超过 5000 字这比“用户周报内容”有用得多。8.3 使用指令三原则职责单一只包含当前技能的使用方法不要顺带教育模型“如何做人”。有输入输出示例在指令模板中给一个 few-shot 示例模型结构稳定性显著提升。与全局 Prompt 解耦不要把技能说明写死在系统 Prompt通过动态拼接注入。8.4 技能包要版本化技能的变更应该像代码变更一样走 review 和发布流程。在 manifest.json 里维护版本号调用日志里记录技能版本一旦线上效果回退可以快速定位是哪一次技能变更出了问题。有条件的团队可以在技能包目录上直接用 Git 管理。8.5 测试要跟上每个技能包至少包含三个用例happy path正常输入验证输出结构。edge case空字符串、超长文本、缺失字段。error path非法输入验证是否抛出明确异常。这些用例应当在技能注册时自动执行一次注册失败就拒绝上线。这能解决“改技能导致线上效果不可控”的最大痛点。8.6 安全边界要明确技能本质上是“带参数的代码执行器”。如果技能包来自外部必须做权限隔离。至少要做到技能代码在受限环境运行不能直接访问生产数据库。技能涉及的文件读写路径做白名单校验。包含网络请求的技能要记录完整的请求日志便于审计。涉及敏感数据的技能不要在日志中打印原始输入。8.7 上下文开销要量化不要靠感觉判断“上下文好像变长了”。应该在每次调用时记录完整的 token 统计把系统提示、技能说明、用户消息、模型输出的 token 分开统计。这样你才能知道技能化的收益是不是被上下文膨胀抵消了。如果发现技能说明开销大优先做技能检索RAG 式不要让所有技能常驻提示。9. 总结与下一步实践方向今天我们围绕 addyosmani/agent-skills 所代表的方向把 Agent 技能化从概念到落地拆了一遍。核心可以浓缩成三句话第一Agent 从“能聊”走向“能用”的关键不是换更大的模型而是把能力的组织方式工程化。第二技能不是函数也不是 Prompt。它是“元数据 输入输出契约 使用指令 执行实现 验证用例”的结构化单元。判断一个技能写得好不好就看这五部分是否齐全、是否独立、是否可测试。第三技能注册、发现、执行三件套是 Agent 工程的骨架。注册保证能力可见发现保证能力可选执行保证能力可控。这三件事不依赖任何具体框架可以先用 Python 几十行代码跑通再迁移到 LangChain、Semantic Kernel 或其他 Agent 框架上。如果你看完这篇文章想动手实践建议按这个顺序挑一个日常被反复调用的原子能力比如文本摘要、信息抽取或格式转换。按照第 3 节的结构给这个能力写一份完整的技能包。用第 4 节的代码搭一个最小注册中心把技能接进去。写三个测试用例验证正常、边界、异常三种情况。然后把这个技能接入你现有的 Agent 流程对比改造前后的效果。做完这一轮你就会发现Agent 开发里大量“玄学”问题本质上都是工程问题。技能化不是终点但它是一个很值得先迈出的方向。后面如果你想继续深入可以关注技能编排多个技能如何组合、技能评测自动化评估技能效果、技能共享团队级技能市场这几个方向它们都是建立在今天这套基础之上的进阶话题。建议把思路和方法保存下来等你要把 Agent 接入真实业务时回来对照排查表逐项检查能少走很多弯路。