하네스 엔지니어링으로 Claude Code 재현하기: AI 에이전트의 숨겨진 아키텍처
프롬프트가 아니라 하네스의 차이
2026년 초 현재, Claude Code는 출시 6개월 만에 연간 매출 10억 달러를 돌파했습니다. 놀라운 건 이 성과가 더 나은 프롬프트 때문이 아니라는 점입니다. Anthropic이 올바른 모델을 중심으로 적절한 **하네스(harness)**를 구축했기 때문이죠.
최근 Claude Code의 실행 추적을 역공학한 결과, 그 핵심은 스트리밍 에이전트 루프, 권한 기반 도구 디스패치 시스템, 그리고 임의로 긴 세션 동안 모델이 집중된 상태를 유지하도록 하는 컨텍스트 관리 레이어였습니다. 그리고 이 하네스는 완전히 재현 가능합니다.

하네스 엔지니어링의 4가지 핵심 원칙
하네스 엔지니어링은 AI 모델 자체가 아니라 모델을 둘러싼 환경을 구축하는 학문입니다. 모델은 추론하고 결정하며, 하네스는 실행하고 제약하며 연결합니다.
핵심 원칙은 다음과 같습니다:
- 모델은 의사결정의 유일한 원천 - 하네스는 모델 출력에 절대 분기하지 않으며, 모델이 요청하는 것만 실행합니다.
- 도구는 모델과 세계 사이의 유일한 인터페이스 - 모든 작업은 타입이 지정되고 스키마 검증된 도구 호출을 통해 수행됩니다.
- 컨텍스트는 관리되는 자원 - 모델이 매 턴마다 보는 것은 의도적으로 선별되고 압축되며 주입됩니다.
- 권한은 선언적 - 허용되는 것, 차단되는 것, 승인이 필요한 것은 구성에 정의되어 있습니다.
1단계: 핵심 에이전트 루프
모든 것의 기반은 단순한 perception-action-observation 사이클입니다. 에이전트는 작업을 받고, 도구를 사용하여 해결책을 시도하며, 결과를 관찰한 후 계속할지 중단할지를 모델이 결정합니다.
def agent_loop(messages: List[Dict], dispatch: Dict):
while True:
response = client.messages.create(
model=MODEL,
system=DEFAULT_SYSTEM,
messages=messages,
tools=BASIC_TOOLS,
max_tokens=8000
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = dispatch_tools(response.content, dispatch)
messages.append({"role": "user", "content": results})
이 루프는 한 줄 수정이든 전체 코드베이스 리팩터링이든 동일하게 실행됩니다. 모든 작업별 지능은 모델에 존재하기 때문입니다.
도구 디스패치 맵 패턴
Claude Code의 우아함은 도구의 수가 아니라, 새로운 도구를 추가하는 데 핵심 루프에 전혀 변경이 필요 없다는 점입니다. 디스패치 맵이 이를 가능하게 합니다:
DISPATCH = {
"bash": lambda inp: run_bash(inp["command"]),
"read": lambda inp: run_read(inp["path"]),
"write": lambda inp: run_write(inp["path"], inp["content"]),
"grep": lambda inp: run_grep(inp["pattern"])
}
루프는 어떤 도구가 존재하는지 완전히 무관합니다. 오직 dispatch[tool_name](input)를 호출하는 방법만 알고 있죠.
2단계: 지식 및 컨텍스트 관리
Claude Code의 92% 프롬프트 접두사 재사용률은 우연이 아닙니다. 시스템이 필요할 때만 도메인 지식을 로드하도록 설계되었기 때문입니다.
온디맨드 스킬 로딩
시스템 프롬프트에는 사용 가능한 스킬에 대한 한 줄 설명만 포함됩니다. 모델이 전문 지식이 필요함을 인식하면 load_skill()을 호출하고, 전체 명령이 정확한 순간에 대화에 직접 삽입됩니다.
code-review:
설명: 코드를 검토하거나 파일을 버그에 대해 감사할 때 사용
pdf:
설명: PDF 문서를 처리하고 추출할 때 사용
agent-builder:
설명: 새로운 에이전트나 하네스 컴포넌트를 설계할 때 사용
백 개의 스킬을 설치해도 시스템 프롬프트는 백 줄이 되며, 백 페이지가 되지 않습니다.
3계층 컨텍스트 압축
모든 장기 세션은 같은 벽에 부딪힙니다. 컨텍스트 창이 도구 출력과 중간 결과로 가득 차는 것이죠. Claude Code는 약 92% 컨텍스트 창 사용량에서 자동으로 압축을 트리거합니다:
- 최근 메시지 - 능동적인 추론 컨텍스트를 포함하므로 그대로 유지
- 이전 메시지 - 전용 압축 API 호출을 통해 단일 요약 블록으로 압축
- 요약 -
.agent_memory.md에 작성되어 다음 세션에서 로드
3단계: 다중 에이전트 조정
단일 에이전트의 한계를 넘어서면 병렬 실행과 전문화가 핵심이 됩니다.
지속적인 팀원과 FSM 프로토콜
Claude Code는 일시적인 서브에이전트 외에도 여러 작업에 걸쳐 지속되는 전문가들을 운영합니다. 각 팀원은 정의된 전문화와 JSONL 파일 받은편지함으로 백그라운드 스레드에서 지속적으로 실행됩니다.
통신은 유한 상태 머신(FSM)으로 조정됩니다:
- IDLE: 새 작업을 받을 수 있음
- REQUESTING: 다른 에이전트에게 요청 중
- WAITING: 응답 대기 중
- RESPONDING: 작업 처리 중
핵심 규칙: 대기 상태에서는 새로운 요청을 할 수 없습니다. 이 단일 규칙이 전체 교착 상태를 완전히 제거합니다.
Git Worktree 작업 격리
병렬 에이전트가 같은 파일에 쓰면 충돌이 발생합니다. Git worktree가 해답입니다. 각 에이전트는 저장소에 대한 자체 디렉터리, 자체 브랜치, 완전한 작업 트리를 갖습니다.
def create_worktree(task_id: str) -> tuple[str, str]:
branch = f"task/{task_id}"
path = f".worktree-{task_id[:8]}"
subprocess.run(["git", "worktree", "add", "-b", branch, path])
return path, branch
두 에이전트가 "core.py"를 수정해도 실제로는 서로 다른 디렉터리의 서로 다른 파일에 쓰고 있습니다. 파일 자체가 별개이므로 쓰기 충돌이 원천적으로 불가능합니다.
4단계: 프로덕션 강화
작동하는 에이전트와 배포 가능한 에이전트 사이의 격차를 메우는 단계입니다.
실시간 토큰 스트리밍
Claude Code에서 스트리밍은 기능이 아니라 기본값입니다. 각 토큰이 생성되는 대로 터미널로 전송됩니다. 수십 개의 도구 호출에 걸친 긴 추론 체인에서 차단 에이전트는 몇 분 동안 침묵하지만, 스트리밍 에이전트는 실시간으로 생각을 보여줍니다.
YAML 규칙 기반 권한 거버넌스
Claude Code의 권한 시스템은 3계층 모델을 사용합니다:
always_deny:
- pattern: "rm -rf /"
reason: "무조건 재귀 루트 삭제"
always_allow:
- pattern: "^ls( |$|-)"
reason: "파일을 나열하는 것은 항상 안전"
ask_user:
- pattern: "^rm "
reason: "파일 삭제는 확인이 필요"
보안 정책은 코드가 아니라 구성에 있습니다. 승인이 필요한 변경은 배포가 아니라 구성 파일 편집으로 충분합니다.

5단계: 고성능 비동기 런타임
병렬 도구 실행
실행 추적 분석으로 밝혀진 Claude Code의 가장 중요한 성능 특성 중 하나는, 불필요한 경우 도구 호출을 순차적으로 실행하지 않는다는 점입니다.
한 턴에 세 번의 grep 호출과 두 번의 읽기를 포함한 응답이 오면, 다섯 개가 동시에 실행됩니다. 턴은 가장 느린 단일 호출 시간에 완료되고, 다섯 개의 합계가 아닙니다.
async def agent_loop(messages):
tool_blocks = [b for b in response.content if b.type == "tool_use"]
if len(tool_blocks) > 1:
print(f"Running {len(tool_blocks)} tools in parallel...")
# 모든 도구 호출을 한 번에
pairs = await asyncio.gather(*[_dispatch_one(b) for b in tool_blocks])
프롬프트 캐싱 최적화
시스템 프롬프트와 도구 정의는 모든 에이전트 세션에서 가장 안정적인 콘텐츠입니다. 이를 캐시 가능하도록 표시하면 첫 번째 호출 이후 모든 호출이 약 10% 비용으로 해당 토큰을 제공받습니다.
SYSTEM_BLOCKS = [
{
"type": "text",
"text": "You are a coding agent...",
"cache_control": {"type": "ephemeral"}
}
]
수백 번의 호출을 수행하는 전체 세션에서 이는 상당한 비용 절감으로 이어집니다.
6단계: 엔터프라이즈 확장
Redis Pub/Sub 메일박스
교육용 JSONL 메일박스를 즉시 전달과 크로스 머신 지원을 갖춘 Redis pub/sub 채널로 교체합니다. 에이전트가 채널에 게시하면 모든 구독자가 밀리초 이내에 수신합니다. 폴링 루프도, 파일 잠금도, 파일 시스템 의존성도 없습니다.
MCP 런타임 통합
Claude Code는 MCP를 기본적으로 지원합니다. 모든 호환 서버의 도구가 에이전트의 도구 레지스트리에서 일급 시민이 됩니다. 파일 시스템 서버는 파일 도구를, git 서버는 git 운영 도구를, 데이터베이스 서버는 쿼리 도구를 추가합니다.
모델은 모든 도구를 내장 도구와 동일하게 호출하며, 도구가 로컬 Python 함수인지 원격 서버 프로세스인지 인식하지 못합니다.
실제 Claude Code와의 차이점
이 재현 구현과 실제 Claude Code 사이에는 몇 가지 차이점이 있습니다:
| 구분 | 재현 구현 | 실제 Claude Code |
|---|---|---|
| 도구 수 | 23개 컴포넌트 | 18개 등록 도구 |
| 캐시 히트율 | 83% (테스트) | 92% (프로덕션) |
| 압축 트리거 | 40K 토큰 | 동적 임계값 |
| 세션 지속성 | JSON 파일 | 분산 스토어 |
하지만 핵심 아키텍처 원칙과 하네스 엔지니어링 접근법은 동일합니다.

마치며
Claude Code의 성공은 더 똑똑한 모델이나 더 나은 프롬프트에서 나온 게 아닙니다. 올바른 하네스 엔지니어링에서 나왔죠. 모델을 더 똑똑하게 만들려 하지 말고, 모델이 일관되고 신뢰할 수 있게 작동할 수 있는 구조를 만드는 것. 그게 진짜 차이를 만드는 것 같습니다.