메뉴
HN
Hacker News • 21일 전

스포티파이 Portal이 내 Claude Code 토큰 사용량을 90% 줄였다

IMP
7/10
핵심 요약

AI 코딩 에이전트의 작업 대부분은 추론이 아닌 파일 읽기·보일러플레이트 코드 생성 같은 단순 I/O라는 점에 착안해, 저렴한 모델(Gemini 2.5 Flash)에 이런 반복 작업을 위임하는 라우팅 시스템을 구축해 Claude Code 토큰 사용량을 90% 절감한 사례다. 스포티파이의 'Portal' 플랫폼에서 'Mode'(선언형 에이전트) 두 개와 'shunt'라는 Claude Code 플러그인으로 자동 라우팅을 구현했다. 토큰 비용이 개발자 월급을 넘어설 것이란 전망 속에 프런티어 모델 비용 최적화가 실무적으로 중요해지는 시점에서 주목받는 접근이다.

번역된 본문

스포티파이의 Portal이 내 Claude Code 토큰 사용량을 90% 줄였다

AI 코딩 에이전트가 나를 위해 하는 일의 대부분은 사고(thinking)가 아니다. I/O다. 하나의 메서드에 대한 질문에 답하려고 파일 다섯 개를 읽는다. 옆에 있는 스무 개의 테스트 파일과 정확히 같은 패턴을 따르는 테스트 파일을 생성한다. 회의 후 문서를 업데이트한다. 수천 토큰이 사라지고 추론은 거의 없다. 아픈 건 좌석 라이선스가 아니라 토큰이다. 그런데 이 모든 걸 엄청나게 과잉 스펙인 프런티어 모델에 먹이고 있는 셈이다.

이 단순 반복 작업(grunt work)을 그만큼 잘 처리하는 더 저렴한 무언가에 위임하고, 정말로 필요한 문제에만 비싼 모델을 쓸 수 있다면 어떨까?

이건 나만의 문제가 아니다. 2028년까지 AI 코딩 비용은 평균 개발자 연봉을 넘어설 것으로 전망된다. 엔지니어링 리더의 4분의 1은 이미 개발자당 월 200~500달러를 토큰에 쓰고 있다. 일부는 2,000달러를 훌쩍 넘긴다. 이런 도구는 그 비용을 스스로 회수하지만, 프런티어 모델의 토큰을 그럴 필요 없는 작업에 태우지 않을 때만 가능한 이야기다.

결국 해결책은 플랫폼 팀도 새 구독도 필요 없었다. 단 두 개의 '모드'면 됐다.

두 개의 모드, 코드 제로

이것이 바로 스포티파이의 Portal에 있는 AiKA Modes가 만들어진 정확히 그런 유즈케이스다. 모드(mode)는 임시 런타임에서 실행되는 선언형 에이전트다. 에이전트를 위한 AWS Lambda라고 생각하면 된다. 명령어(instructions)를 정의하고, 모델을 고르고, temperature 같은 파라미터를 설정하고, MCP 도구를 붙인다. 나머지는 Portal이 처리한다. 관리할 인프라도, API 키도, 장기 실행 서버도 없다. 모드는 Portal CLI나 API에서 호출할 수 있으며, 전사 공개(public) 또는 개인(private)으로 설정할 수 있다.

이 라우터가 작동하려면 두 개의 모드를 만들었다. 아래 예시에서는 둘 다 워커 모델로 Gemini 2.5 Flash를 쓰지만, 모델 필드에는 Portal 인스턴스에 설정된 어떤 모델이든 들어갈 수 있다. 자기한테 맞는 걸 고르면 된다.

모드 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

모드 2: code-writer

테스트, 설정 스캐폴딩, 타입 스텁 등 기존 패턴에서 결과를 예측할 수 있는 모든 작업을 위한 것이다.

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

"코드만 출력하라"는 명령어가 중요하다. 이게 없으면 모델이 모든 것을 마크다운 코드 펜스와 설명 문장으로 감싸서, Claude가 그걸 다시 파싱해야 한다.

라우팅

첫 버전은 CLAUDE.md에 라우팅 규칙 블록을 넣은 것이었다. 그럭저럭 작동했다. Claude가 지시를 읽고 스스로 Portal로 라우팅했다. 하지만 문제가 있었다. 규칙은 권고사항이지 강제가 아니었다. Claude가 무시할 수 있었다. 그리고 모든 프로젝트에 지시 사본이 각각 필요했다.

현재 버전은 shunt라는 Claude Code 플러그이다. 위임은 Portal CLI 액션 레지스트리를 거쳐 이루어지므로, AiKA 플러그인이 활성화된 어떤 Portal 인스턴스에서도 작동한다.

레이어 1: Hooks

Claude Code 훅(hooks)은 모든 도구 호출 전에 발동한다. Shunt는 두 개의 PreToolUse 훅을 등록한다. check-file-size는 모든 Read 호출마다 발동한다. 파일이 설정 가능한 줄 수 임계값(기본값: 350줄)을 초과하면 훅이 읽기를 차단하고 Claude에게 /bulk-reader를 사용하라고 알린다.

