# 2.7 把所有 skill 塞进窗口，还是只放一张索引

## 怎么跑

```bash
# 1. 建 skill 池（不调 API）
.venv/bin/python experiments/2_7_skills/build_pool.py
# 2. 出题：gpt-5 起草候选题，再用三个模型做“不给 skill”的基线，人工复核后定稿
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/make_tasks.py draft
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/make_tasks.py baseline
.venv/bin/python experiments/2_7_skills/make_tasks.py finalize          # 读 selection.json
# 3. 主实验（每个模型一行）
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/run.py grok-4-fast none index full_1 full_10 full_30 full_all index_rep2
# 4. 判分、汇总
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/grade.py
# 5. 修描述后重跑
.venv/bin/python experiments/2_7_skills/fix_descriptions.py plan
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/fix_descriptions.py rewrite
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/run.py grok-4-fast index_fixed
op run --env-file experiments/aib.env -- .venv/bin/python -u experiments/2_7_skills/grade.py && .venv/bin/python experiments/2_7_skills/grade.py analyze
```

接口是 AI Builder Space（OpenAI 兼容）。模型：deepseek-v4.1-flash、grok-4-fast、gpt-5。实验日期 2026-10-13。全部花费约 54 美元（网关账单前后差），其中主实验 28.6 美元，大头是 grok 的 full_all（12.1 美元）。

## 先说网关的三个事实，它们影响怎么读结果

- **deepseek-v4.1-flash（网关兼容 ID 为 `deepseek-v4-flash`）** 走网关自己的 orchestrator：它会给模型挂上自己的 `search` / `extract` 联网工具，并丢掉客户端传的 tools（`probe.json`）。所以 deepseek 的 index 条件没法用原生 tool calling，改用文本协议：模型回一行 `READ_SKILL: <name>`，我们把全文作为下一条 user 消息发回去。为了防止它联网去 GitHub 读这些公开 skill，所有条件的 system prompt 都写了“离线，不要搜索”，并且每次调用都开 debug 记录 orchestrator 是否用了联网工具。结果：主实验和基线里 deepseek 一次联网都没有（`results.json` 里 `n_web_flagged` 全是 0）。探测时我们见过它不听这句话去联网的情况，所以这一列要看。
- **gpt-5 的温度被网关强制改成 1.0**，不是 0。判分也用 gpt-5，所以判官同样是温度 1。
- **grok-4-fast 实际是 xAI grok-4.3（非推理）**。温度 0 下它两次跑同一组题仍然不完全一致（见下文 index_rep2）。

## 实验设计

**skill 池（`pool.json`）。** 来源是公开的 context-infrastructure 仓库 `rules/skills/*.md`，加上 `adhoc_jobs/` 下 origin 名字出现在 `SKILL_ECOSYSTEM.md` 里的公开 skill 仓库的 `skills/*.md` 和 `skills/<dir>/SKILL.md`，只取 git 跟踪的文件，不取 `references/`、`templates/` 和 skill 目录里的子文件。去重规则：同名取较长的那份（context-infrastructure 里有 5 个写作类文件只剩“已迁移到 writing-skill”的指针，换成 writing-skill 里的正文）。密钥扫描只命中占位符（example.com、`replace-with-your-...`），唯一剔除的是 `skill_imessage.md`，它的例子里有一个具体的美国电话号码，无法确认是虚构的（`exclude.json`）。最后是 **139 个 skill，369,217 个 token（cl100k）**。描述取 frontmatter 的 `description`，没有就取第一个标题加第一句；索引里每条截到 240 字符。全量索引 6,682 token。

**题目（`tasks.json`）。** gpt-5 读每个 skill，各提 2 道候选题，要求题目像真实用户的提问、不提 skill 文件名、不照抄原文，并给出逐字摘录和判分要点；摘录不是原文子串的直接丢掉，剩 236 道。三个模型在“不给任何 skill”的情况下各答一遍，169 道三个模型全错。我们只从这 169 道里选，再人工读摘录筛掉：答案在别的 skill 里也写着的（比如 voice-lora 和 voice-lora-rewrite、ios_development 和 simulator_testing 讲的是同一条），依赖占位值或私有路径的，问题本身含糊的。最后 30 道，30 个不同的目标 skill，8 个来自 context-infrastructure，22 个来自 22 个不同的生态仓库。筛选理由在 `selection.json`。

