Spotify 工程师实操:借助 Portal 将 Claude Code 的 Token 消耗降低 90%

Spotify Portal for Backstage 现已正式可用(GA)! 阅读公告
AI 编程 Agent 为我做的大部分工作并不是"思考",而是"输入/输出"(I/O)。
为了回答关于一个方法的问题而读取五个文件;生成一个测试文件,它严格遵循旁边那二十个测试文件相同的模式;开完会之后更新文档。成千上万的 Token 就这样消失了,却几乎没有产生任何推理。拖累成本的不是座位许可(seat license),而是 Token。而你把这一切都喂给了那个严重大材小用的前沿模型。如果能把那些"杂务"路由给更便宜、处理得同样好的方案,而把昂贵的模型留给真正需要它的难题,会怎么样?
这远不只是我一个人的问题。据 Gartner 预测,到 2028 年,AI 编程的成本将超过普通开发者的平均薪资。已有四分之一的工程负责人每月为每个开发者烧掉 $200–$500 的 Token 费用,有些人已经远超 $2000。工具本身是能回本的——但前提是,你不再把前沿模型的 Token 浪费在那些并不需要它们的任务上。
事实证明,解决办法并不需要平台团队,也不需要新的订阅,只需要两个模式(modes)。
两个模式,零代码
这正是 Portal by Spotify 中 AiKA Modes 这种用例要解决的。一个 Mode 就是运行在临时运行时(ephemeral runtime)上的声明式 Agent——可以把它想成"面向 Agent 的 AWS Lambda"。你定义指令、挑选模型、设置温度(temperature)之类的参数,并挂载 MCP 工具,剩下的交给 Portal 处理。无需管理基础设施,无需 API Key,无需长期运行的服务器。Mode 可以通过 Portal CLI 或 API 调用,既可以是公开的(供全公司共享),也可以是私有的。
为了让这个路由器工作起来,我创建了两个 Mode。下面示例中的工作模型都用了 Gemini 2.5 Flash,但模型字段接受你在 Portal 实例中配置的任何模型,选一个适合你的即可。
Mode 1:bulk-reader
用于当 Claude 本会为了回答一个问题而读取多个大文件时。
name: bulk-reader
description: Bulk file reader for code analysis - delegates I/O from Claude Code
instructions: You are a precise code analyst. Read the provided files and answer the question concisely. Output structured bullets only. No greetings, no prose, no preambles. Lead every bullet with the exact name, type, or line number. Use nested bullets for details. Skip anything the caller did not ask for.
visibility: public
model: gemini-2.5-flash
resourceLimits:
temperature: 0.2
tags:
- coding
- delegation
Mode 2:code-writer
用于测试、配置脚手架、类型桩(type stubs),或者任何输出可根据既有模式预测的任务。
name: code-writer
description: Boilerplate code generator - delegates output-heavy work from Claude Code
instructions: You generate code files based on a spec and reference files. Match the existing patterns, conventions, naming, and style exactly. Output only the code — no explanations, no markdown fences unless asked. If the spec is ambiguous, make reasonable choices that match the reference code's patterns.
visibility: public
model: gemini-2.5-flash
resourceLimits:
temperature: 0.2
tags:
- coding
- delegation
那句"只输出代码"(output only the code)的指令至关重要。没有它,模型就会把所有内容用 markdown 围栏包起来,并附带解释性的散文,而 Claude 还得费力去解析这些。
路由(Routing)
这个方案的第一版是写在 CLAUDE.md 里的一段路由规则。它某种程度上能工作:Claude 会读取指令并自我路由到 Portal。但它有问题:这些规则只是建议性质的,而非强制性的——Claude 可以无视它们;而且每个项目都需要一份自己的指令副本。
当前版本是一个名为 shunt 的 Claude Code 插件。委托(delegation)经由 Portal CLI 的动作注册表(actions registry)进行,因此该插件可作用于任何启用了 AiKA 插件的 Portal 实例。
第一层:Hooks
Claude Code 的 hooks 会在每次工具调用之前触发。Shunt 注册了两个 PreToolUse 钩子:
check-file-size 会在每次 Read 调用时触发。如果文件超过可配置的行数阈值(默认 350 行),钩子就会阻断这次读取,并告诉 Claude 改用 /bulk-reader 技能。定向读取(targeted reads)则会放行——因为 Claude 已经知道自己需要的是哪个段落。
check-bash-read 会拦截对大型文件执行 cat、head、tail、less、more 的命令。带管道的命令(如 cat file | grep)会放行,因为它们属于定向读取。
阈值可通过 SHUNT_MIN_LINES 环境变量配置。把它写进你的 shell profile,或写在 .claude/settings.json 里:
{
"env": {
"SHUNT_MIN_LINES": "500"
}
}
第二层:脚本
我有两个 bash 脚本,用来封装对 Portal CLI 的调用。Claude 以带命名参数的方式调用脚本。脚本在内部处理一切:构建请求、调用动作、解包错误,并把 Token 用量报告到 stderr。
Mode 按名称寻址,并由 Portal 解析:不区分大小写,优先使用你自己的 Mode,然后是团队的,最后才是公开的。把公开的 bulk-reader fork 成你自己的定制版本后,你的版本会自动获得更高优先级——无需任何配置。
bulk-read 把每个文件用 XML 标签包裹起来以明确边界,连同问题一起发送给 bulk-reader 模式。
bulk-read --question "What does this service do?" --paths src/Service.java src/Handler.java
# 追问:用同样的路径再问一次
bulk-read --question "Which methods call the database?" --paths src/Service.java src/Handler.java
每次委托都是一次性(one shot)的。这次调用是临时的(服务端不存储任何东西),而在追问时重新发送文件在这方面是免费的——因为语料发给了工作模型,从不会进入 Claude 的上下文。
code-write 向 code-writer 模式发送一份规格(spec)和一个参考文件,去除输出中的 markdown 围栏,并且可以直接写盘。Claude 永远看不到生成的代码。参考文件是必需的:如果没有一个可供比对的模式文件,工作模型就会生成与你的项目完全不搭、脱离上下文的代码。
code-write --spec "Write tests for UserService" --reference tests/OrderTest.java --target tests/UserTest.java
# 输出到标准输出
code-write --spec "Generate a config stub" --reference config/existing.yaml
第三层:技能(Skills)
两个技能文件告诉 Claude 何时以及如何调用这些脚本。技能是包含描述和用法示例的 markdown 文件。当钩子阻断一次读取时,阻断消息会引导 Claude 到 /bulk-reader 技能,后者会展示确切的调用语法。
这种分层设计让系统能够优雅地降级。即使 Claude 没有读取技能描述,钩子仍然会阻断那次昂贵的读取。技能只是让重定向更顺畅而已。
基准测试
在一套 Java 单体仓库(monorepo)上,跨四个场景测试,衡量 Claude 直接读取文件时消耗的 Token,相对于经由 bulk-reader 的摘要、或经由 code-writer 写代码时的消耗。批量读取(bulk-read)的平均节省高达约 90%。
code-write 场景在 Token 上更难衡量,因为如果没有 shunt,Claude 既要读取参考文件,又要以昂贵的输出 Token 形式生成输出。有了 shunt,代码直接落到磁盘上,Claude 根本看不到它。
什么不适用
你无法委托编辑。 工作模型生成的摘要不包含可靠的行号。如果 Claude 需要基于分析结果进行编辑,它仍然必须直接读取特定段落。Hooks 允许针对这一点进行定向读取(带 offset/limit),因此委托省下的是"理解"部分的 Token。
你无法委托推理。 工作模型只找到了表面层面的模式,在我的测试中漏掉了一个隐蔽的线程安全问题。而 Claude 一旦拿到正确的上下文,几秒钟内就发现了它。这套路由明确排除了调试、架构决策以及安全关键型代码。
延迟会累积。 每次委托都是一次网络往返:从 Claude Code 到 Portal 后端,到工作模型,再回来。响应通常需要 10–30 秒,而且 Portal 将单次调用限制在 30 秒,所以非常大的生成任务需要拆分成多次较小的调用。对于大型读取来说这是可接受的,但对小型读取则适得其反。行数阈值正是为此而设——低于该阈值,委托的开销会超过节省。
Token 节省只是起点
这个插件是一件 Claude Code 产物,但它背后的理念是:由 AiKA modes 驱动的模型路由。这些 Mode 才是承重(load-bearing)的部分:
- 它们是可复用的。 同一个 bulk-reader 和 code-writer 模式可以跨每个项目、以及每一个能 shell 调出 Portal CLI 的工具使用。
- 它们是可共享的。 这两个模式在 AiKA 中都是公开的。任何人都可以今天就使用它们,无需自己创建。
- 它们是可组合的。 你可以创建一个用于文档的 doc-writer 模式、一个用于代码审查摘要的 reviewer 模式、一个用于 i18n 的 translator 模式。每一个都只是点几下鼠标的事。
- 它们把路由决策与工作模型解耦。 插件决定何时委托,模式决定如何回应。把 Gemini Flash 换成更便宜的模型、修改系统提示词、添加 MCP 工具——插件都不需要改变。
这才是 AiKA modes 真正的力量所在:它把模型路由从一个系统工程问题,变成了一个配置问题。你不需要构建基础设施,只需描述你想要的东西并给它起个名字。
亲自试试
-
从 spotify/portal-ai-plugins 市场安装两个插件:
claude plugin marketplace add spotify/portal-ai-pluginsclaude plugin install portal@portalclaude plugin install shunt@portal
portal 插件提供了 shunt 委托所依赖的 Portal CLI。
-
在一个新的 Claude Code 会话中,运行
/portal:setup,针对你的 Portal 实例 设置并认证 Portal CLI。 -
准备好之后,只要提出一个跨多个文件的问题即可。
bulk-reader 和 code-writer 模式已经是公开的,所以无需创建任何东西。如果你想自定义它们——不同的工作模型、不同的指令——在 Portal 中 fork 它们即可,你的版本会自动获得优先权。
这些模式可以跨项目复用,也可以与你的团队共享。插件强制执行路由逻辑,所以你无需自己去想。点此了解关于 modes 的更多信息。