Skip to content
Published on

AI 하네스란 무엇인가: Skills, Context, Hooks, Permissions로 AI를 제어하는 오케스트레이션 아키텍처

Authors

1. AI 하네스란? — 왜 LLM만으로는 부족한가

Raw LLM의 한계

GPT-4o, Claude Sonnet, Gemini Pro 같은 대형 언어 모델(LLM)은 놀라운 능력을 가지고 있습니다. 하지만 모델 자체만으로는 실용적인 AI 시스템을 만들 수 없습니다.

Raw LLM에게 "우리 프로젝트의 PR을 리뷰해줘"라고 하면 어떻게 될까요?

  • 프로젝트 코드를 읽을 수 없습니다 (도구 없음)
  • 어떤 코딩 컨벤션을 따르는지 모릅니다 (컨텍스트 없음)
  • git diff를 실행할 수 없습니다 (권한 없음)
  • 이전 리뷰 히스토리를 기억하지 못합니다 (기억 없음)
  • 리뷰 후 자동으로 코멘트를 달 수 없습니다 (워크플로우 없음)

LLM은 강력한 엔진이지만, 엔진만으로는 자동차가 되지 않습니다.

말에게 마구를 씌우는 것

"Harness(하네스)"는 원래 말에게 씌우는 마구(馬具)를 의미합니다. 야생마는 엄청난 힘을 가지고 있지만, 마구 없이는 그 힘을 원하는 방향으로 쓸 수 없습니다. 마구를 씌워야 마차를 끌고, 밭을 갈고, 짐을 나를 수 있습니다.

AI 하네스도 같은 원리입니다:

  • 야생마 = Raw LLM (GPT-4, Claude, Gemini)
  • 마구 = AI Harness (시스템 프롬프트, 도구, 권한, 스킬, 훅)
  • 마차/밭 = 실제 업무 (코드 리뷰, 데이터 분석, 고객 지원)

마구가 잘 설계되면 말의 힘을 최대한 활용할 수 있고, 잘못 설계되면 말이 엉뚱한 방향으로 달리거나 위험한 행동을 합니다.

Raw LLM vs Harnessed LLM 비교

항목Raw LLMHarnessed LLM
도구 접근없음파일 읽기/쓰기, API 호출, DB 쿼리
컨텍스트대화 내용만프로젝트 구조, 코드베이스, 문서
권한무제한 (또는 전혀 없음)세밀한 권한 제어
기억세션 내 대화만장기 기억, 프로젝트 히스토리
워크플로우없음스킬, 훅, 파이프라인
안전장치모델 자체 안전가드레일, 권한 모델, 입출력 검증
일관성프롬프트에 의존시스템 프롬프트로 안정적

2025년 AI 엔지니어링의 무게중심 이동

2023년까지 AI 엔지니어링의 핵심은 모델 훈련이었습니다. 더 크고, 더 똑똑한 모델을 만드는 것이 목표였습니다.

2025년, 무게중심이 완전히 이동했습니다: 모델 오케스트레이션이 핵심입니다.

왜냐하면:

  1. 기반 모델이 충분히 강력해졌습니다 — Claude Sonnet 4, GPT-4o는 대부분의 작업에 충분한 지능을 가지고 있습니다
  2. 차별화는 하네스에서 일어납니다 — 같은 모델을 사용해도 하네스 설계에 따라 결과가 크게 달라집니다
  3. 엔터프라이즈 요구사항 — 보안, 감사, 권한 관리, 비용 추적이 필수가 되었습니다
  4. 에이전트의 부상 — Devin, Claude Code, GitHub Copilot Agent 같은 자율 에이전트들이 모두 정교한 하네스를 사용합니다

AI 하네스는 2025-2026년 AI 엔지니어링의 가장 중요한 개념입니다.


2. 하네스의 7가지 구성 요소

AI 하네스는 7가지 핵심 구성 요소로 이루어져 있습니다. 각 요소를 깊이 있게 살펴보겠습니다.

2-1. System Prompt (기본 규칙)

시스템 프롬프트는 AI의 헌법입니다. AI가 어떤 역할을 맡고, 어떤 규칙을 따르며, 어떤 제약사항을 지켜야 하는지를 최상위 수준에서 정의합니다.

시스템 프롬프트의 구성 요소

  1. 역할 정의: AI가 무엇인지, 어떤 전문성을 가졌는지
  2. 행동 규칙: 어떤 방식으로 응답하고 행동해야 하는지
  3. 제약사항: 절대 해서는 안 되는 것
  4. 출력 형식: 응답의 형태와 구조
  5. 안전 규칙: 보안, 프라이버시, 저작권 관련 규칙

Claude Code의 시스템 프롬프트 구조

Claude Code는 매우 정교한 시스템 프롬프트를 사용합니다:

You are Claude Code, Anthropic's official CLI for Claude.
Given the user's message, you should use the tools available
to complete the task.

Your strengths:
- Searching for code across large codebases
- Analyzing multiple files to understand architecture
- Investigating complex questions
- Performing multi-step research tasks

Guidelines:
- For file searches: search broadly when you don't know
  where something lives
- NEVER create files unless absolutely necessary
- ALWAYS prefer editing existing files

핵심은 역할(CLI 도구), 강점(코드 검색, 분석), 규칙(파일 생성 최소화)이 명확하게 구분되어 있다는 점입니다.

시스템 프롬프트 작성 베스트 프랙티스

# 좋은 시스템 프롬프트의 구조

## 1. 역할 선언 (Who)
당신은 시니어 백엔드 엔지니어입니다.
Java/Spring Boot 전문가이며, 10년 이상의 경험이 있습니다.

## 2. 행동 규칙 (How)
- 코드 리뷰 시 보안 취약점을 최우선으로 확인하세요
- 성능 문제가 있으면 Big-O 분석과 함께 지적하세요
- 개선 제안은 반드시 코드 예제와 함께 제시하세요

## 3. 제약사항 (Boundaries)
- 코드를 직접 수정하지 마세요, 제안만 하세요
- 외부 라이브러리 추가를 권장하지 마세요
- 기존 아키텍처 패턴을 따르세요

## 4. 출력 형식 (Format)
리뷰 결과는 다음 형식으로 작성하세요:
- [심각도]: 설명 + 제안

실전 예제: 역할별 시스템 프롬프트

코드 리뷰어:

You are a senior code reviewer specializing in security
and performance. Review every PR with these priorities:
1. Security vulnerabilities (SQL injection, XSS, CSRF)
2. Performance bottlenecks (N+1 queries, memory leaks)
3. Code maintainability (naming, structure, SOLID)
Never approve code with critical security issues.

데이터 분석가:

You are a data analyst with expertise in Python, SQL,
and statistical analysis. When analyzing data:
1. Always validate data quality first
2. Provide confidence intervals for estimates
3. Visualize results with appropriate charts
4. Explain findings in business-friendly language

2-2. Tools (도구)

도구(Tools)는 AI가 외부 세계와 상호작용하는 인터페이스입니다. LLM 자체는 텍스트만 생성할 수 있지만, 도구를 통해 파일을 읽고, 명령어를 실행하고, API를 호출할 수 있습니다.

Function Calling의 작동 방식

