메뉴
HN
Hacker News 32일 전

Opus 도입으로 LLM 비용을 줄인 방법

IMP
8/10
핵심 요약

테라바이트급 CI 로그를 분석하는 에이전트 시스템에서, 저렴한 모델(Haiku)이 먼저 중복 이슈를 필터링하고 복잡한 문제만 비싼 모델(Opus)로 전달하는 '트리아제(Triage)' 패턴을 도입해 비용을 크게 절감한 사례입니다. 로그를 프롬프트에 억지로 넣지 않고 에이전트에게 SQL 인터페이스를 제공하여 필요한 데이터만 능동적으로 가져오게 하며, Opus는 가설을 수립하고 Haiku 하위 에이전트들에게 실제 분석 작업을 지시하는 효율적인 아키텍처를 구축했습니다.

번역된 본문

지난주 우리는 테라바이트(TB) 단위의 CI(지속적 통합) 로그를 LLM에 입력하는 방법에 대해 글을 썼습니다. 해커뉴스(Hacker News)에 달린 대부분의 질문은 로그 자체에 대한 것이 아니었습니다. 사람들의 관심은 에이전트에게 쏠려 있었습니다. 어떤 모델을 사용하는지, 모델 간에 어떻게 조율하는지, 그리고 총비용이 얼마나 드는지에 대한 것이었습니다.

오늘 우리는 Opus 4.6을 실행하면서, 예전에 모든 것을 Sonnet 4.0으로 실행했을 때보다 더 적은 비용을 지불하고 있습니다. 그 이유는 주로 Opus가 '하지 않는 것'에 있습니다. 실패의 80%는 결코 Opus에까지 도달하지 않으며, Opus에 도달하더라도 로그 라인을 직접 읽지 않습니다.

그 아키텍처는 다음과 같습니다:

비싼 에이전트가 필요한지 저렴한 에이전트가 결정하게 하기

지난주에 우리는 약 4,000건의 CI 실패를 분석했습니다. 그중 818건은 새로운 문제였습니다. 나머지 3,187건은 이미 알려진 문제가 다시 발생한 것이었습니다. 불안정한 테스트(flaky test), 인프라의 일시적인 오류, 이미 감지된 적 있는 네트워크 문제 등이었습니다. 80%의 경우 답이 '중복된 이슈'일 때 비싼 모델을 깨우는 것은 전혀 의미가 없습니다.

불행히도 우리는 중복을 결정론적으로 감지할 수 없었습니다. 동일한 작업이 완전히 다른 이유로 여러 번 실패할 수 있으므로, 이전에 본 적 있는 문제인지 알려면 실제로 로그를 살펴봐야만 합니다. 우리는 처음에 비용과 성능의 균형을 맞추기 위해 Sonnet을 사용했습니다. 작동은 했지만, 최악의 선택이었습니다. 여전히 비용이 많이 들었고 결과도 최신 프론티어 모델만큼 좋지 않았기 때문입니다.

우리는 '트리아저(triager)' 패턴으로 전환했습니다. 매우 구체적이고 좁은 임무를 가진 Haiku 에이전트를 둔 것입니다. 문제가 이미 추적 중인지 아닌지 판단하는 역할입니다. 추적 중이라면 거기서 바로 중단하고, 그렇지 않다면 Opus로 에스컬레이션합니다.

Haiku로 중복을 감지하는 것은 약간 도전적인 일로 밝혀졌습니다. 작업을 최대한 쉽게 만들 필요가 있었으므로, 이전 실패에 대한 오류 메시지를 첨부하고 Haiku에 두 가지 검색 도구를 제공했습니다. 알려진 오류 조각에 대한 정확한 일치 검색과 유사하지만 동일하지는 않은 오류에 대한 의미론적 검색(pgvector)입니다.

RAG(검색 증강 생성)는 죽었지만, 의미론적 검색은 꽤 훌륭합니다. "operator does not exist bigint character varying"과 "migration type mismatch on installation_id"는 다른 문자열이지만 근본 원인은 같으며, 의미론적 검색은 이를 찾아냅니다.

Haiku 에이전트는 로그를 읽고 오류 메시지를 검색하며 알려진 실패와 일치하는지 확인한 후 결정을 내립니다. 확신이 없으면 에스컬레이션합니다. 오탐지(False positive)는 약간의 비용만 들지만, 미탐(False negative)은 실제 문제를 놓치게 만듭니다. 5번의 실패 중 4번은 절대 Opus에 도달하지 않습니다. 트리아저를 통한 일치 여부 확인은 전체 조사보다 약 25배 적은 비용이 듭니다.

에이전트가 직접 컨텍스트를 가져오게 하라(Pull), 밀어 넣지 마라(Push)

