refactor(skills): use native workspace symlinks instead of prompt injection#1246
Conversation
…ection - Add setupAssistantWorkspace() to symlink enabled skills into CLI-native skill directories (.gemini/skills/, .claude/skills/, .agents/skills/) - Enable Gemini CLI native SkillManager (skillsSupport: true) - Remove skills index injection from GeminiAgentManager and AcpAgentManager - Support symbolic links in fsBridge and AcpSkillManager file discovery - Treat existing skills as success (skip copy) during skill import
关于 PR 描述中提到的问题我仔细看了 main 分支上 skill 注入的实际逻辑,有几个地方和 PR 描述的说法存在出入: 1. "Skills 脚本完全无法执行" — ACP 侧不成立PR 描述说"系统只是单纯把 SKILL.md 的文字内容注入到 system prompt",但实际
ACP agent(Claude Code / OpenCode 等)拿到完整路径后,用 2. "System Prompt Token 浪费" — 程度被夸大注入的是轻量级的 INDEX(每个 skill 只有一行 name + description),不是 SKILL.md 全文。5 个 skills 的索引可能只有几百 token,称不上"大量消耗"。 3. Gemini 侧确实存在路径缺失问题Gemini 的 没有包含路径信息。当 SKILL.md body 中引用了相对路径的脚本(如 4. UI Bug — 确认存在i18n 插值缺失和 custom skills 列表渲染源错误这两个修复是正确的。 建议:用更小的改动解决 Gemini 路径问题既然问题只存在于 Gemini 的 // Before
export function buildSkillContentText(skills: SkillDefinition[]): string {
return skills.map((s) => `[Skill: ${s.name}]\n${s.body}`).join('\n\n');
}
// After — 加入 skill 目录路径,让 Gemini 能解析相对路径引用
export function buildSkillContentText(skills: SkillDefinition[]): string {
return skills.map((s) => {
const skillDir = path.dirname(s.location);
return `[Skill: ${s.name}]\n[Skill Directory: ${skillDir}]\n${s.body}`;
}).join('\n\n');
}这样 Gemini 收到的内容变为: Gemini 就能知道脚本文件在哪,用绝对路径执行。 相比 symlink 方案的优势:
建议拆分为两个 PR:
|
…symlinks # Conflicts: # src/process/initAgent.ts # src/renderer/pages/settings/AssistantManagement.tsx
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
背景與問題
目前 AionUi 的 Skills 機制存在一個根本性缺陷:系統只是單純把
SKILL.md的文字內容注入到 system prompt / 首條訊息中(prompt injection),讓 AI「知道」有哪些技能可用。但這種做法有三個嚴重問題:問題 1:Skills 腳本完全無法執行
以
pptx技能為例,其目錄結構包含實際的 Python/JS 腳本:SKILL.md中寫的指令是python ooxml/scripts/unpack.py(相對路徑),但因為這些腳本根本不存在於 AI 的工作空間中,所以 AI 嘗試執行時會直接報錯找不到檔案。同理,SKILL.md中引用的html2pptx.md等參考文件也無法被讀取。結論:目前的 Skills 功能只能當「文字提示」用,包含腳本或參考文件的技能實質上是壞的。
問題 2:System Prompt Token 浪費
現有機制透過
buildSystemInstructionsWithSkillsIndex()(Gemini)和prepareFirstMessageWithSkillsIndex()(ACP),將所有啟用的 Skills 的 SKILL.md 索引文字注入到 system prompt 或首條訊息中。每次對話都會消耗大量 token 在這些靜態文字上,即使 AI 在當輪對話中根本不需要使用任何技能。問題 3:助手設定中技能移除 UI Bug
{{name}},沒有正確插值替換為技能名稱。availableSkills(系統所有自訂技能),而移除操作只更新了customSkillsstate。解決方案
核心改動:以 Workspace Symlink 取代 Prompt Injection
在建立對話時,將使用者目錄中啟用的 skills 以 symbolic link 方式放入 CLI 原生的 skills 目錄,讓各 CLI 的 SkillManager 自動發現和載入技能。
每個 agent 只 symlink 到自己的原生 skills 目錄:
.gemini/skills/{name}/.claude/skills/{name}/.agents/skills/{name}/(通用 fallback)安全性考量:只在臨時 workspace(非使用者指定的自訂 workspace)中建立 symlink,避免污染使用者專案目錄。
重要行為變更:System Prompt 不再包含 SKILL 資訊
本次重構後,system prompt / 首條訊息中不再注入任何 Skills 索引或 SKILL.md 內容。
變更前:
GeminiAgentManager:透過buildSystemInstructionsWithSkillsIndex()將 skills 索引注入presetRules→ 作為 system prompt 的一部分AcpAgentManager:透過prepareFirstMessageWithSkillsIndex()將 skills 索引注入首條使用者訊息變更後:
GeminiAgentManager:presetRules只包含助手的人格規則(如「你是專業運動教練」),不再包含 Skills 索引。Skills 由 CLI 的原生 SkillManager 透過掃描.gemini/skills/目錄自動發現,AI 使用activate_skill工具按需載入AcpAgentManager:首條訊息只注入presetContext(人格規則),不再附加 skills 索引。Skills 同樣透過 workspace 中的 symlink 目錄被 CLI 原生發現好處:
實際效果截圖
Gemini CLI — workspace 中只有
.gemini/skills/Claude Code — workspace 中正確建立
.claude/skills/各檔案變更說明
src/process/initAgent.ts(+121)setupAssistantWorkspace()函式:根據 agent 類型將啟用的 skills symlink 到對應的原生目錄AGENT_SKILLS_DIRS映射表:只列出有專屬目錄的 CLI,其餘走DEFAULT_SKILLS_DIRSsetupAssistantWorkspace()呼叫src/agent/gemini/cli/config.ts(+4 / −3)skillsSupport: false → true:啟用 Gemini CLI 原生的 SkillManagersrc/process/bridge/fsBridge.ts(+8 / −4)entry.isSymbolicLink()判斷success: true(跳過複製)src/process/task/AcpAgentManager.ts(+4 / −8)prepareFirstMessageWithSkillsIndex()呼叫presetContext(助手人格規則)的注入src/process/task/AcpSkillManager.ts(+2 / −2)entry.isSymbolicLink()判斷src/process/task/GeminiAgentManager.ts(+8 / −12)buildSystemInstructionsWithSkillsIndex()呼叫presetRules直接傳遞原始的助手人格規則src/renderer/pages/settings/AssistantManagement.tsx(+7 / −5)name參數測試驗證
bun run lint— 通過,無錯誤bun run test— 452 passed, 2 failed(configureChromium.test.ts上游既有問題,與本 PR 無關).gemini/skills/目錄中正確建立 symlinkactivate_skill工具成功發現並載入技能