AI의 도구 사용은 다음 과정을 거칩니다:

  1. 사용자가 요청을 보냄
  2. AI가 어떤 도구를 사용할지 결정 (추론)
  3. AI가 도구 호출을 생성 (JSON 형식)
  4. 하네스가 실제로 도구를 실행
  5. 결과를 AI에게 반환
  6. AI가 결과를 해석하고 다음 행동 결정

도구 정의 구조

도구는 JSON Schema로 정의됩니다:

{
  "name": "read_file",
  "description": "Reads a file from the local filesystem. Supports text files, images, and PDFs.",
  "input_schema": {
    "type": "object",
    "properties": {
      "file_path": {
        "type": "string",
        "description": "The absolute path to the file to read"
      },
      "offset": {
        "type": "number",
        "description": "The line number to start reading from"
      },
      "limit": {
        "type": "number",
        "description": "The number of lines to read"
      }
    },
    "required": ["file_path"]
  }
}

핵심 원칙:

  • 이름: 동사 + 명사 (read_file, search_code, run_command)
  • 설명: AI가 언제 이 도구를 사용해야 하는지 명확하게
  • 파라미터: JSON Schema로 강타입, required/optional 구분

Claude Code의 핵심 도구들

Claude Code는 다음과 같은 도구들을 제공합니다:

도구용도예시
Bash셸 명령 실행git status, npm test
Read파일 읽기소스 코드, 설정 파일
Write파일 생성새 파일 작성
Edit파일 수정기존 코드 수정
Grep패턴 검색코드베이스 내 검색
Glob파일 찾기파일명 패턴 매칭
WebSearch웹 검색최신 문서, API 참조
WebFetch웹 페이지 가져오기URL 내용 읽기

MCP(Model Context Protocol)로 도구 확장

MCP는 AI 에이전트가 외부 도구와 데이터 소스에 접근하는 표준 프로토콜입니다. USB-C가 다양한 장치를 하나의 인터페이스로 연결하듯, MCP는 다양한 서비스를 AI에 연결합니다.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost/db"
      }
    }
  }
}

MCP 서버를 추가하면 AI는 GitHub 이슈를 관리하거나, 데이터베이스를 쿼리하거나, Slack 메시지를 보내는 등의 새로운 도구를 획득합니다.

도구 설계 5원칙

  1. 단일 책임: 하나의 도구는 하나의 일만 한다
  2. 명확한 설명: AI가 언제 사용해야 하는지 이해할 수 있어야 한다
  3. 강타입 파라미터: JSON Schema로 입력을 엄격하게 검증
  4. 실패 처리: 도구 실행 실패 시 의미 있는 에러 메시지 반환
  5. 부작용 최소화: 읽기 작업은 안전하게, 쓰기 작업은 확인 후 실행

2-3. Context (맥락)

컨텍스트는 AI가 현재 상황을 이해하는 데 필요한 모든 정보입니다. 같은 질문이라도 컨텍스트에 따라 완전히 다른 답이 필요합니다.

CLAUDE.md: 프로젝트별 지시문

CLAUDE.md는 프로젝트의 사용 설명서입니다. AI가 해당 프로젝트에서 어떻게 행동해야 하는지를 정의합니다.

# Project Guidelines

## Build Commands

- npm run dev: 개발 서버 실행
- npm run build: 프로덕션 빌드
- npm test: 테스트 실행

## Code Conventions

- TypeScript strict mode 사용
- 함수형 컴포넌트만 사용 (클래스 컴포넌트 금지)
- CSS-in-JS 대신 Tailwind CSS 사용

## Architecture

- src/components: 재사용 가능한 UI 컴포넌트
- src/hooks: 커스텀 훅
- src/lib: 유틸리티 함수
- src/app: Next.js App Router 페이지

CLAUDE.md는 계층적으로 로딩됩니다:

  1. 홈 디렉토리의 CLAUDE.md (전역 설정)
  2. 프로젝트 루트의 CLAUDE.md (프로젝트 설정)
  3. 하위 디렉토리의 CLAUDE.md (모듈별 설정)

하위 디렉토리의 설정이 상위를 오버라이드합니다.

코드베이스 인덱싱

AI 하네스는 프로젝트의 파일 구조, 의존성, 아키텍처를 인덱싱합니다:

프로젝트 구조 분석:
- package.json → 의존성, 스크립트
- tsconfig.json → TypeScript 설정
- .eslintrc → 코드 스타일 규칙
- .gitignore → 제외 파일 패턴
- 디렉토리 구조 → 아키텍처 패턴 추론

Memory: 세션 간 기억

.remember/ 디렉토리는 AI의 장기 기억 저장소입니다:

.remember/
  core-memories.md   # 프로젝트 핵심 정보
  now.md             # 현재 작업 상태
  today.md           # 오늘의 작업 기록
  recent.md          # 최근 작업 히스토리
  archive.md         # 오래된 기록 보관소

Context Window 관리 전략

Claude의 컨텍스트 윈도우는 200K 토큰(최대 1M)이지만, 효율적으로 관리해야 합니다:

  1. 요약(Summarization): 긴 파일은 핵심만 추출
  2. 청크(Chunking): 필요한 부분만 선택적으로 로딩
  3. 우선순위(Priority): 현재 작업과 관련성 높은 정보 우선
  4. 동적 로딩: 필요할 때 추가 컨텍스트를 가져오기
  5. 서브에이전트 위임: 독립 작업은 별도 컨텍스트에서 처리

2-4. Skills (스킬)

스킬은 재사용 가능한 도메인 지식과 워크플로우의 묶음입니다. 프롬프트 템플릿과 비슷하지만, 트리거 조건, 도구 활용 지시, 단계별 워크플로우를 포함하는 더 풍부한 개념입니다.

SKILL.md 파일 구조

---
trigger: 'when user asks to review a PR'
description: 'Comprehensive code review workflow'
---

# Code Review Skill

## Step 1: Gather Information

- Read the PR description and diff
- Identify changed files and their purposes
- Check the PR against project conventions (CLAUDE.md)

## Step 2: Security Review

- Check for SQL injection, XSS, CSRF vulnerabilities
- Verify input validation on all user inputs
- Ensure secrets are not hardcoded

## Step 3: Performance Review

- Identify N+1 query patterns
- Check for unnecessary re-renders (React)
- Verify proper indexing for DB queries

## Step 4: Generate Review

- Use structured format: severity + description + suggestion
- Include code examples for each suggestion
- Summarize with overall assessment

Claude Code의 내장 스킬 예시

Claude Code는 여러 내장 스킬을 가지고 있습니다:

  • commit: git 상태 확인, 변경사항 분석, 커밋 메시지 작성, 커밋 실행
  • review-pr: PR 분석, 코드 리뷰, 코멘트 작성
  • test-driven-development: 테스트 먼저 작성, 구현, 리팩토링
  • brainstorming: 아이디어 발산, 구조화, 우선순위 결정

스킬 vs 프롬프트 템플릿

항목프롬프트 템플릿스킬
트리거수동 선택자동 감지
도구 활용없음도구 사용 지시 포함
워크플로우단일 프롬프트다단계 워크플로우
컨텍스트정적동적 컨텍스트 로딩
학습없음결과 피드백 반영 가능

스킬 작성 가이드

