Ssul's Blog

Harness Engineering 이해하기(feat Claude) 본문

AI & ML/학습하기

Harness Engineering 이해하기(feat Claude)

Ssul 2026. 3. 16. 01:30

처음 LLM이 등장했을때, Prompt엔지니어링이 중요하다고 했었다. 그러다 Context...그러다 이제는 Harness다!!

Prompt Engineering > Context Engineering > Harness Engineering

 

Harness는 사용자 입력을 받아 context를 조립하고, 모델의 도구 호출을 가로채서 실행하고, 결과를 다시 context에 넣어 루프를 돌리는 오케스트레이터다. 쉽게 말해, 모델이 "두뇌"라면 Harness는 "신경계 + 근골격계"에 해당한다.

그런데 이 Harness가 실제로 어떻게 생겼는지 — context window 안에 뭐가 들어있고, 어떤 순서로 조립되는지 — 제대로 설명한 글은 거의 없다.

이 글에서는 Claude Code(claude.ai의 코드 실행 환경)가 매 API 호출마다 조립하는 context window의 실제 구조를 완전히 해부한다. 직접 Claude에게 "네가 지금 받고 있는 context를 보여줘"라고 요청해서 분석한 결과다.


 

1. 전체 구조: 6개의 덩어리

Claude Code의 context window는 크게 6개의 개념 덩어리로 구성된다.

① System prompt — Anthropic이 주입하는 불변 지시문. 톤, 안전 규칙, 검색 규칙, 저작권 준수 등 Claude의 "인격"과 "행동 원칙"을 정의한다. 사용자가 수정할 수 없다.

② User context — 사용자 상태 정보. 과거 대화에서 추출된 userMemories, 사용자 위치, 연결된 서비스 목록 등. "이 사용자가 누구인지"를 모델에게 알려주는 영역이다.

③ Tool registry — 모델이 사용할 수 있는 모든 도구의 정보. 이것이 가장 복잡한 영역인데, 3단계 깊이로 구성된다(후술).

④ Skill catalog — 특정 작업(Word 문서 생성, PDF 처리 등)의 베스트 프랙티스 문서 시스템. 역시 2단계 구조다.

⑤ Environment config — 네트워크 허용 도메인, 파일시스템 마운트 규칙, Artifact 생성 규칙 등 실행 환경의 제약 조건들.

⑥ Conversation state — 대화 메시지와 도구 실행 결과의 누적 이력. 유일하게 턴마다 성장하는 영역이다.

핵심 공식은 이렇다:

Context window = ①~⑤ 고정 비용 + ⑥ 가변 비용

대화가 길어지면 ⑥이 커지고, 모델의 "작업 기억"이 압박받는다. 이것이 /compact가 존재하는 이유다.


2. System prompt — 11개 블록의 거대한 지시문

System prompt 안에는 놀라울 정도로 많은 블록이 들어있다.

product_information — 모델명(Claude Opus 4.6 등), API 모델 스트링, 접근 채널, 프롬프팅 가이드 URL, 광고 정책까지 포함.

claude_behavior — 가장 큰 블록. 내부에 7개 하위 섹션이 있다. 거부 규칙(refusal_handling), 법률/재무 면책(legal_and_financial_advice), 톤과 포매팅(tone_and_formatting — "리스트 최소화, 이모지 제한, 따뜻한 톤"), 사용자 웰빙(user_wellbeing — 자해 방지, 정신건강 위기 대응), 정치적 중립(evenhandedness), 지식 컷오프(knowledge_cutoff), 실수 대응(responding_to_mistakes) 등.

memory_system — 메모리 동작 원리, 적용 규칙, 금지 표현("Based on my memories" 등), Good/Bad 예시까지 상세하게 정의.

computer_use — 파일 처리 규칙, Artifact 생성 기준, 패키지 관리법. 그리고 여기에 Skill 시스템의 개요"SKILL.md를 먼저 읽어라"는 지시가 포함된다.

search_instructions — 언제 웹 검색을 할지/말지, 쿼리 작성법, 인용 규칙, 15단어 인용 제한 등.

anthropic_api_in_artifacts — Artifact 내에서 Claude API를 호출하는 방법. 그리고 중요한 것: 현재 사용자가 연결한 MCP 서버의 URL 목록이 여기 명시된다.

이 외에도 citation_instructions, end_conversation_tool_info, persistent_storage_for_artifacts 등이 포함된다.

이 모든 것이 매 턴 고정 비용으로 context window를 차지한다.


 

