- Published on
AI 하네스 해부 — 모델을 에이전트로 만드는 스캐폴딩 (루프·도구·컨텍스트, 그리고 직접 만들기)
- Authors

- Name
- Youngju Kim
- @fjvbn20031
프롤로그 — 같은 모델, 다른 하네스, 다른 결과
2026년에 흔히 보는 장면: 두 팀이 같은 프론티어 모델을 쓴다. 한 팀의 에이전트는 티켓을 받아 PR을 열고, 다른 팀의 에이전트는 30분째 같은 파일을 헛돌고 있다.
차이는 모델이 아니다. **하네스(harness)**다.
모델은 엔진이다. 하네스는 차다. 같은 엔진을 경주용 섀시에 얹느냐 고장난 트럭에 얹느냐에 따라, 주행 경험은 완전히 달라진다.
2023~2024년의 AI 엔지니어링은 "어떤 모델을 쓰는가"였다. 2026년의 AI 엔지니어링은 대부분 **"그 모델을 어떻게 둘러싸는가"**다. 모델 제공자는 몇 곳으로 수렴했지만, 하네스는 폭발적으로 다양해졌다 — Claude Code, Cursor, Codex CLI, Aider, OpenClaw… 이들은 같은 모델 API를 호출한다. 다른 것은 하네스다.
이 글은 하네스를 해부한다. 에이전트 루프, 도구 실행 계층, 컨텍스트 관리, System Prompt, 제어 흐름, 실패 모드. 그리고 40줄짜리 최소 하네스를 직접 만들고, 하네스를 어떻게 평가하는지까지 본다.
1장 · 하네스란 무엇인가
하네스 = LLM을 둘러싸고, 그것을 "에이전트"로 만드는 모든 코드.
LLM 자체는 무상태 함수다 — 텍스트가 들어가면 텍스트가 나온다. 그게 전부다. 파일을 읽지도, 명령을 실행하지도, 기억하지도 못한다. 하네스가 그 모든 것을 한다.
┌──────────────────────────────────────┐
│ 하네스 │
│ ┌────────────┐ ┌────────────────┐ │
│ │ System │ │ 컨텍스트 관리 │ │
│ │ Prompt │ │ (윈도우·압축) │ │
│ └────────────┘ └────────────────┘ │
│ ┌────────────┐ ┌────────────────┐ │
│ │ 에이전트 │──▶│ 모델 (LLM) │ │
│ │ 루프 │◀──│ = 엔진 │ │
│ └─────┬──────┘ └────────────────┘ │
│ ▼ │
│ ┌────────────┐ ┌────────────────┐ │
│ │ 도구 실행 │ │ 제어 흐름·훅· │ │
│ │ 계층 │ │ 권한 게이트 │ │
│ └────────────┘ └────────────────┘ │
└──────────────────────────────────────┘
하네스가 소유하는 것:
- 루프 — 모델을 한 번이 아니라 반복해서 부른다.
- 도구 — 모델이 "행동"할 수 있게 한다.
- 컨텍스트 — 무엇을 모델에게 보여줄지 결정한다.
- System Prompt — 모델의 정체성과 규칙.
- 제어 흐름 — 언제 멈추고, 언제 사람을 부르고, 언제 병렬화할지.
- 안전장치 — 무한 루프·비용 폭주·잘못된 도구 호출을 막는다.
모델을 바꾸면 추론의 질이 바뀐다. 하네스를 바꾸면 에이전트의 행동 자체가 바뀐다. 그래서 둘을 분리해서 생각해야 한다.
2장 · 에이전트 루프 — 하네스의 심장
하네스의 핵심은 놀랍도록 단순하다. while 루프다.
1. 모델을 부른다 (현재 메시지들을 넘긴다)
2. 모델의 응답을 본다:
- 최종 답변인가? → 루프 종료
- 도구 호출인가? → 3으로
3. 도구를 실행한다
4. 도구 결과를 메시지에 추가한다
5. 1로 돌아간다
이게 ReAct 패턴의 본질이다 — think → act → observe를 반복한다. 모델이 "생각"(추론)하고, "행동"(도구 호출)하고, 하네스가 "관찰"(결과 주입)을 먹여준다.
종료 조건 — 루프는 반드시 멈춰야 한다
while에서 가장 중요한 건 탈출이다. 종료 조건이 약하면 에이전트는 영원히 돈다.
| 종료 조건 | 의미 |
|---|---|
| 최종 답변 | 모델이 도구 없이 텍스트만 반환 |
| 스텝 상한 | N번 반복하면 강제 종료 (예: 40) |
| 토큰 예산 | 누적 토큰이 한도를 넘으면 종료 |
| 명시적 신호 | 모델이 done 같은 종료 도구를 호출 |
| 에러 | 복구 불가능한 실패 |
| 사람 개입 | 사용자가 중단 |
스텝 상한과 토큰 예산은 선택이 아니다. 이 두 가지가 없는 하네스는 비용 폭탄이다.
3장 · 도구 실행 계층
모델은 "행동"하지 못한다. 모델이 하는 건 "이 도구를 이 인자로 호출하고 싶다"는 구조화된 출력을 내는 것뿐이다. 실제 실행은 하네스가 한다.
도구 호출의 생애주기
1. 하네스가 모델에게 "도구 목록"을 알려준다 (이름·설명·인자 스키마)
2. 모델이 도구 호출 블록을 출력한다 (도구 이름 + 인자)
3. 하네스가 그 블록을 파싱한다
4. 하네스가 인자를 검증한다 (스키마 위반 → 에러를 모델에 반환)
5. 하네스가 도구를 실행한다 (종종 샌드박스 안에서)
6. 하네스가 결과(stdout·stderr·반환값·에러)를 캡처한다
7. 하네스가 결과를 "도구 결과" 메시지로 만들어 대화에 추가한다
8. 루프로 돌아가 모델을 다시 부른다
핵심: 모델은 도구의 존재를 "설명"으로만 안다. 도구 설명이 부실하면 모델은 도구를 잘못 쓴다. 도구 정의는 사실상 프롬프트의 일부다.
도구 실행에서 하네스가 책임지는 것
- 검증 — 모델이 보낸 인자가 스키마에 맞는가. 안 맞으면 실행하지 말고 에러를 돌려준다 (모델이 자가 수정하게).
- 샌드박싱 — 도구 실행은 격리된 환경에서. 호스트·프로덕션과 분리.
- 에러 캡처 — 도구가 죽어도 하네스는 안 죽는다. 에러를 잡아 모델에게 "관찰"로 먹인다.
- 타임아웃 — 모든 도구 호출에 타임아웃. 멈춘 도구가 루프를 멈추게 두지 마라.
- 결과 포맷팅 — 거대한 출력(로그 10만 줄)을 그대로 넣으면 컨텍스트가 터진다. 잘라내거나 요약한다.
MCP — 도구 계층의 표준
2026년에는 도구를 하네스마다 새로 구현하지 않는다. MCP(Model Context Protocol) 서버가 "도구 묶음"을 표준 인터페이스로 노출하고, 어떤 하네스든 그것을 연결한다. 하네스의 도구 계층은 점점 "MCP 클라이언트"가 되고 있다.
4장 · 컨텍스트 관리 — 가장 어려운 부분
모델의 컨텍스트 윈도우는 유한하다. 그리고 에이전트 루프는 매 스텝마다 메시지를 쌓는다 — 도구 호출, 도구 결과, 추론. 100스텝짜리 작업은 컨텍스트를 압도한다.
무엇을 넣고 무엇을 뺄지 결정하는 것이 하네스의 가장 어려운 책임이다.
Context Rot — 컨텍스트는 썩는다
컨텍스트가 길어질수록 모델 성능은 그냥 "유지"되지 않는다 — 저하된다. 오래된 도구 결과, 폐기된 계획, 무관한 탐색 흔적이 쌓이면 모델은 신호와 잡음을 구분하기 어려워진다. 이걸 **context rot(컨텍스트 부패)**이라 부른다.
윈도우에 "들어간다"고 다 같은 게 아니다. 앞쪽·뒤쪽이 중간보다 잘 쓰인다. 그래서 하네스는 단순히 "다 넣기"가 아니라 무엇을, 어디에 둘지 설계해야 한다.
컨텍스트를 다루는 기법
| 기법 | 설명 |
|---|---|
| Pruning | 오래된·무관한 메시지를 잘라낸다 |
| Compaction | 오래된 대화를 요약본으로 압축한다 |
| Retrieval | 필요할 때만 외부에서 관련 조각을 가져온다 (RAG) |
| Sub-agent 격리 | 하위 작업을 별도 컨텍스트에서 돌려 본문을 오염시키지 않는다 |
| 구조화 | 도구 결과를 원문이 아니라 구조화·요약된 형태로 |
| 외부 메모리 | 파일·DB에 상태를 두고, 컨텍스트엔 포인터만 |
핵심 통찰: 컨텍스트 윈도우는 RAM이 아니라 작업대다. 무한히 쌓는 곳이 아니라, 지금 작업에 필요한 것만 올려두는 곳이다. 하네스의 일은 이 작업대를 깨끗하게 유지하는 것이다.
5장 · System Prompt — 하네스의 헌법
System Prompt는 하네스가 모델에게 주는 헌법이다. 매 루프마다 모델이 보는, 변하지 않는 토대.
여기에 들어가는 것:
- 정체성 — "너는 무엇이고, 무엇을 위해 존재하는가."
- 규칙·제약 — 절대 하지 말 것, 항상 할 것.
- 도구 사용 지침 — 도구를 언제·어떻게 쓸지.
- 출력 형식 — 응답의 구조.
- 안전 경계 — 거부해야 할 요청, 사람을 불러야 할 상황.
여기에 컨텍스트 파일(CLAUDE.md, AGENTS.md 등)이 더해진다 — 프로젝트별 규칙·아키텍처·관례. System Prompt가 "하네스의 헌법"이라면 컨텍스트 파일은 "이 프로젝트의 지역법"이다.
핵심: 같은 모델 + 같은 도구라도, System Prompt가 다르면 완전히 다른 에이전트다. System Prompt는 하네스가 모델의 "성격"을 빚는 곳이다.
6장 · 제어 흐름 — 루프 위의 오케스트레이션
기본 루프(2장) 위에, 성숙한 하네스는 제어 흐름을 얹는다.
Sub-agent — 컨텍스트 격리
하위 작업을 새로운 컨텍스트에서 돌리는 별도 에이전트. "이 디렉터리 구조를 조사해줘" 같은 탐색 작업을 서브에이전트에 맡기면, 수십 개의 파일 읽기가 본문 컨텍스트를 오염시키지 않는다. 서브에이전트는 결과 요약만 돌려준다. context rot 방어의 핵심 도구.
Hook — 결정론적 끼어들기
에이전트 루프의 특정 지점(도구 실행 전, 응답 후 등)에서 도는 결정론적 코드. 모델의 판단이 아니라 고정된 규칙. 예: "어떤 도구든 실행 전에 린터를 돌린다", "커밋 전에 테스트를 강제한다."
권한 게이트 — 행동하기 전에 멈춤
고위험 도구(파일 삭제, 배포, 외부 전송)는 실행 전에 정책 검사 또는 사람 승인을 거친다. 하네스는 "읽기 도구는 자동, 쓰기 도구는 게이트" 같은 정책을 강제한다.
병렬화
서로 의존성이 없는 도구 호출은 동시에. 독립적인 파일 읽기 3개를 직렬로 할 이유가 없다. 단, 상태를 건드리는 호출은 직렬로.
Human-in-the-loop
되돌릴 수 없는 결정 앞에서 하네스는 멈추고 사람에게 묻는다. 자율성과 안전의 다이얼은 하네스가 쥐고 있다.
7장 · 실패 모드와 복원
성숙한 하네스와 데모 하네스의 차이는 실패를 어떻게 다루는가에서 갈린다.
| 실패 모드 | 증상 | 하네스의 방어 |
|---|---|---|
| 무한 루프 | 같은 행동을 반복, 진전 없음 | 스텝 상한, 루프 감지(반복 행동 탐지) |
| 도구 에러 | 도구가 죽음, 400 응답 | 에러를 잡아 모델에 "관찰"로 전달, 재시도 상한 |
| 환각된 도구 | 존재하지 않는 도구 이름 호출 | 레지스트리와 대조, 모델에 "그런 도구 없음" 반환 |
| Context rot | 길어진 컨텍스트에서 품질 저하 | 압축, 서브에이전트 격리, pruning |
| 비용 폭주 | 토큰이 조용히 쌓임 | 토큰 예산, 자가 수정 횟수 상한 |
| 잘못된 자신감 | 틀린 답을 확신에 차서 | 검증 도구(테스트·타입체크)를 루프에 |
| 스코프 이탈 | 시킨 것 이상을 함 | System Prompt 제약, 권한 게이트 |
핵심 원칙: 에이전트는 실패한다 — 하네스가 그 실패를 흡수하고, 복구 가능하면 복구하고, 아니면 깨끗하게 멈춰야 한다. 실패를 막는 게 아니라 실패를 다루는 게 하네스의 일이다.
8장 · 실제 하네스들 — 무엇이 다른가
같은 모델 API를 호출하는데, 경험이 다르다. 하네스가 다르기 때문이다.
| 하네스 | 표면 | 특징 |
|---|---|---|
| Claude Code | CLI | 서브에이전트, 훅, 스킬, MCP, 권한 모드, CLAUDE.md |
| Cursor | IDE | 에디터가 곧 하네스 표면, 백그라운드 에이전트, diff 중심 |
| Codex CLI | CLI | 샌드박스 실행, AGENTS.md |
| Aider | CLI | git 네이티브, repo map, 커밋 중심 |
| OpenClaw | 메시징 앱 | 로컬 게이트웨이, Skills/ClawHub, 멀티채널 |
| Claude Agent SDK | 라이브러리 | 하네스 자체를 만드는 도구 |
이들의 차이는 거의 다 하네스 설계 결정이다:
- 표면(CLI vs IDE vs 메시징)
- 컨텍스트 파일 관례(
CLAUDE.mdvsAGENTS.md) - 도구 실행 모델(샌드박스의 강도)
- 제어 흐름(서브에이전트·훅 지원 여부)
- 권한 모델(얼마나 자율적인가)
Claude Agent SDK가 특히 흥미롭다 — 그건 완성된 하네스가 아니라 하네스를 만드는 라이브러리다. 루프·도구·컨텍스트 관리를 제공하고, 당신은 그 위에 자기 하네스를 짓는다.
9장 · 직접 만들기 — 40줄짜리 최소 하네스
하네스의 본질은 단순하다. 최소 하네스를 직접 만들어 보면 명확해진다.
import anthropic
client = anthropic.Anthropic()
# 도구 정의 — 모델은 이 "설명"으로만 도구를 안다
TOOLS = [{
"name": "run_bash",
"description": "Run a bash command and return its output.",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
}]
def execute_tool(name, args):
# 실제 실행 — 현실에서는 샌드박스 안에서
import subprocess
if name == "run_bash":
r = subprocess.run(args["command"], shell=True,
capture_output=True, text=True, timeout=30)
return (r.stdout + r.stderr)[:4000] # 결과를 잘라 컨텍스트 보호
return f"Unknown tool: {name}" # 환각된 도구 방어
def agent_loop(task, max_steps=20): # 스텝 상한 = 종료 보장
messages = [{"role": "user", "content": task}]
for step in range(max_steps):
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
system="You are a coding agent. Use tools to complete the task.",
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
# 도구 호출이 없으면 → 최종 답변 → 루프 종료
tool_uses = [b for b in resp.content if b.type == "tool_use"]
if not tool_uses:
return resp.content
# 도구를 실행하고 결과를 메시지에 주입
results = []
for tu in tool_uses:
output = execute_tool(tu.name, tu.input)
results.append({
"type": "tool_result",
"tool_use_id": tu.id,
"content": output,
})
messages.append({"role": "user", "content": results})
return "Step limit reached" # fail-closed
이게 전부다. while 루프 + 도구 실행 + 메시지 주입. Claude Code도, Cursor도, 이 뼈대 위에 — 컨텍스트 관리, 서브에이전트, 훅, 권한, 더 나은 도구, 더 나은 System Prompt를 — 얹은 것이다.
위 40줄에서 빠진 것이 곧 "프로덕션 하네스"의 일이다: 컨텍스트 압축(4장), 권한 게이트(6장), 실패 복원(7장), 스트리밍 UX, 관측성, 비용 추적.
10장 · 하네스를 평가하기 — 모델 eval과 다르다
"이 모델 좋아?"는 모델 eval이다. "이 하네스 좋아?"는 다른 질문이다.
하네스 eval의 핵심: 모델을 고정하고, 하네스만 바꿔서 측정한다.
모델 eval: 하네스 고정, 모델 A vs 모델 B
하네스 eval: 모델 고정, 하네스 A vs 하네스 B
측정할 지표:
- 작업 완료율 — 같은 작업 묶음을 하네스 A/B로 돌렸을 때 성공 비율.
- 스텝 수 — 완료까지 몇 번의 루프? 적을수록 효율적.
- 작업당 비용 — 토큰·시간. 하네스가 컨텍스트를 잘 관리하면 싸진다.
- 에러 복구율 — 도구가 실패했을 때 하네스가 복구시키는 비율.
- 이탈률 — 스코프를 벗어나거나 무한 루프에 빠지는 비율.
같은 프론티어 모델이라도 하네스가 나쁘면 약한 모델 + 좋은 하네스보다 실무 작업에서 진다. 그래서 하네스 eval은 모델 eval만큼 중요하다 — 그런데 대부분 측정하지 않는다.
11장 · 하네스가 모델만큼 중요한 이유
정리하자. 2026년에 "AI 엔지니어링"이라 부르는 일의 대부분은 하네스 엔지니어링이다.
- 모델 제공자는 소수로 수렴했다 — 모델은 사실상 **상품(commodity)**에 가까워졌다.
- 차별화는 그 모델을 어떻게 둘러싸느냐에서 나온다 — 루프, 도구, 컨텍스트, 제어 흐름.
- 같은 모델, 다른 하네스 → 한쪽은 PR을 열고 한쪽은 헛돈다.
- 프론티어 모델 + 나쁜 하네스 < 약한 모델 + 좋은 하네스 (실무 작업 기준).
이건 1장의 비유로 돌아간다. 엔진(모델)은 점점 비슷해진다. 경주는 차(하네스)에서 갈린다.
그리고 좋은 소식: 하네스는 엔지니어링 가능하다. 모델은 당신이 못 바꾼다. 하지만 루프, 종료 조건, 도구 설명, 컨텍스트 관리, 권한 게이트, 실패 복원 — 이건 전부 당신이 설계하는 코드다. AI 엔지니어가 실제로 통제할 수 있는 표면이 바로 하네스다.
에필로그 — 하네스를 의식하라
이 글의 한 문장 요약: 모델을 고를 때만큼 하네스를 의식하라.
도구를 쓸 때는 "이 하네스가 루프를 어떻게 도는가, 컨텍스트를 어떻게 관리하는가, 실패를 어떻게 다루는가"를 본다. 하네스를 만들 때는 — 40줄 뼈대 위에 — 컨텍스트 관리·권한·복원을 얹는다. 하네스를 평가할 때는 모델을 고정하고 하네스만 잰다.
다음 10년의 AI 엔지니어링은 더 큰 모델을 기다리는 게 아니다. 더 나은 하네스를 짓는 것이다.
12개 항목 체크리스트
- 하네스의 에이전트 루프에 종료 조건이 명확한가 (스텝 상한·토큰 예산)?
- 도구 인자를 실행 전에 검증하는가?
- 도구 실행이 샌드박스 안에서 도는가?
- 모든 도구 호출에 타임아웃이 있는가?
- 거대한 도구 출력을 잘라내거나 요약하는가?
- 컨텍스트 압축·pruning 전략이 있는가?
- 탐색성 하위 작업을 서브에이전트로 격리하는가?
- 고위험 도구에 권한 게이트가 있는가?
- 무한 루프 감지(반복 행동 탐지)가 있는가?
- 환각된 도구 이름을 레지스트리로 거르는가?
- 자가 수정 루프에 횟수 상한이 있는가?
- 하네스를 (모델 고정하고) 따로 평가하는가?
안티패턴 10가지
- 종료 조건 없는 루프 — 비용 폭탄.
- 도구 인자를 검증 없이 실행.
- 도구 실행을 호스트에서 직접 (샌드박스 없음).
- 거대한 출력을 컨텍스트에 그대로 주입.
- "다 넣기" 컨텍스트 전략 — context rot.
- 탐색 작업을 본문 컨텍스트에서 — 오염.
- 고위험 도구에 권한 게이트 없음.
- 도구 에러에 하네스가 같이 죽음.
- 모델만 평가하고 하네스는 평가 안 함.
- 하네스를 "모델 호출 래퍼"로만 취급 — 거기서 멈춤.
다음 글 예고
다음 글 후보: 서브에이전트 오케스트레이션 — 멀티 에이전트 하네스 설계, 컨텍스트 압축 심층 — 무엇을 버리고 무엇을 남길까, 하네스 eval 스위트 구축 — 모델을 고정하고 하네스를 측정하기.
"모델은 엔진이다. 하지만 경주는 차에서 갈린다. 2026년 AI 엔지니어링의 무게중심은 모델이 아니라 하네스다."
— AI 하네스 해부, 끝.