좋은 스킬을 만드는 핵심 원칙:

  1. 명확한 트리거: 어떤 상황에서 활성화되는지 정확하게 정의
  2. 단계별 분해: 복잡한 작업을 작은 단계로 분해
  3. 도구 매핑: 각 단계에서 어떤 도구를 사용하는지 명시
  4. 예외 처리: 실패 시나리오와 대안 경로 정의
  5. 출력 형식: 결과물의 형태를 구체적으로 정의

2-5. Hooks (훅)

훅은 AI의 도구 사용 전후에 자동으로 실행되는 스크립트입니다. 마치 Git의 pre-commit, post-commit 훅처럼, AI의 행동에 자동화된 검증과 처리를 추가합니다.

훅의 4가지 유형

  1. PreToolUse: 도구 사용 전에 실행

    • 용도: 입력 검증, 권한 확인, 파라미터 변환
    • 예: Write 도구 호출 전에 파일 경로 검증
  2. PostToolUse: 도구 사용 후에 실행

    • 용도: 결과 검증, 포맷팅, 린트, 테스트
    • 예: 파일 저장 후 자동으로 ESLint 실행
  3. Notification: 알림 전에 실행

    • 용도: 알림 내용 가공, 필터링
  4. Stop: 에이전트 정지 시 실행

    • 용도: 정리 작업, 상태 저장

설정 예시: settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "echo 'File modification detected'"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "/bin/sh -c 'cd PROJECT_DIR && npx eslint --fix FILE_PATH 2>/dev/null || true'"
      },
      {
        "matcher": "Bash",
        "command": "/bin/sh -c 'if echo TOOL_INPUT | grep -q \"git commit\"; then cd PROJECT_DIR && npm test; fi'"
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "command": "/bin/sh -c 'echo NOTIFICATION_CONTENT >> /tmp/ai-notifications.log'"
      }
    ]
  }
}

실전 훅 시나리오

시나리오 1: 파일 저장 후 자동 포맷팅

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "/bin/sh -c 'npx prettier --write FILE_PATH'"
      }
    ]
  }
}

시나리오 2: 커밋 전 자동 테스트

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/bin/sh -c 'if echo TOOL_INPUT | grep -q \"git commit\"; then npm test || exit 1; fi'"
      }
    ]
  }
}

시나리오 3: 위험한 명령어 차단

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/bin/sh -c 'if echo TOOL_INPUT | grep -qE \"rm -rf|drop table|format\"; then echo \"BLOCKED: Dangerous command detected\" && exit 1; fi'"
      }
    ]
  }
}

2-6. Permissions (권한)

권한 모델은 AI가 수행할 수 있는 작업의 범위를 정의하는 보안 계층입니다. 최소 권한 원칙(Principle of Least Privilege)을 AI에 적용합니다.

작업 위험도 분류

위험도작업 유형예시정책
안전읽기파일 읽기, 검색, 조회자동 허용
주의쓰기파일 수정, 생성확인 후 허용
위험삭제/실행파일 삭제, 배포명시적 승인 필요
금지시스템 변경OS 설정, 계정 변경항상 차단

allowedTools, disallowedTools

{
  "permissions": {
    "allowedTools": [
      "Read",
      "Glob",
      "Grep",
      "Bash(git status)",
      "Bash(git diff)",
      "Bash(npm test)"
    ],
    "disallowedTools": [
      "Bash(rm *)",
      "Bash(sudo *)",
      "Bash(curl * | sh)",
      "Write(/etc/*)",
      "Write(/usr/*)"
    ]
  }
}

Sandbox 모드 vs 에이전트 모드

Sandbox 모드:

  • 네트워크 접근 차단
  • 파일 시스템 읽기 전용
  • 셸 명령 제한
  • 안전한 실험 환경

에이전트 모드:

  • 네트워크 접근 허용
  • 파일 시스템 읽기/쓰기
  • 셸 명령 허용 (패턴 기반 필터링)
  • 실제 작업 수행 환경

최소 권한 원칙 적용

AI 에이전트에 최소 권한 원칙을 적용하는 단계:

  1. 필요한 도구만 제공: 코드 리뷰에 배포 도구는 불필요
  2. 파일 범위 제한: 프로젝트 디렉토리 외부 접근 차단
  3. 명령어 화이트리스트: 허용된 셸 명령만 실행 가능
  4. 시간 제한: 장시간 실행 방지
  5. 비용 제한: 최대 토큰 사용량 설정

2-7. Memory (기억)

메모리는 AI가 과거의 상호작용과 학습 내용을 유지하는 메커니즘입니다.

단기 기억 vs 장기 기억

단기 기억 (Short-term Memory):

  • 현재 대화 세션의 컨텍스트
  • 컨텍스트 윈도우 내의 모든 메시지
  • 세션 종료 시 사라짐

장기 기억 (Long-term Memory):

  • .remember/ 디렉토리에 파일로 저장
  • 세션 간 지속
  • 프로젝트 수준 또는 사용자 수준

기억 파일 구조

# core-memories.md

- 이 프로젝트는 Next.js 14 + TypeScript + Tailwind CSS를 사용합니다
- 배포는 Vercel로 합니다
- DB는 PostgreSQL (Supabase)입니다

# now.md

현재 작업: 사용자 인증 모듈 리팩토링 중
진행 상황: NextAuth.js에서 Lucia Auth로 마이그레이션 진행 중
블로커: 소셜 로그인 콜백 URL 설정 문제

# today.md

- 09:30 인증 모듈 분석 완료
- 10:15 Lucia Auth 설정 파일 생성
- 11:00 세션 관리 로직 마이그레이션
- 14:00 소셜 로그인 통합 시작

에피소딕 메모리 vs 시맨틱 메모리

에피소딕 메모리 (사건 기반):

  • "어제 사용자가 인증 버그를 수정해달라고 했다"
  • "지난주에 데이터베이스 스키마를 변경했다"
  • 구체적인 사건과 시간 정보 포함

시맨틱 메모리 (지식 기반):

  • "이 프로젝트는 REST API를 사용한다"
  • "팀의 코딩 컨벤션은 Airbnb 스타일이다"
  • 일반적인 사실과 규칙

메모리 관리 전략

  1. 자동 요약: 긴 대화는 핵심 포인트만 추출하여 저장
  2. 중요도 기반 정리: 자주 참조되는 기억은 유지, 불필요한 기억은 아카이브
  3. 계층적 구조: 핵심 기억 > 최근 기억 > 아카이브
  4. 충돌 해결: 새로운 정보가 기존 기억과 충돌하면 업데이트
  5. 프라이버시: 민감한 정보는 암호화하거나 저장하지 않음

3. Claude Code 하네스 아키텍처 심화

3-1. 에이전트 루프 (Agent Loop)

Claude Code의 핵심은 에이전트 루프입니다. 단순한 질문-응답이 아니라, 도구를 사용하고, 결과를 관찰하고, 다시 행동하는 반복 순환입니다.

에이전트 루프 흐름:

1. User Input (사용자 요청)
      |
2. System Prompt + Context 로딩
      |
3. LLM 추론 (어떤 행동을 할지 결정)
      |
4. Tool Call 결정
      |
5. Hook (PreToolUse) 실행
      |  (검증 실패 시 → 3으로 돌아감)
      |
6. Tool 실행
      |
7. Hook (PostToolUse) 실행
      |
8. 결과 관찰 (Observation)
      |
9. 추가 행동 필요? → Yes → 3으로 돌아감
      |               No
      |
