Anvil은 프로젝트의 모든 레포지토리에서 기능(clarify, plan, build, review, PR)을 처음부터 끝까지 처리하며, 각 단계별로 가장 저렴한 모델을 사용합니다.
벤더 종속성 없음. 추가 비용 없음. 호스팅 플랜 없음.
Anvil은 오픈소스이며 자체 호스팅이 가능한 AI 코딩 에이전트입니다. 이는 엔드투엔드 LLM 파이프라인(clarify → plan → build → review → PR)으로, Claude, GPT, Gemini, OpenRouter에서 실행되거나 Ollama & OpenCode를 통해 완전히 로컬로 실행됩니다. Model Context Protocol (MCP)을 사용하며 사용자 자신의 API 키를 사용합니다. TypeScript로 작성되었습니다.
대시보드 미리보기 — 파이프라인 오케스트레이션, 실시간 에이전트 활동, 지식 그래프, 비용 원장. 전체 데모는 gif를 클릭하여 시청하세요
|
라우팅은 실행(run) 단위가 아닌 단계(stage) 단위로 이루어집니다. 단일 기능이 여러분의 개입 없이 세 가지 다른 제공업체를 거쳐 흐를 수 있습니다. 파이프라인은 어떤 공급자가 어느 단계를 수행했는지 신경 쓰지 않습니다. |
|
단계별 라우팅을 통해 프리미엄 모델은 실제로 필요한 곳에만 표시됩니다. 읽기 전용 연구와 긴 수정 루프는 무료 등급에서 유지됩니다. |
|
모든 HTTP 어댑터는 수작업으로 구현되었습니다 |
Ollama는 완전히 오프라인으로 작동합니다. OpenCode의 월 $10 Zen 구독은 전체 로컬 등급을 대체하며, GPU가 필요하지 않습니다. 클라우드는 필요한 소수의 단계에만 사용됩니다. |
# 1. 설치
npm install -g @esankhan3/anvil-cli
# 2. 프로젝트 설정 (대화형 — 몇 가지 질문에 답변)
...
이것이 전체 온보딩 과정입니다. anvil init은 ~/.anvil/을 생성하고, models.yaml을 시드하며, 프로젝트의 factory.yaml을 스캐폴딩하고, 헬스 체크를 실행합니다. anvil dashboard는 WebSocket 제어 평면을 부팅하고 UI를 엽니다.
처음 사용자인가요? 전체 워크스루 — 전제 조건, 제공업체 키를 얻는 방법, anvil init이 무엇을 물어볼지, 문제 해결 방법은 docs/getting-started.md에 있습니다.
여덟 개의 제공업체가 기본으로 포함되어 있습니다. 하나의 설정 파일로 단계별로 이를 선택할 수 있습니다. 각 어댑터는 동일한 스트리밍 형식, 동일한 UpstreamError 재시도 구조, 그리고 동일한 호출당 비용 계산 방식을 사용합니다.
| Provider | Tier slot | Best for |
|---|
| OpenCode (Zen) | local | 호스팅형 오픈 코딩 모델, 월 $10 고정 요금 — GPU 집약적인 Ollama 대체 |
| Ollama | local | 완전 오프라인, 자체 GPU, 임베딩 + 재순위화(reranking) |
| Claude (CLI) | cheap / premium | 최고 수준의 추론 능력, 네이티브 도구 사용 |
| OpenAI | cheap / premium | GPT-5, o-series 추론 |
| Gemini | cheap / premium | 긴 컨텍스트, Gemini 2.5 Pro |
| OpenRouter | any | 단일 키로 수백 개의 모델 접근 가능 |
| Google ADK | premium | ADK의 러너 시맨틱(runner semantics)이 필요할 때 |
| Gemini CLI | utility | 서브프로세스 폴백(Subprocess fallback) |
라우팅은 실행(run) 단위가 아닌 단계(stage) 단위로 이루어집니다. 같은 기능이라도 사용자가 아무것도 할 필요 없이 세 개의 프로바이더를 거쳐 흐를 수 있습니다:
clarify → Ollama / OpenCode local ~ $0.00
plan → Claude Sonnet 심층 분석 ~ $0.05
build → Ollama / OpenCode local ~ $0.00
...
이 모든 것이 ~/.anvil/stage-policy.yaml 파일의 YAML에 정의되어 있습니다.
. 프리미엄 모델은 실제로 프리미엄 모델이 필요한 곳에만 표시됩니다. 읽기 전용 연구(Read-only research)와 수정-재시도 루프는 무료 티어에 고정되어 있으며, 설계상 에스컬레이션할 수 없습니다. Ollama나 OpenCode를 사용한 일반적인 실행은 클라우드 호출에서 한 자릿수 달러가 소모됩니다.
만약 모델이 429(Too Many Requests) 오류, 5xx 오류, 할당량 제한에 걸리거나 실행 중간에 생존성 검사(liveness probe)에 실패하면, Anvil의 체인 워커(chain-walker)는 남은 실행 동안 해당 모델을 사용하지 않고 같은 티어 내 다음 항목으로 폴백(fallback)합니다. 동일한 프로바이더일 수도 있고 다를 수도 있으며, 이는 사용자 선택에 달려 있습니다. 파이프라인은 멈추지 않으며, 사용자에게 스택 트레이스를 표시하지도 않고, 문제가 있는 모델을 재시도하며 이중 청구하지 않습니다. 모든 폴백 경로는 라우팅 이벤트를 발생시켜 어떤 모델이 왜 건너뛰어졌는지 정확히 확인할 수 있게 합니다.
clarify → adk:gemini-2.5-flash ❌ (프로바이더 생존성 실패)
↪ opencode/kimi-k2.6 ✅ (체인 내 다음 항목, 동일 티어)
build → opencode/qwen3.5-plus ❌ (429 — Alibaba 상위 시스템)
...
두 가지 계층의 감지 메커니즘이 있습니다: 실행 시작 시 사전적(proactive) 생존성 검사(Ollama /api/tags, 클라우드의 경우 환경 변수 존재 여부)와 반응적(reactive) 덕 타이핑된 UpstreamError입니다.
모든 어댑터 호출을 확인합니다. 설정 가능
실행별 재시도 시도 횟수 제한은 models.yaml에서 지정할 수 있습니다.
(walker.max_attempts)
대규모 로컬 모델들이 동시에 하나의 GPU를 공유할 수는 없습니다.
만약 clarify 단계와 build 단계 모두 무거운 Ollama 모델을 사용하려 한다면, 단순한 동시성(concurrency)은 OOM(Out-of-Memory) 오류로 이어집니다. Anvil의 exclusive_slot: true 플래그는 이러한 모델들을 프로세스 로컬 FIFO 큐 뒤에 배치하여 한 번에 GPU에 상주하는 독점 모델이 하나만 있도록 합니다.
이 큐가 모든 과정을 처리합니다:
모델 전환 시 강제 제거(Hard eviction on switch). 모델 A에서 B로 이동할 때, Ollama에게 명시적으로 A의 가중치(weights)를 해제하도록 지시하고, GPU가 실제로 이를 해제했는지 폴링(poll)하여 확인한 후에야 B를 로드합니다. 두 개의 큰 모델이 잠시 동안 GPU에 동시에 존재하는 일은 없습니다.침입자 감지(Intruder detection). 호스트에서 외부적으로 실행된 Ollama 세션(예: 다른 터미널에서 ollama run을 실행한 경우)은 다음 독점 로드 전에 제거됩니다. 따라서 두 개의 큰 모델이 나란히 몰래 들어오는 것을 막아줍니다.임베딩 및 리랭커 우회(Embeddings + rerankers bypass). 이들은 충분히 작아서 함께 공존할 수 있으므로 큐에 영향을 주지 않습니다.동일 모델 호출은 무료입니다(Same-model calls are free). 동일한 ID로 연속해서 호출하는 경우 제거 단계를 완전히 건너뜁니다. 여러 레포지토리에 걸쳐 전개되는 단계는 한 번의 모델 로드 비용만 지불합니다.
~/.anvil/models.yaml에서 모델을 독점(exclusive)으로 지정하세요:
- id: ollama/qwen2.5-coder:14b
provider: ollama
tier: local
...
이 기능을 사용하면 수동적인 순서 지정이나 OOM 오류 없이, 실제로 보유한 VRAM 용량에 관계없이 동일한 장치에서 여러 개의 큰 로컬 모델을 혼합하여 사용할 수 있습니다.
모든 어댑터 호출은 내장된 LiteLLM 가격 스냅샷으로부터 계산된 실제 gen_ai.usage.cost 속성을 첨부합니다. 대시보드에서는 콜(call)별, 단계(stage)별, 실행(run)별 지출액을 실시간으로 보여줍니다. OpenTelemetry 내보내기(export)를 사용하면 Langfuse, Tempo 또는 Honeycomb에서도 동일한 수치를 가져올 수 있습니다.
추정치가 없습니다. 놀랄 일도 없습니다.
9단계 기능 파이프라인 — 명확화(clarify), 계획(plan), 구축(build), 테스트(test), 검증(validate), 배포(ship) — 프로젝트의 모든 저장소에 걸쳐 전개됩니다. 단계별 도구 권한, 검증-수정 재시도 루프(validate-fix retry loops), 제공업체가 429 에러를 반환할 때 모델 간 체인 폴백(chain-fallback) 기능을 지원합니다.
코드를 작성하기 전에 구조화된 마크다운 계획을 생성합니다. 수정된 파일, 교차되는 계약(contracts), 플래그가 지정된 위험 요소, 추정 비용 등을 포함합니다. 계획 검증기(Plan validators)는 누락된 테스트, 누락된 롤백 전략, 잘못된 단계 라우팅을 감지합니다. 에이전트는 계획 단계를 건너뛸 수 없습니다.
증거 게이트(evidence gates), 인시던트 바인딩(incident binding), KB 컨텍스트, 범위 일치(scope matching), 거부 필터링(dismissal filtering) 및 생성된 테스트를 실행하는 검증기(verifier)를 통한 다중 패스 자동 검토 기능을 제공합니다. GitHub에 인라인 댓글과 요약본을 게시합니다.
장기 프로젝트 메모리 기능은 작업 기억(working), 일화 기억(episodic), 의미론적 기억(semantic), 절차적 기억(procedural), 프로필(profile)의 다섯 가지 유형으로 구성됩니다. 자동 학습기가 제안하고, 수면 시간 승인자(sleeptime ratifier)가 결정합니다. 코드 사실 편차 감지(Code-fact drift detection)는 기본 파일이 변경될 때 메모리가 정확하게 유지되도록 합니다.
멀티 저장소 우선(Multi-repo first). 하나로 충분합니다.
tree-sitter를 이용한 AST 인식 청킹, 하이브리드 검색(벡터 + BM25 + 그래프 + 재순위화), 14가지 교차 저장소 간 엣지 전략을 갖춘 프로젝트 그래프. 대시보드가 사용하는 것과 동일한 엔진이 모든 클라이언트를 위한 MCP 서버로도 노출됩니다.
제공업체 키, 모델 레지스트리, 단계 정책(stage policy), OTel 엔드포인트 — 모두 대시보드 UI에서 편집할 수 있습니다. 다음을 기록합니다:
코드베이스의 실제 규칙(명명 규칙, 임포트, 테스트, 오류 처리 등)을 추출하여 살아있는 문서(living docs) 형태로 구성하고, 반복되는 위반 사항은 강력한 규칙으로 승격시킵니다. 에이전트는 같은 실수를 두 번 반복하지 않습니다.
모든 실행은 재생 가능합니다. 차이점(Diffs), PR URL, 검토자 판정, 비용 내역, 사용된 모델 폴백 기록 등이 저장됩니다. 실패한 단계에서 모든 실행을 재개할 수 있으며, 배포된 모든 실행은 원클릭으로 롤백할 수 있습니다.
읽기 전용 조사 기능 —
타겟 수정 워크플로우는 엄격한 재시도 루프를 가집니다. 로컬 환경 및 저가형 티어에 제한하여, 실패하는 테스트가 같은 작업을 다섯 번 시도하며 프리미엄 토큰을 소모하는 것을 방지합니다.
OpenTelemetry 스팬(spans)과 생성형 AI 의미론적 규칙을 사용합니다. Langfuse, Tempo, Honeycomb 또는 OTLP와 호환되는 모든 도구를 플러그인할 수 있습니다. 기본적으로 비활성화되어 있습니다.
개인 정보 보호가 안전한 프롬프트 마스킹(prompt redaction). 호출당 실제 비용 장부(cost ledger) 기록. |
|
제공사(Provider)가 다운되거나, 할당량(quota wall)에 도달하거나, 생존성 프로브(liveness probe)에 실패하면 어떻게 될까요?
체인 워커(chain-walker)는 나머지 실행 동안 모델을 소진하고 동일한 티어의 다음 항목으로 이동합니다. 이는 사전 예방적(실행 시작 시 생존성 프로브) 기능과 사후 대응적( |
모든 실행은 단계별로 체크포인트가 생성됩니다. 실패한 단계부터 재개하여 그 이전에 수행했던 저렴한 단계를 다시 실행할 필요가 없습니다. 한 번의 클릭으로 모든 배포된 실행을 롤백할 수 있습니다 — 브랜치 + PR 삭제, 복원된 작업 공간, 감사 로그 보존. |
Anvil의 지식 기반 검색기(knowledge-base retriever)는 독립적인 MCP 서버로 제공됩니다. Claude Code, Claude Desktop, Cursor 또는 모든 MCP 클라이언트에서 사용할 수 있으며, 동일한 하이브리드 검색 방식(vector + BM25 + graph + rerank), 동일한 프로젝트 그래프를 사용하며 대시보드가 필요 없습니다. |
|
패턴-2 내구성 실행(Pattern-2 durable execution). 모든 단계와 모든 부수 효과는 SQLite 이벤트 로그에 기록됩니다. |
|
다중 프로세스 임대 중재(Multi-process lease arbitration). 각 실행은 영구 저장소(durable store)에 TTL이 적용된 임대(lease)를 보유하며, 활성 프로세스가 이를 주기적으로 확인합니다(heartbeats). 실행 도중에 충돌하더라도 형제 프로세스(sibling process)가 부팅 시 자동으로 인계받습니다 — 고아 스캔(orphan scan)은 만료된 임대를 찾아 이를 주장하고, 자동 재개 큐(auto-resume queue)는 영구 커서(durable cursor)로부터 워크플로우를 다시 재생합니다. 수동 개입이나 중복 작업이 없습니다. |
정책 편집기(Policy editor)는 다음 위치에 있습니다. |
텔레메트리(Telemetry)는 기본적으로 비활성화되어 있습니다. 이를 켜면 모든 어댑터 호출은 GenAI 시맨틱 컨벤션(semantic conventions)을 가진 OpenTelemetry 스팬(span)을 방출합니다 — 프롬프트 + 완료 토큰, 비용, 지연 시간, 모델, 제공사, 오류 클래스. OTLP와 호환되는 백엔드를 연결할 수 있습니다.
# 1. 실제 OTLP 컬렉터로 내보내기 — Langfuse, Tempo, Honeycomb 등
echo 'OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:3000/api/public/otel/v1/traces' >> ~/.anvil/.env
echo 'OTEL_SERVICE_NAME=anvil-dashboard' >> ~/.anvil/.env
...
대시보드를 재시작하면 트레이스가 흐르기 시작합니다.
| 변수 (Variable) | 기본값 (Default) | 설명 (What it does) |
|---|
ANVIL_OTEL_DISABLED | unset | 하드 킬 스위치 — 1로 설정하여 모든 기능을 비활성화합니다. |
ANVIL_OTEL_RECORD_CONTENT | 0 | 스팬에 프롬프트와 완료 텍스트를 포함하려면 1로 설정합니다 (속성당 8 KB로 자름). |
OTEL_LOG_LEVEL | NONE | 디버깅 시 SDK 오류가 표시되도록 ERROR / INFO / DEBUG로 설정합니다. |
ANVIL_OTEL_BATCH | unset | 스팬 내보내기를 배치(batch) 처리하도록 1로 설정합니다 (I/O 감소, 도착 지연 약간). |
기본적으로 스팬은 구조는 가지고 있지만 내용은 포함하지 않습니다 — 모델, 비용, 지연 시간, 오류 클래스 등이 모두 첨부됩니다. 프롬프트와 완료 텍스트는 디스크에만 저장됩니다.
Anvil은 infra/observability/ 경로에 조정된 Langfuse compose 파일을 제공합니다.
— Langfuse 3.x + Postgres + ClickHouse + Redis + MinIO를 포함하며, Anvil이 내보내는 OTLP HTTP 엔드포인트용으로 사전 연결되어 있습니다. 외부 클론이 필요하지 않습니다:
# http://localhost:3000에서 번들된 스택을 시작합니다.
docker compose -f infra/observability/docker-compose.yml up -d
# ~/.anvil/.env 에서 (Langfuse UI에서 생성한 키를 사용하세요)
...
Anvil의 대시보드는 포트 3000에서 로컬 Langfuse를 자동으로 감지합니다 — 실행 중이고 OTEL_EXPORTER_OTLP_ENDPOINT를 직접 설정하지 않았다면, 대시보드가 이를 자동으로 연결합니다. 종료는 docker compose -f infra/observability/docker-compose.yml down -v로 수행합니다.
- 파이프라인 단계당 하나의 부모 스팬을 생성하여 모든 어댑터 호출과 재개(resume)를 단일 트레이스에 연결합니다 (
anvil.agent.session).
- LLM 호출당 자식 스팬을 생성하며,
gen_ai.invoke, gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens 및 에이전트가 수행하는 모든 도구 호출에 대한 실제 USD(gen_ai.usage.cost) 자식 스팬을 생성하고, 일치하는 gen_ai.tool.<name> tool_result가 도착하면 닫힙니다.
- 호출(invoke) 스팬에 라우팅 결정 (routing-decision) 속성 그룹 (
anvil.routing.*)을 추가하여 특정 모델이 왜 선택되었는지, 그리고 실행 도중에 어떤 모델들이 사용되었는지 확인할 수 있습니다.
OTLP 내보내기는 대시보드의 비용 패널에서 보여주는 것과 동일한 수치를 전달합니다. 단일 진실 공급원(One source of truth)입니다.
Anvil은 TypeScript 모노레포입니다. 각 패키지는 하나의 책임을 가지며, 대시보드가 이들을 연결합니다.
┌────────────────────────┐
│ anvil dashboard │ 제어 평면 (control plane)
│ (React + WebSocket) │
...
세 가지 다른 프런트가 동일한 엔진을 사용합니다:
—anvil
CLIinit, doctor, dashboard
(프런트 도어)대시보드— 실시간 에이전트 활동을 포함한 전체 파이프라인 제어 기능— 독립형 코드 검색 제품: MCP 서버, code-search-mcp
code-search
CLI (index, /query, /status, /reset, /daemon, /serve, /mcp), 그리고 code-search-daemon
장기 실행 인덱서. 세 가지 바이너리(bin)가 하나의 설치로 작동합니다. Anvil 에이전트 스택 없이도 작동합니다.