**条件（每个模型、每道题一次调用，题与题之间没有历史）。**

- `none`：不给 skill。
- `full_k`：system prompt 里放 k 个 skill 全文，k = 1、10、30、全部。固定种子打乱一次池子，干扰项取这个顺序里的前 k-1 个非目标 skill，所以同一个 k 下每道题看到的干扰项相同；目标 skill 插在按题号定的随机位置。`full_all` 就是整个池子按这个顺序排。
- `index`：system prompt 里只有 139 行索引，模型用 `read_skill(name)` 打开全文，最多打开 4 次，然后必须回答。
- `index_rep2`：和 index 一模一样再跑一遍，用来量“同样的输入，结果会变多少”。
- `index_fixed`：只改 index 里没打开目标 skill 的那些 skill 的一句话描述，重跑这些题，再加 10 道原来答对的题看有没有改坏。

**判分。** gpt-5 当判官，看题目、逐字摘录、判分要点和答案，输出 `{correct, reason}`。

## 结果

### 准确率、输入 token、延迟、花费（`results.json` 的 `cells`）

| 模型 | none | full_1 | full_10 | full_30 | full_all | index | index_rep2 |
|---|---|---|---|---|---|---|---|
| deepseek-v4.1-flash | 2/30 | 28/30 | 30/30 | 30/30 | 30/30 | 24/30 | 24/30 |
| gpt-5 | 1/30 | 30/30 | 28/30 | 29/30 | 放不下 | 22/30 | 22/30 |
| grok-4-fast | 0/30 | 27/30 | 27/30 | 27/30 | 19/30 | 13/30 | 13/30 |

| 模型 | 条件 | 平均输入 token（接口计费） | 平均延迟（秒） | 30 题总花费（美元） |
|---|---|---|---|---|
| deepseek-v4.1-flash | full_1 | 2,977 | 3.9 | 0.021 |
| | full_10 | 16,610 | 4.5 | 0.083 |
| | full_30 | 64,455 | 4.7 | 0.298 |
| | full_all | 336,205 | 5.7 | 1.521 |
| | index | 30,691 | 18.5 | 0.396 |
| gpt-5 | full_1 | 2,552 | 30.4 | 0.647 |
| | full_10 | 16,305 | 31.1 | 1.210 |
| | full_30 | 65,781 | 35.9 | 3.060 |
| | full_all | 340,111（被拒） | | 0 |
| | index | 13,959 | 36.1 | 1.264 |
| grok-4-fast | full_1 | 2,615 | 3.7 | 0.108 |
| | full_10 | 15,500 | 4.7 | 0.597 |
| | full_30 | 61,641 | 6.9 | 2.333 |
| | full_all | 321,925 | 16.6 | 12.101 |
| | index | 10,923 | 6.7 | 0.430 |

index 的 token、延迟、花费是一道题内所有轮次的总和。花费：deepseek 和 grok 取网关 trace 里的 `estimated_cost_usd`，gpt-5 按 1.25 / 10 美元每百万输入 / 输出 token 估算，都没有扣缓存折扣。full_all 的 30 道题共享很长的前缀，服务端很可能命中了缓存，deepseek full_all 只要 5.7 秒可能和这个有关，但网关不报缓存命中，我们无法确认。

池子在各个 k 下的大小（cl100k，30 道题的最小 / 平均 / 最大）：k=1 为 435 / 2,528 / 6,432；k=10 为 15,463 / 17,584 / 21,460；k=30 为 71,010 / 72,852 / 77,007；全部为 369,217。

