agentic-awesome-skills 与 Jetski/Cortex + Gemini 的延迟加载集成:在上下文窗口内安全使用 1,936+ 技能
【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,115+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/gh_mirrors/an/agentic-awesome-skills
本指南面向基于 Jetski/Cortex(或同类自定义 Host)接入 Gemini 的开发者,核心解决"如何在不超过模型上下文窗口的前提下,安全、可扩展地使用 agentic-awesome-skills 仓库中 1,936+ 个技能"。读完本文,你将掌握"轻量级清单 + 按需延迟加载 + 显式上限"的集成范式,能复现可运行的 TypeScript/Node.js 加载器,并具备排查TrajectoryChatConverter截断崩溃循环的实战能力。
在 Jetski/Cortex 这类 Host 中集成技能库时,一个典型报错是:
TrajectoryChatConverter: could not convert a single message before hitting truncation
问题通常不在于技能本身,而在于加载方式——一次性把整库技能指令灌入上下文,任何模型都扛不住。下文先剖析反模式,再给出仓库官方推荐的替代方案。
1. 应避免的反模式:全量加载
在启动或每次请求时做以下任何一件事,都会迅速耗尽上下文窗口:
- 在启动时读取所有
skills/*/SKILL.md目录; - 将所有
SKILL.md的内容拼接进单个系统提示词; - 为每次请求重新注入整个技能库。
对于超过 1,936 个技能,这种方法在添加任何用户消息之前就已经填满上下文,进而触发截断错误。从仓库结构看,skills/目录下每个技能子目录都含一份SKILL.md(例如 skills/brainstorming/SKILL.md),全量拼接的 token 成本随技能数线性膨胀,是不可持续的。
2. 推荐模式:清单驱动 + 延迟加载
正确的集成遵循四条关键原则:
- 轻量级清单:使用仓库根目录的 skills_index.json 作为规范清单,只了解存在哪些技能,不加载完整文本;
data/skills_index.json仅为兼容性镜像; - 延迟加载:仅针对对话中实际调用的技能(例如消息中出现
@skill-id时)读取对应SKILL.md; - 显式限制:对每轮加载的最大技能数 / token 数施加限制,并提供清晰的回退机制;
- 路径安全:读取
SKILL.md之前,验证清单中的路径始终保持在SKILLS_ROOT内。
推荐的完整流程如下:
- 引导(Bootstrap):代理启动时读取
skills_index.json,构建id -> meta映射; - 消息解析:调用模型之前,从用户 / 系统消息中提取所有
@skill-id引用; - 解析:用引导映射把找到的 id 映射为
SkillMeta对象; - 延迟加载:仅针对这些 id 读取
SKILL.md(最多不超过可配置上限); - 提示词构建:系统消息中只包含被选中技能的定义。
仓库在 docs/integrations/jetski-gemini-loader/ 提供了可直接参考的示例实现(loader.mjs+README.md),并在 docs/users/discovery-manifest.md 中固化了这一契约,本文第 4 节将结合真实源码展开。
3.skills_index.json清单结构与字段契约
文件skills_index.json是一个对象数组(JSON Array),每个技能一个条目。例如:
{ "id": "brainstorming", "path": "skills/brainstorming", "category": "planning", "name": "brainstorming", "description": "Use before any creative or constructive work.", "risk": "safe", "source": "official", "date_added": "2026-02-27" }关键字段:
id:在@id提及中使用的标识符(例如@brainstorming);path:包含SKILL.md的目录(例如skills/brainstorming/);category:用于 UI / 搜索分组的归类;name:显示名称;description:简短的用途 / 触发条件摘要;risk:仓库定义的技能风险标签;source:来源与信任元数据;date_added:ISO 日期字符串或null。
要获得技能定义文件的完整路径:
fullPath = path.join(SKILLS_ROOT, meta.path, "SKILL.md")注意:
SKILLS_ROOT是安装仓库的根目录(例如~/.agents/skills)。
从 schemas/skills-index.v1.schema.json 可以看到更完整的契约细节:id最小长度为 1,path必须匹配^skills/前缀,category、name、description、risk、source、date_added均为必需字段;同时允许附加字段,plugin元数据(含targets、setup、reasons)为可选。仓库的根清单实际还扩展了tags等字段,说明清单契约对附加字段保持开放。
关于data/skills_index.json兼容性镜像
仓库同时维护了data/skills_index.json。根据 docs/users/discovery-manifest.md 的说明,两者必须保持相同内容,data/镜像的存在只是为了兼容仍从data/子树读取清单的下游客户端。新集成应优先读取根目录skills_index.json,仅在宿主必须读取data/子树时才回退到镜像。
4. 集成实现:从伪代码到可运行源码
完整可运行示例位于 docs/integrations/jetski-gemini-loader/(纯 Node.js ESM 模块,无需 TypeScript 运行时即可直接 import)。
4.1 基本类型
type SkillMeta = { id: string; path: string; name: string; description?: string; category?: string; risk?: string; };在真实实现 loader.mjs 中,该类型以 JSDoc@typedef形式声明,Message类型则限定role为"system" | "user" | "assistant"。
4.2 引导:加载清单
function loadSkillIndex(indexPath: string): Map<string, SkillMeta> { const raw = fs.readFileSync(indexPath, "utf8"); const arr = JSON.parse(raw) as SkillMeta[]; const map = new Map<string, SkillMeta>(); for (const meta of arr) { map.set(meta.id, meta); } return map; }对应源码见 loader.mjs 的loadSkillIndex:读取 JSON 后逐条map.set(meta.id, meta),整个过程不触碰任何SKILL.md,因此引导成本极低。
4.3 解析消息以查找@skill-id
const SKILL_ID_REGEX = /@([a-zA-Z0-9-_./]+)/g; function resolveSkillsFromMessages( messages: { role: string; content: string }[], index: Map<string, SkillMeta>, maxSkills: number ): SkillMeta[] { const found = new Set<string>(); for (const msg of messages) { let match: RegExpExecArray | null; while ((match = SKILL_ID_REGEX.exec(msg.content)) !== null) { const id = match[1]; if (index.has(id)) { found.add(id); } } } const metas: SkillMeta[] = []; for (const id of found) { const meta = index.get(id); if (meta) metas.push(meta); if (metas.length >= maxSkills) break; } return metas; }真实源码中的实现更严谨:先由collectReferencedSkillIds用String.prototype.matchAll收集引用集合(天然去重,见 loader.mjs L24-L37),再由assertValidMaxSkills校验maxSkills必须是大于 0 的整数(loader.mjs L39-L45),最后按序截断到上限(loader.mjs L59-L75)。
4.4SKILL.md文件的延迟加载(含路径安全)
async function loadSkillBodies( skillsRoot: string, metas: SkillMeta[] ): Promise<string[]> { const bodies: string[] = []; for (const meta of metas) { const fullPath = path.join(skillsRoot, meta.path, "SKILL.md"); const text = await fs.promises.readFile(fullPath, "utf8"); bodies.push(text); } return bodies; }仓库的真实实现(loader.mjs L77-L116)在此之上做了三层路径安全防护,值得完整采纳:
- 相对路径检查:
path.relative(rootPath, skillDirPath)若以..开头或为绝对路径,则抛出Skill path escapes skills root; - 目录类型检查:通过
lstat确认技能目录是普通目录而非符号链接,避免链接逃逸; - 解析后二次校验:对最终
SKILL.md做realpath后再次path.relative校验,任何解析到SKILLS_ROOT之外的情况都直接抛错。
这种"读取前校验 + 读取后复核"的做法,正是原文档第 2 节"路径安全"原则的落地实现。
4.5 构建 Jetski/Cortex 提示词
在TrajectoryChatConverter之前的预处理阶段,按如下方式组装消息:
async function buildModelMessages( baseSystemMessages: { role: "system"; content: string }[], trajectory: { role: "user" | "assistant" | "system"; content: string }[], skillIndex: Map<string, SkillMeta>, skillsRoot: string, maxSkillsPerTurn: number, overflowBehavior: "truncate" | "error" = "truncate" ): Promise<{ role: string; content: string }[]> { const referencedSkills = resolveSkillsFromMessages( trajectory, skillIndex, Number.MAX_SAFE_INTEGER ); if ( overflowBehavior === "error" && referencedSkills.length > maxSkillsPerTurn ) { throw new Error( `Too many skills requested in a single turn. Reduce @skill-id usage to ${maxSkillsPerTurn} or fewer.` ); } const selectedMetas = resolveSkillsFromMessages( trajectory, skillIndex, maxSkillsPerTurn ); const skillBodies = await loadSkillBodies(skillsRoot, selectedMetas); const skillMessages = skillBodies.map((body) => ({ role: "system" as const, content: body, })); return [...baseSystemMessages, ...skillMessages, ...trajectory]; }仓库的 loader.mjsbuildModelMessages与其一一对应,并有两个额外细节:
- 默认值:
maxSkillsPerTurn = 8,overflowBehavior = "truncate"; - 零技能短路:
selectedMetas.length === 0时直接返回[...baseSystemMessages, ...trajectory],连一次文件系统访问都不会发生。
建议:加入 token 估算,在上下文窗口接近限制时对
SKILL.md做截断或摘要。参考加载器已支持显式回退overflowBehavior: "error",让宿主在超限时明确失败而非静默丢弃技能。
使用方式(摘自 jetski-gemini-loader/README.md)大致如下:
const REPO_ROOT = "/path/to/agentic-awesome-skills"; const SKILLS_ROOT = REPO_ROOT; const INDEX_PATH = path.join(REPO_ROOT, "skills_index.json"); // 1. 启动时加载一次清单 const skillIndex = loadSkillIndex(INDEX_PATH); // 2. 调用模型前构建消息 const modelMessages = await buildModelMessages({ baseSystemMessages, trajectory, skillIndex, skillsRoot: SKILLS_ROOT, maxSkillsPerTurn: 8, overflowBehavior: "error", }); // 3. 把 modelMessages 交给 Jetski/Cortex + Gemini 客户端 // 例如 trajectoryChatConverter.convert(modelMessages)5. 处理上下文溢出
为避免出现用户难以理解的报错,建议设置:
- 安全阈值:例如上下文窗口的 70-80%;
- 每轮最大技能数限制:例如 5-10 个。
超过阈值时的两种策略:
- 缩减:减少本轮包含的技能数量(例如按最近使用或优先级排序后裁剪);
- 显式报错:向用户返回明确错误,例如:
"在此轮中请求了太多技能。减少消息中的
@skill-id数量或将其分为多个步骤。"
在参考实现中,overflowBehavior: "error"会抛出带技能上限数字的明确异常;"truncate"则按清单顺序截断。需要指出的是,"截断"是静默行为,实际加载的技能可能与用户期望不完全一致,因此在偏好显式失败的环境中应保持overflowBehavior: "error"(docs/integrations/jetski-gemini-loader/README.md 也给出了同样的建议)。
6. 推荐的测试场景
上线前至少覆盖以下三个场景:
- 场景 1 — 简单消息("hi")
- 没有
@skill-id→ 不加载SKILL.md→ 提示词保持较小 → 无错误。
- 没有
- 场景 2 — 少量技能
- 消息包含 1-2 个
@skill-id→ 仅加载相关SKILL.md→ 无溢出。
- 消息包含 1-2 个
- 场景 3 — 大量技能
- 消息包含许多
@skill-id→ 触发maxSkillsPerTurn限制或 token 检查 → 无静默溢出(可断言error模式下抛出异常、truncate模式下消息数受限)。
- 消息包含许多
这三个场景分别验证了"零技能短路"、"按需加载"与"显式上限"三条核心路径,可以快速回归加载器的行为是否符合预期。
7. 技能子集与捆绑包:进一步控制加载面
除延迟加载外,还可以通过缩小技能安装面来控制上下文:
- 将不需要的技能移至
skills/.disabled/,在某些环境中将其排除; - 使用 docs/users/bundles.md 描述的捆绑包(Bundles),只加载主题分组。
捆绑包是"按角色与专业水平整理的精选技能集合",例如 "Essentials" 起步包、Web Wizard、Security Engineer 等。它们在仓库中以plugins/agentic-bundle-*/目录形式存在(如plugins/agentic-bundle-aas-web-app-builder/),属于可安装的插件子集与激活预设,而非可被@调用的巨型技能。若希望捆绑包表现为"聚焦的活动子集"而非全量安装,可借助仓库的激活脚本:
# macOS/Linux ./scripts/activate-skills.sh --clear Essentials ./scripts/activate-skills.sh --clear "Web Wizard" # Windows .\scripts\activate-skills.bat --clear Essentials从 docs/users/bundles.md 的说明可以看出,捆绑包内的每个技能都指向具体技能目录,这一映射关系与清单中path字段一致,因此延迟加载器可以直接复用。
8. 如果已在崩溃循环中:Windows 恢复指南
若主机在截断错误后反复重新打开同一份损坏轨迹(表现为应用启动即崩溃、或删除问题技能后仍回到同一错误),可参考 docs/users/windows-truncation-recovery.md 的完整恢复流程:
- 备份先行:备份
%USERPROFILE%\.gemini\antigravity-browser-profile\Default、%AppData%\antigravity、%USERPROFILE%\.gemini\antigravity(若技能安装在其他目录也一并备份); - 删除问题技能或包:默认安装路径为
%USERPROFILE%\.gemini\antigravity\plugins\skills; - 删除本地存储:清掉 Antigravity 浏览器配置中的
Local Storage、Session Storage、IndexedDB,以及%AppData%\antigravity下的Local Storage、Session Storage; - 清空临时目录:
%TEMP%; - 重启并用延迟加载 + 显式限制重新安装:只装真正需要的技能,或切换为带显式上限的延迟加载集成。
该文档还附带一个社区贡献的批处理恢复脚本(Anti-Gravity_Recovery_Tool_Universal),会自动执行备份、清理Local Storage等步骤,使用前请先审阅脚本内容。
为防止问题再次出现:
- 更偏好显式失败时,保持
overflowBehavior: "error"; - 持续验证解析出的路径是否保持在
skillsRoot内。
9. 总结
- 切勿将所有
SKILL.md拼接到单个提示词中; - 使用根目录 skills_index.json 作为轻量级规范清单;仅在宿主必须读取
data/子树时使用data/skills_index.json兼容性镜像; - 基于
@skill-id按需加载技能; - 设置明确限制(每轮最大技能数、token 阈值);
- 参考实现见 docs/integrations/jetski-gemini-loader/loader.mjs,清单契约见 schemas/skills-index.v1.schema.json 与 docs/users/discovery-manifest.md,崩溃恢复见 docs/users/windows-truncation-recovery.md。
遵循以上模式,Jetski/Cortex + Gemini 就可以以安全、可扩展且与现代模型上下文窗口兼容的方式,使用整个 agentic-awesome-skills 技能库。
【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,115+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/gh_mirrors/an/agentic-awesome-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考