十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent技能管理实战:从函数堆积到可编排的技能体系

Agent技能管理实战:从函数堆积到可编排的技能体系 做 Agent 这几年我最大的感受是真正拖垮一个智能体项目的往往不是模型能力不够而是代码仓库越来越像一座垃圾山。今天这个 agent-skills 相关的话题我还要从一次差点推翻重来的重构说起它几乎改变了我对 Agent 工程化的全部理解。半年前我们团队做了一个内部客服助手当时把所有的工具函数直接堆在一个 tools.py 里判断订单、查物流、退换货、催发货十几个函数。刚开始效果还行但随着业务规则增加模型频繁选错工具明明该查物流它偏去调了订单详情用户问一句“我手机什么时候到”它回一句“您的订单已签收”。后来我把所有函数拆成独立模块给每个模块写了详细描述效果才稳定下来。那段时间我反复琢磨的问题就是Agent 真正需要的不只是一堆函数接口而是一套可管理、可编排、可评估的“技能体系”。agent-skills 这个名字恰恰承载的就是这一整套方法论。如果你也在做 Agent 应用或者正被“工具越来越多、效果越来越差”困扰这篇文章应该能帮到你。我会从为什么必须做技能管理、技能怎么建模、怎么把多个技能编排成一个完整流程、到落地实测中踩过的坑完整拆开讲一遍。不会只给概念每一步都有我可以直接复现的代码片段和配置说明。1. Agent 技能碎片化为什么每个智能体团队最后都会绕回“技能管理”这条路1.1 函数堆积时代的崩溃现场先复盘一下我是怎么从“函数”走到“技能”的。项目早期每个业务操作就是一个 Python 函数用 tool 装饰器挂给大模型。问题爆发在函数数量超过 15 个之后。最典型的表现是意图混淆。比如我们有一个 query_order 和一个 query_logistics参数都是 order_id。模型经常在这个二选一里犯糊涂原因是两个函数的描述都写了“根据订单号查询信息”模型根本分不清哪个更匹配当前问题。还有更隐蔽的比如 get_refund_status 和 get_after_sale_detail业务上这两个其实是同一件事的不同视图但模型不知道于是出现了一个问题问两遍、拿到两套答案的尴尬局面。另一个大问题是上下文污染。每个工具执行完都会把原始结果塞回对话历史几个任务串下来上下文里塞满了 JSON。模型在长上下文中提取关键信息的准确率明显下降用户问“刚才那单退款到哪一步了”模型开始东拉西扯。这不是代码质量问题而是抽象层次出了问题。函数是给程序员复用的技能才是给 Agent 复用的。函数的输入输出是类型签名技能的输入输出是语义契约。这个认知转变是整个 agent-skills 设计的起点。1.2 技能与函数的本质区别我理解的“技能”是一个可以被 Agent 理解、调度、组合和评估的独立能力单元。判断标准是这个单元是否携带足够的自我描述信息让模型在不需要查看源码的情况下就知道它适合解决什么问题、需要什么输入、会产生什么影响。一个合格的技能至少要包含四层信息技能名称全局唯一用动宾短语一眼能看出它做什么技能描述不是写给人类看的注释而是写给模型看的“使用说明书”输入输出 schema明确每个参数的含义、格式、约束执行副作用声明这个技能是否会修改数据、是否需要权限、是否会调用外部服务这些信息由开发者维护但在运行时由模型消费。所以技能描述的质量直接决定了 Agent 的选择准确率。函数时代我们写 docstring 是给 IDE 提示看的技能时代我们写描述是给大模型做决策用的这两者的措辞逻辑完全不一样。还有一个被很多人忽略的点技能必须具备版本。函数接口变了改个签名然后全局搜调用处就能改完技能变了影响的是所有依赖它的流程编排和记忆缓存没有版本管理你根本不知道当前跑的是哪一套逻辑。这是 agent-skills 把技能当作“一等公民”来管理的一个核心原因。2. 把技能当作“一等公民”agent-skills 的核心建模思路2.1 技能描述文件写给模型看的说明书在 agent-skills 的理念里每个技能都有自己独立的描述文件。我习惯用 YAML 维护因为它比 JSON 更易读也容易写注释。一个典型的技能定义长这样name: check_refund_progress description: - 查询退款申请的处理进度。当用户询问“退款到哪一步了”“退款什么时候到账” “钱退回来没有”等问题时使用。执行前必须先通过 verify_user_identity 技能完成身份校验。查询结果为快照数据如需最新状态请配合 force_refresh 参数。 version: 2.1.0 emoji_policy: none inputs: order_id: type: string description: 电商平台订单号格式为 10 位数字 required: true force_refresh: type: boolean description: 是否强制从支付渠道拉取最新退款流水默认 false required: false default: false outputs: schema: type: object properties: status: type: string enum: [processing, success, failed, expired] estimated_arrival: type: string description: 预计到账时间格式为 ISO 8601仅在 processing 时有值 last_update: type: string description: 最近一次状态变更时间 side_effects: - reads_user_payment_flow - requires_identity_verified描述里我刻意用了“当用户询问……时使用”这种话术这比写“查询退款进度”有效得多。模型看到的是用户表达层面的触发条件而不是函数层面的功能摘要。这是一个我从惨痛教训里得出的经验后面会专门展开。2.2 技能的注册与发现机制技能定义写好了还不够还要有一个运行时机制让 Agent 知道“当前有哪些技能可用”。agent-skills 的注册中心解决的就是这个问题。注册中心维护一张技能索引表核心字段包括技能名、语义指纹、输入摘要、当前版本、健康状态、平均延迟、最近失败率。Agent 在每次会话开始前拉取一次技能索引然后根据用户问题从中筛选候选技能。这个过程可以理解为“文件的目录页”——模型不需要翻开每一页只需要看目录就知道去哪一章找答案。我早期试过把所有技能描述全塞进 system prompt结果 token 消耗爆炸而且模型在大量文本中反而抓不住重点。后来改成“两阶段召回”先在注册中心做一次粗筛挑出 3 到 5 个候选技能再把候选技能的完整描述注入 prompt。这让选型准确率提升了大概 20 个百分点也让单次请求的 prompt 体积缩小了 60%。注册中心还负责技能的生命周期管理。下线一个技能时不会立刻摘除而是标记为 deprecated给存量会话一个过渡期。升级技能时采用蓝绿策略灰度比例按流量百分比控制。这些机制在单体工具函数时代都是不存在的但它们才是 Agent 应用能长期稳定运行的基石。3. 技能编排从“单个技能可用”到“多技能协同干活”的临界点3.1 为什么单技能正确不代表流程正确单个技能可用只解决了“模型能不能调用对工具”的问题。但在真实业务里用户诉求往往要串联多个技能才能完成。拿我们上线过的售后流程举例用户说“我上周买的手机到了但屏幕有问题想退货”完整链路至少是先调用 verify_user_identity 确认用户身份再调用 query_order_info 找到对应订单调用 query_after_sale_policy 判断是否符合退货条件调用 create_refund_request 创建退款申请最后调用 notify_user 把结果通知用户这五个技能如果靠模型在单轮对话里自由发挥任何一个环节选错都会导致流程断裂。比如模型可能在身份没验证时就创建了退款申请或者用错了订单号。所以技能编排的核心是设计一套机制来约束调度顺序、传递中间数据、处理分支异常。3.2 用 DAG 描述流程拓扑agent-skills 的编排引擎采用 DAG有向无环图来描述技能之间的依赖关系。每个节点是一个技能每条边是数据流或控制流。一个售后流程的 DAG 定义可以抽象成verify_user_identity ↓ query_order_info ↓ query_after_sale_policy ↓ ↓ 符合条件 不符合条件 ↓ ↓ create_refund_request → notify_user ↓ notify_user注意一个关键设计条件分支不是把分支逻辑写在技能内部而是交给编排引擎判断。因为技能本身应当保持单一职责分支判断属于流程层。query_after_sale_policy 返回状态码和原因说明后编排器的决策节点根据结果选择后续路径。这保证技能可以被复用到不同流程里不会出现“这个技能只有在这个流程里能用”的耦合。数据在技能间传递时编排引擎会维护一份共享上下文每个技能声明自己需要读哪些字段、写哪些字段。数据流字段在技能的声明里写清楚引擎在运行前做静态校验发现字段缺失就直接报错而不是让技能运行到一半才发现问题。3.3 模型自由规划与固定流程的平衡讲到这里肯定有人会问那 LLM 的自主规划能力不是白费了吗我做了一些实验得出结论完全自由的规划适合探索型任务比如“帮我想一个团建方案”而确定性流程适合业务型任务比如“处理一个退款请求”。两者的判断标准很简单**步骤顺序错了结果是否会产生严重错误。**如果能就适合固化流程如果不能就让模型自由发挥。agent-skills 在这两者之间采取混合架构。业务主链路用 DAG 固定但在每个节点内部保留模型的决策空间。比如 create_refund_request 执行后如果系统返回“余额不足”或“订单状态不允许”引擎会把异常信息回到决策模块由模型判断是重试、换方式还是转人工。你会发现这既保住了业务的合规性也没有牺牲模型的灵活性。这一层设计对流程稳定性的提升是肉眼可见的。上线编排引擎后我们的售后流程完成率从 61% 提升到了 89%而且出错的场景都集中在单一技能内部而不是流程跳转环节。4. 落地案例用 agent-skills 搭一个能处理日常事务的团队助手4.1 从需求抽象到技能拆解光讲概念很难有体感我拿一个实际做过的“团队助手”项目来完整走一遍。目标很朴素让一个对话机器人帮团队处理三件事——查知识库、生成周报、发起审批。第一步不是写代码而是做需求拆解。我把“生成周报”拆成三个技能collect_work_logs汇总团队成员本周的工作记录、summarize_weekly_report调用模型生成周报草稿、send_report_to_channel发送到指定群组。为什么不直接做一个 generate_weekly_report 的大技能因为“汇总数据”和“生成文本”是两种性质完全不同的操作前者是数据读取后者是模型生成未来“生成文本”可能被替换成更强的模型而“汇总数据”的逻辑不会变。按技术边界而不是按业务场景拆技能这是拆解的核心原则。4.2 环境搭建与核心配置搭建 agent-skills 运行环境其实相当轻量。核心组件是三个技能注册中心、编排引擎、技能执行器。我用一个简单的 Python 项目来组织agent-skills-demo/ ├── skills/ │ ├── knowledge_base/ │ │ ├── skill.yaml │ │ └── handler.py │ ├── weekly_report/ │ │ ├── skill.yaml │ │ └── handler.py │ └── approval/ │ ├── skill.yaml │ └── handler.py ├── registry/ │ └── index.py ├── orchestrator/ │ └── engine.py └── main.py技能执行器用装饰器模式注册handler.py 里的核心逻辑大致如下# skills/knowledge_base/handler.py from agent_skills import skill skill(knowledge_base_search) def search_docs(query: str, top_k: int 5) - list[dict]: Search internal knowledge base and return relevant snippets. vectors embed(query) results vector_db.search(vectors, top_ktop_k) return [{title: r.title, snippet: r.snippet, score: r.score} for r in results]这里有个很实用的配置细节skill.yaml 里的 description 字段我是从真实用户提问语料里提炼触发词才定稿的。比如 knowledge_base_search 的描述初稿是“搜索内部知识库”后来改成“当用户询问公司制度、报销标准、考勤规则、设备申请流程等问题时调用此技能检索相关文档”。这一改动让该技能的召回命中率提升明显。描述不是写一次就完了要跟着真实对话数据持续迭代。4.3 一个完整会话的执行链路追踪在这个演示项目里用户说“帮我查一下年假有多少天然后生成一份本周工作周报发到群里”。这是一个典型的复合请求涉及两个领域。编排引擎的处理过程可以拆成这几步意图分诊引擎先判断这是一个多技能协同任务而不是单一技能调用候选召回注册中心从索引中召回 knowledge_base_search、query_leave_balance、collect_work_logs、summarize_weekly_report、send_report_to_channel路径规划根据技能依赖关系引擎生成两条并行子链——A 链查年假B 链生成并发送周报并发执行A、B 两条子链没有数据依赖可以并行减少整体延迟汇总输出两条子链的结果引导模型组织最终回复实际运行中我特意观察了 B 链内部的一个细节collect_work_logs 需要获取当前团队成员列表依赖另一个用户服务。如果用户服务超时整个 B 链都会挂住。后来我在这个节点加了缓存和降级策略缓存 30 分钟内的成员列表超时则从缓存读缓存也没有就直接返回错误让模型告诉用户“稍后再试”。这个改动让周报生成的成功率提高了许多。5. 实测阶段最容易踩的坑技能描述写得不对全盘皆输5.1 一次“描述冲突”引发的选型事故说一个我在真实项目中排查了一整天才解决的问题。现象是用户问“我要离职怎么走流程”模型竟然调用了发起加班审批的技能。看日志时我一开始完全没头绪两个技能从字面上看差异很大模型怎么会搞混后来我把注册中心里所有技能描述导出来逐个看发现问题出在离职流程这个技能的描述里写了“用于员工离职交接、资产归还、工资结算”这里。而加班审批的描述里写了“当用户提到‘流程’时优先考虑使用此技能”。模型在处理“怎么走流程”这个模糊表达时被“优先使用”这种措辞干扰选择了加班审批。这个坑的根因是技能描述之间出现了关键词交叉覆盖且部分描述带了过强的误导性指令。排查过程是这样的先复现问题确认不是偶发把模型调用日志中的 prompt 完整导出查看两个技能的描述原文用一个测试集反复触发统计两个技能的召回重叠度定位到高权重关键词“流程”在两个描述中都出现修改加班审批技能的描述明确限定“当用户明确提到加班、调休、补休等关键词时”同时删除离职流程技能里的笼统表述修复之后我在小流量试点跑了一周误选率从 5.8% 降到了 1.2%。这也验证了一件事排查模型选型问题时不要急着调 prompt 或换模型第一时间检查技能描述。技能描述才是 Agent 决策的第一依据。5.2 技能间的隐式依赖运行时才发现已经晚了另一个高频事故是技能间的隐式依赖。比如 create_refund_request 内部假设订单状态是从 query_order_info 返回的如果未来流程里有人直接调 create_refund_request 而不是走完整链路它的逻辑就会因缺上游数据而报错。这类问题单测是测不出来的因为单测里我们已经把前置条件 mock 好了。agent-skills 的做法是在注册中心强制声明依赖requires: - skill: query_order_info provides: [order_status, payment_method]编排引擎在构建 DAG 时会先做静态检查如果 create_refund_request 声明了 requires query_order_info而当前流程里没有该前置节点就直接抛错。这个设计逼着开发者把隐式依赖显式化。上手第一周会觉得很繁琐觉得“多此一举”但当你同时维护二十几个技能时这份显式声明的价值就体现出来了——它相当于一张技能的依赖关系地图让重构时不再提心吊胆。5.3 上下文污染技能的“记忆”不该无限增长最后一个必须讲的坑是上下文污染。技能执行完执行结果会返回主干模型但如果结果本身太大比如查知识库返回了 20 条文档片段这些内容会全部塞进对话历史后续模型生成时既容易被无关片段干扰也会因为 token 太多导致响应变慢甚至截断。我在 agent-skills 的上下文管理里做了三件事结果裁剪知识检索只保留 top 3 条高分段落且每条截断到 200 字以内状态摘要执行完一个技能后引擎把“技能名 核心结论”压缩成一句话放回短期记忆原始结果进入临时存储不再进入模型上下文引用回收当用户明确表示“不需要了解细节”或者进入下一个任务时上一任务的中间结果会被清理这三件事做完后长会话场景下的模型回答准确率明显回升。很多 Agent 项目用着用着效果变差不是模型退化了而是上下文里的垃圾越来越多了。控制技能产生的上下文噪声和优化算法一样重要。6. 把技能当产品运营可观测性、评估与灰度发布6.1 技能调用日志里藏着系统健康的全部秘密技能系统上线不是终点持续运营才是。我在 agent-skills 里为每个技能注入了完整的调用追踪日志里必须包含这几个维度的信息请求维度用户原始输入、触发的技能名、重试次数、完整延迟决策维度模型在选择该技能时的置信度、候选技能列表、被淘汰技能及淘汰原因结果维度技能执行是否成功、返回数据大小、异常堆栈有了这些日志我可以在一个看板上同时监控每个技能的调用量、失败率、平均延迟。但我们发现一个反直觉的现象有时候技能执行成功率很高但用户满意度却在下降。追查日志后发现模型虽然选对了技能却把技能返回的关键信息漏掉了比如查到了退款状态是“失败”但模型在回复时只说了“退款已处理”没有提“失败”这个关键点。这是模型对技能结果的二次加工出了问题光看技能执行指标根本发现不了。后来我增加了一个“关键字段透传率”指标用正则从技能输出中提取必填字段再检查模型最终回复是否覆盖这些字段。这个指标出来后很多潜在体验问题都浮出水面了。6.2 用评测集给技能选型做“体检”技能评测这件事我建议从第一天就启动不要等系统上线后再补。做法是维护一个评测集里面每条样本包含四部分用户问题、正确技能、禁忌技能、期望输出字段。比如一条样本是“我的订单被快递弄丢了怎么办”正确技能是 query_logistics_exception禁忌技能是 create_after_sale_order。因为用户在确认丢件原因之前不应该直接创建售后单。评测逻辑是在固定模型配置下跑完整链路计算三个分数召回率正确技能是否被选中、误用率是否选了禁忌技能、字段完整率关键信息是否传达到位。每周跑一次评测把分数变化和本周技能描述改动关联起来。这个方法帮我抓到过不少“回归”——某次优化了技能 A 的描述结果技能 B 的误用率涨了因为 A 和 B 有共享关键词。评测集就是技能系统的安全网没有它你根本不知道自己改坏了什么。6.3 灰度发布与快速回滚技能升级是日常操作但直接全量替换是危险行为。agent-skills 支持按用户维度灰度先放 5% 流量跑新版本技能观察错误率和调用失败率连续稳定运行两天后才逐步扩大到 30%、60%、100%。如果某一步指标异常立即回滚到上一个版本。有一个设计让回滚格外轻松技能执行器的内部接口保持兼容新版技能只替换 handler 实现不改变技能定义文件中的名称、参数和输出结构。这样回滚只是把流量切回旧版本不需要改调用方代码。如果新版本技能真的需要改输入输出结构我会把它注册为一个全新技能名比如 v2然后在编排层做流量切换。这样做虽然多了一点维护成本但换来的是随时可以安全回退的确定性。这套灰度机制落地后团队对技能迭代心态变了。以前升级一个技能像是“拆弹”现在就像日常发版——有评估、有监控、有兜底改起来从容得多。最后再分享一点个人心得。我见过很多团队把 Agent 效果不佳归咎于模型选型不当、prompt 不够精细却忽略了一个更基础的问题技能的抽象、描述、编排和运营有没有像对待正式产品一样对待它agent-skills 这条路真正教会我的不是某一个框架或者工具链而是把 Agent 的能力拆成可治理的单元让每一个能力点都有描述、有测试、有版本、有监控。如果你正在搭自己的 Agent 系统别急着堆功能先从技能建模开始把地基打好后面才走得稳。
返回列表