窗口能不能装下：gpt-5 在网关上的输入上限是 272,000 token，整个池子算出来是 340,111 token，30 道题全部返回 400，是上限的 125%。这一格记作“放不下”，不是答错。deepseek 和 grok 都装下了全池（336,205 和 321,925 token）；网关没有公布这两个模型的窗口大小，所以我们不报占比。

### 假设 1：全塞进去，准确率会不会随 skill 变多而掉

看模型。grok 从 full_1 到 full_30 一直是 27/30，到 full_all 掉到 19/30。逐题看，full_all 相对 full_1 丢了 11 道、多对了 3 道（T02、T08、T30）。丢的那 11 道，目标 skill 在 139 个里的位置是 23、29、31、33、46、51、64、67、74、113、119，没有集中在中间或某一端。读答案，丢分的方式主要是两种：一种是拿了别的 skill 的内容来答（T05 去讲 tavily 的缓存，T01 去讲 semantic-search 的相对路径），一种是没用 skill 里的规则，按常识给了一个差不多的数（T06 说间隔 3 到 5 秒，skill 写的是 2 到 3 秒）。

deepseek 没有掉：full_1 28/30，full_10、full_30、full_all 都是 30/30。full_1 错的两道（T12、T15）在更大的 k 下反而答对了。gpt-5 在它装得下的范围内也没有明显下降（30、28、29），差的这一两道在不同 k 之间换来换去，和 index_rep2 里看到的波动是同一个量级。

所以在 34 万 token 这个量级上，“稀释”是否发生取决于模型：grok 掉了 8 道，deepseek 一道没掉，gpt-5 根本装不下。

### 假设 2：只给索引，准确率能不能保住、成本能不能降

成本降了。和全塞相比，grok 的输入 token 从 321,925 降到 10,923（约 1/29），30 道题的花费从 12.10 美元降到 0.43 美元，延迟从 16.6 秒降到 6.7 秒。deepseek 的输入从 336,205 降到 30,691（约 1/11），花费从 1.52 降到 0.40 美元，但延迟反而从 5.7 秒涨到 18.5 秒，因为 index 至少要多一轮往返，而它在网关上的每一轮都更慢。gpt-5 和它能装下的最大一格 full_30 比，输入从 65,781 降到 13,959，延迟和花费差不多（每题多一轮推理）。

准确率没有保住。三个模型的 index 都低于对应的 full_1：deepseek 24 对 28，gpt-5 22 对 30，grok 13 对 27。grok 的 index（13）比它最差的 full_all（19）还低。index_rep2 和 index 的总分完全一样（24、22、13），所以这个差距不是一次运气。

### 假设 3：index 下的错误是不是变成了“没打开 / 打开错”

是的，几乎全是。

| 模型 | index 答错 | 一个都没打开 | 只打开了别的 skill | 打开了目标但答错 |
|---|---|---|---|---|
| deepseek-v4.1-flash | 6 | 2 | 4 | 0 |
| gpt-5 | 8 | 3 | 4 | 1 |
| grok-4-fast | 17 | 12 | 4 | 1 |

三个模型合计 31 个错误里，29 个是没走到目标 skill，只有 2 个是打开了目标却答错。打开了目标的题，答对率是 deepseek 23/23、gpt-5 21/22、grok 12/13。另有一道 T17（视频生成接口超时后能不能重发）三个模型都没打开目标也答对了，这道题的答案接近常识。

没走到目标有两种不同的原因，要分开看。

**第一种是问题本身没有线索。** 出题时要求不点名 skill，有 7 道题因此连产品名都没提（T01、T05、T10、T11、T22、T28、T29，标注在 `task_cues.json`，这是看完失败之后做的人工标注）。比如 T29 问“按最近两天导出收到的邮件为 Markdown，`--after/--before` 能不能填 ISO 时间”，没说是 Resend，三个模型都去打开了 Outlook 的 skill，而 Outlook 的 skill 里确实也有导出邮件为 Markdown 的命令。在这 7 道上，deepseek 和 gpt-5 都是 2/7，grok 是 1/7。在其余 23 道有线索的题上，deepseek 22/23，gpt-5 20/23，grok 12/23。这说明索引式加载有一个全塞没有的前提：用户的说法要能和某一行描述对上。全塞的时候模型直接看到了内容，不需要这一步。

