2.7 & 2.8 节实验:139 个公开 Skill 四档长度塞入窗口 vs INDEX.md 渐进式披露与描述改写实验(30 道考题)
| 文件 / 目录 | 说明 | 大小 |
|---|---|---|
| 📁 ../ | 返回上级目录 | - |
| 📁 graded/ | 子目录 | - |
| 📁 raw/ | 子目录 | - |
| 📄 README.md | 30.0 KB | |
| 📄 baseline.log | 387 B | |
| 📄 build_pool.py | 5.9 KB | |
| 📄 candidates.json | 222.9 KB | |
| 📄 candidates_baseline.json | 1.8 MB | |
| 📄 candidates_raw.json | 265.4 KB | |
| 📄 candidates_summary.json | 36.2 KB | |
| 📄 descriptions_fixed.json | 11.0 KB | |
| 📄 draft.log | 3.2 KB | |
| 📄 exclude.json | 150 B | |
| 📄 fix_descriptions.py | 3.2 KB | |
| 📄 grade.log | 2.0 KB | |
| 📄 grade.py | 10.3 KB | |
| 📄 index_fixed_plan.json | 1.7 KB | |
| 📄 lib.py | 5.9 KB | |
| 📄 make_tasks.py | 7.2 KB | |
| 📄 pool.json | 76.0 KB | |
| 📄 pool_text.json | 1.4 MB | |
| 📄 probe.json | 5.4 KB | |
| 📄 probe.py | 2.3 KB | |
| 📄 results.json | 18.5 KB | |
| 📄 results_before_rubric_fix.json | 18.6 KB | |
| 📄 run.py | 9.6 KB | |
| 📄 run_deepseek-v4.1-flash.log | 9.1 KB | |
| 📄 run_fixed_deepseek-v4.1-flash.log | 1.2 KB | |
| 📄 run_fixed_gpt-5.log | 1.1 KB | |
| 📄 run_fixed_grok-4-fast.log | 1.5 KB | |
| 📄 run_gpt-5.log | 11.4 KB | |
| 📄 run_grok-4-fast.log | 7.4 KB | |
| 📄 run_rep2_deepseek-v4.1-flash.log | 2.3 KB | |
| 📄 run_rep2_gpt-5.log | 1.8 KB | |
| 📄 run_rep2_grok-4-fast.log | 1.7 KB | |
| 📄 selection.json | 4.2 KB | |
| 📄 spotcheck.json | 59.7 KB | |
| 📄 spotcheck_review.json | 1.3 KB | |
| 📄 task_cues.json | 424 B | |
| 📄 tasks.json | 32.7 KB |
# 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-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)。探测时我们见过它不听这句话去联网的情况,所以这一列要看。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}。
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);网关没有公布这两个模型的窗口大小,所以我们不报占比。
看模型。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 根本装不下。
成本降了。和全塞相比,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),所以这个差距不是一次运气。
是的,几乎全是。
| 模型 | 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 里“答错且没打开目标”的题的目标 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 没打开)
ai_session_search_archive(deepseek 没打开)
apple_photos(deepseek、gpt、grok 没打开)
asr(grok 没打开)
codex_cli(grok 没打开)
dataforseo(grok 没打开)
eink_diary(grok 没打开)
koyeb(grok 没打开)
openreview(deepseek、gpt、grok 没打开)
skill_image_generation(grok 没打开)
skill_opencode_data(deepseek、grok 没打开)
skill_process_launcher(gpt、grok 没打开)
skill_resend_email(deepseek、gpt、grok 没打开)
skill_typefully_twitter(grok 没打开)
speaker_notes(deepseek、gpt、grok 没打开)
video-scoring-audio(gpt 没打开)
workflow_bilibili_whisper_transcription(grok 没打开)
Qwen/Qwen3-ASR-1.7Bzun_music(grok 没打开)
可以看到原描述的几种毛病:只写“是什么”不写“什么时候用”(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 的模型答对”的筛题规则。
build_pool.py → pool.json(元数据、描述、扫描命中)、pool_text.json(全文)、exclude.jsonmake_tasks.py → candidates_raw.json、candidates.json、candidates_baseline.json、candidates_summary.json;人工筛选 selection.json → tasks.jsonlib.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.jsontask_cues.json(哪些题没有产品线索,人工标注)spotcheck.json、spotcheck_review.json(判分抽查)*.log(各步运行日志)2026 年 10 月第三轮改写(v3)时,本实验拆分为两部分写入正文:2.7(ch02_07.md)使用只给对口 1 个 / 给 10 个 / 给 30 个 / 139 个全塞四档展示名义窗口与有效上下文的衰减和成本;2.8(ch02_08.md)使用只给对口 1 个 / 139 个全塞 / 只放索引(按需读)对比渐进式披露、路由错因(29/31 死在没打开对的文件)与改写 18 条描述的效果。下面这些实现细节只保留在本 README:
cl100k 369,217 token)。none;只给对口 1 个 = full_1;给 10 个 = full_10;给 30 个 = full_30;139 个全塞 = full_all;只放索引(按需读) = index(重跑 = index_rep2)。READ_SKILL: 名字 文本协议并实际路由到 DeepSeek V4.1 Flash、gpt-5 温度被强制为 1、grok-4-fast 实际是 grok-4.3)只写在本 README,正文只说“通过 AI Builder Space 接口测试了三个模型”。