直接说结论:Skill 系统正在成为 AI Agent 工程化落地的一块关键拼图。HagiCode 把“技能”做成了一等公民,让 AI 能力的组织方式从“写死提示词”变成了“声明式技能文件”。这篇文章会从架构设计、目录结构、MCP 边界、可扩展机制、实操案例到排坑经验,一次性讲清楚怎么搭一个能持续长出技能的 AI 技能管理平台。
先交代一下背景。做 AI Agent 相关开发的朋友,最近两年应该没少听到“Skill”这个词。从 Anthropic 的 Claude Code 把 Skill 作为扩展机制推到台前,到 Codex、Cursor、OpenCode 这些 AI 编程工具纷纷跟进,再到 Spring AI 也在往技能注册的方向靠,Skill 几乎成了 AI 应用开发绕不开的抽象层。HagiCode Skill 系统本质上就是干这件事的:把 AI 能力拆成可复用、可扩展、可治理的“技能单元”,让 Agent 不再只会聊天,而是能按需调用工具、执行流程、输出结构化结果。
我自己在多个项目里实践过这套思路,从最早的“提示词写死在代码里”,到后来用 Skill 目录管理几十个技能,整个过程踩了不少坑,也总结出了一些可复用的套路。HagiCode 的设计理念和我不谋而合的地方在于:它解决的是三个非常实际的问题。第一,技能怎么定义、怎么组织,才能让模型“看得懂、用得上”;第二,技能数量上来了以后,怎么维护、升级、避免冲突;第三,第三方开发者怎么把自己的专业能力沉淀成技能,而不需要改 Agent 主程序。带着这三个问题去读这篇文章,你会更有体感。
1. HagiCode Skill 系统的设计思路与整体架构
1.1 从“写死提示词”到“技能即文件”
很多团队最早做 Agent,实现方式就是提示词硬编码。用户说“帮我分析一下这个项目”,代码里就拼一段固定的 prompt,再调一个写死的函数,把上下文传给大模型。这种做法的痛点是:每加一个能力,就要改代码、走发布流程,提示词和业务逻辑完全耦合,时间一长维护成本高得吓人。HagiCode 换了个思路——把技能定义成一组标准文件,放在约定的目录里,由 Agent 在运行时按需发现和加载。这个思路可以叫“技能即文件”,和配置中心、插件体系的思路一脉相承,只是粒度更细,语义更明确。
“技能即文件”的直接好处是什么呢?我在自己项目里实测下来,最大的感受就是:技能不需要重新编译,不需要改动主程序,就能热插拔。我把一个“接口文档生成”技能丢进 skills 目录,再配一个简单的注册清单,Agent 下一次对话就能识别并调用它。整个上线过程不用重启服务,对现有系统几乎零侵入。对比之前那种硬编码工具函数的方案,维护体验不是一个量级。
这里有一个关键点要展开说:为什么“文件”比“数据库记录”更合适?我一开始也想过把技能存到数据库里,后来发现文件系统天然具备几个优势。第一,可版本化,技能文件可以直接进 Git,每次改动有历史记录,可以 review、可以回滚;第二,可测试,技能目录拉下来就能在本地跑,不依赖线上环境;第三,生态友好,第三方贡献者通过 Pull Request 就能提交新技能,不需要数据库权限。HagiCode 在这一点上选了和社区一致的做法,看似简单,实际是经过权衡的。
1.2 技能粒度:按任务划分,不按原子操作划分
设计技能粒度是我踩过最深的坑之一。最初我天真地认为,技能当然拆得越细越灵活,于是做了“读取文件”“搜索代码”“运行测试”这种一个功能一个技能。结果 Agent 每次完成一个简单目标,都要串五六个技能,上下文 token 开销巨大,而且经常出现调用顺序错乱——模型并不知道应该先读文件还是先搜代码,我也没有在技能描述里给它讲清楚。后来才意识到,技能粒度应该按“任务”来划分,而不是按“原子操作”来划分。
一个合格的技能,应该对应一个完整的、可以用自然语言描述的任务。比如“项目体检”“接口文档生成”“代码评审”“依赖安全扫描”,这些都是任务。而“读取文件”“写入文件”是原子操作,它们应该被封装在技能内部,作为脚本的一部分,而不是暴露给模型让模型自己拼。HagiCode 的设计在这个点上非常清晰:元信息负责描述“技能是干什么的”,说明文档负责教模型“技能怎么做”,脚本负责真正执行“底层操作”。三者配合,模型看到的是任务级描述,调用的是封装好的能力,而不是面对一堆细碎函数手足无措。
我的建议是,技能粒度可以参考“一个技能对应一次完整的用户诉求”这个标准。如果一个任务的完成需要多个步骤,那这些步骤应该写进技能内部的工作流文档里,由技能自己去调度,而不是让外部 Agent 去编排。这样做还有一个隐藏好处:技能的脚本和说明文档可以独立测试,只要你把输入输出定义清楚,技能的复用率会大幅提升。
1.3 整体架构分层:注册中心、运行时、沙箱
HagiCode 的整体架构,拆开来看可以分成三层。最上层是注册中心,负责维护所有技能清单、版本、依赖关系,相当于一个技能目录。中间是运行时,负责把 Agent 的意图转换成具体的技能调用,包括技能匹配、参数注入、上下文拼装。最底层是执行沙箱,负责实际运行技能脚本,并对网络、文件系统、外部命令做权限控制。
这个分层的逻辑,和操作系统管理进程的思路很相似。注册中心相当于“进程表”,运行时相当于“调度器”,沙箱相当于“用户态隔离”。为什么一定要分这么多层?因为要把“技能声明”和“技能执行”彻底解耦。这样当技能脚本出问题的时候,不会拖垮整个 Agent 进程;技能升级时,只需要替换注册中心里的版本引用;技能有风险时,可以在沙箱里直接禁用它,而不用改其他代码。这些好处在本地小玩具项目里不明显,一旦上了生产环境,碰上线上故障,你就知道分层是多么重要了。
三层里面最容易被忽略、但实际最值得关注的是沙箱层。很多 Skill 系统暴露出安全问题,基本都是栽在这一层。你把技能开放给第三方作者编写,就相当于让别人在你的进程里跑代码。如果不做权限隔离,一个恶意技能就能读取你的环境变量、删除你的文件、向外发送数据。HagiCode 在沙箱里做了命令白名单、路径访问控制、网络策略三项基础隔离能力。虽然目前的能力边界还不够细,但方向是对的。我自己在排查线上问题时,80% 的异常都出在沙箱权限配置上,这部分后面专门开一节讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心细节解析:一个技能到底由什么组成
2.1 SKILL.md:给模型看的说明书
一个标准的 HagiCode Skill,核心构成是一个 SKILL.md 文件。这个文件的地位相当于技能的“说明书”加“入口”,它的质量直接决定模型能不能正确理解并调用这个技能。SKILL.md 的头部通常带 YAML 格式的 frontmatter,里面声明技能的名称、描述、版本、作者、依赖项等信息。正文部分则用自然语言描述这个技能的使用场景、输入输出、执行步骤、注意事项。
我写 SKILL.md 有一个习惯:把正文想象成“写给一个聪明但缺乏常识的新同事的操作手册”。不是让它去读代码就能理解一切,而是要明确告诉它:什么情况下应该用这个技能,输入参数怎么传,输出怎么解析,有哪些边界条件。举个例子,如果写一个“项目体检”技能,我不仅会写“检查项目代码质量”,还会写清楚体检的维度包括哪些、报告输出的格式是什么、哪些情况属于异常、异常时应该怎么处理。这样的说明书才有价值。
这里要特别强调描述的重要性。模型对技能的选择,全靠 description 字段来做语义匹配。我见过太多技能写不好,根本原因是描述写得过于泛泛。比如“分析代码”这种描述,模型根本不知道什么时候该调它。反过来,“当用户要求检查代码规范、发现潜在 bug、评估代码可维护性时,使用此技能分析项目源码并生成问题清单”这种描述,命中率会高出一个量级。实操中,我会在描述里写清楚触发场景、输入需求、输出内容三要素。
2.2 scripts 目录:真正干活的脚本
SKILL.md 是大脑,scripts 目录就是手和脚。一个技能通常会有若干个脚本文件,负责完成具体的底层操作,比如解析文件、调用外部 API、执行命令行工具、生成报告等。HagiCode 对脚本语言没有硬性限制,我一般用 Python 和 Shell 混写,Python 处理复杂逻辑,Shell 做快速文件操作。脚本和说明书通过标准输入输出衔接,Agent 负责把参数透传给脚本,脚本把结构化结果返回给 Agent,再由模型整理成最终回答。
这里有一个设计细节值得展开:脚本的输入输出最好用 JSON 格式。为什么?因为大模型处理 JSON 的稳定性远高于处理自由文本。我在早期用“自然语言描述输出格式”的脚本上栽过跟头,模型解析结果时经常出错。改成 JSON 之后,解析成功率接近 100%。具体做法是:脚本从标准输入读 JSON 参数,执行完把结果以 JSON 打印到标准输出,错误信息也按统一结构输出。这样整个技能调用链路清晰可控,排查问题也方便。
脚本的工作目录隔离也要注意。HagiCode 默认会把脚本扔到一个临时工作目录里运行,避免污染主项目。但我会额外要求脚本内部使用相对路径,且不依赖外部环境变量——你永远不知道 Agent 每次启动时会给你一个什么样的环境。把脚本写成“纯函数”风格,能极大提升技能的可移植性和可测试性。
2.3 资源文件与依赖管理
除了 SKILL.md 和脚本,技能还可以携带资源文件,比如模板、配置文件、示例数据、参考文档。这些资源放在 assets 或 templates 目录下,脚本可以按约定路径读取。HagiCode 在资源文件的组织上不强制,但如果你想让技能具备商业化或社区化的分发能力,资源文件的版本管理就必须纳入技能包的整体版本体系。
依赖管理是个大问题。一个技能可能依赖 Python 包、npm 包或者系统命令。HagiCode 的做法是允许在技能元信息里声明依赖项,运行时负责检查并安装缺失的依赖。但这里有一个实际心得:依赖越多,技能在不同环境间的稳定性就越差。我在生产环境里遇到过 Python 包版本冲突导致技能大面积失效的情况,最终靠“技能自带依赖快照”才解决。所以现在我会为每个技能建一个虚拟环境,或者使用容器化方案隔离依赖,宁可启动慢一点,也不让技能之间互相“污染”。
另外,资源文件不要把大体积内容直接塞进技能包。比如你想做一个“文档搜索”技能,不应该把索引文件放进技能包里,而应该让技能在首次运行时主动构建索引。这样技能包保持轻量,部署快,升级时也不会因为资源文件过大而拖慢流程。总之,资源文件遵循最小化原则,能用脚本生成的绝不手动维护。
3. Skill、MCP 与 Plugin:边界和协同
3.1 三者的职责定位到底哪里不同
现在 AI 生态里“Skill / MCP / Plugin”三个概念经常混着提,很多人搞不清边界。我直接给一个能落地的判断标准:Skill 管的是“怎么做”,MCP 管的是“能连谁”,Plugin 管的是“能用什么”。Skill 描述的是一个任务的完成流程,比如“怎么对代码做安全扫描”;MCP 提供的是外部资源的标准访问协议,比如“怎么连 GitHub API”“怎么连数据库”;Plugin 则是宿主应用的功能扩展包,可能是 UI 组件、权限策略、数据源适配器的集合。
用生活化的类比来理解:Skill 是一个员工的工作手册,上面写着完成一项工作时按什么步骤来;MCP 是公司的通讯录和电话线,告诉员工可以找谁合作;Plugin 是员工手里的工具箱,提供了各种现成工具。三者不冲突,反而是互补关系。一个完整的 Agent 技能,往往既要包含 Skill 定义的工作流程,又要通过 MCP 访问外部数据,还要调用 Plugin 提供的界面或工具函数。
3.2 什么场景该用 Skill,什么场景该用 MCP
实操中的选择标准,我总结成一句话:如果这个能力是“流程型”的,做成 Skill;如果这个能力是“连接型”的,做成 MCP。比如“从 GitHub 拉取 Issue 列表”是连接型能力,应该用 MCP 的 GitHub Server;“根据 Issue 自动生成每周周报”是流程型能力,应该做成 Skill,内部再调用 GitHub MCP 拉取数据。
一旦把这两个概念分开,系统的灵活性会大幅提升。同一个 Skill 可以对接不同 MCP Server,同一个 MCP Server 也可以被多个 Skill 复用。这种“技能与连接解耦”的设计,是 HagiCode 可扩展性的底层支撑之一。我在实际项目里,把 MCP Server 当作“数据中台”,把 Skill 当作“业务应用”,数据中台的接口一旦变化,不需要改 Skill 逻辑,只需要调整 MCP 的映射配置,维护成本能省一大截。
3.3 组合使用的实战姿势
一个常见的组合套路是:Skill 负责定义任务流程和多步骤编排,MCP 负责提供外部系统访问能力,Plugin 负责做交互层优化。比如做一个“自动发布”技能,Skill 规定发布流程是跑测试、打镜像、推仓库、发通知;其中跑测试和打镜像通过命令行脚本完成,推仓库走 MCP 的 Docker Registry Server 接口,发通知用 Plugin 的 IM 工具。这样每个环节都职责单一,替换任何一个环节都不影响其他部分。
组合时最容易犯的错误是“大而全”。有人倾向于把一个 Skill 拆到多个 MCP Server 上,或者把所有工具都塞进一个 Plugin。我在维护过程中多次吃亏后,总结出一套“最小依赖”原则:Skill 默认只依赖一个主 MCP Server,额外的外部访问全部通过参数注入。这样测试时只需要 mock 一个 Server,部署时不需要同时启动所有依赖。HagiCode 的配置体系支持这种模式,关键是你要克制扩展的欲望,该拆的时候拆,该合的时候合。
4. 可扩展性设计:让平台能持续长出新的技能
4.1 技能注册与发现机制
一个可扩展的技能平台,第一个要解决的是“技能怎么被找到”。HagiCode 采用了“目录注册 + 语义搜索”的发现机制。系统启动时扫描 skills 目录下所有以 SKILL.md 为入口的技能包,读取 frontmatter 里的描述并建立索引;Agent 收到用户请求后,先对意图做语义向量化,再与技能索引做相似度匹配,返回最相关的几个技能候选,由模型决定最终调用哪个。
这种做法的好处是技能发现是“软匹配”的,不需要用户指定技能名。但代价是描述质量不好的技能会被淹没。我在调优过程中发现,给技能描述里增加“触发场景”和“典型用户提问”部分,能显著提升召回率。比如在“代码评审”技能描述里写上“当用户说'帮我看看这段代码有没有问题'或'review 一下这个 PR'时使用”,召回率能从 60% 提升到 90% 以上。本质上,这是把“词汇匹配”升级成了“场景匹配”。
另外还有一个性能层面的细节:技能索引最好做预计算和缓存,不要让每次请求都重新扫描技能目录。我见过一个开源项目,技能数量一多,系统启动就要花几分钟扫描目录,非常影响体验。HagiCode 在索引设计上做了增量更新,新增技能时只重建受影响的索引分片,这个思路值得学习。
4.2 命名空间、版本管理与冲突解决
技能多了以后,命名冲突是必然的。两个技能可能都叫 report,但一个是代码报告,一个是财务报告。HagiCode 用命名空间来隔离技能,类似编程语言里的包名。具体格式是 author/name,比如 hagicode/code-review。这样即使两边都叫 report,因为命名空间不同,互相不干扰。我建议从第一天开始就强制使用命名空间,后期省去大量改名和迁移的麻烦。
版本管理方面,技能包需要遵循语义化版本规范(MAJOR.MINOR.PATCH)。MAJOR 版本更新代表破坏性变更,比如输入参数变了、输出格式变了;MINOR 版本代表向后兼容的新功能;PATCH 版本代表修 bug。Agent 在调用技能时,可以通过清单锁定版本范围。这样做的好处是:当你升级技能依赖的底层库或者调整输出格式时,不必一次性更新所有调用方,可以灰度推进。
冲突解决是最隐蔽的坑。当两个技能被同时选中时,它们的运行时环境可能产生资源竞争,比如都占用同一个临时文件,或者都修改同一个环境变量。解决办法是:给每个技能分配独立的临时目录和进程环境,并在技能执行结束后清理资源。HagiCode 的沙箱机制支持这种隔离,但需要你自己在技能脚本里做规范约定。我团队内部的规范是:所有临时文件必须写到系统提供的临时目录,不允许写公共目录;环境变量的修改要有前缀,比如 SKILL_。
4.3 热插拔与动态加载
可扩展平台的一个核心诉求是“技能能随时上线下线”,不能每次更新都要重启整个 Agent。HagiCode 通过文件监视和版本切换实现热插拔。技能目录里的文件发生变化时,注册中心自动重新加载受影响的技能,不需要主进程重启。Agent 执行完当前技能后,下一次调用会自动使用新版本。这个机制在已经上线的生产环境里非常实用。
我实际操作中有一个体会:热加载虽然方便,但千万不能做得太“自动”。如果没有版本控制,一个技能作者改了一行代码,线上所有 Agent 立刻使用新版,风险极大。我的做法是分两个环境:开发环境开启自动热加载,方便快速迭代;生产环境关闭自动加载,改为手动触发版本切换。HagiCode 本身没有强制这个策略,但你可以在部署配置里做这层控制,把“自动”和“可控”结合起来。
另一个和动态加载相关的是“技能依赖的动态解析”。如果一个新技能依赖一个尚未安装的 Python 包,HagiCode 是否自动安装?我建议不要默认自动安装,而是把依赖声明展示给管理员确认。自动安装在开发环境很爽,但在生产环境可能导致依赖混乱和系统不稳定。宁可多一个手动确认步骤,也要确保可重复、可审计。
4.4 安全与权限隔离
安全性是技能平台能不能真正开放给第三方的关键。一个恶意的技能完全可以利用 Agent 所在的进程权限,读取敏感文件、发起对外请求、篡改数据。HagiCode 在安全方面做了三层设计:进程沙箱、文件系统白名单、网络策略。进程沙箱限制技能脚本只能执行指定范围内的系统命令;文件系统白名单规定技能能读写的目录范围;网络策略控制技能是否能访问外部网络以及访问哪些域名。
安全配置的粒度把握很关键。网络策略如果太严,技能没法访问外部 API,功能大打折扣;如果太松,任何技能都能向外发送数据,隐私泄露风险非常高。我的做法是:默认禁止所有外部网络访问,在技能注册清单里显式声明需要的网络域名和端口,由管理员审核后放行。文件系统同理,默认只允许在技能专属工作目录内读写,需要额外权限的要在清单里声明。
还有一类容易被忽略的安全问题:依赖链安全。技能引用的第三方库本身可能携带漏洞。我会在 CI 流水线里对技能包做依赖漏洞扫描,同时给技能加上数字签名,确保技能包的完整性和来源可追溯。HagiCode 目前的签名机制还不算完善,但作为平台运营方,这块不能省。安全没有银弹,只能一层一层叠上去。
5. 实操案例:从零编写一个“项目体检”Skill
5.1 定义目标、输入与输出
纸上谈兵再多,不如把袖子撸起来做一个。我来演示一个具体的技能:项目体检。这个技能的目标是:给定一个项目目录,自动分析代码质量、发现潜在 bug、评估可维护性,并输出一份结构化报告。这是团队里每天都要用的东西,做成 Skill 之后,Agent 一句“帮我的项目做个体检”就能触发。
首先定义输入输出。输入:项目路径(必填)、分析深度(可选,基础/深度/全面三个级别)。输出:JSON 格式的报告,包含总体评分、问题列表(每个问题有严重级别、文件位置、问题描述、修复建议)、统计信息(代码行数、文件数、复杂度均值)。我把输出格式定为 JSON 的原因前面说过:模型解析稳定,也方便后续接上报系统或可视化面板。同时,为了让整个流程足够好调试,我在输出里加一个 meta 字段,记录技能版本和耗时。
5.2 编写 SKILL.md 和主脚本
SKILL.md 的 frontmatter 我这样写:
yaml复制---
name: project-health-check
description: 当用户要求检查项目代码质量、发现潜在 bug、评估可维护性时,使用此技能分析项目源码并生成问题清单。
version: 1.0.0
author: techlead
depends_on:
- python: ">=3.10"
- pip: pydantic
- pip: radon
tags: [code-quality, health-check, refactor]
permissions:
network: false
filesystem:
- read_only: true
allow_paths: ["{input_path}"]
---
描述里我刻意把触发场景放进了 description,这是前面说过的“场景匹配”技巧。正文部分我会写清楚:第一步扫描项目结构,第二步用 radon 计算圈复杂度,第三步用 pylint 做基础检查,第四步汇总生成报告。每个步骤都会有具体的操作提示和预期产出,让模型知道检查是否正常进行。
主脚本我用 Python 写,核心逻辑包括三块:遍历项目文件、调用分析工具、生成 JSON 报告。文件遍历要做一些过滤,跳过 node_modules、.git、dist、__pycache__ 这类目录,否则分析时间会不可控。复杂度分析用 radon 库,它可以直接输出可度量的指标。每个扫描结果都会记录到结构化数据结构里,最后统一渲染成 JSON 输出。
python复制#!/usr/bin/env python3
"""project health check skill - core script"""
import json
import sys
import os
from pathlib import Path
def load_config():
raw = sys.stdin.read().strip()
if not raw:
return {"project_path": ".", "depth": "basic"}
config = json.loads(raw)
return config
def should_skip(dirname):
return dirname in {".git", "node_modules", "dist", "build", "__pycache__", ".venv", "venv"}
def scan_project(root):
files = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if not should_skip(d)]
for filename in filenames:
if filename.endswith((".py", ".js", ".ts", ".java", ".go")):
full_path = Path(dirpath) / filename
files.append(full_path)
return files
def analyze_files(files, depth):
# 实际实现会调用 pylint、radon 等工具
# 这里保留结构示意
result = {
"overall_score": 0,
"issues": [],
"stats": {"files": len(files), "lines": 0, "avg_complexity": 0},
"meta": {"version": "1.0.0"}
}
return result
if __name__ == "__main__":
config = load_config()
try:
project_files = scan_project(config["project_path"])
report = analyze_files(project_files, config.get("depth", "basic"))
print(json.dumps(report, ensure_ascii=False, indent=2))
except Exception as exc:
print(json.dumps({"error": str(exc)}, ensure_ascii=False))
sys.exit(1)
5.3 注册、测试与迭代
技能文件写好后,放进 skills/techlead/project-health-check/ 目录。HagiCode 会自动扫描并注册。我会先做一次“冒烟测试”,直接在命令行里模拟 Agent 调用:向脚本传入一个测试项目的路径,检查返回的 JSON 是否符合预期结构。有问题的点就改脚本或改说明文档,直到输出稳定。
接下来把技能接入 Agent 测试。我会用几类典型的用户提问来验证触发效果,比如“帮我看下这个项目有没有问题”“代码质量怎么样”“有没有潜在的 bug”。每类提问执行一遍,记录技能是否被正确命中、报告内容是否合理。如果触发率偏低,我会回到 SKILL.md 里补充更多触发场景的描述。这个迭代过程基本决定了技能最终的可用性。
上线后还要持续监控。我会在技能里加一个简单的执行日志,记录每次调用的耗时和返回码。如果发现某个技能调用失败率偏高,通常不是模型问题,而是脚本容错不够,比如没有处理好“项目太大导致超时”这种情况。所以技能脚本的定位是:宁可返回一个局部结果加一个 warning,也不要因为超时让用户体验到“技能不存在”。这些边角处理,正是技能从“demo”变成“生产可用”的分水岭。
6. 常见问题与排查技巧实录
6.1 技能不生效,先别怪模型
“我都写好了,为什么 Agent 就是不调用?”这是我在社区里见过最多的问题。多数情况根本不是模型的问题,而是技能本身没被正确注册或描述命中率太低。排查步骤我一般按这个顺序来:先看技能目录是否在扫描范围内,看注册清单里有没有出现这个技能的记录;再看 SKILL.md 的 frontmatter 格式是否合法,YAML 里一个空格错了都会导致解析失败;最后再检查描述质量,看是否覆盖了用户的典型提问方式。
这一套排查下来,至少能解决 80% 的“技能不生效”问题。我的经验是,不要一开始就在提示词上做文章。先确认外层的注册和发现机制是通的,再往模型层面找原因。很多时候,问题是技能的 description 写得太“官方”,模型在语义匹配时没有把它和用户意图关联起来。把描述改成用户口吻,问题立刻消失,这种情况我遇到过太多次。
6.2 上下文爆炸与 token 成本失控
技能系统上线后,最容易失控的指标是 token 消耗。原因有两个:一是技能说明文档太长,每次调用都把全文塞进上下文;二是技能输出过于冗长,Agent 为了整理结果又额外消耗大量 token。我踩过的坑是某次技能返回了一个 2 万行的全量扫描结果,模型为了把它浓缩成 200 字摘要,一次对话烧掉了上万 token,成本直接翻了好几倍。
应对办法有三条。第一,SKILL.md 正文要精简,只保留模型决策和执行必需的信息,尽量控制在 1500 字以内;第二,脚本输出默认只返回摘要和关键路径,完整明细写到临时文件里,Agent 按需再读;第三,给技能加一层“输出上限”保护,脚本在返回 JSON 时做截断,超过阈值直接丢给模型一段提示,让它提醒用户“结果太大,建议先看摘要”。这些手段叠加起来,token 成本基本能降 40% 以上。
6.3 沙箱权限报错与依赖环境问题
沙箱权限是技能跑不起来的另一大类原因。常见报错包括:脚本尝试访问工作目录之外的文件、脚本需要执行一个不在白名单里的系统命令、脚本需要访问外部网络但网络策略默认是关闭的。我的排查思路是:先把技能的权限声明打开到“能跑通”的程度,功能正常后再逐项收紧,把配置收敛到最小必要权限。千万不要一开始就“禁止一切”,那只会让自己排查问题更难。
依赖环境问题也经常冒头。特别是 Python 技能,本地跑得好好的,放进 HagiCode 就报 ModuleNotFoundError。这种问题通常是因为技能没有在元信息里声明依赖,或者声明的版本和沙箱环境的版本冲突。我建议开发技能时就写清楚依赖,并提供一个 requirements.txt 或 pyproject.toml,HagiCode 在注册时读取这些声明,提前准备好环境。如果还是出问题,最快的排查方式是进入沙箱环境手动装依赖,再逐个 import。
还有一个容易被忽略的坑:技能脚本里用了绝对路径。脚本如果写死了 /Users/xxx/project 这种路径,换一台机器必然失败。正确的做法是统一用相对路径,或者通过环境变量拿到项目根目录,再拼接路径。我在团队里定过一条规矩:技能脚本不允许出现任何硬编码绝对路径,否则 CI 直接不通过。这条规矩救了我们很多次,强烈建议你也这么做。
最后分享一个我反复验证过的体会:可扩展的技能管理平台,最难的不是写技能,而是定规范。HagiCode 把技能的发现、注册、执行、隔离这些底层机制搭好了,但一个技能能不能被真正用好,取决于你给技能定的结构规范、描述规范和容错规范是否清晰。把规范定在前面,后面几百个技能进来都不会乱;规范定得模糊,十个技能就能把系统拖垮。所以如果你是团队的 Agent 平台负责人,建议先花时间把技能模板和规范打磨好,再放量让团队往里面加技能,这条路我走下来,是最稳的。