**第二种是 grok 根本不打开。** grok 30 道里有 13 道一个 skill 都没打开（rep2 里是 15 道），其中大部分问题里明明写着 Codex、Koyeb、dataforseo、B 站、Gemini 这样的名字，索引里也有对应的行。它直接按自己的知识作答，而且答得很笃定（T19 说 `--tail 100 --follow` 可以用；skill 写的是 `--tail` 是布尔开关）。deepseek 和 gpt-5 只在 3 到 4 道题上没打开。

### 修描述有没有用（index_fixed）

做法：把 index 里“答错且没打开目标”的题的目标 skill 收集起来（三个模型合计 18 个），让 gpt-5 只看 skill 正文和原描述，重写成一句“什么情况下该打开它”的说明，不给它看题目。我们人工读了一遍，只改了一处：codex_cli 的新描述里写了“web_search 不支持等错误”，正好是 T09 问的症状，删成了“参数、配置、沙箱、审批等问题”（原稿留在 `descriptions_fixed.json` 的 `hand_edits`）。其余的新描述也会提到用户场景，比如 resend 的“查看/导出收到的邮件”，这是这类改写本来就该做的事，但它离题目也更近了，这一点读者要知道。

| 模型 | 原来没打开而答错 | 改后答对 | 同一批题在 index_rep2 里答对 | 10 道原本答对的抽样，改后仍对 | 同一抽样在 index_rep2 里仍对 |
|---|---|---|---|---|---|
| deepseek-v4.1-flash | 6 | 4（T01、T22、T24、T29） | 0 | 10 | 10 |
| gpt-5 | 7 | 4（T10、T23、T24、T29） | 1（T24） | 10 | 10 |
| grok-4-fast | 16 | 2（T19、T26） | 2（T14、T26） | 7 | 8 |

deepseek 和 gpt-5 上，修描述把一半多的失败救回来了，修好的题都是改后打开了目标 skill；只靠重跑，同一批题 deepseek 一道都不会变对，gpt-5 只变对一道。被救回的题里包括 T01、T10、T22、T29 这几道“问题里没线索”的题：新描述写清了使用场景，模型就能从“导出收到的邮件”“统计会话库”这样的说法对上去。T05（OpenReview）和 T11（Apple Photos）在所有模型、所有 index 条件下都没救回来，问题里既没有产品名也没有足够独特的场景。

grok 上看不出效果：改后救回 2 道，和单纯重跑救回的 2 道一样多；16 道里仍有 9 道一个 skill 都没打开；原本答对的抽样里还掉了 3 道（T03、T07、T23），而 T03、T07 的描述根本没改，掉分和 rep2 里的波动是同一量级。对一个不愿意去打开文件的模型，描述写得再好也没有用。

修前和修后的描述（修前是索引里实际显示的版本，截到 240 字符）：

**ai_session_profanity_rate**（gpt、grok 没打开）

- 原：Analyze profanity rate in recent local AI-session user messages with batched sub-agent labeling, a versioned local cache, JSON output, and model-composition charts.
- 改：当需在本地 AI 会话日志中统计近期用户消息的脏话占比、在不暴露原文的前提下产出私有 JSON 与图表，并以批量子代理标注和本地缓存加速、兼容 OpenCode/Claude Code/Codex/Antigravity 或 AI Session Export 存档时，使用 ai-session-profanity-rate CLI（prepare/ingest/visualize）。

**ai_session_search_archive**（deepseek 没打开）

- 原：AI Session Search & Archive: Find prior AI sessions across a unified Markdown archive without depending on a single vendor's history UI.
- 改：当用户需在本地统一 Markdown 归档（如由 ai_session_export 导出）中找回过往 AI 会话，仅记得标题/人名/项目/日期/ID或大概内容，并需限定来源或跨源检索时使用；覆盖 OpenCode/Claude Code/Codex/Antigravity/Second Mind，先用 rg 做命名实体搜索，模糊记忆再用 semantic-search，支持时生成 OpenCode 深链或返回文件链接。

