컨텍스트 킷 vs Forge 가드레일. 작은 모델을 프런티어 신뢰도까지 끌어올리는 두 가지 방법
TL;DR. Forge (CAIS 2026 발표 프레임워크)는 셀프호스트 작은 모델을 런타임 가드레일로 감싼다. 재시도 넛지, 스텝 강제, 에러 복구, 컨텍스트 압축, VRAM 예산 관리. 8B 모델이 53퍼센트에서 99퍼센트로 올라간다고 보고됐다. 내 컨텍스트 엔지니어링 킷은 여섯 개 마크다운 파일 (CLAUDE.md, AGENTS.md, MEMORY.md, TESTING.md, GLOSSARY.md, ADR 템플릿) 로 입력 프레임을 다시 짠다. 실제 아키텍처 감사 태스크에서 Gemma 4 31B 가 9/12 에서 11/12 로 올라갔다. Claude Opus 4.7 대비 75 → 92 퍼센트 패리티. 같은 문제 영역. 다른 메커니즘. 다른 비용 라인. 이 글은 두 접근을 나란히 놓고, 어디서 겹치고 어디서 안 겹치는지, 그리고 둘 다 적용하면 어떻게 될지를 짚는다.
두 접근이 푸는 같은 문제
작은 오픈웨이트 모델을 한 번이라도 멀티스텝 에이전트 루프에 돌려본 사람은 같은 현상을 봤을 것이다. 단일 스텝 정확도는 괜찮다. 루프가 길어지면 무너진다.
수학이 잔인하다. 스텝당 95퍼센트 정확도가 다섯 스텝 체이닝되면 끝까지 가는 비율은 77퍼센트. 아홉 스텝이면 63퍼센트. 이게 누적 신뢰도 문제고, 멀티스텝 에이전트 태스크에서 "그냥 프런티어 모델 써라" 가 디폴트 답이었던 이유다.
같은 갭을 정반대 방향에서 공격하는 작업 두 개가 최근에 나왔다.
하나는 Forge. ACM CAIS 2026 에서 Antoine Zambelli (Texas Instruments) 가 발표한 프레임워크다. Forge 는 런타임에 앉는다. 에이전트 루프를 지켜보다가 부분 실패가 나오면 재시도를 유도하고, 스텝 순서를 강제하고, 컨텍스트가 부풀면 압축하고, 컨슈머 하드웨어의 VRAM 예산을 관리한다. 컨퍼런스 페이지의 헤드라인 숫자. 80억 파라미터 모델이 Forge 와 함께 99퍼센트 신뢰도에 도달한다. 가드레일 없는 프런티어 API 모델들이 49 ~ 87퍼센트 범위로 떨어지는 동일한 태스크에서. 이 프로젝트를 띄운 Hacker News 스레드 (현 시점 106점, 댓글 35개) 가 인용한 프레이밍은 "8B 모델을 53퍼센트에서 99퍼센트로" 였다.
다른 하나는 내가 지난 2주간 이 블로그에 시리즈로 올린 작업이다. 명제가 다르다. 런타임에 모델을 가로채는 대신, 모델이 태스크를 보기 전에 입력 프레임을 다시 짠다. 여섯 파일 컨텍스트 킷 (CLAUDE.md 는 프로젝트 컨벤션, AGENTS.md 는 출력 스키마, MEMORY.md 는 누적 발견, TESTING.md 는 어설션 패턴, GLOSSARY.md 는 용어, ADR 템플릿은 의사결정 기록) 을 시스템 프롬프트에 한 번 로드한다. 실제 아키텍처 감사 결과. Gemma 4 31B 가 12개 정답 발견 중 11개를 잡았다. Claude Opus 4.7 은 12/12. 같은 모델을 킷 없이 돌렸을 때는 9/12.
두 라인이 같은 지표를 본다. 작은 오픈 모델이 프로덕션에 쓸 만한 프런티어 근접 신뢰도에 도달하는 것. 메커니즘은 완전히 다르다. 비용 라인도 완전히 다르다. 둘을 같이 쌓는 실험은 내가 아는 한 양쪽 모두 안 했다.
접근 1. 컨텍스트 킷. 입력 프레임을 다시 짠다
컨텍스트 킷은 전적으로 프롬프트 쪽에 산다. 마크다운 여섯 파일. 세션 시작에 시스템 프롬프트로 한 번 로드. 런타임 콜백 없음, 재시도 루프 없음, 에이전트 하니스 없음. 모델이 킷을 읽고, 태스크를 읽고, 답을 쓴다.
각 파일에 들어가는 내용은 이런 모양이다.
# CLAUDE.md (발췌)
## 이 코드베이스에서 봐온 실패 패턴
- "silent self-correction" 안티패턴. 내부 상태 드리프트를
모델이 사용자에게 알리지 않고 조용히 복구. 톤 영역은 OK.
상태나 돈이 걸린 영역에서는 금지.
- "plain text only" 안티패턴. 모든 중간 표현을 문자열로
강제. 구조화된 워크로드에서는 깨진다.
- "universal claim with disclaimer" 냄새. 섹션 제목은
일반성을 약속하고 마지막 소절에서 면책. 잡아낼 것.
## 도메인 용어
- P0/P1/P2/P3 = 우리 스펙의 네 stratum. 정의는 GLOSSARY.md.
- "Stratum" vs "Interceptor". Stratum 은 수직 모델의 순서
있는 레이어. Interceptor 는 가로지르는 래퍼. 혼동 금지.
# AGENTS.md (발췌). 비평 패스용 출력 스키마.
output_schema:
findings:
- id: F-{n}
severity: [info, warn, error, critical]
principle_violated: <CLAUDE.md 에 등록된 이름>
evidence: <입력에서 따온 인용 구간>
proposed_fix: <한 문장>
confidence: <0.0-1.0>
signature_insight:
single_most_actionable_fix: <string>
rationale: <한 문단>
킷이 동시에 세 가지를 한다. 첫째, 모델이 찾아야 할 실패 패턴 이름을 미리 박아준다. 매 콜마다 모델이 분류 체계를 새로 발명할 필요가 없다. 둘째, 출력 스키마를 못 박는다. 다운스트림 툴링이 결정론적으로 파싱할 수 있다. 셋째, 과거 발견을 들고 다닌다. 매 이터레이션마다 같은 결함을 다시 발견하지 않는다.
이 접근의 비용 라인은 두 군데에 있다. 킷을 쓰는 작업이 진짜 노동이다. 중간 복잡도 프로젝트에서 여섯 파일 합쳐 약 2,500 토큰. 유지는 규율이다. 프로덕션에서 새 실패 패턴이 보이면 CLAUDE.md 로 들어간다. 아키텍처 결정은 ADR 폴더로 간다. 킷은 살아있다.
추론 비용이 두 번째 자리다. 프롬프트 캐싱이 인풋 쪽을 거의 공짜로 만든다. Anthropic 의 5분 캐시 TTL 과 OpenRouter 의 캐싱 지원으로 캐시 히트시 반복 입력 토큰이 정가의 10퍼센트로 떨어진다. Gemma 4 31B 가 백만 토큰당 $0.12 / $0.37 인 상황에서, 7,500 토큰 캐시된 시스템 프롬프트 + 2,000 토큰 태스크 + 4,000 토큰 출력이 감사 한 번에 약 $0.003. 내가 돌린 4모델 풀 감사가 총 추론 비용 $0.05. 시리즈 4편 글에 정확한 숫자.
발견율은 킷 없이 9/12 에서 킷 적용 시 11/12 로 움직였다. 그게 75 → 92퍼센트 숫자다. 태스크 하나, 프롬프트 구조 하나, temperature 0.3 하나. 벤치마크 용어로 N=1. 동향 신호로 받고 동료 리뷰 통과한 결과로는 받지 말 것.
메커니즘은 순수하게 "추론 콜 앞단". 추론 시점에 도는 건 단일 모델 콜 하나뿐. 가로챌 에이전트 루프가 없다. 재시도 예산이 없다. 하니스가 없다.
접근 2. Forge. 런타임에 가로챈다
Forge 는 반대 모양이다. 컨슈머 하드웨어 (8 ~ 14GB VRAM 대역) 에 셀프호스트 모델이 이미 떠 있고, 7스텝 중 3스텝에서 망가지는 툴 사용 에이전트 루프가 돌고 있다는 전제다. Forge 가 루프를 감싸고, 모델이 헛발질하면 개입한다.
CAIS 2026 데모 페이지의 가드레일 스택 설명을 그대로 옮기면:
재시도 넛지, 스텝 강제, 에러 복구, 컨텍스트 압축, 하드웨어 인식 VRAM 예산 관리.
각 컴포넌트가 무엇을 하는지에 대한 합리적 재구성이다. 정확한 코드는 공개 페이지에 없어서, 명명된 함수 기반의 정보된 추측이다.
# Forge 스타일 가드레일 래퍼의 개념적 재구성.
# 이름은 발표된 메커니즘과 일치. 내용은 예시.
class GuardrailedAgent:
def __init__(self, model, tools, max_steps=10, vram_budget_gb=8):
self.model = model
self.tools = tools
self.max_steps = max_steps
self.vram_budget_gb = vram_budget_gb
self.context = []
def step(self, task):
for i in range(self.max_steps):
self.compact_if_over_budget()
response = self.model.generate(self.context, task)
if self.is_malformed(response):
# 재시도 넛지: 툴 스키마를 다시 주입
self.context.append(self.retry_nudge(response))
continue
if not self.respects_step_order(response):
# 스텝 강제: 순서 어긋난 툴 콜 거절
self.context.append(self.order_violation_msg(response))
continue
tool_result = self.execute(response)
if tool_result.is_error():
# 에러 복구: 에러 메시지를 컨텍스트에 다시 접어
# 넣어서 구조화된 재시도
self.context.append(self.error_recovery_prompt(tool_result))
continue
return tool_result
return self.fallback()
핵심 속성. 가드레일이 툴 비종속적 (tool-agnostic) 이다. 에이전트가 무엇을 하는지 모른다. 깨진 JSON 이 어떻게 생겼는지, 순서 어긋난 툴 콜이 어떻게 생겼는지, VRAM 예산을 깰 컨텍스트가 어떻게 생겼는지를 안다. 개입은 로컬하고 기계적이고 싸다.
보고된 결과. 8B 모델이 Forge 아래에서 멀티스텝 워크플로 99퍼센트 완료. Hacker News 가 인용한 "53퍼센트에서 99퍼센트" 가 헤드라인 숫자다. CAIS 2026 페이지 자체는 가드레일 없는 베이스라인을 49 ~ 87퍼센트 (프런티어 API 들이) 로 보고하므로, "53퍼센트" 숫자는 논문 안의 특정 8B 베이스라인 설정에서 나온 것 같지만 작성 시점에 공개 PDF 로 교차 검증은 못 했다. 정성적인 모양은 잘 뒷받침된다. 작은 모델 + 가드레일이 가드레일 없는 프런티어 모델을 멀티스텝 태스크에서 이긴다.
비용 라인은 런타임에 산다. 가드레일 개입 하나당 추가 모델 콜이 든다 (재시도, 정정된 스텝, 복구된 에러). 논문의 평가 하니스가 9개 시나리오 x 50회 x 50개 이상의 모델/백엔드 구성으로 돌았다. 콜이 많다. 컨슈머 하드웨어에서는 달러 비용은 사실상 공짜지만 레이턴시와 쓰루풋 비용은 실재한다. API 호스팅된 작은 모델에서는 개입당 비용이 쌓인다. 세 번 재시도가 필요한 런은 한 번 생성 대신 네 번 생성을 결제한다.
셋업 작업도 런타임 인프라다. Forge 를 에이전트 하니스에 통합해야 하고, 스텝 강제 레이어가 읽을 수 있는 형태로 툴 스키마를 정의해야 하고, VRAM 예산 관리자를 본인 GPU 에 맞게 튜닝해야 한다. CLAUDE.md 쪽 작업은 어떤 콜이 나가기 전에 끝나 있다. Forge 쪽 작업은 나가는 모든 콜 주변에서 계속 돈다.
둘이 어디서 다른가
두 접근의 대비를 가장 깔끔히 잡는 프레임은, 둘이 같은 스택의 다른 레이어에 산다는 것이다.
| 차원 | 컨텍스트 킷 | Forge 가드레일 |
|---|---|---|
| 개입 지점 | 추론 전 (입력 프레임) | 추론 중 (런타임 루프) |
| 메커니즘 | 실패 패턴 명명, 스키마 고정, 메모리 이어가기 | 재시도 넛지, 스텝 강제, 에러 복구, VRAM 예산 |
| 작업이 어디 있나 | 글쓰기 시간 (여섯 MD 파일) | 런타임 (모든 콜을 감싸는 가드레일 래퍼) |
| 콜당 한계 비용 | 프롬프트 캐시 적용 시 거의 0 | 개입당 추가 콜 한 번 |
| 타겟 실패 모드 | 모델이 도메인이나 출력 계약을 이해 못함 | 모델이 멀티스텝 루프 안에서 헛발질 |
| 툴 인식? | Yes (도메인 용어 내장) | No (설계상 툴 비종속) |
| 세션 간 지속성 | Yes (디스크 위 파일) | No (라이브 프로세스 상태) |
| 셋업 노력 | 초기 높음, 유지 낮음 | 프레임워크 있으면 초기 낮음, 워크로드별 튜닝 필요 |
| 최적 태스크 | 단일 비평, 감사, 구조화 출력 작성 | 멀티스텝 툴 사용 에이전트 루프 |
| 보고된 리프트 | 아키텍처 감사 발견 9/12 → 11/12 (태스크 하나, N=1) | 9개 에이전트 시나리오에서 53 → 99퍼센트 (시나리오당 50회) |
가장 유용한 사고 프레임은 "노동 이전" 이다. 컨텍스트 킷은 추론 예산에서 글쓰기 예산으로 작업을 옮긴다. 여섯 파일 쓰는 데 한 번 결제한다. 이후 추론 콜에는 거의 결제하지 않는다. Forge 는 반대다. 작은 모델이 루프 안에서 헛발질할 것을 받아들이고, 정정이 필요한 순간에 추론 시점에서 비용을 지불한다. 단, 정정이 필요할 때만.
워크로드가 "문서 하나를 매우 신중하게 한 번 감사" 면 컨텍스트 킷이 맞다. 감사가 단일 콜이다. 가드레일 칠 루프가 없다.
워크로드가 "7스텝 브라우저 자동화 에이전트를 하루 200번" 이면 Forge 가 맞다. 모든 가능한 브라우저 자동화 실패를 커버하는 컨텍스트 킷 글쓰기 예산은 무한대다. 깨진 JSON 과 순서 어긋난 클릭을 잡는 런타임 가드레일은 다룰 만하다.
대부분의 실제 워크로드는 섞여 있다. 그래서 조합이 흥미롭다.
가설적 조합. 두 레이어를 같은 워크로드에 쌓는다
어느 쪽 논문도 조합을 테스트하지 않았다. 아래는 가설이지 결과가 아니다. 가설을 구체적으로 적어두는 이유는 부분적으로는 가설을 명시화하기 위해서, 부분적으로는 다음 한 달 안에 이 실험을 실제로 돌리고 싶어서다.
명제. 두 개입이 겹치지 않는 실패 모드를 친다. 그래서 이득이 합산에 가깝지 중복이 아닐 것.
# 가설: 두 레이어를 쌓는다.
# 컨텍스트 킷이 입력을 짠다. Forge 가 루프를 감싼다.
# 다루는 실패 모드는 대체로 분리.
context_kit = load_context_kit([
"CLAUDE.md", # 실패 패턴 + 도메인 용어
"AGENTS.md", # 출력 스키마
"MEMORY.md", # 누적 발견
"TESTING.md", # 어설션 패턴
"GLOSSARY.md", # 명명된 용어
"docs/adr/0001.md" # 의사결정 기록
])
agent = GuardrailedAgent(
model=Gemma4_31B,
tools=[browser, file_io, search],
system_prompt=context_kit,
max_steps=10,
vram_budget_gb=8,
)
# 추론 시점에:
# - 모델이 도메인 용어를 안다 (컨텍스트 킷).
# - 모델이 자기 레벨에서 깨진 출력이 무엇인지 안다
# (컨텍스트 킷 AGENTS.md 스키마).
# - 하니스가 스텝 순서를 잡고 재시도한다 (Forge).
# - 하니스가 긴 루프 동안 VRAM 부풀음을 관리한다 (Forge).
이득이 합산에 가깝고 곱셈이나 중복이 아닐 것으로 보는 이유.
컨텍스트 킷 실패 모드는 대부분 "모델이 이 도메인에서 좋은 출력이 어떻게 생겼는지 모른다" 다. 실패 패턴 명명과 스키마 고정이 이걸 잡는다. 그래도 모델은 가끔 깨진 JSON 을 뱉고, 스키마에서 드리프트하고, 잘못된 툴을 부른다. 그건 런타임 증상이다.
Forge 실패 모드는 대부분 "모델이 파싱 안 되거나 워크플로를 전진시키지 않는 무언가를 만들었고, 복구해야 한다" 다. 재시도 넛지와 스텝 강제가 이걸 잡는다. 다만 Forge 는 태스크 개념 자체를 잘못 잡은 모델은 못 고친다. "감사" 를 "요약" 으로 이해한 모델은 같은 틀린 답으로 열 번 재시도할 것이다.
두 레이어가 다른 카테고리의 실수를 다룬다. 같이 쌓으면 예측은:
- 컨텍스트 킷 단독: 75 → 92퍼센트 (관측, N=1).
- 8B 모델 위 Forge 단독: 53 → 99퍼센트 (논문 보고).
- 같이: 95 ~ 99퍼센트 대역 어디. 입력 품질이 좋아져서 바닥이 둘 다보다 위에 있을 것. 런타임 복구가 빠져나간 것을 여전히 잡을 것.
솔직한 버전은 모른다는 것. 두 논문이 다른 태스크에서 다른 걸 잰다. 그 숫자들을 교차 적용하는 건 남이 했으면 "허술하다" 고 지적할 종류의 행동이다. 다음 스텝은 태스크를 고정하고 두 레이어를 각각 켜고 끄는 단일 실험. 6월의 프로젝트.
어느 쪽을 언제 쓰는가
워크로드 모양에 따른 짧은 결정 규칙.
컨텍스트 킷을 써라 만약:
- 태스크가 단일 콜이나 단일 콜에 가까울 때. 감사, 비평, 구조화 작성.
- 출력 계약이 루프보다 중요할 때. 파싱 가능한 JSON 이 필요하지 견고한 7스텝 브라우저 내비게이션이 필요한 게 아닐 때.
- 긴 시스템 프롬프트를 잘 따르는 모델을 쓸 때. Gemma 4 31B 가 그렇다. 더 작은 모델은 아닐 수 있다.
- 같은 태스크 모양을 반복해 돌릴 예정일 때. 킷 쓰는 비용이 콜들에 걸쳐 회수된다.
- 병목이 "모델이 내 도메인을 이해 못한다" 일 때.
Forge 스타일 가드레일을 써라 만약:
- 태스크가 실제 툴을 쓰는 멀티스텝일 때. 브라우저 에이전트, 파일시스템 에이전트, 다중 API 워크플로.
- 컨슈머 하드웨어에서 셀프호스트 작은 모델을 돌리는 중이고 대안이 프런티어 API 요금일 때.
- 스텝 순서가 중요하고 모델이 순서를 어기는 게 관측됐을 때.
- 루프 동안 컨텍스트 부풀음이 모델을 깨뜨릴 때. 압축이 중요할 때.
- 병목이 "모델이 루프 안에서 헛발질해서 런이 중단된다" 일 때.
둘 다 고려하라 만약:
- 워크로드가 멀티스텝 이면서 도메인 특화일 때. 대부분의 실제 프로덕션 워크로드.
- 실패 패턴 단일 진실원천 (CLAUDE.md) 이 있고 런타임 가드레일이 그걸 참조할 수 있을 때.
- 워크로드를 볼륨으로 돌리고 있고 재시도 콜 하나의 비용이 의미 있어지기 시작할 때.
둘 다 안 쓰고 프런티어 요금 결제하라 만약:
- 워크로드가 비정기적이고 단명일 때. 1회성 스크립트는 둘 다 셋업 비용이 안 맞다.
- 킷 유지할 시간도 없고 모델 호스팅할 인프라도 없을 때.
- 틀린 답의 비용이 충분히 커서 최대 캐파에 한 방을 원하고 결제할 여유가 있을 때.
다음에 돌릴 실험
이 글에서 직접 테스트 가능한 가설은, 컨텍스트 킷과 런타임 가드레일을 같은 워크로드에 쌓으면 이득이 대략 합산이라는 것. 가장 싼 실험 버전:
- 태스크 고정. 앞 글의 아키텍처 감사 태스크 (정답 발견 12개) 사용.
- 컨슈머 하드웨어에서 도는 작은 오픈 모델 하나 선정. Gemma 4 31B 가능. Llama 3.1 8B 가 Forge 논문 베이스라인에 더 가깝다.
- 이진 변수 두 개 토글. 컨텍스트 킷 on/off, 가드레일 래퍼 on/off.
- 셀당 20회 돌림. 발견율과 런당 비용 측정.
- 프런티어 베이스라인 (Claude Opus 4.7) 과 비교. 두 레이어 모두 끈 상태.
2x2 디자인이라 솔로 개발자가 주말 안에 돌릴 수 있다. 결과가 어디로 떨어지든, 두 레이어가 합쳐지는지 간섭하는지를 알려준다. 어느 쪽이든 글로 쓸 예정.
푸터
이 글은 작은 오픈웨이트 모델용 컨텍스트 엔지니어링 시리즈의 다섯 번째다. 앞 네 편은 수학과 감사 결과를 더 자세히 다룬다.
- 비용 엔지니어링 수학편: Gemma 4 API 비용을 87퍼센트 깎았다. 수학.
- 아키텍처 감사편: 7,500토큰 아키텍처 스펙을 4 모델에 돌렸다.
- 방어 패스편: Gemma 4 가 자기가 지은 걸 방어할 수 있나.
Forge 출처: Antoine Zambelli, Forge: Closing the Agentic Reliability Gap Between Self-Hosted and Frontier Language Models, ACM CAIS 2026.
여섯 파일 컨텍스트 엔지니어링 킷 풀 셋은 GitHub 에 MIT 라이선스로 공개 (agent-starter-kit). 케이스 스터디 글이 함께 묶인 큐레이션 버전은 크몽 유료 템플릿. 두 링크 다 리포 README 에 있다.
이 스택을 본인 워크로드에 시도해본다면, 내가 가장 보고 싶은 비교 숫자는 2x2. 킷 on/off 와 가드레일 래퍼 on/off 를 같은 태스크와 같은 모델로 교차한 결과. 반증 실험 환영.
Jack. wildeconforce.com