10. 최종 응답 생성

이 루프는 max_turns 제한에 도달하거나, AI가 "작업 완료"로 판단할 때까지 계속됩니다.

실전 예시: PR 리뷰 루프

Turn 1: git diff 실행 (Bash 도구)
   → 변경된 파일 목록과 diff 획득

Turn 2: 변경된 파일 읽기 (Read 도구)
   → 전체 컨텍스트 파악

Turn 3: CLAUDE.md 읽기 (Read 도구)
   → 프로젝트 컨벤션 확인

Turn 4: 관련 테스트 파일 찾기 (Grep 도구)
   → 테스트 커버리지 확인

Turn 5: npm test 실행 (Bash 도구)
   → 테스트 통과 여부 확인

Turn 6: 리뷰 결과 생성 (최종 응답)
   → 보안, 성능, 유지보수성 관점 리뷰

3-2. 서브에이전트 아키텍처

복잡한 작업은 하나의 에이전트가 처리하기 어렵습니다. Claude Code는 서브에이전트 아키텍처를 사용합니다.

메인 에이전트와 서브에이전트

메인 에이전트 (Orchestrator)
    |
    +--- 서브에이전트 A: "src/ 디렉토리 분석"
    |       (독립 컨텍스트, 파일 읽기/검색 도구)
    |
    +--- 서브에이전트 B: "tests/ 디렉토리 분석"
    |       (독립 컨텍스트, 테스트 실행 도구)
    |
    +--- 서브에이전트 C: "문서 업데이트"
            (독립 컨텍스트, 파일 쓰기 도구)

메인 에이전트: 각 서브에이전트의 결과를 통합하여 최종 응답 생성

서브에이전트의 핵심 특징:

  • 독립 컨텍스트: 메인 에이전트의 컨텍스트를 오염시키지 않음
  • 병렬 실행: 독립적인 작업은 동시에 실행 가능
  • 결과 통합: 메인 에이전트가 결과를 수집하고 종합

Background Agent vs Foreground Agent

  • Foreground Agent: 사용자와 직접 대화하는 메인 에이전트
  • Background Agent: 독립적으로 작업을 수행하는 비동기 에이전트

Background Agent 사용 시나리오:

  • 대규모 리팩토링 작업
  • 여러 파일에 걸친 테스트 실행
  • 문서 자동 생성

Worktree Isolation

Git Worktree를 활용한 격리:

메인 프로젝트 (main branch)
    |
    +--- .claude/worktrees/feature-auth/
    |       (독립 브랜치, 독립 작업 디렉토리)
    |
    +--- .claude/worktrees/fix-bug-123/
            (독립 브랜치, 독립 작업 디렉토리)

각 Worktree는 독립적인 파일 시스템을 가지므로, 서로의 작업에 영향을 주지 않습니다.

3-3. settings.json 완전 해부

Claude Code의 설정은 settings.json에서 관리됩니다.

전역 설정 vs 프로젝트 설정

설정 위치 (우선순위 순):
1. 프로젝트/.claude/settings.json (최우선)
2. 홈디렉토리/.claude/settings.json (전역)
3. 기본값

전체 설정 구조

{
  "permissions": {
    "allowedTools": ["Read", "Glob", "Grep"],
    "disallowedTools": ["Bash(rm *)"]
  },
  "hooks": {
    "PreToolUse": [],
    "PostToolUse": [],
    "Notification": [],
    "Stop": []
  },
  "env": {
    "NODE_ENV": "development",
    "DEBUG": "true"
  },
  "model": "claude-sonnet-4-20250514",
  "theme": "dark"
}

설정 우선순위

가장 높음 ← 프로젝트 설정 ← 전역 설정 ← 기본값 → 가장 낮음

프로젝트 설정이 전역 설정을 오버라이드하므로, 프로젝트별로 다른 권한과 훅을 설정할 수 있습니다.


4. Claude Agent SDK로 커스텀 하네스 만들기

4-1. Python SDK

Anthropic의 Claude Agent SDK는 커스텀 AI 에이전트를 구축하기 위한 프레임워크입니다.

기본 에이전트 생성

import anthropic
from typing import Any

# Anthropic 클라이언트 초기화
client = anthropic.Anthropic()

# 도구 정의
tools = [
    {
        "name": "read_file",
        "description": "Read the contents of a file at the given path",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Absolute file path to read"
                }
            },
            "required": ["path"]
        }
    },
    {
        "name": "list_directory",
        "description": "List files and directories at the given path",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Directory path to list"
                }
            },
            "required": ["path"]
        }
    },
    {
        "name": "search_code",
        "description": "Search for a pattern in the codebase",
        "input_schema": {
            "type": "object",
            "properties": {
                "pattern": {
                    "type": "string",
                    "description": "Regex pattern to search for"
                },
                "file_type": {
                    "type": "string",
                    "description": "File extension to filter (e.g., py, ts)"
                }
            },
            "required": ["pattern"]
        }
    }
]


def execute_tool(name: str, input_data: dict) -> str:
    """도구를 실행하고 결과를 반환합니다."""
    import os
    import subprocess

    if name == "read_file":
        try:
            with open(input_data["path"], "r") as f:
                return f.read()
        except FileNotFoundError:
            return f"Error: File not found: {input_data['path']}"

    elif name == "list_directory":
        try:
            entries = os.listdir(input_data["path"])
            return "\n".join(entries)
        except FileNotFoundError:
            return f"Error: Directory not found: {input_data['path']}"

    elif name == "search_code":
        try:
            cmd = ["grep", "-rn", input_data["pattern"], "."]
            if "file_type" in input_data:
                cmd.extend(["--include", f"*.{input_data['file_type']}"])
            result = subprocess.run(cmd, capture_output=True, text=True)
            return result.stdout or "No matches found"
        except Exception as e:
            return f"Error: {str(e)}"

    return f"Unknown tool: {name}"


def run_agent(user_message: str, max_turns: int = 10) -> str:
    """에이전트 루프를 실행합니다."""
    system_prompt = """You are an expert code reviewer.
    Analyze code for security vulnerabilities, performance issues,
    and maintainability problems. Use the provided tools to
    explore the codebase before giving your review."""

    messages = [{"role": "user", "content": user_message}]

    for turn in range(max_turns):
        # LLM 호출
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system=system_prompt,
            tools=tools,
            messages=messages,
        )

        # 응답 처리
        if response.stop_reason == "end_turn":
            # 최종 텍스트 응답 추출
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text
            return "Agent completed without text response"

        # 도구 호출 처리
        if response.stop_reason == "tool_use":
            # assistant 메시지 추가
            messages.append({
                "role": "assistant",
                "content": response.content,
            })

            # 각 도구 호출 실행
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result,
                    })

            messages.append({
                "role": "user",
                "content": tool_results,
            })

    return "Agent reached maximum turns without completing"


# 실행
if __name__ == "__main__":
    review = run_agent("Review the authentication module in src/auth/")
    print(review)

권한 모델 추가