**apple_photos**（deepseek、gpt、grok 没打开）

- 原：Live-unverified alpha for Apple Photos inspection and gated PhotoKit mutations. Never use mutation commands on a production library.
- 改：当需对macOS的Apple Photos库做只读检查（搜索/过滤、元数据备份、取回原片、ID映射），或在一次性测试库中用PhotoKit规划/试运行导入与删除时使用；覆盖Apple Photos、apple-photos CLI与原生PhotoKit辅助程序，严禁在生产库执行变更。

**asr**（grok 没打开）

- 原：Local speaker-attributed transcription with Nemotron diarization on CPU and Qwen3-ASR on Apple Silicon. The current agent can prepare, edit, and finalize a transcript. Acoustic inference stays on the machine. A configured editor may send te…
- 改：当用户需在Mac（Apple Silicon）本地完成带说话人归属的语音转写、对齐与受控编辑并导出结果，或要安装/配置/诊断该流程时打开；涵盖 asr-skill CLI（init/doctor/diarize/align/prepare-edit/finalize-edit/clean/run）、Nemotron diarization、Qwen3-ASR(MLX)。

**codex_cli**（grok 没打开）

- 原：Codex CLI: Execute a Codex task headlessly and recover a clean last agent message or schema-validated JSON payload directly from disk.
- 改：当需在本机/CI无交互运行 Codex 任务并将最后 agent 消息或经 JSON Schema 校验的结果写盘，或排查 codex exec 的参数、配置、沙箱、审批等问题时打开；涵盖 Codex CLI（codex exec、resume、review）。

**dataforseo**（grok 没打开）

- 原：DataForSEO Skill: Use this skill when an agent needs external SEO demand data: keyword suggestions, related keywords, search volume, CPC, keyword difficulty, search intent, SERP feature hints, or domain-ranked keywords.
- 改：当用户要获取外部SEO需求数据（关键词拓展/相关词、搜索量、CPC、难度、意图、SERP特征提示、域名排名关键词）并与GSC/GA4做机会评估时使用；覆盖DataForSEO与dataforseo-skill CLI（balance/keyword-suggestions/related-keywords/keywords-for-site）。

**eink_diary**（grok 没打开）

- 原：墨记 eink_diary Skill（根 skill）: 给定一个时间窗，自动完成：读取该窗口内用户的真实近况 → 让 AI 把它理解成一段画面描述 → 用图像模型生成一幅竖版 3:4 适配 1200×1600 的意象画 → 刷到 13.3" E6 电子纸 → 归档当天图与画面描述。
- 改：当用户要生成今天/某时段的电子纸日记画、刷新或补刷墙上 E‑ink、仅推送本地图片、查看/开启本地预览或排查定时刷新时打开；覆盖 eink-diary 命令套件（collect/synthesize/run/display）、Pi display server 与 E6 电子纸刷屏，并编排邮件/微信/AI 会话采集和图像生成技能。

**koyeb**（grok 没打开）

- 原：Operate Koyeb serverless applications, services, deployments, sleep/wake lifecycles, domains, and secrets using the official Koyeb CLI and standard-library launcher.
- 改：当需要用官方 Koyeb CLI（经标准 launcher）安全地在 Koyeb 执行：应用/服务/部署与实例的发现和诊断、日志与指标查看、配置更新与（重）部署、扩缩与休眠/唤醒、域名和密钥管理时打开。

**openreview**（deepseek、gpt、grok 没打开）

- 原：Skill: OpenReview API: 查询 AI 学术会议的论文 metadata 和作者 profile。
- 改：当用户需获取 ICLR/NeurIPS/ICML 等由 OpenReview 托管会议的论文元数据与 accepted 列表、按姓名或 tilde ID 查询作者 profile（含机构经历）、或批量查作者信息时，使用 OpenReview API 与其 CLI 工具（openreview-py，需账号认证）。