3. Tool registry — 3계층 깊이의 도구 등록 시스템

3-A. Direct tools — 전체 Schema 항상 주입

bash_tool, view, create_file, str_replace, web_search, web_fetch, google_drive_search, places_search, weather_fetch 등 — 이 도구들의 전체 JSON Schema(파라미터 이름, 타입, 설명, enum 값, 제약 조건)가 매 턴 context에 포함된다.

모델 입장에서는 즉시 호출 가능하다. 하지만 토큰 비용이 높다. 도구가 20개면 20개의 Schema가 매번 들어간다.

3-B. Deferred tools — 카탈로그만 주입, Schema는 나중에

Gmail 같은 도구는 다르게 처리된다. tool_search라는 메타 도구의 description 안에 카탈로그가 내장되어 있다:

 
 
Available deferred tools (7):
  Gmail:gmail_create_draft — Creates a new email draft...
  Gmail:gmail_search_messages — Searches Gmail messages...

이름과 한 줄 설명만 들어있다. 전체 Schema는 tool_search를 호출해야 반환된다.

왜 이렇게 하나? Gmail 도구 7개의 전체 Schema를 매 턴 주입하면 토큰 낭비다. 카탈로그만 넣고, 사용자가 "이메일 확인해줘"라고 하면 그때 Schema를 로드한다.

3-C. MCP registry — 검색으로만 발견

아직 연결되지 않은 MCP 서버(Asana, Jira, Slack 등)는 search_mcp_registry + suggest_connectors 도구로만 접근 가능하다. 어떤 서버가 사용 가능한지조차 검색해봐야 알 수 있다.

npm registry에서 패키지를 검색하는 것과 같은 레벨이다. 연결 후에는 3-A(Direct) 또는 3-B(Deferred)로 승격된다.


4. Skill catalog — Lazy loading의 교과서적 구현

 

Skill 시스템이 context에 주입되는 방식은 도구 registry의 Deferred 패턴과 정확히 동일하다.

Stage 1: 카탈로그 — 항상 context에 존재

<available_skills> 블록 안에 각 skill의 name, description, location 3가지가 매 턴 주입된다:

<skill>
  <name>docx</name>
  <description>
    Use this skill whenever the user wants to 
    create, read, edit Word documents (.docx)...
    Triggers include: 'Word doc', '.docx'...
    Do NOT use for PDFs, spreadsheets...
  </description>
  <location>/mnt/skills/public/docx/SKILL.md</location>
</skill>

여기서 description이 핵심이다. "Use this skill whenever..."로 시작하는 이 텍스트가 사실상 트리거 조건문 역할을 한다. 모델은 사용자 입력을 이 description들과 매 턴 매칭하여 어떤 skill을 읽어야 할지 판단한다.

키워드 매칭뿐 아니라 네거티브 조건("Do NOT use for PDFs")까지 명시되어 있어서, 모델이 잘못된 skill을 로드하는 것을 방지한다.

Stage 2: SKILL.md 전문 — on-demand 로드

매칭된 skill의 SKILL.md 파일 전체를 view 도구로 읽는다. 이 결과는 tool_result로 대화 이력에 쌓인다.

SKILL.md 안에는 필요한 패키지, 단계별 작업 절차, 코드 템플릿, 품질 체크리스트, 에러 핸들링 패턴, 안티패턴 경고 등이 포함되어 있다.

강조 장치: additional_skills_reminder

카탈로그와 별도로, "매 요청 시 SKILL.md를 먼저 view로 읽어라"는 강조 지시문이 system prompt에 한 번 더 삽입된다. 카탈로그를 읽고도 SKILL.md를 로드하지 않는 것을 방지하기 위한 안전장치다.

 

5. 모든 것을 관통하는 하나의 설계 원칙

여기까지 읽으면 패턴이 보인다. Skill, Deferred tool, MCP registry — 전부 같은 구조다:

멤버항상 주입 (카탈로그)On-demand (본체)로드 수단
Skill available_skills description SKILL.md 전문 view tool
Deferred tool tool_search desc 카탈로그 전체 JSON Schema tool_search
MCP registry 도구 존재 자체 서버 목록 → 연결 → Schema search_mcp_registry

설계 원칙은 하나다:

"자주 쓰는 것은 작게 항상 넣고, 드물게 쓰는 것은 크게 나중에 로드한다."