몇몇 사람들이 20만 줄이 넘는 로그를 어떻게 처리하는지 물었습니다. 우리는 그 많은 로그를 프롬프트에 밀어 넣지(Push) 않습니다. 대신 에이전트에게 ClickHouse에 대한 SQL 인터페이스를 제공하여 필요한 것을 직접 요청하게(Pull) 합니다.

그 이유는 단순히 토큰 비용 때문만은 아닙니다. 에이전트에게 특정 로그 라인 세트를 건네면, 문제가 실제로 무엇인지 알기도 전에 무엇이 관련이 있는지에 대해 이미 판단을 내린 것입니다. 에이전트는 사용자가 제공한 것에 고착됩니다. 실제 원인이 다른 곳에 있다면, 그것을 찾기가 더 어려워집니다. 이것은 디버깅 세션을 시작할 때 "문제가 이 파일에 있다고 생각해"라고 말하는 것과 같은 이유로 바람직하지 않습니다. 조사가 시작되기도 전에 편향을 만든 것입니다.

우리는 지난주에 SQL 설정에 대해 자세히 설명했지만, 요약하자면 다음과 같습니다. 원시 데이터가 있는 테이블(github_logs, 로그 라인당 하나의 행)과 사전 집계된 데이터가 있는 세트(materialized views, 워크플로우별 실패율, 작업 시간, 결과 카운트)가 있습니다. 대부분의 조사는 materialized views에서 시작하여 원인의 범위를 좁힌 다음, 필요할 때 원시 로그를 자세히 살펴봅니다.

우리는 에이전트에게 어떤 테이블을 쿼리할지 알려주지 않습니다. 대신 응답 자체를 사용하여 점진적으로 에이전트를 안내합니다. 쿼리가 너무 많은 행을 반환하면 행을 자르고 더 구체적인 materialized view를 사용하도록 제안합니다. 로그가 아직 수집되지 않은 경우 GitHub CLI를 가리킵니다. 에이전트는 우리가 모든 경로를 미리 예상할 필요 없이 스스로 필요한 것을 파악합니다.

비싼 에이전트는 계획하고, 저렴한 에이전트는 일한다

Opus는 무엇이 실패했는지 확인하고 가설을 세운 다음, Haiku 하위 에이전트를 생성하여 실제 세부 조사 작업을 수행합니다. 각 하위 에이전트는 Opus로부터 프롬프트를 받습니다. 무엇을 검색할지, 어떻게 검색할지, 무엇을 반환할지 등이 정확히 지정됩니다.

