TL;DR
- Con số "90%" của Spotify là trung bình của các kịch bản đọc hàng loạt, đo bằng input token ước tính trên một monorepo Java. Bài viết không đưa ra con số chi phí bằng đô la và cũng không có điểm chất lượng.
- Dựng lại bằng Claude Code thuần (một hook PreToolUse, hai subagent giá rẻ, một quy tắc định tuyến ba dòng) và đo trên Fastify qua bốn kịch bản, 16 lần chạy, mẫu này giảm context của model chính 59.6% và tổng chi phí 33.1%.
- Các deny hook không kích hoạt lần nào trong các lần chạy đã đo. Khoản tiết kiệm đến từ quy tắc định tuyến trong CLAUDE.md; hook chỉ là lưới an toàn cho ngày model bỏ qua quy tắc đó.
- Ủy quyền luôn chậm hơn, trung bình +65.3% thời gian thực. Ở kịch bản viết test nhỏ, nó còn tốn hơn 2.6%.
- Hai cái bẫy: hook cũng kích hoạt bên trong subagent, nên hãy cho worker của bạn được miễn, và các lệnh đọc theo dải
sed -nlọt thẳng qua một hook chỉ theo dõicat,headvàtail. - Bản tóm tắt của Haiku reader có lỗi sự kiện trong hai trên tám lần chạy được ủy quyền. Hãy giữ lượt kiểm chứng của model chính.
Các phép đo cho thấy gì
Plugin Shunt của Spotify định tuyến công việc hàng loạt ra khỏi model chính qua hai "mode": một bulk-reader và một code-writer, cả hai chạy Gemini 2.5 Flash trong các ví dụ, với trường model chấp nhận bất kỳ model nào được cấu hình trong instance Portal s1. Việc định tuyến có ba lớp. Hook check-file-size kích hoạt ở mọi lần Read và chặn các file vượt ngưỡng số dòng cấu hình được, mặc định 350, rồi hướng model sang skill bulk-reader; hook check-bash-read bắt cat, head, tail, less và more trên file lớn, còn các lệnh có pipe thì được cho qua s1. Mã nguồn hook và hai skill nằm trong repo công khai s2, và có thể đọc riêng phần kiểm tra kích thước s3. Bản thân các mode nằm trong Portal, nền tảng nội bộ của Spotify, nên plugin không thể chạy ngoài công ty ở dạng phát hành s4.
Phần benchmark khá mỏng. Spotify thử bốn kịch bản trên một monorepo Java, "measuring tokens Claude would consume reading files directly vs. consuming the bulk-reader's summary", và báo cáo mức tiết kiệm trung bình khi đọc hàng loạt khoảng 90% s1. Chính bài viết nói rằng kịch bản ghi code khó đo bằng token hơn, bản tóm tắt của worker không có số dòng đáng tin nên không thể ủy quyền việc sửa file, worker đã bỏ sót một lỗi thread-safety tinh vi mà model chính bắt được, và mỗi lần ủy quyền thêm 10 đến 30 giây, với Portal giới hạn một lần gọi ở 30 giây s1. Thread trên Hacker News nêu cùng những câu hỏi về việc con số 90% thực sự đo cái gì s7.
Bản dựng lại thay các mode của Portal bằng hai subagent của Claude Code với file định nghĩa cố định model: một reader Explore chạy Haiku và một code-writer chạy Sonnet s6. Phần deny là một hook PreToolUse trả về quyết định deny theo định dạng JSON hook hiện hành s5. Repo thử nghiệm là fastify/fastify ở commit ac28821d, 294 file .js/.ts, 78270 dòng, 63 file trên 350 dòng. Model id theo session JSON: cuộc hội thoại chính claude-opus-5[1m], reader claude-haiku-4-5-20251001, writer claude-sonnet-5. Mỗi kịch bản chạy hai lần cho mỗi cấu hình, tổng 16 lần chạy đo, là các phiên claude -p một lượt với settings chỉ của project để hai bên có system prompt giống hệt nhau s5.
Chỗ mẫu này thắng: S2, câu hỏi call graph qua ba file lib/route.js (691 dòng), lib/reply.js (1090) và lib/request.js (398), đi từ context chính trung bình 357165.5 token xuống 73440.0 (-79.4%) và từ 0.5810500000000001 USD xuống 0.21823605000000001 USD (-62.4%). Chỗ nó không thắng: S4, viết test cho một source 45 dòng theo một file tham chiếu 19 dòng, tốn 0.29465575 USD khi không ủy quyền và 0.3022213 USD khi ủy quyền (+2.6%), vì Sonnet là một context đầy đủ thứ hai (13004 so với 18729 cache-read token) và model chính vẫn đọc lại file được sinh ra rồi chạy test s6.
Ba phát hiện quan trọng hơn các con số phần trăm. Thứ nhất, hook không kích hoạt lần nào trong 16 lần chạy đo: với quy tắc định tuyến trong CLAUDE.md, model chính tự chạy wc -l rồi ủy quyền. Lần deny duy nhất quan sát được đến từ một lần chạy kiểm chứng không có CLAUDE.md, khi model bị từ chối Read, rồi bị từ chối cat -n, và trả lời chỉ từ grep -n mà không hề gọi tool Agent s5. Thứ hai, hook chạy bên trong subagent: ở hai lần chạy bị loại, chính Haiku reader bị hook kiểm tra kích thước deny và quay sang đọc từng đoạn bằng offset/limit. Cách sửa là một đoạn thoát case "$agent_type" in Explore|code-writer) exit 0 ở đầu hook, với tên trường được xác nhận từ stdin đã ghi log s5. Thứ ba, ở cấu hình baseline model chính không bao giờ dùng tool Read. Nó đọc mọi file qua Bash (cat -n, sed -n '1,200p', sed -n '200,560p'), nên một hook chỉ theo dõi Read sẽ không bắt được gì, và một hook Bash chỉ khớp cat, head và tail không pipe vẫn để các dải sed -n lọt qua s3.
Chất lượng được kiểm tra với source bằng grep. Baseline cho số dòng sai ở một lần chạy S2 (nó dump file bằng sed -n không kèm số dòng và đếm tay). Cấu hình ủy quyền cho ba lỗi sự kiện ở lần chạy S2 còn lại và hai lỗi ở một lần chạy S3, tất cả đều truy về việc tin nguyên văn bản tóm tắt của Haiku: sai caller của buildRequest/buildReply, một hằng số không được export bị liệt kê như export, một iterator đã được phủ bị đánh dấu là chưa phủ. Ở chỗ model chính dùng output token để kiểm chứng lại bằng grep (S3, 4534 so với 4738 output token), các câu trả lời đúng s6. Ở kịch bản viết test, mọi file được sinh ra đều pass: 12/12, 6/6, 7/7 và 10/10 test. Một báo cáo trên Reddit cho thấy kiểu lỗi liên quan, khi model chính sinh worker trên sai model vì không có gì cố định nó s8, đó chính là điều hook require-model và trường model: trong các file agent ngăn chặn.
Số liệu đo
Trung bình của 2 lần chạy cho mỗi ô. "Main context" là input + cache_creation + cache_read token tính cho model chính trong suốt phiên, con số có thể so sánh với "tokens in the main context" của Spotify. A = Claude Code thuần, B = hook + subagent + quy tắc CLAUDE.md.
| kịch bản | main context A | main context B | thay đổi | main output A | main output B | thay đổi | tổng chi phí A | tổng chi phí B | thay đổi | thời gian A s | thời gian B s | thay đổi |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| S1 | 88693.0 | 51551.5 | -41.9% | 1424.5 | 1060.5 | -25.6% | 0.13910675 | 0.08653685 | -37.8% | 21.817500000000003 | 43.799499999999995 | +100.8% |
| S2 | 357165.5 | 73440.0 | -79.4% | 3835.0 | 2555.5 | -33.4% | 0.5810500000000001 | 0.21823605000000001 | -62.4% | 51.637 | 129.036 | +149.9% |
| S3 | 303807.5 | 114135.5 | -62.4% | 6192.0 | 4636.0 | -25.1% | 0.451037 | 0.3738534 | -17.1% | 93.321 | 124.64099999999999 | +33.6% |
| S4 | 143431.5 | 121818.0 | -15.1% | 5275.5 | 3340.0 | -36.7% | 0.29465575 | 0.3022213 | +2.6% | 65.7125 | 86.857 | +32.2% |
| cả 4 | 223274.375 | 90236.25 | -59.6% | 4181.75 | 2898.0 | -30.7% | 0.366462375 | 0.2452119 | -33.1% | 58.122 | 96.08337499999999 | +65.3% |
Quy trình: hai bản shallow clone giống hệt từng byte của fastify/fastify ở ac28821d; repo-shunt chỉ thêm .claude/ (settings, hai file agent, ba hook) và một quy tắc định tuyến trong CLAUDE.md, không gì khác. Mỗi phiên: claude -p "<prompt>" --output-format json --setting-sources project --strict-mcp-config với MCP config rỗng, không có --model, timeout 600 s. Bốn prompt, giống hệt ở cả hai phía: S1 các export của lib/reply.js, S2 call graph qua ba file lib, S3 các method của lib/hooks.js so với độ phủ của test/hooks.test.js, S4 viết test/head-route.test.js theo test/noop-set.test.js. Các con số được đọc từ modelUsage và total_cost_usd của session JSON, không làm tròn. Các câu trả lời được kiểm tra với source bằng grep; các test được sinh ra được chạy bằng node --test.
Làm vào thứ Hai
- Chạy
wc -ltrên repo của bạn và đếm số file trên 350 dòng. Nếu con số gần bằng không thì dừng ở đây: ngưỡng này tồn tại vì ủy quyền tốn hơn mức tiết kiệm được trên các file nhỏ. - Thêm một quy tắc định tuyến ba dòng vào CLAUDE.md: file vượt ngưỡng giao cho subagent reader, code theo mẫu có sẵn giao cho subagent writer, còn debug và kiến trúc ở lại với model chính. Trong các phép đo, quy tắc này làm toàn bộ công việc.
- Tạo
.claude/agents/Explore.mdvớimodel: haikuvà.claude/agents/code-writer.mdvớimodel: sonnettrong frontmatter, để model của worker được cố định trong file chứ không để orchestrator tự chọn. - Viết hook PreToolUse trên Read làm lưới an toàn, trả về quyết định deny theo định dạng JSON hook hiện hành, và cho các dòng đầu của nó exit 0 khi
agent_typelà một trong các worker của bạn. - Mở rộng hook Bash ra ngoài
cat,headvàtail: khớp các dảised -nvàcat -ntrên file lớn, cho các lệnh có pipe và grep đi qua. - Chạy một câu hỏi thật có và không có thư mục
.claude/, bằngclaude -p --output-format json, rồi so sánhtotal_cost_usdvàduration_ms, không chỉ riêng cột input. - Kiểm tra hai câu trả lời được ủy quyền với source bằng grep trước khi tin bản tóm tắt của reader; tính lượt kiểm chứng của model chính vào chi phí.
- Đo cả một phiên nhiều lượt: kết quả một lượt để main context ở mức 50k đến 119k token so với 84k đến 414k khi không ủy quyền, nên câu hỏi thứ hai lẽ ra bắt đầu rẻ hơn, nhưng điều đó chưa được đo.
Đọc thêm
- Đọc định dạng hook và trường
agent_typetrong tài liệu tham chiếu chính thức trước khi chép một hook từ blog; hình dạng của deny và các trường trên stdin là thứ giúp việc miễn trừ subagent khả thi s5. - Tài liệu subagent nói về trường frontmatter
modelvà các giới hạn tool, đó là cách giữ cho một reader chỉ-đọc và rẻ s6. - Phần "What doesn't work" của chính Spotify là phần hữu ích nhất của bài: không ủy quyền việc sửa (bản tóm tắt không có số dòng đáng tin), không ủy quyền việc suy luận (một lỗi thread-safety bị bỏ sót), mỗi vòng đi về mất 10 đến 30 giây s1.
- README của Shunt cho thấy cấu trúc ba lớp (hook, script, skill) và nội dung các skill bảo model khi nào nên ủy quyền; phần văn của skill mới là thứ đáng chuyển thể, không phải hook s2.
- Portal mode là một lớp cấu hình trên một model cộng một system prompt; cùng ý tưởng đó ánh xạ sang một file agent của Claude Code có trường
models4. - Thread Hacker News là nơi các câu hỏi về cách đo được nêu ra đầu tiên, và nó là danh sách kiểm tra tốt cho bất kỳ tuyên bố tiết kiệm token nào s7.
- Một thread Reddit ghi lại cảnh một orchestrator tự sinh năm worker trên chính model đắt tiền của nó; hãy cố định model trong file agent và, nếu muốn đảm bảo cứng, deny các lệnh gọi Agent không có trường
models8.
Nguồn
- Portal by Spotify cut my Claude Code token usage by 90%, Spotify Engineering. Vì sao nên đọc: tuyên bố gốc, thiết kế ba lớp và một phần hạn chế trung thực làm yếu đi tiêu đề.
- Shunt plugin (spotify/portal-ai-plugins), GitHub. Vì sao nên đọc: các hook, script và nội dung skill thực tế, đủ ngắn để đọc hết.
- check-file-size hook source, GitHub. Vì sao nên đọc: phép kiểm tra 350 dòng chỉ trong vài dòng shell, mẫu cho deny của riêng bạn.
- Portal Modes documentation, Spotify. Vì sao nên đọc: "mode" là gì, để thấy vì sao nó ánh xạ sang một file subagent.
- Claude Code hooks reference, Anthropic. Vì sao nên đọc: định dạng deny hiện hành và các trường trên stdin, gồm trường nhận diện một subagent.
- Claude Code subagents, Anthropic. Vì sao nên đọc: trường frontmatter
modelvà allowlist tool cho một worker chỉ-đọc, rẻ. - Hacker News discussion of the Spotify post, Hacker News. Vì sao nên đọc: các câu hỏi về việc con số 90% đo cái gì, được đặt ra trước khi có ai đo lại.
- Fable spawned five Fable agents instead of Opus (r/ClaudeCode), Reddit. Vì sao nên đọc: kiểu lỗi mà một trường
modelđược cố định sẽ ngăn chặn.
FAQ
Con số 90% có sai không?
Nó chỉ đo một thứ: input token ước tính trong main context cho các kịch bản đọc hàng loạt trên các file Java lớn. Trên cùng loại chỉ số đó, bản dựng lại thấy mức 41.9% đến 79.4% ở các kịch bản đọc. Nó không nói gì về chi phí, thời gian hay chất lượng câu trả lời, và bài viết cũng không tuyên bố như vậy.
Tôi có cần Portal để làm được việc này không?
Không. Việc định tuyến nằm trong một quy tắc CLAUDE.md, hai file agent có model được cố định và một hook PreToolUse. Portal cung cấp model cho worker ở Spotify; một dòng model: haiku làm cùng việc đó trong Claude Code thuần.
Khi nào ủy quyền tốn hơn?
Khi các file nhỏ. Kịch bản viết test 45 dòng tốn hơn 2.6% khi ủy quyền vì writer là một context đầy đủ thứ hai và model chính vẫn đọc lại và chạy test kết quả. Mọi lần chạy được ủy quyền cũng chậm hơn, trung bình +65.3%.
Vì sao hook không bao giờ kích hoạt?
Vì quy tắc định tuyến trong CLAUDE.md khiến model chính kiểm tra wc -l rồi ủy quyền trước khi thử đọc. Hook chỉ quan trọng khi model bỏ qua quy tắc, điều đã xảy ra trong lần chạy kiểm chứng không có CLAUDE.md.
AIDive