Problem / 问题
中文
- PI-Desktop 每一轮请求都会把所有可见 skill 的完整
id — name: description 塞进 system prompt 的 # Skills 区块。
- 实测:561 个已安装 skill、331 个可见时,该区块 73,130 bytes;395 个可见时 85,457 bytes,占固定前缀约 69%。
- 同一 session 实测固定前缀 token 长度:
req1 = 29,479 tokens(占该轮 89,944 峰值上下文的 32.8%)。这部分成本每一轮都付一次(后续以 cacheRead 计费,但仍是同一个前缀在反复上传)。
- 这段内容是模型极少完整阅读的目录元数据:基准 session 中共 31 次工具调用,
Skill 工具一次都没被用过。
- 目前没有任何开关可以调整。我已确认 app 设置模型只暴露 theme / mode / permissions / command shell / context usage display / large-paste threshold / font / language / proxy /
contextCompaction,没有任何 skill 相关项;在 app.asar(main + renderer,49 个文件)中搜索 compact_all、skillsCompact、namesOnly、showDescriptions、hideDescriptions、descriptionMode、skillVisibility 等候选键,0 命中。
English
Every request injects the full id — name: description line for every visible skill into the # Skills block of the system prompt. Measured on a real install: 73,130 bytes with 331 visible skills; 85,457 bytes with 395 visible (≈69% of the fixed prefix). Measured fixed-prefix token length: 29,479 tokens at req1, i.e. 32.8% of that session's 89,944-token peak context — paid on every single request. In the baseline session, 31 tool calls were made and Skill was never used once.
There is no switch for this today. Verified: the settings model exposes theme, mode, permissions, command shell, context-usage display, large-paste threshold, font, language, proxy and contextCompaction — nothing skill-related; and a targeted search of app.asar (main + renderer, 49 files) for compact_all, skillsCompact, namesOnly, showDescriptions, hideDescriptions, descriptionMode, skillVisibility returns zero hits.
Relevant code, resources/agent-runtime/sidecar.js:
// src/plugin-skills-prompt.ts
function pluginSkillsPrompt(skills) {
if (!skills.length) return void 0;
return [
"# Skills",
"",
`Plugins have taught you the following skills. ...`,
"",
...skills.map((skill) => {
const description = skill.description?.trim();
return `- \`${skill.id}\` \u2014 ${skill.name}${description ? `: ${description}` : ""}`;
})
].join("\n");
}
No mode switch, no truncation. Note the formatter already emits the names-only shape when description is absent.
Proposed change / 期望改动
新增一个 skills 显示模式设置(三个值),并可选加一个描述长度上限:
| mode |
输出 |
用途 |
full(默认,= 现状) |
- `id` — Name: Description |
现有用户行为不变 |
names |
- `id` — Name |
大型 skill 库推荐默认 |
off |
省略整个 # Skills 区块 |
完全在别处管理 skill 的场景 |
可选:描述上限(字符数),给想要折衷的用户。
为什么 names 是最佳平衡点:每个 skill 的名称仍留在 prompt 里,可发现性不变——模型依然能看到全部 id,并可用 Skill 工具载入任何一个(该工具本来就会重新读取完整正文,且 pluginSkillsDigest 已把正文排除在快取摘要之外)。消失的只是那一行简介,而同一区块本来就在告诉模型「先读完整 skill 文件再动手」。
实测(真实 331 个可见 skill):
| 变体 |
区块 bytes |
相对现状 |
每请求省下的 tokens(@4.05–4.59 B/tok) |
| full(现状) |
73,130 |
— |
— |
| names only |
19,190 |
−73.8% |
−11,752 … −13,319 |
| full,描述截到 120 字符 |
50,862 |
−30.4% |
−4,851 … −5,498 |
最小改动:
function pluginSkillsPrompt(skills, mode = "full") {
if (!skills.length || mode === "off") return void 0;
return [
"# Skills",
"",
`Plugins have taught you the following skills. ...`,
"",
...skills.map((skill) => {
const description = mode === "names" ? "" : skill.description?.trim();
return `- \`${skill.id}\` \u2014 ${skill.name}${description ? `: ${description}` : ""}`;
})
].join("\n");
}
两处调用点都要传入解析后的 mode(目前都写作 pluginSkillsPrompt(this.pluginSkills)):
RuntimeSession 构造 → 喂养 defaultSystemPrompt
composeSystemPrompt() → if (tools.has(SKILL_TOOL_NAME)) { ... } 分支
快取安全要求(重要)
本 runtime 的 prompt 快取依赖前缀稳定,因此:
- mode 必须在 session 建立时解析一次并保持不变,与目前的
baseSystemPrompt 相同;不要每轮重新读取设置。
- mode 必须纳入 catalog digest(目前为
pluginSkillsDigest(this.pluginSkills) === pluginSkillsDigest(requestedPluginSkills),用于让 runtime 退休并重建 prompt)。否则切换设置要么不生效,要么更糟——在 session 中途改变前缀、把快取整个打坏(我在实测中见过一次同类事件造成约 29k tokens 的重新前算)。
验收标准
names 模式下 # Skills 区块对每个 skill 都符合现有的「无描述」格式。
- 切换模式在新 session 生效,且反映在
pluginSkillsDigest(runtime 会退休)。
- 331 个 skill 的库,固定前缀下降 ≥ 11,000 tokens(可用下方方法验证)。
- 所有 skill 名称仍然存在,且
Skill(id=…) 仍能载入每一个。
- 稳态 cache hit 维持 ≥ 98%(基准 session 实测中位数 98.16%)。
Skill 工具描述仍说明如何载入,区块的引导句仍要求「先读完整文件再动手」。
Alternatives / 其他方案
- 只靠卸载/停用 skill — 已经可行,我也确实用这个方法砍掉了 64 个 skill。但它是钝器:用能力换 token,且每个 skill 都要人来判断。显示模式是用简介文字换 token,所有能力仍可触达。
- 把描述截断到 N 字符 — 可行的折衷(120 字符省 30.4%),但描述会读起来像被切断的片段,且仍留下约 50 KB。
- 直接缩短 SKILL.md 里的 description — 会伤害同一批文件的其他消费者(其他 agent、skill 列表 UI、人类阅读)。该修的是 prompt 的格式层,不是内容层。
另外:本提议与 #203(暴露 compaction 阈值)方向一致——都是把上下文预算交还给用户;与 #287 / #150(skill 市场、GitHub 链接)不冲突,可并存。
Additional context / 补充信息
数字怎么量出来的(可复现)
PI-Desktop 的序列化顺序是 [固定前缀][tools][对话],供应商做前缀快取。当 tools 数组在 session 中途变动时,快取会刚好在固定前缀结尾断开,此时 cacheRead 就等于固定前缀 token 长度。 这个事件可以刻意触发:调用一次 ToolSearch 启用任一 on-demand 工具,然后读下一个请求的 usage。数据源:~/.pi-desktop/sessions/<id>.jsonl 中每条 assistant 消息的 meta.usage。
以此法取得的读数:
| session |
时点 |
断点处 cacheRead |
触发 |
8d58db77 |
变更前 |
27,392 |
启用 Grep |
3eac5178 |
停用 64 个 skill 后 |
24,704 |
启用 BrowserPreview |
区块 bytes 的来源:skills 根目录下的 SKILL.md frontmatter(name、description),用 pluginSkillsPrompt() 的格式字符串重组后量 UTF-8 bytes。
环境:Windows 11、PI-Desktop 0.14.8、561 个已安装 skill(停用后 331 个可见)、provider opencode-go(https://opencode.ai/zen/go/v1)、model deepseek-v4.1-flash、上下文窗口 1M。
先例:Hermes Agent 已有等价开关 skills.compact_all: true,其文档描述为「system prompt 中的 skill 索引只显示 skill 名称(不含描述)……每个 skill 名称仍可见,且仍可透过 skill_view() 载入——只是丢掉描述」,并自估该区块省约 67%,与本次实测的 −73.8% 同量级。
建议标签:area: skills、enhancement(我没有 triage 权限,无法在提交时自行设置)。
提交前已检索全部 224 个 issue,未发现重复。
Problem / 问题
中文
id — name: description塞进 system prompt 的# Skills区块。req1= 29,479 tokens(占该轮 89,944 峰值上下文的 32.8%)。这部分成本每一轮都付一次(后续以 cacheRead 计费,但仍是同一个前缀在反复上传)。Skill工具一次都没被用过。contextCompaction,没有任何 skill 相关项;在app.asar(main + renderer,49 个文件)中搜索compact_all、skillsCompact、namesOnly、showDescriptions、hideDescriptions、descriptionMode、skillVisibility等候选键,0 命中。English
Every request injects the full
id — name: descriptionline for every visible skill into the# Skillsblock of the system prompt. Measured on a real install: 73,130 bytes with 331 visible skills; 85,457 bytes with 395 visible (≈69% of the fixed prefix). Measured fixed-prefix token length: 29,479 tokens atreq1, i.e. 32.8% of that session's 89,944-token peak context — paid on every single request. In the baseline session, 31 tool calls were made andSkillwas never used once.There is no switch for this today. Verified: the settings model exposes theme, mode, permissions, command shell, context-usage display, large-paste threshold, font, language, proxy and
contextCompaction— nothing skill-related; and a targeted search ofapp.asar(main + renderer, 49 files) forcompact_all,skillsCompact,namesOnly,showDescriptions,hideDescriptions,descriptionMode,skillVisibilityreturns zero hits.Relevant code,
resources/agent-runtime/sidecar.js:No mode switch, no truncation. Note the formatter already emits the names-only shape when
descriptionis absent.Proposed change / 期望改动
新增一个 skills 显示模式设置(三个值),并可选加一个描述长度上限:
full(默认,= 现状)- `id` — Name: Descriptionnames- `id` — Nameoff# Skills区块可选:
描述上限(字符数),给想要折衷的用户。为什么
names是最佳平衡点:每个 skill 的名称仍留在 prompt 里,可发现性不变——模型依然能看到全部 id,并可用Skill工具载入任何一个(该工具本来就会重新读取完整正文,且pluginSkillsDigest已把正文排除在快取摘要之外)。消失的只是那一行简介,而同一区块本来就在告诉模型「先读完整 skill 文件再动手」。实测(真实 331 个可见 skill):
最小改动:
两处调用点都要传入解析后的 mode(目前都写作
pluginSkillsPrompt(this.pluginSkills)):RuntimeSession构造 → 喂养defaultSystemPromptcomposeSystemPrompt()→if (tools.has(SKILL_TOOL_NAME)) { ... }分支快取安全要求(重要)
本 runtime 的 prompt 快取依赖前缀稳定,因此:
baseSystemPrompt相同;不要每轮重新读取设置。pluginSkillsDigest(this.pluginSkills) === pluginSkillsDigest(requestedPluginSkills),用于让 runtime 退休并重建 prompt)。否则切换设置要么不生效,要么更糟——在 session 中途改变前缀、把快取整个打坏(我在实测中见过一次同类事件造成约 29k tokens 的重新前算)。验收标准
names模式下# Skills区块对每个 skill 都符合现有的「无描述」格式。pluginSkillsDigest(runtime 会退休)。Skill(id=…)仍能载入每一个。Skill工具描述仍说明如何载入,区块的引导句仍要求「先读完整文件再动手」。Alternatives / 其他方案
另外:本提议与 #203(暴露 compaction 阈值)方向一致——都是把上下文预算交还给用户;与 #287 / #150(skill 市场、GitHub 链接)不冲突,可并存。
Additional context / 补充信息
数字怎么量出来的(可复现)
PI-Desktop 的序列化顺序是
[固定前缀][tools][对话],供应商做前缀快取。当 tools 数组在 session 中途变动时,快取会刚好在固定前缀结尾断开,此时cacheRead就等于固定前缀 token 长度。 这个事件可以刻意触发:调用一次ToolSearch启用任一 on-demand 工具,然后读下一个请求的 usage。数据源:~/.pi-desktop/sessions/<id>.jsonl中每条 assistant 消息的meta.usage。以此法取得的读数:
cacheRead8d58db77Grep3eac5178BrowserPreview区块 bytes 的来源:skills 根目录下的
SKILL.mdfrontmatter(name、description),用pluginSkillsPrompt()的格式字符串重组后量 UTF-8 bytes。环境:Windows 11、PI-Desktop 0.14.8、561 个已安装 skill(停用后 331 个可见)、provider
opencode-go(https://opencode.ai/zen/go/v1)、modeldeepseek-v4.1-flash、上下文窗口 1M。先例:Hermes Agent 已有等价开关
skills.compact_all: true,其文档描述为「system prompt 中的 skill 索引只显示 skill 名称(不含描述)……每个 skill 名称仍可见,且仍可透过skill_view()载入——只是丢掉描述」,并自估该区块省约 67%,与本次实测的 −73.8% 同量级。建议标签:
area: skills、enhancement(我没有 triage 权限,无法在提交时自行设置)。提交前已检索全部 224 个 issue,未发现重复。