원문 보기
원문 보기 (영어)
Last week we wrote about feeding terabytes of CI logs to an LLM . Most of the questions on Hacker News weren&#x27;t about the logs. They were about the agent: which models, how they coordinate, and how much it all costs. Today we run Opus 4.6 and pay less than when we ran everything on Sonnet 4.0. The reason is mostly what Opus doesn&#x27;t do: 80% of failures never reach it, and when they do, it never reads a log line. The architecture looks like this: Let a cheap agent decide if the expensive one is needed Last week we analyzed around 4,000 CI failures. 818 were new problems. The other 3,187 were a known issue surfacing again: a flaky test, an infrastructure hiccup, a network blip we&#x27;d already detected. It makes no sense to wake up an expensive model when 80% of the time the answer is "it&#x27;s a duplicate". Unfortunately, we can&#x27;t deterministically detect duplicates: the same job can fail multiple times for completely different reasons, so you need to actually look at the logs to know if you&#x27;ve seen this before. We initially used Sonnet for this to balance cost and performance. It worked, but it was the worst of both worlds: still expensive, and the results weren&#x27;t as good as a frontier model. We switched to the "triager" pattern: a Haiku agent with a very specific and narrow job. Is this issue already tracked or not? If it is, stop right there. If not, escalate to Opus. Detecting duplicates with Haiku proved a bit challenging. We needed to make the job as easy as possible, so we attached error messages to previous failures and gave Haiku two search tools: exact matching for known error snippets, and semantic search (pgvector) for similar-but-not-identical errors. RAG is dead, but semantic search is pretty neat. operator does not exist bigint character varying and migration type mismatch on installation_id are different strings but the same root cause, and semantic search surfaces that. The Haiku agent reads the logs, searches error messages, tries to match against known failures, and makes a call. When in doubt, it escalates. A false positive costs a little money; a false negative means we miss something real. 4 out of 5 failures never reach Opus. A triager match costs around 25x less than a full investigation. Let the agent pull context, don&#x27;t push it Several people asked how we handle logs that are 200K+ lines. We don&#x27;t push them into the prompt. We give the agent a SQL interface to ClickHouse and let it ask for what it needs. The reason isn&#x27;t just token cost. If you hand an agent a specific set of log lines, you&#x27;ve already made a judgment about what&#x27;s relevant before you know what the problem actually is. The agent anchors to what you gave it. If the real cause is somewhere else, you&#x27;ve made it harder to find. It&#x27;s the same reason you don&#x27;t want to lead a debugging session by saying "I think the problem is in this file": you&#x27;ve biased the investigation before it started. We wrote about the SQL setup in detail last week , but the short version: there&#x27;s one table with raw data ( github_logs , one row per log line) and a set of materialized views with pre-aggregated data: failure rates by workflow, job timings, outcome counts. Most investigations start with the materialized views to narrow down the cause, then drill into raw logs when they need to. We don&#x27;t tell the agent which table to query. Instead, we use the responses themselves to guide it progressively. If a query returns too many rows, we truncate and suggest a more specific materialized view. If logs aren&#x27;t ingested yet, we point it to the GitHub CLI. The agent figures out what it needs without us having to anticipate every path in advance. Expensive agents plan, cheap agents do the work Opus looks at what failed, forms a hypothesis, and spawns Haiku sub-agents to do the actual digging. Each sub-agent gets a prompt from Opus: exactly what to search, how to search, what to return. Sub-agents are capped at one level deep; they can&#x27;t spawn sub-agents of their own. Unbounded fan-out is how you get runaway costs. A few weeks ago three Storybook CI jobs failed on the same commit, all crashing at pnpm install . Opus started by asking a sub-agent to fetch the error messages from the failing pnpm install step. ClickHouse didn&#x27;t have the logs yet, so the sub-agent fell back to the GitHub CLI. Sub-agent #1 prompt: Fetch the CI logs for this run. Return the exact error messages from the pnpm install step, the full error output, especially the last 50-100 lines. Result: gyp ERR! not found: make . re2@1.23.0 couldn&#x27;t compile because make wasn&#x27;t on the runner. Opus searched existing insights (no match), then queried ClickHouse for the failure trend over 14 days: Feb 23: 0.2% failure rate Feb 24: 1.1% Feb 25: 8.0% <- inflection point Something clearly changed on Feb 25. Opus spawned Sub-agent #2 : Investigate what changed around Feb 24-25. Failure rate went from 0.2% to 8%. The error is gyp ERR! not found: make . Run git log on the workflow file and package.json for that window. Build dependencies had been removed during an unrelated migration. Correct for that migration, but re2 still needed make to compile natively. Opus spawned Sub-agent #3 to verify the current workflow state, then created the insight with root cause and fix. The orchestrator never read a line of logs, git history, or code itself. A few things worth noting: Cost. Haiku handles ~65% of all input tokens but only ~36% of our LLM spend. The expensive model thinks; the cheap model reads. Without the model hierarchy, the daily bill more than doubles. Opus plans as it goes. It starts with a hypothesis, but each sub-agent&#x27;s results shape the next step. In this investigation it got the error, searched history, then asked what changed. Each round informed the next. Over a third of our investigations go multi-round, and new problems need roughly twice the investigation depth of known ones. Context hygiene. The orchestrator&#x27;s context stays clean: structured summaries from sub-agents, not raw log output. Each sub-agent starts with a clean slate and its context is discarded when it&#x27;s done. Tool call output accumulates fast, and stale context from earlier in a session degrades decisions later. Directed search. "Return the exact error messages from the pnpm install step" is a very different prompt than "analyze these logs". Opus decides what to look for; Haiku finds it. Haiku&#x27;s input/output ratio is 86:1 (reads a lot, returns focused extracts), while the orchestrator is around 50:1 (synthesizes and decides). Haiku absorbs the data so Opus doesn&#x27;t have to. This wasn&#x27;t possible 6 months ago Six months ago we were on Sonnet 4.0. It struggled to write correct ClickHouse queries: wrong tables, missing filters, reading far too much data. Haiku 4.0 wasn&#x27;t useful for anything beyond yes/no classification. Today Opus 4.6 can plan investigations and write precise sub-agent prompts. Haiku 4.5 can handle narrow, directed tasks because the tasks are scoped tightly enough that a fast cheap model can execute them. Upgrading to a frontier model made costs go down. The pattern generalizes We built this for CI logs but the pattern applies to anything with high event volume: security logs, IoT telemetry, financial data. Most events are noise or repeats, and the expensive model should only see the ones that aren&#x27;t. There&#x27;s a fourth layer we haven&#x27;t covered: reassessment. The system periodically checks whether what it concluded is still true, closing stale insights, catching false positives, verifying that fixes worked. That&#x27;s a post on its own. We&#x27;re still tuning where the sub-agent boundary sits. Sometimes spawning a sub-agent costs more than doing it inline because the setup overhead outweighs the savings. The hardest part wasn&#x27;t making the agent smarter. It was building the layers that stop it from r