class PermissionModel:
    """AI 에이전트의 권한을 관리합니다."""

    def __init__(self):
        self.allowed_paths = ["/project/src/", "/project/tests/"]
        self.blocked_commands = ["rm", "sudo", "chmod"]
        self.max_file_size = 1_000_000  # 1MB

    def check_file_access(self, path: str) -> bool:
        """파일 접근 권한을 확인합니다."""
        return any(path.startswith(p) for p in self.allowed_paths)

    def check_command(self, command: str) -> bool:
        """명령어 실행 권한을 확인합니다."""
        return not any(cmd in command for cmd in self.blocked_commands)

    def validate_tool_call(self, tool_name: str, input_data: dict) -> tuple:
        """도구 호출의 유효성을 검증합니다.
        Returns (is_allowed, reason)"""
        if tool_name == "read_file":
            path = input_data.get("path", "")
            if not self.check_file_access(path):
                return False, f"Access denied: {path}"

        if tool_name == "execute_command":
            cmd = input_data.get("command", "")
            if not self.check_command(cmd):
                return False, f"Blocked command: {cmd}"

        return True, "Allowed"

이벤트 처리와 모니터링

import time
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class AgentEvent:
    """에이전트의 행동을 기록하는 이벤트입니다."""
    event_type: str  # tool_call, tool_result, llm_response, error
    timestamp: float = field(default_factory=time.time)
    tool_name: Optional[str] = None
    input_data: Optional[dict] = None
    output_data: Optional[str] = None
    tokens_used: int = 0
    duration_ms: float = 0


class AgentMonitor:
    """에이전트의 행동을 모니터링합니다."""

    def __init__(self):
        self.events: list[AgentEvent] = []
        self.total_tokens = 0
        self.total_cost = 0.0

    def log_event(self, event: AgentEvent):
        self.events.append(event)
        self.total_tokens += event.tokens_used

    def get_summary(self) -> dict:
        return {
            "total_events": len(self.events),
            "total_tokens": self.total_tokens,
            "tool_calls": sum(
                1 for e in self.events if e.event_type == "tool_call"
            ),
            "errors": sum(
                1 for e in self.events if e.event_type == "error"
            ),
            "total_duration_ms": sum(
                e.duration_ms for e in self.events
            ),
        }

4-2. TypeScript SDK

TypeScript로도 동일한 패턴으로 하네스를 구축할 수 있습니다.

import Anthropic from '@anthropic-ai/sdk'

// 도구 정의
const tools: Anthropic.Tool[] = [
  {
    name: 'query_database',
    description: 'Execute a SQL query against the analytics database',
    input_schema: {
      type: 'object' as const,
      properties: {
        query: {
          type: 'string',
          description: 'SQL query to execute (SELECT only)',
        },
        database: {
          type: 'string',
          description: 'Database name',
          enum: ['analytics', 'users', 'products'],
        },
      },
      required: ['query', 'database'],
    },
  },
  {
    name: 'create_chart',
    description: 'Create a data visualization chart',
    input_schema: {
      type: 'object' as const,
      properties: {
        chart_type: {
          type: 'string',
          enum: ['bar', 'line', 'pie', 'scatter'],
        },
        data: {
          type: 'string',
          description: 'JSON string of chart data',
        },
        title: {
          type: 'string',
          description: 'Chart title',
        },
      },
      required: ['chart_type', 'data', 'title'],
    },
  },
]

// 권한 모델
class QueryPermissions {
  private readonly allowedOperations = ['SELECT']
  private readonly blockedPatterns = [
    /DROP\s/i,
    /DELETE\s/i,
    /UPDATE\s/i,
    /INSERT\s/i,
    /ALTER\s/i,
    /TRUNCATE\s/i,
  ]

  validateQuery(query: string): { allowed: boolean; reason: string } {
    for (const pattern of this.blockedPatterns) {
      if (pattern.test(query)) {
        return {
          allowed: false,
          reason: `Blocked: destructive operation detected`,
        }
      }
    }
    return { allowed: true, reason: 'Query is safe' }
  }
}

// 도구 실행
async function executeTool(name: string, input: Record<string, unknown>): Promise<string> {
  const permissions = new QueryPermissions()

  if (name === 'query_database') {
    const query = input.query as string
    const validation = permissions.validateQuery(query)
    if (!validation.allowed) {
      return `Permission denied: ${validation.reason}`
    }
    // 실제 DB 쿼리 실행 (여기서는 시뮬레이션)
    return JSON.stringify({
      columns: ['date', 'revenue', 'users'],
      rows: [
        ['2025-01', 150000, 12000],
        ['2025-02', 165000, 13500],
        ['2025-03', 180000, 15000],
      ],
    })
  }

  if (name === 'create_chart') {
    return `Chart created: ${input.title} (${input.chart_type})`
  }

  return `Unknown tool: ${name}`
}

// 에이전트 루프
async function runDataAnalysisAgent(question: string): Promise<string> {
  const client = new Anthropic()

  const systemPrompt = `You are a data analyst agent.
    Analyze data by querying databases and creating visualizations.
    Always validate data before making conclusions.
    Provide insights in clear, business-friendly language.`

  const messages: Anthropic.MessageParam[] = [{ role: 'user', content: question }]

  const maxTurns = 10

  for (let turn = 0; turn < maxTurns; turn++) {
    const response = await client.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 4096,
      system: systemPrompt,
      tools,
      messages,
    })

    if (response.stop_reason === 'end_turn') {
      for (const block of response.content) {
        if (block.type === 'text') {
          return block.text
        }
      }
      return 'Agent completed'
    }

    if (response.stop_reason === 'tool_use') {
      messages.push({
        role: 'assistant',
        content: response.content,
      })

      const toolResults: Anthropic.ToolResultBlockParam[] = []
      for (const block of response.content) {
        if (block.type === 'tool_use') {
          const result = await executeTool(block.name, block.input as Record<string, unknown>)
          toolResults.push({
            type: 'tool_result',
            tool_use_id: block.id,
            content: result,
          })
        }
      }

      messages.push({
        role: 'user',
        content: toolResults,
      })
    }
  }

  return 'Agent reached maximum turns'
}

// 실행
async function main() {
  const result = await runDataAnalysisAgent('What was the revenue trend in Q1 2025?')
  console.log(result)
}

main().catch(console.error)

5. 다른 AI 하네스 프레임워크 비교

프레임워크 비교표

프레임워크언어핵심 개념하네스 수준학습 곡선
Claude Agent SDKPython/TS도구, 권한, 이벤트가장 완전중간
LangGraphPython그래프 기반 워크플로우높음높음
CrewAIPython멀티에이전트 협업중간낮음
AutoGenPython에이전트 대화중간중간
Semantic KernelC#/PythonMS 에코시스템 통합중간중간
DSPyPython프롬프트 최적화낮음높음

Claude Agent SDK

강점:

  • Anthropic 모델과 최적 호환
  • 내장 권한 모델, 도구 사용, 이벤트 시스템
  • 프로덕션 레벨 안정성

하네스 구현:

  • System Prompt + Tools + Permissions가 SDK 레벨에서 통합
  • 에이전트 루프가 내장되어 있어 별도 구현 불필요

LangGraph

강점:

  • 복잡한 워크플로우를 그래프로 시각화
  • 상태 관리, 조건 분기, 병렬 실행 지원
  • 체크포인트와 롤백 기능

하네스 구현:

from langgraph.graph import StateGraph, END
from typing import TypedDict


class ReviewState(TypedDict):
    code_diff: str
    security_issues: list
    performance_issues: list
    review_summary: str