프로그래밍의 lazy import와 동일한 패턴이다. 7개 skill의 SKILL.md를 매번 전부 주입하면 수만 토큰 낭비다. Gmail 7개 도구의 Schema를 매번 넣을 필요도 없다. 카탈로그(짧은 description)만 항상 넣고, 실제 내용은 필요할 때만 로드한다.

그리고 on-demand로 로드된 모든 결과물 — SKILL.md, tool_search Schema, bash 실행 로그 — 은 결국 Conversation state 영역에 tool_result로 쌓인다. 대화가 길어지면 초기에 로드한 SKILL.md가 context에서 밀려나는 것도 이 때문이다.


6. Agent loop — 한 턴의 전체 실행 흐름

이 모든 구조가 실제로 어떻게 동작하는지, 한 턴의 흐름을 따라가 보자.

① 사용자 입력 수신 — "Word 문서 만들어줘"

② Context 조립 — Harness가 System prompt + userMemories + CLAUDE.md + MEMORY.md + Tool definitions + Skill catalog + 대화 이력을 하나의 context window로 조립.

③ Skill 매칭 — available_skills의 description을 스캔. "Word document", ".docx" 키워드가 docx skill과 매칭됨.

④ SKILL.md 로드 — view /mnt/skills/public/docx/SKILL.md 호출. tool_result로 context에 추가.

⑤ 작업 실행 — SKILL.md의 지침에 따라 bash(패키지 설치), create_file(Python 스크립트), bash(스크립트 실행) 등 도구 연쇄 호출. 각 호출마다 Permission 검사.

⑥ 결과 반환 — present_files로 완성된 .docx 파일을 사용자에게 제공.

이 전체 과정에서 모델은 여러 번의 도구 호출 → 결과 수신 → 재추론을 반복한다. 이것이 Agentic loop이다. 모델이 "더 할 일이 없다"고 판단할 때까지 루프가 돌아간다.


7. Claude Code vs Claude Code (CLI) — 같은 원칙, 다른 구현

이 글에서 분석한 것은 claude.ai 웹/앱 환경의 구조다. CLI 버전인 Claude Code는 같은 원칙을 따르지만 구현 디테일이 다르다:

구성 요소claude.ai (웹/앱)Claude Code (CLI)
사용자 기억 userMemories (자동 추출) MEMORY.md (파일 직접 편집)
프로젝트 지시 System prompt 고정 CLAUDE.md (계층적 병합)
도구 권한 Anthropic 관리 settings.json (allowedTools/deniedTools)
라이프사이클 Hook 없음 PreToolUse, PostToolUse, Notification
Slash commands 없음 /init, /compact, /memory, /config 등
MCP 설정 Anthropic UI에서 연결 settings.json의 mcpServers 블록

CLI 버전에서 추가되는 핵심 개념들:

Commands — /init, /compact, /memory 같은 slash command는 모델이 아닌 Harness가 직접 처리하는 명령어다. LLM 추론 없이 즉시 실행된다.

Hooks — PreToolUse(도구 실행 직전), PostToolUse(도구 실행 직후), Notification(이벤트 알림) 시점에 사용자 스크립트를 끼워넣는 메커니즘. 모델의 비결정론적 행동에 결정론적 가드레일을 씌울 수 있다. 예를 들어 rm -rf를 절대 차단하는 것을 코드로 보장할 수 있다.

Permission system — 모든 도구 호출은 실행 전 4단계 권한 검사를 통과해야 한다: Hook 검사 → 설정 검사 → 모드 검사 → 사용자 확인.


8. 핵심 정리

Context window는 "유한한 자원"이다

매 API 호출마다 고정 비용(System prompt + 도구 Schema + Skill 카탈로그 등)이 수천 토큰을 차지한다. 여기에 대화 이력이 턴마다 누적된다. 이 제약 안에서 최대한 많은 정보를 효율적으로 전달하는 것이 Harness 설계의 핵심이다.

Lazy loading이 모든 곳에 적용된다

Skill, Deferred tool, MCP registry, CLAUDE.md 하위 디렉토리 — 전부 "카탈로그만 항상 넣고, 본체는 필요 시 로드"하는 같은 패턴이다. 프로그래밍의 lazy import와 동일한 원리가 AI 에이전트 설계에도 적용된다.

Harness가 진짜 엔지니어링이다

모델 선택이나 프롬프트 작성보다, 이 Harness의 구조 — context 조립, 도구 등록, 권한 관리, 라이프사이클 Hook — 를 이해하고 설계하는 것이 AI 에이전트 개발의 본질이다. 이것이 Context Engineering이다.