**skill_image_generation**（grok 没打开）

- 原：Image Generation Skill: Use this skill when the user asks an AI agent to generate images, edit an image with a prompt, upscale an existing image, or choose between Gemini and OpenAI image models for a local artifact-producing workflow.
- 改：当用户需本地生成/编辑图片、放大现有图或合成，并在 Gemini 与 OpenAI 图像模型间择优时使用；覆盖本地 CLI generate-image（含 scripts/generate-image）与 Python 模块入口，产出本地文件。

**skill_opencode_data**（deepseek、grok 没打开）

- 原：OpenCode Data Skill: Use this skill when a user asks you to inspect local OpenCode SQLite data, archive sessions, compact a database, or query token/session data across main and archive databases.
- 改：当用户需检查或维护本地 OpenCode SQLite 数据（主库/归档库），如查看统计、查询跨主/归档库的会话/Token、规划并归档会话、压缩数据库或导出会话记录时，打开本技能；覆盖 opencode_skill CLI 与相关 Python 环境。提交Prompt/批处理或定时任务请用对应技能。

**skill_process_launcher**（gpt、grok 没打开）

- 原：Process Launcher Skill: Use Process Launcher when a local workflow needs to start a command through a trusted localhost API, inspect process status, read output logs, schedule durable one-shot jobs, run a small set of YAML-declared always-o…
- 改：当用户需在本机通过受信任的localhost API 启动本地命令/脚本、做延时/定时/周期与持久一次性调度、后台查看/停止进程和读取输出日志，或管理YAML声明的常驻服务与周期任务（尤其在macOS需继承TCC权限）时，打开Process Launcher。

**skill_resend_email**（deepseek、gpt、grok 没打开）

- 原：Resend Email Skill: Use Resend for agent-controlled email sending, received email handling, and team-wide suppression inspection and management.
- 改：当用户要用Resend代发邮件（草拟/预览/确认发送、重试/幂等）、查看/导出收到的邮件与附件、按时间做收件箱梳理，或进行团队级Suppressions查询/增删与异常排查、配置自检时打开。覆盖Resend API与resend_email_skill CLI（send/received/suppressions等）操作。

**skill_typefully_twitter**（grok 没打开）

- 原：Typefully Twitter Skill — Agent Reference: Use this skill when the user asks an agent to work with X/Twitter through Typefully or analytics.x.com:
- 改：当用户要求经 Typefully 操作 X/Twitter（起草/排程/发布/列出/删除草稿、长度校验）、取 Typefully 账号级指标，或用 analytics.x.com 获取单帖分析/小时曲线/受众、诊断帖型并本地存储，或需要 typefully-twitter CLI 与凭据诊断时打开；覆盖 Typefully 发布/指标与 X per-post analytics。

**speaker_notes**（deepseek、gpt、grok 没打开）

- 原：Speaker Notes — Spoken-Delivery Contract: Read this file when creating, expanding, or substantially rewriting speaker notes.
- 改：当需撰写/扩充/重写演示文稿的speaker notes，或把书面稿口语化为可直读的talk track，并修复转场与时间预算、完成交接QA时，打开本文件；适用于幻灯片讲解词，配合skill_presentation.md。

**video-scoring-audio**（gpt 没打开）

- 原：Compose, render and objectively verify short music cues and sound effects for video, programmatically and without a DAW. Covers the MIDI + fluidsynth + soundfont pipeline for music that has to hit specific timecodes, the Google Lyria genera…
- 改：当需为视频/动画/短片程序化（无需DAW）生成或添加配乐/背景/过门/SFX并精准对齐分镜/时间码，权衡 Google Lyria 音乐API 与本地 MIDI+fluidsynth+soundfont，使用 ffmpeg 做响度规范化/尾剪，或需客观测量验证与对比变体、在旁白下铺底且代理无法听音时，打开。

**workflow_bilibili_whisper_transcription**（grok 没打开）