def analyze_security(state: ReviewState) -> ReviewState:
    """보안 취약점을 분석합니다."""
    # LLM 호출로 보안 분석
    state["security_issues"] = ["SQL injection in login.py:42"]
    return state


def analyze_performance(state: ReviewState) -> ReviewState:
    """성능 이슈를 분석합니다."""
    # LLM 호출로 성능 분석
    state["performance_issues"] = ["N+1 query in users.py:87"]
    return state


def generate_review(state: ReviewState) -> ReviewState:
    """최종 리뷰를 생성합니다."""
    issues = state["security_issues"] + state["performance_issues"]
    state["review_summary"] = f"Found {len(issues)} issues"
    return state


# 그래프 구성
workflow = StateGraph(ReviewState)
workflow.add_node("security", analyze_security)
workflow.add_node("performance", analyze_performance)
workflow.add_node("review", generate_review)

workflow.set_entry_point("security")
workflow.add_edge("security", "performance")
workflow.add_edge("performance", "review")
workflow.add_edge("review", END)

app = workflow.compile()

CrewAI

강점:

  • 직관적인 멀티에이전트 모델 (역할 기반)
  • "Agent = Role + Goal + Backstory" 패턴
  • 에이전트 간 협업이 자연스러움

하네스 구현:

from crewai import Agent, Task, Crew

security_reviewer = Agent(
    role="Security Reviewer",
    goal="Find all security vulnerabilities in the code",
    backstory="You are a senior security engineer with 15 years "
              "of experience in application security.",
    tools=[],  # 도구 목록
)

performance_reviewer = Agent(
    role="Performance Reviewer",
    goal="Identify performance bottlenecks and optimization opportunities",
    backstory="You are a performance engineering specialist "
              "who has optimized systems serving millions of users.",
    tools=[],
)

review_task = Task(
    description="Review the authentication module for security issues",
    agent=security_reviewer,
    expected_output="List of security vulnerabilities with severity ratings",
)

crew = Crew(
    agents=[security_reviewer, performance_reviewer],
    tasks=[review_task],
)

result = crew.kickoff()

6. 하네스 설계 패턴

AI 하네스를 설계할 때 활용할 수 있는 주요 패턴들을 살펴봅니다.

6-1. Router Pattern (라우터 패턴)

입력을 분석하여 적절한 전문 에이전트로 라우팅하는 패턴입니다.

사용자 입력
    |
    v
[Router Agent] → "코드 리뷰 요청" → Code Review Agent
               → "데이터 분석 요청" → Data Analysis Agent
               → "문서 작성 요청" → Documentation Agent
               → "일반 질문"       → General Assistant

적합한 상황:

  • 다양한 유형의 요청을 처리해야 할 때
  • 전문 에이전트가 각 도메인에 최적화되어 있을 때
  • 단일 에이전트로는 모든 도메인을 커버하기 어려울 때

6-2. Orchestrator-Worker Pattern (오케스트레이터-워커 패턴)

중앙 오케스트레이터가 작업을 분할하고 워커에게 배분하는 패턴입니다.

[Orchestrator]
    |
    +--- "파일 A 리뷰해줘" → [Worker 1] → 결과 A
    |
    +--- "파일 B 리뷰해줘" → [Worker 2] → 결과 B
    |
    +--- "파일 C 리뷰해줘" → [Worker 3] → 결과 C
    |
    v
[Orchestrator] → 결과 A + B + C 통합 → 최종 리뷰

적합한 상황:

  • 대규모 작업을 병렬로 처리해야 할 때
  • 작업을 독립적인 단위로 분할할 수 있을 때
  • 결과를 통합하는 로직이 필요할 때

6-3. Pipeline Pattern (파이프라인 패턴)

순차적으로 처리하는 패턴입니다. 각 단계의 출력이 다음 단계의 입력이 됩니다.

[분석 Agent] → 코드 분석 결과
    |
    v
[계획 Agent] → 리팩토링 계획
    |
    v
[실행 Agent] → 코드 수정
    |
    v
[검증 Agent] → 테스트 실행 + 결과 확인

적합한 상황:

  • 작업에 명확한 순서가 있을 때
  • 각 단계가 이전 단계의 결과에 의존할 때
  • 단계별 품질 검증이 필요할 때

6-4. Evaluator-Optimizer Pattern (평가자-최적화 패턴)

결과를 평가하고 기준 미달 시 재시도하는 패턴입니다.

[Generator Agent] → 초안 생성
    |
    v
[Evaluator Agent] → 품질 평가 (1-10점)
    |
    +--- 점수 >= 8 → 완료
    |
    +--- 점수 < 8 → 피드백과 함께 Generator에게 재요청
                         |
                         v
                    [Generator Agent] → 수정본 생성
                         |
                         v
                    [Evaluator Agent] → 재평가
                         ...

적합한 상황:

  • 출력 품질이 중요할 때
  • 품질을 객관적으로 측정할 수 있을 때
  • 반복을 통한 개선이 가능할 때

6-5. Guardrails Pattern (가드레일 패턴)

입출력 검증으로 안전한 AI 행동을 보장하는 패턴입니다.

사용자 입력
    |
    v
[Input Guardrail] → 유해 콘텐츠 필터링
    |                 프롬프트 인젝션 탐지
    |                 입력 정규화
    v
[AI Agent] → 작업 수행
    |
    v
[Output Guardrail] → PII 마스킹
    |                  환각 탐지
    |                  형식 검증
    v
최종 응답

구현 예시:

class InputGuardrail:
    """입력을 검증하고 정제합니다."""

    def validate(self, user_input: str) -> tuple:
        # 프롬프트 인젝션 탐지
        injection_patterns = [
            "ignore previous instructions",
            "system prompt",
            "you are now",
            "disregard all",
        ]
        for pattern in injection_patterns:
            if pattern.lower() in user_input.lower():
                return False, "Potential prompt injection detected"

        # 입력 길이 제한
        if len(user_input) > 10000:
            return False, "Input too long"

        return True, "Valid input"


class OutputGuardrail:
    """출력을 검증하고 정제합니다."""

    def validate(self, output: str) -> str:
        # PII 마스킹
        import re
        # 이메일 마스킹
        output = re.sub(
            r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
            "[EMAIL_REDACTED]",
            output,
        )
        # 전화번호 마스킹
        output = re.sub(
            r"\b\d{3}[-.]?\d{4}[-.]?\d{4}\b",
            "[PHONE_REDACTED]",
            output,
        )
        return output

7. 실전: 나만의 코드 리뷰 하네스 만들기

실제로 동작하는 코드 리뷰 하네스를 처음부터 끝까지 만들어 보겠습니다.

7-1. 프로젝트 구조

code-review-harness/
  src/
    agent.py          # 에이전트 루프
    tools.py           # 도구 정의 및 실행
    permissions.py     # 권한 모델
    guardrails.py      # 입출력 검증
    memory.py          # 기억 관리
    monitor.py         # 모니터링
  skills/
    code-review.md     # 코드 리뷰 스킬
    security-audit.md  # 보안 감사 스킬
  config/
    settings.json      # 설정 파일
  tests/
    test_agent.py      # 에이전트 테스트
  README.md
  requirements.txt

7-2. System Prompt 설계

You are an expert code reviewer specializing in Python
and TypeScript applications.

