资讯动态

Agent技能系统架构解析:从堆提示词到能力沉淀的工程实践

发布时间:2026/10/21 8:34:38 来源:尧图企业网站定制
1. agent-skills到底在解决什么从上下文堆砌到能力沉淀1.1 一个让我彻底改观的翻车现场先讲一个真实的场景。上个月我在调一个能自动整理项目周报的agent需求很简单从仓库的提交记录里提取本周改动再按模块归类生成摘要。第一版我图省事把所有规则都写进了系统提示词——文件路径怎么解析、提交信息里的关键词怎么映射、输出格式是什么洋洋洒洒写了一大段。结果怎么样小仓库没问题一旦提交记录超过几十条模型就开始选择性失明漏掉三分之一的改动。我以为是提示词不够长又加了更多约束结果更糟截断、重复、格式错乱甚至开始编造根本不存在的提交记录。后来我换了个思路把解析提交记录这件事拆出来写成单独的函数让agent在需要时主动调用它。调用前传给函数的是筛选条件调用后拿到的是结构化结果主流程里的提示词反而精简到只剩开局一句你是周报助手。同样的数据准确率从不到七成直接拉到九成五以上。这个转变让我真正意识到一件事agent的能力边界不取决于提示词有多长而取决于你给它准备了多少把趁手的工具——所谓技能就是这些工具的最小封装单元。1.2 技能与提示词的本质差别很多人会把技能误解为更长的提示词或者更精细的few-shot示例这是两码事。提示词是给模型的指令它本质上是软约束模型可以自由发挥也可以在不经意间违背。技能则是一个硬接口输入是定义好的参数输出是调用某个函数、某个API、某段代码后的真实结果。模型在这个接口里没有自由创作的空间拿到什么数据就处理什么数据。我用一个生活化的类比来解释提示词就像你向同事口头描述帮我把这些文件整理一下具体怎么整理、整理成什么样全看这个同事的理解和临场发挥技能则像是你递给同事一张表格模板告诉他按这个模板的列填第几列填什么格式填完发我。前者灵活但有歧义后者受限但可靠。对于生产环境里的agent可靠性远比灵活性重要。所以我在设计agent时养成了一个习惯凡是具有确定性逻辑的操作一律下沉为技能凡是需要模型判断和生成的环节才保留在提示词里。这种做法不仅提升了准确率还让后续的调试变得异常轻松——出了问题先定位是技能返回了错误数据还是模型理解了错误意图排查链路直接缩短一半。2. 技能系统的最小可用架构注册、检索、执行三段式2.1 技能注册把能力清单写清楚技能系统的第一个核心模块是注册表。你可以把它想象成一本技能词典agent在每一次任务开始前会先浏览这本词典了解自己能用哪些工具然后决定下一步调哪个。注册表里的每个条目通常长这样技能ID全局唯一、技能名称、功能描述、参数定义、返回结果定义、依赖项。ID是程序内部用的名称是给模型看的描述则是让模型判断这个技能是否适合当前任务的关键依据。我曾经犯过一个典型错误描述里只写解析Git提交记录结果模型在遇到按模块归类改动的任务时不知道这个技能也能用于归类。后来我把描述改成了解析Git提交记录并按模块归类返回结构化改动列表可用于周报生成、代码审查、变更统计等场景模型的调用率立刻上来了。描述的要义在于写清两件事这个技能能做什么以及什么场景下应该想起它。前者是功能边界后者是调用时机。两者缺一不可只写前者会导致模型不知道该什么时候用只写后者会导致模型在不需要时强行调用。2.2 技能检索让模型在关键时刻想起它注册表里的技能可能有很多个但每次任务的上下文窗口是有限的不可能把几十个技能的完整描述全部塞进去。所以需要一层检索机制只把当前任务最相关的几个技能描述暴露给模型。我目前采用的方案是双路召回一路走关键词匹配把技能描述和任务文本做分词对齐另一路走语义向量把技能描述和任务文本各自编码成向量计算相似度。两路结果做加权合并取Top K送入上下文。这个方案实现起来不难但效果比单纯用关键词或单纯用向量都好很多。某次测试里纯关键词召回的技能命中率只有61%纯向量召回是78%双路合并能到85%以上。值得注意的是检索的目标不是找到能完成任务的技能而是找到可能相关的技能。所以召回时宁多勿缺让模型做最终取舍但如果相关技能都没被召回到模型就完全没有调用它的可能这是最糟糕的情况。因此Top K不宜设得太小我一般根据技能总数动态调整技能不多时直接全量暴露技能很多时才启用检索。2.3 技能执行隔离上下文与安全边界技能执行层是整个系统的落地终端也是最容易出问题的地方。我单独强调两点上下文隔离和权限边界。上下文隔离指每次技能执行都应该在独立环境中完成输入输出只通过参数和返回值传递不在执行过程中读写模型的主上下文。这样做的好处是技能内部产生的中间过程比如循环处理每一行数据、调用外部API时的临时输出不会污染主对话流最大化降低了模型后续推理被干扰的可能。代价是需要多写一些输入输出转换的胶水代码但这个成本非常值得。权限边界则是另一个容易忽略的点。一个技能要能访问外部资源文件系统、数据库、网络API它能做什么、不能做什么必须在执行环境里卡死。我在本地实验时遇到过一档事某个技能的程序运行依赖是抓取网页内容结果它试图访问本地文件系统虽然因为环境隔离只读而执行失败了但也给我提了个醒——技能的能力边界必须提前收紧不然一旦上了生产环境一个小小的越权可能就是安全漏斗。3. 从零写一个agent-skills库目录结构、描述规范与参数约束3.1 目录与文件组织我常用的技能目录结构参考如下agent-skills/ ├── registry.json ├── skills/ │ ├── git_commit_analyzer/ │ │ ├── SKILL.md │ │ ├── main.py │ │ └── requirements.txt │ ├── doc_parser/ │ │ ├── SKILL.md │ │ ├── main.py │ │ └── requirements.txt │ └── ... └── runner/ ├── execute.py └── retriever.py每个技能一个独立目录SKILL.md是给模型看的元信息描述main.py是执行逻辑两者彻底分离。这样设计的好处是脚手架非常清晰新增技能只需要建一个目录、写两份文件别的什么都不用动。registry.json则是给检索机制用的索引内容大概是技能ID、名称、描述摘要、参数列表和入口文件路径。这与每个技能目录里的SKILL.md会有内容重叠但定位不同registry.json是全局索引SKILL.md是单技能详情。全局索引要精炼、可快速扫描单技能详情要完整、供模型深入理解。3.2 描述怎么写才不容易被模型忽略我见过太多人把技能描述写成函数注释风格比如本函数用于计算两个整数的和。这种描述对模型一点意义都没有——它等同于没说。模型需要的是行为语义和触发场景不是函数签名。一个有效的技能描述长这样# 技能名称代码提交分析器 ## 能力描述 解析指定时间范围内的代码提交记录提取提交人、时间、改动文件列表、提交说明 并按模块/目录维度聚合归类输出结构化的改动摘要。 ## 典型使用场景 - 周报自动生成收集本周各模块的改动情况 - 代码审查辅助定位指定提交涉及的具体文件 - 变更影响分析评估某文件被哪些提交改动过 ## 输出格式 { modules: [{ name: ..., changes: [ ... ] }] }描述里最核心的部分是典型使用场景。模型在推理时会在内心把当前任务和已有经验做匹配你直接给它写清这个技能适合出现在哪些任务里匹配成本瞬间下降。我实测发现加了场景描述之后模型在正确场景下调用技能的频率提升了将近40%。3.3 参数约束的必要性技能的参数定义方面我强烈建议给每个参数补充类型、是否必填、取值范围或格式要求。这里有个很容易踩的坑——参数名取得太抽象。比如date_range这个名字模型不知道你是要2025-01-01到2025-01-31这样的字符串还是要last_week这样的相对时间。你必须在描述里给出明确的格式说明和示例值。{ start_date: { type: string, required: true, description: 开始日期格式YYYY-MM-DD如2025-01-01, example: 2025-01-01 }, end_date: { type: string, required: true, description: 结束日期格式YYYY-MM-DD如2025-01-31, example: 2025-01-31 } }参数约束的意义不只是让程序能跑通更是替模型兜底。模型偶尔会因为幻觉填出不合理参数比如把日期格式填错有了格式约束和执行前的校验逻辑这类错误就变成了可观测的、可控的异常而不是静默失败。4. 我的实测数据五类技能场景的效果对比4.1 测试任务设计为了验证技能化改造到底带来多少收益我做了一组对照实验选了五类典型任务每类任务分别用两种方式实现纯提示词驱动和技能系统驱动。测试集是每组20个真实场景样本衡量指标是主要功能正确完成率、平均耗时和上下文token消耗量。任务类型纯提示词正确率技能系统正确率上下文token节省比例结构化数据提取72%93%58%多步骤工具调用链54%87%41%长文本摘要归类67%85%36%定时任务执行48%91%62%跨源数据核对61%82%45%这个结果其实在我的预期之内但差异之大还是有点超出想象。尤其是多步骤工具调用链和定时任务执行纯提示词方案的正确率低得可怜原因很好理解任务越长模型在推理过程中越容易丢失最初的目标约束绕来绕去就偏了。4.2 技能系统为什么稳定性更高技能系统提升正确率的机制并不神秘从根子上讲它把任务的复杂度从模型一次性推理转移到了程序分步执行每一步都有明确的输入输出契约模型只需要在状态节点上做轻量判断剩下的重活交给程序逻辑去完成。模型一次推理需要同时兼顾的东西越少出错的概率自然越低。拿跨源数据核对举例纯提示词方案要求模型分别理解两个数据源的格式、字段映射关系、不一致时的处理规则然后自己完成对比——这一步涉及大量精确匹配和边界判断恰恰是模型的弱项。技能系统则把解析数据源A和解析数据源B分别做成技能模型只负责判断该调用哪个技能、拿到结果后再决定要不要调用核对函数而核对函数里的逐字段比较逻辑是代码完成的精确度100%。模型不需要在每一条数据上都做精细判断它只做高层决策。4.3 Token消耗的隐性收益除了准确率token消耗的节省同样值得关注。技能系统将固定逻辑抽离后提示词部分可以大幅精简系统提示词从上千字压缩到两百字以内。流程中的中间状态不用全部塞进上下文只保留必要的摘要结果。这带来的直接好处是响应延迟降低更间接但同样重要的好处是减少了上下文溢出导致的历史信息丢失——对于长会话场景这一点影响更大。把这两个收益加到一起结论已经非常明确只要是能拆出确定性操作的agent任务做成技能几乎总是优于堆提示词。5. 踩坑清单技能描述被忽略、循环调用与上下文污染5.1 技能调用率断崖下跌描述里缺少负面排除某一次我新增了几个技能之后发现其中一个旧技能突然没人用了。翻调用日志模型在应该调用它的场景里选择了另一个技能。排查了半天才发现问题出在新技能的描述里写了可处理各种格式文件且没有标注自己不能做什么模型被这个更宽泛的描述吸引走了。从那以后我规定技能描述必须包含不适用场景这一节。比如文件解析技能明确写上不适用于处理超过100MB的超大文件不适用于扫描类型的文件。负面排除看似在限制技能的使用范围实际上是在防止模型乱选从而提高整体调用准确率。5.2 循环调用与递归保护钩子技能系统运行时一个让人头疼的问题是循环调用任务A调用技能B技能B的结果又触发任务A再次调用技能B两个环节互相咬住直到超时才结束。我第一次遇到时以为是模型出了问题后来定位到是技能B的输出结构里包含了任务描述原文触发了主循环的再次调度。修复方案很简单加了一个调用次数上限和循环检测的钩子每次技能调用前检查同一技能在本次任务中的已调用次数超过阈值就拒绝执行并返回提示。这个保护机制现在已经成为我所有技能系统的标配任何新技能上线都默认带上省掉了无数后端排障的烦恼。5.3 上下文污染中间输出惹的祸还有一个隐蔽的坑发生在技能执行之后、返回值给到模型之前。如果主流程直接拼接技能的全部中间输出比如调试信息、库版本、缓存内容这些无关内容会被模型当作任务上下文的一部分来理解导致幻觉概率上升。我用了一个简单的过滤规则技能返回内容必须经过白名单字段提取只带文档定义过的结构化字段其他一律丢弃。这个规则从机制上杜绝了上下文污染的可能。另外补充一点技能描述本身也在消耗上下文所以保持精简仍然很重要。对长期运行在多任务环境中的agent建议定期清理那些调用率极低的技能或者把它们的描述压缩成摘要只保留必要触发信息进一步降低无谓开销。6. 后续扩展方向多技能协作与管理工作流技能系统跑通之后我一直在想它更大的潜力空间在哪里。目前做得比较顺的是把多个技能串成一个工作流模板比如需求分析 → 代码检索 → 生成变更说明 → 输出评审清单这条链路每个环节对应一个技能各环节之间通过标准数据结构交接。这相当于把一次复杂的任务拆成一条流水线模型在每一站只需要做单点判断整体稳定性又往上走了一截。更进一步的设想是给技能加一层依赖关系管理让技能A的结果自动触发技能B的调用而不是等模型来决策。这个想法还处于雏形阶段因为是让决策的主动权部分转移给规则引擎而不是完全交给模型判断需要做挺多边界设计后续如果有进展再单独写一篇聊聊。另外我建议每个刚接触agent-skill系统的人从小而垂直的需求入手不要一上来就规划一个覆盖几十个技能的宏大架构。先挑一个确定性较高的操作做成技能跑通注册、检索、执行、日志这条完整链路再做第二个、第三个。等到技能的质地打磨到位再考虑工作流和协作调度这样节奏最稳踩坑的成本也最低。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