- 原：视频下载与语音识别工作流（Qwen ASR 优先）: 当前推荐默认模型：`Qwen/Qwen3-ASR-1.7B`
- 改：当需批量下载/抓取 Bilibili 或 YouTube 视频并在本地进行语音转文字，或遇到 B 站反爬/352 错误、时间轴与全文取舍、Qwen ASR 与 Whisper 选择等问题时查看；涵盖 yt-dlp、Qwen ASR(MLX)、Whisper、本地 transcribe 脚本。

**zun_music**（grok 没打开）

- 原：ZUN-Style Arrangement Skill: Produce a short arrangement, typically one complete phrase of 8 bars, about 15 s, in which a listener both recognises the source tune within the first phrase and hears it as ZUN-style.
- 改：当用户想把公有领域旋律做成ZUN/Touhou风的短编曲（约8小节≈15秒）并立即获得可播放MP3/MIDI，或想借由对比变体学习ZUN声像特征时打开；覆盖本仓库工作流与Best Practice、src/zun_music工具、scripts/render_example.py/serve.py与pytest校验；依赖FluidSynth、FFmpeg和SoundFont。

可以看到原描述的几种毛病：只写“是什么”不写“什么时候用”（openreview、speaker_notes）；描述被截断在半句（typefully 以冒号结尾，后面的列表没进索引）；第一句话被提取成了一个默认模型名（bilibili）；全英文，而用户用中文提问。改后的描述还有一个没控制住的变量：都换成了中文，平均也更长（全量索引从 6,682 涨到 7,804 token）。我们没法从这次实验里分开“写清使用场景”和“换成中文”各自的贡献。

## 判分可靠性

我们抽了 24 条判分（12 条判对、12 条判错，固定种子），逐条对照摘录和答案人工判：24 条里 21 条一致（`spotcheck.json`、`spotcheck_review.json`）。3 条不一致都是判官太严，而且集中在两道题上：T20 的判分要点要求写出“退出码 20”，但题目没问退出码；T16 的答案说接近当前时刻的时间戳“会被拒绝”，判官认为原文只说“可能被拒绝”。我们改了这两道题的判分要点（原版留在 `tasks.json` 的 `rubric_original`），只对这两道题重新判分。改之前的汇总在 `results_before_rubric_fix.json`：变化是 deepseek full_10 29→30，gpt-5 none 0→1、full_1 29→30、full_10 27→28、full_30 28→29、index 21→22、index_rep2 21→22，grok full_1 / full_10 / full_30 都是 26→27。结论的方向没有变。改后这 3 条都与人工判断一致。剩下的 21 条里没有发现判官放水的情况，判错的多半有实据（给了另一个数、另一个参数，或者拒答）。

`none` 条件下仍有 3 次答对：deepseek 的 T17 和 T25，gpt-5 的 T20（改判分要点之后，“先回底座再开始”这条可以靠常识猜到）。每道都只有一个模型答对，仍满足“不超过 1/3 的模型答对”的筛题规则。

## 注意事项

- **题量小。** 每格 30 道，一道题就是 3.3 个百分点。gpt-5 和 grok 两次跑 index，总分一样，但分别有 4 道题的对错互换了；所以 2 到 3 道以内的差别不要解读。deepseek 两次完全一致。
- **一道题一次调用。** 没有在同一道题上重复采样，模型的不确定性只靠 index_rep2 粗略估了一次。
- **题目的写法偏向全塞。** 出题规则要求不点名 skill，有 7 道题因此连产品名都没提。这对全塞没有影响（内容就在眼前），对索引是实打实的惩罚。真实用户多半会说“Resend”“OpenReview”，所以 index 在真实使用中的打开率很可能比这里高；但这是推测，这次数据没有测。
- **只到 34 万 token。** full_all 是这个池子的全部 139 个 skill。“稀释”在更大的池子上会怎样，我们没有测。deepseek 在这个量级上没有掉分，不代表它在更大的量级上也不掉。
- **模型依赖很强。** 同一套题，全塞时 grok 掉 8 道、deepseek 不掉；索引时 grok 有一半题不打开任何文件，另外两个模型只有一成。教材里如果要写成一般规律，需要说明这是三个模型上的观察。
- **deepseek 走的是文本协议，不是原生 tool calling**，而且它所在的网关会给它挂联网工具。主实验里没有发生联网，但读者自己复现时要注意这一点。
- **温度。** gpt-5 和判官都是温度 1（网关强制）；deepseek、grok 是温度 0，grok 仍有波动。
- **出题和判分都用了 gpt-5，gpt-5 也是被测模型之一。** 题目经过三个模型的无 skill 基线筛选和人工复核，但同一家模型出题、判分、作答，可能有我们看不出的偏向。
- **花费是网关估算，没有扣缓存折扣。** 绝对值只能作量级参考，比例更可靠。