## Your Responsibilities
1. Security: Find vulnerabilities (injection, XSS, auth issues)
2. Performance: Identify bottlenecks (N+1 queries, memory leaks)
3. Maintainability: Check code quality (naming, structure, SOLID)
4. Testing: Verify test coverage and quality

## Rules
- NEVER modify code directly. Only suggest changes.
- Always explain WHY something is an issue, not just WHAT.
- Provide code examples for every suggestion.
- Rate issues by severity: Critical, High, Medium, Low.

## Output Format
For each issue found:
[SEVERITY] file:line - Description
Suggestion: How to fix it
Example: Code snippet showing the fix

7-3. 도구 구현

# tools.py
import subprocess
import os


def git_diff(base_branch: str = "main") -> str:
    """현재 브랜치와 base 브랜치의 diff를 가져옵니다."""
    result = subprocess.run(
        ["git", "diff", f"{base_branch}...HEAD"],
        capture_output=True,
        text=True,
    )
    return result.stdout


def read_file(path: str) -> str:
    """파일 내용을 읽습니다."""
    try:
        with open(path, "r") as f:
            lines = f.readlines()
        # 줄 번호 추가
        numbered = [f"{i+1}: {line}" for i, line in enumerate(lines)]
        return "".join(numbered)
    except FileNotFoundError:
        return f"File not found: {path}"


def search_pattern(pattern: str, directory: str = ".") -> str:
    """코드베이스에서 패턴을 검색합니다."""
    result = subprocess.run(
        ["grep", "-rn", pattern, directory,
         "--include=*.py", "--include=*.ts",
         "--include=*.tsx", "--include=*.js"],
        capture_output=True,
        text=True,
    )
    return result.stdout or "No matches found"


def run_tests(test_path: str = "") -> str:
    """테스트를 실행합니다."""
    cmd = ["python", "-m", "pytest", "-v"]
    if test_path:
        cmd.append(test_path)
    result = subprocess.run(cmd, capture_output=True, text=True)
    return f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"


def get_file_history(path: str, count: int = 5) -> str:
    """파일의 git 히스토리를 가져옵니다."""
    result = subprocess.run(
        ["git", "log", f"-{count}", "--oneline", "--", path],
        capture_output=True,
        text=True,
    )
    return result.stdout

7-4. 스킬 정의

# skills/code-review.md

---

trigger: "when user asks to review code or a PR"
description: "Comprehensive code review workflow"

---

## Code Review Workflow

### Step 1: Gather Context

1. Run git diff to see all changes
2. Read the changed files completely
3. Check project conventions (CLAUDE.md, .eslintrc)
4. Identify the purpose of the changes

### Step 2: Security Analysis

Check for these common vulnerabilities:

- SQL injection (raw queries, string interpolation)
- XSS (unescaped user input in HTML)
- Authentication issues (weak tokens, missing checks)
- Authorization issues (missing role checks)
- Sensitive data exposure (API keys, passwords in code)

### Step 3: Performance Analysis

Check for these common issues:

- N+1 database queries
- Missing database indexes
- Unnecessary re-renders (React)
- Memory leaks (unclosed resources)
- Blocking operations in async code

### Step 4: Code Quality Analysis

Check for these issues:

- Unclear naming conventions
- Functions exceeding 50 lines
- Missing error handling
- Duplicated code
- SOLID principle violations

### Step 5: Generate Review Report

Format each issue as:
[SEVERITY] file:line - Description
Use severity levels: Critical, High, Medium, Low, Info

7-5. 권한 모델

# permissions.py

class ReviewPermissions:
    """코드 리뷰 에이전트의 권한을 정의합니다."""

    # 읽기만 허용 - 코드 수정 불가
    ALLOWED_TOOLS = [
        "git_diff",
        "read_file",
        "search_pattern",
        "run_tests",
        "get_file_history",
    ]

    BLOCKED_TOOLS = [
        "write_file",
        "delete_file",
        "execute_command",
        "deploy",
    ]

    # 접근 가능한 디렉토리
    ALLOWED_PATHS = [
        "src/",
        "tests/",
        "lib/",
        "config/",
    ]

    # 접근 불가 디렉토리
    BLOCKED_PATHS = [
        ".env",
        "secrets/",
        "credentials/",
        ".git/",
    ]

    def is_tool_allowed(self, tool_name: str) -> bool:
        if tool_name in self.BLOCKED_TOOLS:
            return False
        return tool_name in self.ALLOWED_TOOLS

    def is_path_allowed(self, path: str) -> bool:
        # 차단 경로 확인
        for blocked in self.BLOCKED_PATHS:
            if blocked in path:
                return False
        # 허용 경로 확인
        return any(path.startswith(p) for p in self.ALLOWED_PATHS)

8. 하네스 평가 및 모니터링

하네스 품질 메트릭

AI 하네스의 품질을 측정하는 주요 메트릭:

메트릭설명목표값
도구 사용 정확도올바른 도구를 올바른 파라미터로 호출하는 비율95% 이상
태스크 완료율사용자 요청을 성공적으로 완료하는 비율90% 이상
안전 위반 빈도가드레일을 우회하거나 권한을 위반하는 빈도0%
평균 턴 수작업 완료까지의 평균 에이전트 루프 반복 횟수5 이하
비용 효율성작업당 평균 토큰 사용량과 API 비용업무에 따라 상이

LLM-as-Judge

다른 LLM을 사용하여 에이전트의 출력 품질을 평가하는 방법입니다:

def evaluate_review_quality(
    original_code: str,
    review_output: str,
    evaluator_model: str = "claude-sonnet-4-20250514"
) -> dict:
    """LLM을 사용하여 코드 리뷰 품질을 평가합니다."""
    evaluation_prompt = f"""
    Evaluate this code review on a scale of 1-10 for each criterion:

    1. Accuracy: Are the identified issues real problems?
    2. Completeness: Were important issues missed?
    3. Actionability: Are suggestions specific and implementable?
    4. Communication: Is the review clear and constructive?

    Original Code:
    {original_code}

    Review Output:
    {review_output}

    Respond in JSON format:
    accuracy, completeness, actionability, communication, overall, feedback
    """

    # LLM 호출하여 평가 수행
    # ... (실제 API 호출 코드)
    return evaluation_result

A/B 테스트

프롬프트나 도구 변경 시 A/B 테스트로 비교합니다:

  1. Baseline 설정: 현재 하네스의 성능 측정
  2. 변경 적용: 새로운 시스템 프롬프트, 도구, 스킬 적용
  3. 동일 입력 테스트: 같은 테스트 케이스로 두 버전 실행
  4. 메트릭 비교: 정확도, 완료율, 비용 비교
  5. 통계적 유의성 확인: 차이가 우연이 아닌지 검증

비용 추적

class CostTracker:
    """에이전트의 API 호출 비용을 추적합니다."""

    # 모델별 토큰당 가격 (USD)
    PRICING = {
        "claude-sonnet-4-20250514": {
            "input": 0.003 / 1000,
            "output": 0.015 / 1000,
        },
        "claude-opus-4-20250514": {
            "input": 0.015 / 1000,
            "output": 0.075 / 1000,
        },
    }

    def __init__(self):
        self.total_input_tokens = 0
        self.total_output_tokens = 0
        self.model = "claude-sonnet-4-20250514"

    def add_usage(self, input_tokens: int, output_tokens: int):
        self.total_input_tokens += input_tokens
        self.total_output_tokens += output_tokens

    def get_total_cost(self) -> float:
        pricing = self.PRICING[self.model]
        return (
            self.total_input_tokens * pricing["input"]
            + self.total_output_tokens * pricing["output"]
        )

    def get_report(self) -> str:
        cost = self.get_total_cost()
        return (
            f"Input tokens: {self.total_input_tokens:,}\n"
            f"Output tokens: {self.total_output_tokens:,}\n"
            f"Total cost: ${cost:.4f}"
        )

