
做Agent开发这段时间我最大的体会是模型本身不是瓶颈真正让人头疼的是“同样的功能每次都要重新造一遍”。你让Agent查个天气、算个账、写个会议纪要每个项目都要重新写提示词、调参数、测逻辑。直到我把技能skills这一层抽象出来整个开发节奏才顺了。这就是我从“agent-skills”这个项目里沉淀下来的一套方法把高频能力封装成独立技能包让Agent按需加载、即插即用。这篇文章就跟大家聊聊技能库的设计思路、完整实战流程以及我踩过的那些坑。适合正在做Agent应用开发、想给智能体加上“手脚”的同学参考哪怕你是刚接触Agent的新手也能照着做出第一个可用的技能。1. Agent技能是什么先搞懂概念再动手1.1 从“会说话”到“会干活”差距就在技能上大模型本身只会“说话”你问它什么它答什么但让它“做事”——比如读取一个本地文件、调用一个API、操作一个数据库——它就需要外部能力的支持。技能就是把这些外部能力包装成一个标准化的单元让模型知道“什么时候可以用、怎么用、用了之后会得到什么”。我习惯把技能理解成“给Agent装上的插件”。没有技能时Agent是一个只能聊天的顾问有了技能它就变成一个能动手的助理。这个助理知道什么时候该掏出哪个工具也知道工具用完之后怎么把结果整理给你。1.2 技能、工具、工作流、记忆别混为一谈做Agent开发时这几个概念经常被搞混。我一开始也在这方面吃过亏结构设计得乱七八糟后来干脆理了一张对照表团队里新人也靠这张表快速上手。概念定义典型例子生命周期工具Tool单一功能的执行单元无状态查天气API、发HTTP请求短调用完即结束技能Skill围绕一个目标任务封装的能力组合可能包含多个工具和提示词会议纪要技能调用转写工具 文本Summarize 行动项提取中每次调用都会重新组织工作流Workflow多个步骤的固定编排有明确的先后顺序每天早上自动拉取邮件→生成摘要→推送IM长持续运行记忆MemoryAgent对话过程中的历史信息存储与检索用户偏好记录、历史对话摘要持久跨会话保留通过这个表能看出技能是介于工具和工作流之间的产物它比工具更“高维”因为它包含任务规划和上下文判断它比工作流更“轻量”因为技能的调用是动态的模型根据当前对话决定是否启用而不是写死的流程。1.3 为什么之前的Agent做不了技能现在行了三年前你跟模型说“你去调一下这个API”它大概率会把API文档背给你听但不知道该传什么参数。现在不一样了模型已经具备比较强的“意图识别 参数抽取”能力它能从一句“帮我整理一下今天的需求会议记录”里自动推导出“需要调用一个名为 summarize_meeting 的技能并填入参数 meeting_text”。这背后的逻辑是模型推理能力的提升但这不等于技能就能自动做好。技能的设计质量直接决定了Agent的最终表现。同一个会议纪要需求技能描述写得好的模型一次就能命中描述写得含糊的模型可能调去查天气了。提示不要指望模型自己“发明”技能。你给它什么样的技能目录、什么样的描述文本它就有什么样的能力边界。技能库的设计本质上是在定义Agent的能力上限。2. 技能库设计思路像搭积木一样规划Agent能力2.1 一个技能只干一件事别把流程塞进去这是我在项目里定的第一条规矩。很多新手喜欢把“写周报”这个技能做得无所不包读取待办事项、分析项目进度、生成PDF、发送邮件一个技能全干完。后果就是技能描述变得特别长模型很难判断什么情况下该调用它。更麻烦的是一旦其中一个环节出问题整个技能就废了排错成本极高。我建议的拆分原则是“动词 宾语”的最小粒度不要“整理周报并发送邮件”要“提取待办事项” / “生成周报文本” / “发送邮件”这样拆开之后模型可以自由组合今天用户说“周报先别发我想改一下”它就能只调用前两个技能不会因为“发邮件”这个动作卡住整个流程。2.2 技能的目录结构长什么样一个技能就是一个文件夹里面至少包含两块内容技能描述文件和执行代码。我目前用的结构是skills/ summarize_meeting/ SKILL.md scripts/ main.py assets/ prompt_templates/ summary_template.txt这里SKILL.md是技能的“身份证”负责告诉模型这个技能是干什么的、什么情况下用、参数怎么传。scripts目录放实际执行代码assets目录放一些辅助资源比如提示词模板、配置文件。这个结构最大的好处是技能天生就是可移植的。你在这个项目里验证过的技能复制到另一个项目里只要依赖环境一致就能直接用。我这个agent-skills项目的很多技能现在已经在三个不同场景里跑着了。2.3 技能描述怎么写才能让模型准确命中技能描述是整个设计里最关键的文本。模型不看你的代码实现只看你的描述来决定“要不要调用这个技能”。描述写得好不好直接决定了技能的命中率和误触发率。我总结了四个写法要点触发场景明确用“当用户提到XXX时”开头而不是笼统的功能介绍。参数说明完整列出每个参数的名称、类型、含义、是否必填。示例对话兜底给一个“用户说啥 → 技能怎么回应”的示例模型会参考这个格式来匹配意图。负面提示防误触发明确写“不要用于XXX场景”。我早期写过一个“代码审查”技能描述里只写了“对代码进行审查并输出问题清单”结果模型在用户聊到“我这段代码为什么报错”的时候也调用了它。问题是用户只想快速解决报错并不想要完整的问题清单。后来我在描述里加了一句“仅当用户明确要求全面代码检查时使用单纯排错请使用debug_analyze技能”误触发率马上下来了。3. 实操实战从零实现一个“会议纪要与行动项提取”技能3.1 技能目标拆解以最常见的“会议纪要”场景为例。这个技能要解决的核心需求是用户丢过来一段会议录音转写文本技能自动完成三件事——生成分章节摘要、提取行动项、识别责任人和截止时间。这个需求拆开后技能的输入输出就很清晰了输入会议转写文本长文本可能5000字以上输出结构化JSON对象包含摘要列表、行动项列表、责任人、截止日期拆解这一步的价值在于它决定了技能的代码该怎么写。我当时没有选择让模型一次性生成全部输出而是拆成两步先用一个模型调用做摘要再用另一个模型调用做行动项提取。这样每步的上下文比较短输出质量稳定得多。3.2 skill.md的完整内容示例以下是我在这个项目里实际使用的SKILL.md你可以直接抄作业再根据自己的场景微调--- name: summarize_meeting description: 当用户提供会议录音转写文本或完整会议记录时自动生成结构化会议纪要包括分章节摘要、行动项、责任人与截止时间。 when_to_use: 用户明确提出总结会议写会议纪要提取待办事项或粘贴一段较长的会议文本并要求整理时。 input_params: meeting_text: type: string description: 会议转写原文或详细记录 required: true meeting_title: type: string description: 会议名称可选默认取文本前20字 required: false not_for: 不要用于单句提问的简单摘要不要用于非会议场景的文本总结。 example: user: 这是我今天客户会的转写帮我整理一下纪要... assistant: 已生成会议纪要共3个章节提取行动项4条其中2条今天截止。 --- # 执行说明 1. 调用 scripts/process_meeting.py 2. 脚本会先对长文本做分段预处理再分别调用摘要和行动项提取 3. 最终输出标准JSON并转成易读的Markdown格式返回给用户写的时候有个细节not_for字段我最初没加后来发现模型会把这个技能用在“帮我总结一下这篇文章”的场景里因为它把“总结”和“会议纪要”混淆了。加上负面提示之后这个问题基本消失了。3.3 技能代码从纯模型调用到工程化处理脚本的核心逻辑我贴几个关键片段完整代码在项目仓库里。先看分段预处理会议转写文本经常超长直接丢给模型会爆上下文所以需要切分def split_meeting_text(text: str, chunk_size: int 3000) - list[str]: 按段落边界切分会议文本避免切断语义完整的句子 paragraphs text.strip().split(\n) chunks [] current [] current_len 0 for para in paragraphs: para_len len(para) if current_len para_len chunk_size and current: chunks.append(\n.join(current)) current [] current_len 0 current.append(para) current_len para_len 1 if current: chunks.append(\n.join(current)) return chunks注意这里的切分是按段落边界做的而不是简单按字符数硬切。我一开始图省事直接按长度切片结果经常出现一句话被切成两半模型在摘中间内容的时候完全乱套。按段落切分这个细节让输出质量提升了一个档次。摘要和行动项提取的核心是提示词模板。我把它独立放在assets目录里方便批量修改。摘要部分的模板长这样你是会议摘要助手。根据以下会议内容生成分章节摘要。 要求 - 每个章节提炼核心观点使用不超过30字的总结性句子 - 章节按讨论主题划分不要按时间顺序硬拆 - 使用中文输出 会议内容 {{chunk_text}} 输出格式 ## 章节一章节标题 - 核心观点1 - 核心观点2这里用模板而不是直接在代码里写死字符串是为了后面改提示词不用动Python代码。我在实际迭代中改提示词的次数远超改代码的次数模板独立出来确实省了不少事。最后把两步调用的结果拼装成一个标准JSONdef format_output(summary_text: str, action_items: list[dict]) - dict: return { summary: summary_text, action_items: action_items, meta: { source_length: len(meeting_text), generated_at: datetime.now().isoformat() } }一个很重要的工程细节输出里带上meta字段记录输入长度和生成时间。这在后面调优排查的时候特别有用能快速定位某天某个文本为什么输出异常。3.4 技能接入Agent并完成测试技能文件写好后接入Agent的方式取决于你用的框架。如果你用的是自己搭的Agent核心逻辑就是让模型在每次对话时先决策要不要调用技能再传参执行。我自己的做法是维护一个技能注册表def discover_skills(skills_dir: str) - list[dict]: skills [] for skill_dir in skills_dir.iterdir(): skill_meta yaml.safe_load((skill_dir / SKILL.md).read_text().split(---)[1]) skills.append({ name: skill_meta[name], description: skill_meta[description], when_to_use: skill_meta.get(when_to_use, ), not_for: skill_meta.get(not_for, ), params: skill_meta.get(input_params, {}) }) return skills每轮对话把技能目录里的name、description、when_to_use拼进系统提示词让模型决定是否调用。这样就实现了技能的“即插即用”。初版测试时我给模型喂四种输入一句话请求、多行转写文本、带姓名的约定内容、完全无关的闲聊分别验证“正常命中”“正常执行”“参数抽取完整”和“不误触发”四种情况。光这一步就把技能描述里两个含糊的措辞揪了出来。4. 技能生命周期管理从装载到退役4.1 技能数量多了之后先解决“选择困难症”技能只有三五个的时候模型很容易选。但等你积累了二三十个技能模型开始“犯迷糊”了。我在项目里出现过一次特别搞笑的情况用户说“帮我算一下这个月的支出”模型居然调用了“计算BMI”技能。排查之后发现问题出在技能注册表没有做分层或权限控制。后来我引入了一个简单的分组逻辑让模型只在一级分组内选择[技能分组] - 办公效率类meeting_summary, email_draft, todo_extract - 数据分析类expense_cal, data_visual, csv_parser - 生活服务类weather_query, bmi_calc, calendar_event模型先判断用户请求属于哪个分组再在分组内选择具体技能。这个分层选择策略让误触发率下降了大概一半。4.2 技能更新迭代的版本管理技能不是写完就完了它需要随着模型版本升级和业务需求变化持续更新。在我这个项目里每个技能目录都有自己独立的Git历史。更新流程是这样的修改SKILL.md之前先手动跑一遍旧的典型案例留存结果修改之后再跑一遍同样的案例对比输出差异如果输出质量提升就提交并在commit message里写清楚“改了什么描述、为什么改”这个“先留存基线再改”的习惯帮了我大忙。有几次我改描述觉得只会变好结果跑测试发现反而变差了全靠基线对比才能及时发现回退。4.3 技能之间的去重与冲突消解技能一多很容易出现两个技能干类似的事。比如我同时有“weekly_report”和“work_summary”两个技能用户说“帮忙整理一下这周的工作”模型有时候调这个有时候调那个输出格式还不一样。我的处理原则是合并同类项保留更通用、更稳定的那一个把另一个的独有功能合并进来。与其让模型在两个模糊技能之间猜不如把能力合并成一个边界清晰的技能。4.4 建立技能的反馈评估体系我后来给技能库加了一个轻量的评估脚本每次技能调用后记录三个指标指标含义采集方式命中率模型在合适场景下调用该技能的比例日志中匹配触发场景成功率技能执行后返回有效结果的比例检查输出JSON是否完整用户满意度用户对技能输出内容的反馈手动抽样打分或用LLM自动打评估不是为了应付汇报而是指导下一步优化方向。比如我的行动项提取技能最初成功率只有七成排查发现是部分转写文本里责任人是代词“他”模型无法对应到具体人名。后来在技能描述里加了一条“如果行动项责任人不明确请标注为‘待确认’”成功率拉到九成以上。5. 实操中的高频问题与排查技巧5.1 技能描述写得越详细越好吗我一度以为技能描述越长模型理解越准确结果伤害很大。SKILL.md超过1500字之后模型反而抓不住重点。因为技能描述和系统提示词一起塞在上下文里太长的描述会稀释模型的注意力。经验值是一条技能描述尽量控制在500字以内能用表格和关键词就不用长句。删掉所有“为了”“旨在”这类废话直接写触发条件和输出格式。5.2 技能输出格式不统一的处理最开始写技能的时候每个技能的返回格式都是自己说了算。后来做Agent调用的时候发现需要写一堆if-else来兼容不同技能的输出格式非常痛苦。建议所有技能统一返回JSON结构至少包含三个字段{ status: success | error, data: { }, message: 给用户看的简短说明 }这个习惯越早养成越好。我后来新写技能第一件事就是把这套格式的模板拷贝进去绝不自行发挥。5.3 技能之间循环调用怎么破有一次我跑了半天没出结果查看日志发现Agent陷入了死循环技能A调用技能B技能B又调用技能A。上下文都爆了还在互相踢皮球。排查之后发现是我把一个文本处理任务的步骤拆得太细两个技能对“处理中”的状态描述互相诱导模型觉得对方更合适继续处理。解决方法是给每个技能加一个“调用深度”限制或者让技能执行后直接返回结果绝不再发起另一个技能的调用。现在我的技能执行原则是技能内部只做自己的事不跨技能指挥。5.4 同一个技能在不同模型上表现差异大这个问题容易被忽视。同一个SKILL.md在GPT-4o上跑得好好的换到开源小模型上就完全失灵。主要原因是小模型对长文本描述的遵循能力弱指令太多就hold不住了。应对策略是给轻量模型准备一个精简版描述只保留触发条件、参数、输出格式把详细示例删掉。我现在项目里每个技能都有两个版本的SKILL.md一份是standard版给能力强的模型一份是lite版给轻量模型。5.5 常见问题速查表问题表现排查方向解决方案技能不触发用户需求已经很明确模型还在自己回答检查when_to_use表达是否和用户口语习惯匹配在描述里补充更多真实用户问法作为正向示例技能误触发无关场景频繁调用检查not_for是否覆盖了容易混淆的场景增加负面示例明确不处理哪些情况参数提取错误技能被调用但传参缺三落四检查参数定义是否规范是否有默认值给必填参数增加“如果用户未提供请明确追问”的说明执行报错脚本偶发失败排查困难检查python脚本是否捕获了异常统一用try-except包裹主流程记录错误日志到本地文件输出格式乱JSON解析失败返回大段文本检查代码里是否强制json.dumps还是直接返回了模型原文后处理强制用json.loads校验不合法就重新生成一次6. 技能库的进阶方向与我的经验小结技能这件事往深了做还能和向量检索结合实现技能的语义自动匹配也可以给技能配置多模态输入直接处理图片截图类的会议内容。我目前只在文本技能上跑通了完整的闭环多模态技能还在探索中但整体思路是一样的描述清晰、输入明确、输出标准。最后再分享一个小技巧技能的名字不要贪图“高级”。我原来给会议技能起名“intelligent_meeting_analyzer”结果模型在解析用户意图时经常识别不出这个名字和服务的关系。改成“meeting_summary”之后命中率立刻上去了。技能名字尽量用业务场景里的高频词汇模型对这类词的理解更稳。根据我个人经验Agent项目做得好不好七成功夫都在技能库的设计和维护上。模型能力就摆在那里区别在于你有没有用一套清晰的抽象把能力组织起来。先把这篇文章里的原则落地再根据你自己的业务场景慢慢迭代这套方法会越用越顺手。