원문 보기
원문 보기 (영어)
Portal by Spotify cut my Claude Code token usage by 90% Most of what an AI coding agent does for me isn't thinking. It's I/O. Reading five files to answer a question about one method. Generating a test file that follows the exact same pattern as the twenty test files next to it. Updating docs after a meeting. Thousands of tokens gone and almost zero reasoning. The seat license isn't what hurts, it's the tokens. And you're feeding all of it to a frontier model that's wildly overqualified. What if you could route the grunt work to something cheaper that handles it just as well, and save the expensive model for the problems that actually need it? It’s hardly just my problem. By 2028, AI coding costs are expected to blow past the average developer's salary. A quarter of engineering leaders already burn $200–$500 per developer per month on tokens. Some are well past $2,000. The tooling pays for itself but only if you stop burning frontier tokens on work that doesn't need them. Turns out, the fix didn't require a platform team or a new subscription. Just two modes. Two modes, zero code This is exactly the kind of use case AiKA Modes in Portal by Spotify were built for. A mode is a declarative agent that runs on an ephemeral runtime - think AWS Lambda, but for agents. You define the instructions, pick a model, set parameters like temperature, and attach MCP tools. Portal handles the rest. No infra to manage, no API keys, no long-running servers. Modes are callable from the Portal CLI or API. They can be public (shared with the whole company) or private. For this router to work I created two modes. Both use Gemini 2.5 Flash as the worker model in the examples below, but the model field accepts any model you have configured in your Portal instance. Pick whichever works for you. Mode 1: bulk-reader For when Claude would otherwise read multiple large files just to answer one question. 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 For tests, config scaffolding, type stubs or anything where the output is predictable from existing patterns. 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 That "output only the code" instruction matters. Without it, the model wraps everything in markdown fences and explanatory prose that Claude then has to parse through. Routing The first version of this was a block of routing rules in CLAUDE.md. It sort of worked: Claude would read the instructions and self-route to Portal. But it had problems. The rules were advisory, not enforced. Claude could ignore them. And every project needed its own copy of the instructions. The current version is a Claude Code plugin called shunt . Delegation goes through the Portal CLI actions registry so the plugin works against any Portal instance with AiKA plugin enabled. Layer 1: Hooks Claude Code hooks fire before every tool call. Shunt registers two PreToolUse hooks: check-file-size fires on every Read call. If the file exceeds a configurable line threshold (default: 350), the hook blocks the read and tells Claude to use the /bulk-reader skill instead. Targeted reads pass through - Claude already knows what section it needs. check-bash-read catches cat, head, tail, less, and more on large files. Piped commands (cat file | grep) pass through since those are targeted reads. The threshold is configurable via the SHUNT_MIN_LINES environment variable. Set it in your shell profile or in .claude/settings.json: { "env": { "SHUNT_MIN_LINES": "500" } } Layer 2: Scripts I have two bash scripts that wrap the Portal CLI calls. Claude calls a script with named arguments. The scripts handle everything internally: building the request, invoking the actions, unwrapping errors, and reporting token usage to stderr. Modes are addressed by name and resolved by Portal: case-insensitively, preferring your own mode, then your team's, then public ones. Fork the public bulk-reader into a customized version and yours automatically takes precedence - no configuration needed. bulk-read wraps each file in XML tags for clear boundaries and sends them to the bulk-reader mode along with the question. bulk-read --question "What does this service do?" --paths src/Service.java src/Handler.java # Follow-up: ask again with the same paths bulk-read --question "Which methods call the database?" --paths src/Service.java src/Handler.java Every delegation is one shot. The invocation is ephemeral (nothing is stored server-side) and re-sending the files on a follow-up is free where it matters, because the corpus goes to the worker model and never enters Claude's context. code-write sends a spec and a reference file to the code-writer mode, strips markdown fences from the output, and can write directly to disk. Claude never sees the generated code. The reference is required: without a file to match patterns against, the worker would generate context-free code that fits nothing in your project. code-write --spec "Write tests for UserService" --reference tests/OrderTest.java --target tests/UserTest.java # Output to stdout code-write --spec "Generate a config stub" --reference config/existing.yaml Layer 3: Skills Two skill files tell Claude when and how to call the scripts. Skills are markdown files with a description and usage examples. When the hook blocks a read, the block message points Claude to the /bulk-reader skill, which shows the exact invocation syntax. This layering means the system degrades gracefully. Even if Claude doesn't read the skill description, the hook still blocks the expensive read. The skill just makes the redirect smoother. The benchmarks Tested against a Java monorepo across four scenarios, measuring tokens Claude would consume reading files directly vs. consuming the bulk-reader's summary or writing code via the code-writer. Mean bulk-read savings were around a whopping 90%. The code-write scenario is harder to measure in tokens because without shunt, Claude both reads the reference files and generates the output as expensive output tokens. With shunt, the code goes straight to disk, Claude never sees it. What doesn't work You can't delegate editing. The worker model's summaries don't include reliable line numbers. If Claude needs to make edits based on the analysis, it still has to read the specific section directly. The hooks allow targeted reads (with offset/limit) for exactly this reason, so delegation saves tokens on understanding. You can't delegate reasoning. The worker model found surface-level patterns but missed a subtle thread-safety bug in my testing. Claude spotted it in seconds once given the right context. The routing explicitly excludes debugging, architectural decisions, and safety-critical code. Latency adds up . Each delegation is a network round-trip: Claude Code to the Portal backend to the worker model and back. Responses typically take 10–30 seconds, and Portal caps a single invocation at 30 seconds, so very large generations need to be split into smaller calls. This is acceptable for large reads, but counterproductive for small ones. The line threshold exi
관련 소식