## 文件

- `build_pool.py` → `pool.json`（元数据、描述、扫描命中）、`pool_text.json`（全文）、`exclude.json`
- `make_tasks.py` → `candidates_raw.json`、`candidates.json`、`candidates_baseline.json`、`candidates_summary.json`；人工筛选 `selection.json` → `tasks.json`
- `lib.py`（调用、提示词、判官）、`probe.py` → `probe.json`（网关模型列表和 tool calling 探测）
- `run.py` → `raw/<模型>__<条件>.json`（答案、打开了哪些 skill、token、延迟、花费、是否联网）
- `grade.py` → `graded/`、`results.json`（`cells` 是各格汇总，`analysis` 是逐题的稀释、修描述前后、线索分组）
- `fix_descriptions.py` → `index_fixed_plan.json`、`descriptions_fixed.json`
- `task_cues.json`（哪些题没有产品线索，人工标注）
- `spotcheck.json`、`spotcheck_review.json`（判分抽查）
- `*.log`（各步运行日志）

## 正文怎么用

- 全塞的代价有两种，一种是钱和时间（grok 全塞 30 道题 12.10 美元，索引 0.43 美元），一种是放不下（gpt-5 直接被拒）。掉准确率只在 grok 上看到了。
- 索引不是免费的：它把问题从“模型能不能在一堆内容里找到那一条”换成了“模型愿不愿意、能不能从一句话描述里认出该打开哪个文件”。这次的失败几乎全在后一步。
- 描述要写“什么时候用”，而且要用用户会说的话。deepseek 和 gpt-5 上，改描述救回了一半多的路由失败。对不愿意打开文件的模型（这次的 grok），改描述救不回来。

## 教材正文移出的细节

2026 年 10 月第三轮改写（v3）时，本实验拆分为两部分写入正文：2.7（`ch02_07.md`）使用只给对口 1 个 / 给 10 个 / 给 30 个 / 139 个全塞四档展示名义窗口与有效上下文的衰减和成本；2.8（`ch02_08.md`）使用只给对口 1 个 / 139 个全塞 / 只放索引（按需读）对比渐进式披露、路由错因（29/31 死在没打开对的文件）与改写 18 条描述的效果。下面这些实现细节只保留在本 README：

- **池子的来源规模。** 公开的 context-infrastructure 仓库里 40 个 skill，加上 55 个公开 skill 仓库，原始文件合计约 1.75MB；去重、剔除 1 个含具体电话号码的文件后是 139 个（分词器口径为 `cl100k` 369,217 token）。
- **正文用的条件名和这里的代号对应关系。** 不给 skill = `none`；只给对口 1 个 = `full_1`；给 10 个 = `full_10`；给 30 个 = `full_30`；139 个全塞 = `full_all`；只放索引（按需读） = `index`（重跑 = `index_rep2`）。
- **正文没有写的执行细节。** 实验日期（2026-10-13）、总花费约 54 美元、判分抽查 24 条中 21 条一致，都以上文为准。
- **网关三个事实**（deepseek 走 `READ_SKILL: 名字` 文本协议并实际路由到 DeepSeek V4.1 Flash、gpt-5 温度被强制为 1、grok-4-fast 实际是 grok-4.3）只写在本 README，正文只说“通过 AI Builder Space 接口测试了三个模型”。