9. 2025-2026 하네스 트렌드

Model-Native 도구 호출

초기 LLM은 도구 사용법을 프롬프트로 가르쳐야 했습니다. 2025년의 최신 모델들은 학습 과정에서 도구 사용법을 내재화했습니다. Claude Sonnet 4, GPT-4o 등은 Function Calling을 네이티브로 지원합니다.

이것이 하네스에 미치는 영향:

  • 도구 설명이 간결해도 정확하게 사용
  • 복잡한 도구 조합을 자율적으로 계획
  • 에러 복구 능력 향상

자율 에이전트의 부상

2025년은 자율 에이전트의 원년입니다:

  • Devin: Cognition이 만든 AI 소프트웨어 엔지니어
  • Claude Code: Anthropic의 CLI 기반 코딩 에이전트
  • OpenAI Codex (CLI): OpenAI의 코딩 에이전트
  • GitHub Copilot Agent Mode: GitHub의 에이전트 모드

이들은 모두 정교한 하네스를 사용합니다. 차이를 만드는 것은 모델의 능력이 아니라 하네스의 설계입니다.

멀티모달 하네스

텍스트만 처리하던 하네스가 이미지, 오디오, 비디오로 확장됩니다:

  • 스크린샷 분석: UI 버그 탐지, 디자인 리뷰
  • 다이어그램 이해: 아키텍처 다이어그램을 코드로 변환
  • 음성 인터페이스: 음성으로 코딩 지시
  • 비디오 분석: 사용자 세션 녹화 분석

Agent-to-Agent 프로토콜

MCP(Model Context Protocol)가 AI 에이전트 간의 표준 통신 프로토콜로 진화하고 있습니다:

  • 에이전트 A가 에이전트 B의 도구를 사용
  • 에이전트 간 작업 위임과 결과 공유
  • 이기종 에이전트(Claude + GPT + Gemini) 간 협업

하네스의 표준화

현재 각 프레임워크마다 하네스 구현 방식이 다르지만, 표준화 움직임이 시작되고 있습니다:

  • MCP: 도구 연결 표준
  • Agent Protocol: 에이전트 인터페이스 표준
  • OpenAPI for Agents: API 기반 에이전트 정의

2026년에는 "하네스 표준"이 등장하여, 어떤 프레임워크에서든 동일한 구성 요소를 사용할 수 있게 될 것으로 전망됩니다.


10. 퀴즈

Q1: AI 하네스의 비유에서 "야생마"에 해당하는 것은 무엇인가요?

정답: Raw LLM (GPT-4, Claude, Gemini 등의 기반 모델)

야생마는 엄청난 힘(지능)을 가지고 있지만, 마구(하네스) 없이는 그 힘을 원하는 방향으로 쓸 수 없습니다. Raw LLM도 마찬가지로, 도구, 컨텍스트, 권한, 스킬 등의 하네스 없이는 실용적인 작업을 수행하기 어렵습니다.

Q2: 하네스의 7가지 구성 요소를 모두 나열하세요.

정답:

  1. System Prompt (시스템 프롬프트)
  2. Tools (도구)
  3. Context (컨텍스트)
  4. Skills (스킬)
  5. Hooks (훅)
  6. Permissions (권한)
  7. Memory (기억)

이 7가지가 결합하여 Raw LLM을 실용적인 AI 에이전트로 변환합니다.

Q3: PreToolUse 훅과 PostToolUse 훅의 차이점은 무엇인가요?

정답:

  • PreToolUse: 도구 사용 에 실행됩니다. 입력 검증, 권한 확인, 위험한 명령어 차단 등의 용도로 사용됩니다. 검증 실패 시 도구 실행을 중단할 수 있습니다.
  • PostToolUse: 도구 사용 에 실행됩니다. 결과 검증, 자동 포맷팅(prettier, eslint), 자동 테스트 등의 용도로 사용됩니다.

Git 훅의 pre-commit(커밋 전 검증)과 post-commit(커밋 후 알림)과 유사한 개념입니다.

Q4: Orchestrator-Worker 패턴과 Pipeline 패턴의 차이점은 무엇인가요?

정답:

  • Orchestrator-Worker 패턴: 중앙 오케스트레이터가 작업을 분할하고 여러 워커에게 병렬로 배분합니다. 각 워커는 독립적으로 작업하고, 오케스트레이터가 결과를 통합합니다. 예: 여러 파일을 동시에 리뷰하기.
  • Pipeline 패턴: 작업이 순차적으로 처리됩니다. 각 단계의 출력이 다음 단계의 입력이 됩니다. 예: 분석 후 계획 후 실행 후 검증.

핵심 차이는 병렬 실행(Orchestrator-Worker) vs 순차 실행(Pipeline)입니다.

Q5: 2025년 AI 엔지니어링의 무게중심이 "모델 훈련"에서 무엇으로 이동했나요? 그 이유는?

정답: 모델 오케스트레이션 (Model Orchestration)

이유:

  1. 기반 모델이 충분히 강력해져서 대부분의 작업에 별도 훈련이 불필요해졌습니다
  2. 같은 모델을 사용해도 하네스 설계에 따라 결과가 크게 달라집니다
  3. 엔터프라이즈에서 보안, 감사, 권한 관리, 비용 추적이 필수가 되었습니다
  4. Devin, Claude Code 같은 자율 에이전트들이 정교한 하네스를 사용하여 차별화를 만들고 있습니다

따라서 "어떤 모델을 쓸까?"보다 "모델을 어떻게 감싸고 제어할까?"가 핵심 질문이 되었습니다.


11. 참고 자료

  1. Anthropic, "Claude Agent SDK Documentation," 2025
  2. Anthropic, "Model Context Protocol (MCP) Specification," 2025
  3. Anthropic, "Claude Code: An Agentic Coding Tool," 2025
  4. Harrison Chase, "LangGraph: Building Stateful Agent Workflows," LangChain Blog, 2025
  5. CrewAI, "Multi-Agent Orchestration Framework Documentation," 2025
  6. Microsoft, "AutoGen: Enabling Next-Gen LLM Applications," 2025
  7. Microsoft, "Semantic Kernel Documentation," 2025
  8. OpenAI, "Function Calling Guide," 2025
  9. Anthropic, "Building Effective Agents," Research Blog, 2025
  10. Lilian Weng, "LLM Powered Autonomous Agents," OpenAI Blog, 2024
  11. Shunyu Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models," ICLR, 2023
  12. Andrew Ng, "Agentic Design Patterns," DeepLearning.AI, 2025
  13. Simon Willison, "Building AI-Powered Tools with LLMs," Blog, 2025
  14. Chip Huyen, "Building LLM Applications for Production," 2025
  15. Devin AI, "How Devin Works: Architecture and Design," Cognition Blog, 2025
  16. GitHub, "Copilot Agent Mode: Architecture Deep Dive," GitHub Blog, 2025