Split View: AI 하네스란 무엇인가: Skills, Context, Hooks, Permissions로 AI를 제어하는 오케스트레이션 아키텍처
AI 하네스란 무엇인가: Skills, Context, Hooks, Permissions로 AI를 제어하는 오케스트레이션 아키텍처
- 1. AI 하네스란? — 왜 LLM만으로는 부족한가
- 2. 하네스의 7가지 구성 요소
- 3. Claude Code 하네스 아키텍처 심화
- 4. Claude Agent SDK로 커스텀 하네스 만들기
- 5. 다른 AI 하네스 프레임워크 비교
- 6. 하네스 설계 패턴
- 7. 실전: 나만의 코드 리뷰 하네스 만들기
- 8. 하네스 평가 및 모니터링
- 9. 2025-2026 하네스 트렌드
- 10. 퀴즈
- 11. 참고 자료
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 LLM | Harnessed LLM |
|---|---|---|
| 도구 접근 | 없음 | 파일 읽기/쓰기, API 호출, DB 쿼리 |
| 컨텍스트 | 대화 내용만 | 프로젝트 구조, 코드베이스, 문서 |
| 권한 | 무제한 (또는 전혀 없음) | 세밀한 권한 제어 |
| 기억 | 세션 내 대화만 | 장기 기억, 프로젝트 히스토리 |
| 워크플로우 | 없음 | 스킬, 훅, 파이프라인 |
| 안전장치 | 모델 자체 안전 | 가드레일, 권한 모델, 입출력 검증 |
| 일관성 | 프롬프트에 의존 | 시스템 프롬프트로 안정적 |
2025년 AI 엔지니어링의 무게중심 이동
2023년까지 AI 엔지니어링의 핵심은 모델 훈련이었습니다. 더 크고, 더 똑똑한 모델을 만드는 것이 목표였습니다.
2025년, 무게중심이 완전히 이동했습니다: 모델 오케스트레이션이 핵심입니다.
왜냐하면:
- 기반 모델이 충분히 강력해졌습니다 — Claude Sonnet 4, GPT-4o는 대부분의 작업에 충분한 지능을 가지고 있습니다
- 차별화는 하네스에서 일어납니다 — 같은 모델을 사용해도 하네스 설계에 따라 결과가 크게 달라집니다
- 엔터프라이즈 요구사항 — 보안, 감사, 권한 관리, 비용 추적이 필수가 되었습니다
- 에이전트의 부상 — Devin, Claude Code, GitHub Copilot Agent 같은 자율 에이전트들이 모두 정교한 하네스를 사용합니다
AI 하네스는 2025-2026년 AI 엔지니어링의 가장 중요한 개념입니다.
2. 하네스의 7가지 구성 요소
AI 하네스는 7가지 핵심 구성 요소로 이루어져 있습니다. 각 요소를 깊이 있게 살펴보겠습니다.
2-1. System Prompt (기본 규칙)
시스템 프롬프트는 AI의 헌법입니다. AI가 어떤 역할을 맡고, 어떤 규칙을 따르며, 어떤 제약사항을 지켜야 하는지를 최상위 수준에서 정의합니다.
시스템 프롬프트의 구성 요소
- 역할 정의: AI가 무엇인지, 어떤 전문성을 가졌는지
- 행동 규칙: 어떤 방식으로 응답하고 행동해야 하는지
- 제약사항: 절대 해서는 안 되는 것
- 출력 형식: 응답의 형태와 구조
- 안전 규칙: 보안, 프라이버시, 저작권 관련 규칙
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의 도구 사용은 다음 과정을 거칩니다:
- 사용자가 요청을 보냄
- AI가 어떤 도구를 사용할지 결정 (추론)
- AI가 도구 호출을 생성 (JSON 형식)
- 하네스가 실제로 도구를 실행
- 결과를 AI에게 반환
- 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원칙
- 단일 책임: 하나의 도구는 하나의 일만 한다
- 명확한 설명: AI가 언제 사용해야 하는지 이해할 수 있어야 한다
- 강타입 파라미터: JSON Schema로 입력을 엄격하게 검증
- 실패 처리: 도구 실행 실패 시 의미 있는 에러 메시지 반환
- 부작용 최소화: 읽기 작업은 안전하게, 쓰기 작업은 확인 후 실행
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는 계층적으로 로딩됩니다:
- 홈 디렉토리의 CLAUDE.md (전역 설정)
- 프로젝트 루트의 CLAUDE.md (프로젝트 설정)
- 하위 디렉토리의 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)이지만, 효율적으로 관리해야 합니다:
- 요약(Summarization): 긴 파일은 핵심만 추출
- 청크(Chunking): 필요한 부분만 선택적으로 로딩
- 우선순위(Priority): 현재 작업과 관련성 높은 정보 우선
- 동적 로딩: 필요할 때 추가 컨텍스트를 가져오기
- 서브에이전트 위임: 독립 작업은 별도 컨텍스트에서 처리
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 프롬프트 템플릿
| 항목 | 프롬프트 템플릿 | 스킬 |
|---|---|---|
| 트리거 | 수동 선택 | 자동 감지 |
| 도구 활용 | 없음 | 도구 사용 지시 포함 |
| 워크플로우 | 단일 프롬프트 | 다단계 워크플로우 |
| 컨텍스트 | 정적 | 동적 컨텍스트 로딩 |
| 학습 | 없음 | 결과 피드백 반영 가능 |
스킬 작성 가이드
좋은 스킬을 만드는 핵심 원칙:
- 명확한 트리거: 어떤 상황에서 활성화되는지 정확하게 정의
- 단계별 분해: 복잡한 작업을 작은 단계로 분해
- 도구 매핑: 각 단계에서 어떤 도구를 사용하는지 명시
- 예외 처리: 실패 시나리오와 대안 경로 정의
- 출력 형식: 결과물의 형태를 구체적으로 정의
2-5. Hooks (훅)
훅은 AI의 도구 사용 전후에 자동으로 실행되는 스크립트입니다. 마치 Git의 pre-commit, post-commit 훅처럼, AI의 행동에 자동화된 검증과 처리를 추가합니다.
훅의 4가지 유형
-
PreToolUse: 도구 사용 전에 실행
- 용도: 입력 검증, 권한 확인, 파라미터 변환
- 예: Write 도구 호출 전에 파일 경로 검증
-
PostToolUse: 도구 사용 후에 실행
- 용도: 결과 검증, 포맷팅, 린트, 테스트
- 예: 파일 저장 후 자동으로 ESLint 실행
-
Notification: 알림 전에 실행
- 용도: 알림 내용 가공, 필터링
-
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 에이전트에 최소 권한 원칙을 적용하는 단계:
- 필요한 도구만 제공: 코드 리뷰에 배포 도구는 불필요
- 파일 범위 제한: 프로젝트 디렉토리 외부 접근 차단
- 명령어 화이트리스트: 허용된 셸 명령만 실행 가능
- 시간 제한: 장시간 실행 방지
- 비용 제한: 최대 토큰 사용량 설정
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 스타일이다"
- 일반적인 사실과 규칙
메모리 관리 전략
- 자동 요약: 긴 대화는 핵심 포인트만 추출하여 저장
- 중요도 기반 정리: 자주 참조되는 기억은 유지, 불필요한 기억은 아카이브
- 계층적 구조: 핵심 기억 > 최근 기억 > 아카이브
- 충돌 해결: 새로운 정보가 기존 기억과 충돌하면 업데이트
- 프라이버시: 민감한 정보는 암호화하거나 저장하지 않음
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 SDK | Python/TS | 도구, 권한, 이벤트 | 가장 완전 | 중간 |
| LangGraph | Python | 그래프 기반 워크플로우 | 높음 | 높음 |
| CrewAI | Python | 멀티에이전트 협업 | 중간 | 낮음 |
| AutoGen | Python | 에이전트 대화 | 중간 | 중간 |
| Semantic Kernel | C#/Python | MS 에코시스템 통합 | 중간 | 중간 |
| DSPy | Python | 프롬프트 최적화 | 낮음 | 높음 |
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 테스트로 비교합니다:
- Baseline 설정: 현재 하네스의 성능 측정
- 변경 적용: 새로운 시스템 프롬프트, 도구, 스킬 적용
- 동일 입력 테스트: 같은 테스트 케이스로 두 버전 실행
- 메트릭 비교: 정확도, 완료율, 비용 비교
- 통계적 유의성 확인: 차이가 우연이 아닌지 검증
비용 추적
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가지 구성 요소를 모두 나열하세요.
정답:
- System Prompt (시스템 프롬프트)
- Tools (도구)
- Context (컨텍스트)
- Skills (스킬)
- Hooks (훅)
- Permissions (권한)
- 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)
이유:
- 기반 모델이 충분히 강력해져서 대부분의 작업에 별도 훈련이 불필요해졌습니다
- 같은 모델을 사용해도 하네스 설계에 따라 결과가 크게 달라집니다
- 엔터프라이즈에서 보안, 감사, 권한 관리, 비용 추적이 필수가 되었습니다
- Devin, Claude Code 같은 자율 에이전트들이 정교한 하네스를 사용하여 차별화를 만들고 있습니다
따라서 "어떤 모델을 쓸까?"보다 "모델을 어떻게 감싸고 제어할까?"가 핵심 질문이 되었습니다.
11. 참고 자료
- Anthropic, "Claude Agent SDK Documentation," 2025
- Anthropic, "Model Context Protocol (MCP) Specification," 2025
- Anthropic, "Claude Code: An Agentic Coding Tool," 2025
- Harrison Chase, "LangGraph: Building Stateful Agent Workflows," LangChain Blog, 2025
- CrewAI, "Multi-Agent Orchestration Framework Documentation," 2025
- Microsoft, "AutoGen: Enabling Next-Gen LLM Applications," 2025
- Microsoft, "Semantic Kernel Documentation," 2025
- OpenAI, "Function Calling Guide," 2025
- Anthropic, "Building Effective Agents," Research Blog, 2025
- Lilian Weng, "LLM Powered Autonomous Agents," OpenAI Blog, 2024
- Shunyu Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models," ICLR, 2023
- Andrew Ng, "Agentic Design Patterns," DeepLearning.AI, 2025
- Simon Willison, "Building AI-Powered Tools with LLMs," Blog, 2025
- Chip Huyen, "Building LLM Applications for Production," 2025
- Devin AI, "How Devin Works: Architecture and Design," Cognition Blog, 2025
- GitHub, "Copilot Agent Mode: Architecture Deep Dive," GitHub Blog, 2025
What is an AI Harness: Skills, Context, Hooks, and Permissions — The Orchestration Architecture Controlling AI
- 1. What Is an AI Harness? — Why a Raw LLM Is Not Enough
- 2. The 7 Building Blocks of a Harness
- 3. Claude Code Harness Architecture Deep Dive
- 4. Building a Custom Harness with the Claude Agent SDK
- 5. Comparing AI Harness Frameworks
- 6. Harness Design Patterns
- 7. Hands-On: Building Your Own Code Review Harness
- 8. Harness Evaluation and Monitoring
- 9. 2025-2026 Harness Trends
- 10. Quiz
- 11. References
1. What Is an AI Harness? — Why a Raw LLM Is Not Enough
The Limits of a Raw LLM
Large language models like GPT-4o, Claude Sonnet, and Gemini Pro possess remarkable capabilities. Yet a model by itself cannot produce a practical AI system.
Ask a raw LLM to "review our project's PR" and here is what happens:
- It cannot read the project code (no tools)
- It does not know which coding conventions to follow (no context)
- It cannot run git diff (no permissions)
- It does not remember previous review history (no memory)
- It cannot post comments automatically after the review (no workflow)
An LLM is a powerful engine, but an engine alone is not a car.
Putting a Harness on a Horse
The word "harness" originally refers to the gear placed on a horse. A wild horse has tremendous power, but without a harness you cannot direct that power where you need it. With a harness, the horse can pull a carriage, plow a field, or haul cargo.
An AI Harness follows the same principle:
- Wild horse = Raw LLM (GPT-4, Claude, Gemini)
- Harness = AI Harness (system prompt, tools, permissions, skills, hooks)
- Carriage / field = Real-world tasks (code review, data analysis, customer support)
A well-designed harness maximizes the horse's strength. A poorly designed harness causes the horse to bolt in the wrong direction — or worse, to do something dangerous.
Raw LLM vs Harnessed LLM
| Aspect | Raw LLM | Harnessed LLM |
|---|---|---|
| Tool access | None | File I/O, API calls, DB queries |
| Context | Conversation only | Project structure, codebase, docs |
| Permissions | Unlimited (or none at all) | Fine-grained access control |
| Memory | Current session only | Long-term memory, project history |
| Workflow | None | Skills, hooks, pipelines |
| Safety | Model-inherent safety only | Guardrails, permissions model, I/O validation |
| Consistency | Depends on prompt | Stabilized by system prompt |
The Paradigm Shift of 2025 AI Engineering
Until 2023, the core of AI engineering was model training — building bigger, smarter models.
By 2025, the center of gravity has shifted completely: model orchestration is the new core.
Why:
- Foundation models are powerful enough — Claude Sonnet 4 and GPT-4o have sufficient intelligence for most tasks
- Differentiation happens in the harness — the same model yields vastly different results depending on harness design
- Enterprise requirements — security, auditing, permission management, and cost tracking are now essential
- Rise of autonomous agents — Devin, Claude Code, and GitHub Copilot Agent all use sophisticated harnesses
The AI Harness is the single most important concept in 2025-2026 AI engineering.
2. The 7 Building Blocks of a Harness
An AI Harness is composed of seven core building blocks. Let us examine each one in depth.
2-1. System Prompt (The Ground Rules)
The system prompt is the AI's constitution. It defines the role, rules, and constraints at the highest level.
Components of a System Prompt
- Role definition: What the AI is and what expertise it possesses
- Behavioral rules: How it should respond and act
- Constraints: What it must never do
- Output format: The shape and structure of responses
- Safety rules: Security, privacy, and copyright guidelines
Claude Code's System Prompt Structure
Claude Code uses a highly sophisticated system prompt:
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
The key insight: role (CLI tool), strengths (code search, analysis), and rules (minimize file creation) are clearly separated.
System Prompt Best Practices
# Structure of a good system prompt
## 1. Role Declaration (Who)
You are a senior backend engineer.
You specialize in Java/Spring Boot with 10+ years of experience.
## 2. Behavioral Rules (How)
- Check for security vulnerabilities first during code review
- Point out performance issues with Big-O analysis
- Always include code examples with suggestions
## 3. Constraints (Boundaries)
- Do not modify code directly; only suggest
- Do not recommend adding external libraries
- Follow existing architecture patterns
## 4. Output Format (Format)
Present review results in this format:
- [Severity]: Description + Suggestion
Practical Examples by Role
Code Reviewer:
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.
Data Analyst:
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 are the interfaces through which AI interacts with the external world. An LLM by itself can only generate text; tools let it read files, execute commands, and call APIs.
How Function Calling Works
The tool-use lifecycle:
- The user sends a request
- The AI decides which tool to use (reasoning)
- The AI generates a tool call (JSON format)
- The harness executes the tool
- The result is returned to the AI
- The AI interprets the result and decides the next action
Tool Definition Structure
Tools are defined with 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"]
}
}
Key principles:
- Name: verb + noun (read_file, search_code, run_command)
- Description: clear enough for the AI to know when to use the tool
- Parameters: strongly typed with JSON Schema, required/optional clearly defined
Core Tools in Claude Code
| Tool | Purpose | Example |
|---|---|---|
| Bash | Execute shell commands | git status, npm test |
| Read | Read files | Source code, config files |
| Write | Create files | New file creation |
| Edit | Modify files | Change existing code |
| Grep | Pattern search | Search across the codebase |
| Glob | File finder | Filename pattern matching |
| WebSearch | Web search | Latest docs, API references |
| WebFetch | Fetch web pages | Read URL content |
Extending Tools with MCP (Model Context Protocol)
MCP is a standard protocol that lets AI agents access external tools and data sources. Just as USB-C connects diverse devices through a single interface, MCP connects diverse services to 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"
}
}
}
}
Adding MCP servers gives the AI new capabilities: managing GitHub issues, querying databases, sending Slack messages, and more.
The 5 Principles of Tool Design
- Single responsibility: One tool, one job
- Clear description: The AI must understand when to use it
- Strongly typed parameters: Strict validation via JSON Schema
- Failure handling: Return meaningful error messages on failure
- Minimize side effects: Reads are safe; writes require confirmation
2-3. Context
Context is all the information the AI needs to understand the current situation. The same question can demand entirely different answers depending on context.
CLAUDE.md: Project-Level Instructions
CLAUDE.md is the project's user manual for the AI. It defines how the AI should behave in that specific project.
# Project Guidelines
## Build Commands
- npm run dev: start development server
- npm run build: production build
- npm test: run tests
## Code Conventions
- TypeScript strict mode
- Functional components only (no class components)
- Tailwind CSS instead of CSS-in-JS
## Architecture
- src/components: reusable UI components
- src/hooks: custom hooks
- src/lib: utility functions
- src/app: Next.js App Router pages
CLAUDE.md files are loaded hierarchically:
- Home directory CLAUDE.md (global settings)
- Project root CLAUDE.md (project settings)
- Sub-directory CLAUDE.md (module-level settings)
Lower-level settings override higher-level ones.
Codebase Indexing
An AI harness indexes the project's file structure, dependencies, and architecture:
Project structure analysis:
- package.json: dependencies, scripts
- tsconfig.json: TypeScript configuration
- .eslintrc: code style rules
- .gitignore: excluded file patterns
- Directory structure: infer architecture patterns
Memory: Cross-Session Persistence
The .remember/ directory is the AI's long-term memory store:
.remember/
core-memories.md # Project essentials
now.md # Current work state
today.md # Today's activity log
recent.md # Recent work history
archive.md # Old records archive
Context Window Management Strategies
Claude's context window is 200K tokens (up to 1M), but efficient management is critical:
- Summarization: Extract only the essentials from long files
- Chunking: Selectively load only the needed portions
- Prioritization: Favor information relevant to the current task
- Dynamic loading: Fetch additional context on demand
- Sub-agent delegation: Process independent tasks in separate contexts
2-4. Skills
Skills are bundles of reusable domain knowledge and workflows. Similar to prompt templates, but richer: they include trigger conditions, tool-usage directives, and step-by-step workflows.
SKILL.md File Structure
---
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
Built-in Skills in Claude Code
Claude Code ships with several built-in skills:
- commit: check git status, analyze changes, write commit message, execute commit
- review-pr: analyze PR, code review, write comments
- test-driven-development: write test first, implement, refactor
- brainstorming: diverge ideas, structure, prioritize
Skills vs Prompt Templates
| Aspect | Prompt Template | Skill |
|---|---|---|
| Trigger | Manual selection | Automatic detection |
| Tool usage | None | Tool-use directives included |
| Workflow | Single prompt | Multi-step workflow |
| Context | Static | Dynamic context loading |
| Learning | None | Can incorporate result feedback |
Skill Authoring Guide
Core principles for writing great skills:
- Clear trigger: Precisely define when the skill activates
- Step-by-step decomposition: Break complex tasks into small steps
- Tool mapping: Specify which tool is used at each step
- Exception handling: Define failure scenarios and fallback paths
- Output format: Concretely define the shape of the deliverable
2-5. Hooks
Hooks are scripts that run automatically before or after AI tool use. Like Git's pre-commit and post-commit hooks, they add automated validation and processing to AI actions.
The 4 Types of Hooks
-
PreToolUse: Runs before tool execution
- Purpose: input validation, permission checks, parameter transformation
- Example: validate file path before a Write call
-
PostToolUse: Runs after tool execution
- Purpose: result validation, formatting, linting, testing
- Example: run ESLint automatically after saving a file
-
Notification: Runs before a notification is sent
- Purpose: notification content processing, filtering
-
Stop: Runs when the agent stops
- Purpose: cleanup tasks, state persistence
Configuration Example: 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'"
}
]
}
}
Real-World Hook Scenarios
Scenario 1: Auto-format after file save
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"command": "/bin/sh -c 'npx prettier --write FILE_PATH'"
}
]
}
}
Scenario 2: Auto-test before commit
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "/bin/sh -c 'if echo TOOL_INPUT | grep -q \"git commit\"; then npm test || exit 1; fi'"
}
]
}
}
Scenario 3: Block dangerous commands
{
"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
The permissions model is the security layer that defines the scope of actions the AI can perform. It applies the Principle of Least Privilege to AI.
Risk Classification of Actions
| Risk Level | Action Type | Examples | Policy |
|---|---|---|---|
| Safe | Read | File reading, search, lookup | Auto-allow |
| Caution | Write | File modification, creation | Allow after confirmation |
| Dangerous | Delete/Execute | File deletion, deployment | Explicit approval required |
| Forbidden | System change | OS settings, account changes | Always blocked |
allowedTools and 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 Mode vs Agent Mode
Sandbox Mode:
- Network access blocked
- File system read-only
- Shell commands restricted
- Safe experimentation environment
Agent Mode:
- Network access allowed
- File system read/write
- Shell commands allowed (with pattern-based filtering)
- Real-work execution environment
Applying the Principle of Least Privilege
Steps to apply least privilege to an AI agent:
- Provide only needed tools: A code review agent does not need deployment tools
- Limit file scope: Block access outside the project directory
- Whitelist commands: Only allowed shell commands can run
- Time limits: Prevent long-running executions
- Cost limits: Set a maximum token budget
2-7. Memory
Memory is the mechanism that lets the AI retain past interactions and learned knowledge.
Short-Term vs Long-Term Memory
Short-Term Memory:
- The current conversation session context
- All messages within the context window
- Disappears when the session ends
Long-Term Memory:
- Stored as files in the
.remember/directory - Persists across sessions
- Project-level or user-level
Memory File Structure
# core-memories.md
- This project uses Next.js 14 + TypeScript + Tailwind CSS
- Deployed on Vercel
- Database is PostgreSQL (Supabase)
# now.md
Current task: refactoring the user authentication module
Progress: migrating from NextAuth.js to Lucia Auth
Blocker: social login callback URL configuration issue
# today.md
- 09:30 Completed authentication module analysis
- 10:15 Created Lucia Auth configuration files
- 11:00 Migrating session management logic
- 14:00 Started social login integration
Episodic Memory vs Semantic Memory
Episodic Memory (event-based):
- "Yesterday the user asked me to fix an auth bug"
- "Last week we changed the database schema"
- Contains specific events and timestamps
Semantic Memory (knowledge-based):
- "This project uses a REST API"
- "The team follows Airbnb coding conventions"
- Contains general facts and rules
Memory Management Strategies
- Auto-summarization: Extract key points from long conversations
- Importance-based pruning: Keep frequently referenced memories, archive the rest
- Hierarchical structure: Core memories > Recent memories > Archive
- Conflict resolution: Update when new information contradicts existing memories
- Privacy: Encrypt or exclude sensitive information
3. Claude Code Harness Architecture Deep Dive
3-1. The Agent Loop
The heart of Claude Code is the agent loop. Rather than simple question-answer, it is an iterative cycle of using tools, observing results, and deciding the next action.
Agent Loop Flow:
1. User Input
|
2. Load System Prompt + Context
|
3. LLM Reasoning (decide what action to take)
|
4. Decide Tool Call
|
5. Execute Hook (PreToolUse)
| (on validation failure -> return to step 3)
|
6. Execute Tool
|
7. Execute Hook (PostToolUse)
|
8. Observe Result
|
9. More actions needed? -> Yes -> return to step 3
| No
|
10. Generate Final Response
This loop continues until the max_turns limit is reached or the AI judges the task complete.
Practical Example: PR Review Loop
Turn 1: Run git diff (Bash tool)
-> Obtain list of changed files and diffs
Turn 2: Read changed files (Read tool)
-> Understand full context
Turn 3: Read CLAUDE.md (Read tool)
-> Check project conventions
Turn 4: Find related test files (Grep tool)
-> Verify test coverage
Turn 5: Run npm test (Bash tool)
-> Confirm tests pass
Turn 6: Generate review result (final response)
-> Review from security, performance, maintainability perspectives
3-2. Sub-Agent Architecture
Complex tasks are hard for a single agent to handle. Claude Code uses a sub-agent architecture.
Main Agent and Sub-Agents
Main Agent (Orchestrator)
|
+--- Sub-Agent A: "Analyze src/ directory"
| (independent context, file read/search tools)
|
+--- Sub-Agent B: "Analyze tests/ directory"
| (independent context, test execution tools)
|
+--- Sub-Agent C: "Update documentation"
(independent context, file write tools)
Main Agent: integrate results from all sub-agents into the final response
Key characteristics of sub-agents:
- Independent context: Does not pollute the main agent's context
- Parallel execution: Independent tasks can run concurrently
- Result integration: The main agent collects and synthesizes results
Background Agent vs Foreground Agent
- Foreground Agent: The main agent that directly converses with the user
- Background Agent: An asynchronous agent that works independently
Background Agent use cases:
- Large-scale refactoring
- Running tests across many files
- Automatic documentation generation
Worktree Isolation
Isolation via Git worktrees:
Main project (main branch)
|
+--- .claude/worktrees/feature-auth/
| (independent branch, independent working directory)
|
+--- .claude/worktrees/fix-bug-123/
(independent branch, independent working directory)
Each worktree has an independent file system, ensuring that tasks do not interfere with one another.
3-3. settings.json In-Depth
Claude Code configuration is managed through settings.json.
Global vs Project Settings
Settings locations (in priority order):
1. project/.claude/settings.json (highest priority)
2. home-directory/.claude/settings.json (global)
3. Defaults (lowest priority)
Full Settings Structure
{
"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"
}
Settings Priority
Highest <- Project settings <- Global settings <- Defaults -> Lowest
Project settings override global settings, allowing different permissions and hooks per project.
4. Building a Custom Harness with the Claude Agent SDK
4-1. Python SDK
Anthropic's Claude Agent SDK is a framework for building custom AI agents.
Basic Agent Creation
import anthropic
from typing import Any
# Initialize the Anthropic client
client = anthropic.Anthropic()
# Define tools
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 filter (e.g., py, ts)"
}
},
"required": ["pattern"]
}
}
]
def execute_tool(name: str, input_data: dict) -> str:
"""Execute a tool and return the result."""
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:
"""Run the agent loop."""
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):
# Call the LLM
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
system=system_prompt,
tools=tools,
messages=messages,
)
# Process the response
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
return "Agent completed without text response"
# Handle tool calls
if response.stop_reason == "tool_use":
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"
# Run
if __name__ == "__main__":
review = run_agent("Review the authentication module in src/auth/")
print(review)
Adding a Permissions Model
class PermissionModel:
"""Manage permissions for the AI agent."""
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:
"""Check file access permission."""
return any(path.startswith(p) for p in self.allowed_paths)
def check_command(self, command: str) -> bool:
"""Check command execution permission."""
return not any(cmd in command for cmd in self.blocked_commands)
def validate_tool_call(self, tool_name: str, input_data: dict) -> tuple:
"""Validate a tool call.
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"
Event Handling and Monitoring
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class AgentEvent:
"""An event recording an agent action."""
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:
"""Monitor agent behavior."""
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
The same patterns apply when building a harness in TypeScript.
import Anthropic from '@anthropic-ai/sdk'
// Tool definitions
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'],
},
},
]
// Permissions model
class QueryPermissions {
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' }
}
}
// Tool execution
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}`
}
// Simulated DB query
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}`
}
// Agent loop
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'
}
// Execute
async function main() {
const result = await runDataAnalysisAgent('What was the revenue trend in Q1 2025?')
console.log(result)
}
main().catch(console.error)
5. Comparing AI Harness Frameworks
Framework Comparison
| Framework | Language | Core Concept | Harness Level | Learning Curve |
|---|---|---|---|---|
| Claude Agent SDK | Python/TS | Tools, permissions, events | Most complete | Medium |
| LangGraph | Python | Graph-based workflows | High | High |
| CrewAI | Python | Multi-agent collaboration | Medium | Low |
| AutoGen | Python | Agent conversations | Medium | Medium |
| Semantic Kernel | C#/Python | MS ecosystem integration | Medium | Medium |
| DSPy | Python | Prompt optimization | Low | High |
Claude Agent SDK
Strengths:
- Optimal compatibility with Anthropic models
- Built-in permissions model, tool use, and event system
- Production-level stability
Harness implementation:
- System Prompt + Tools + Permissions integrated at the SDK level
- Agent loop is built-in; no separate implementation needed
LangGraph
Strengths:
- Visualize complex workflows as graphs
- State management, conditional branching, parallel execution
- Checkpoint and rollback capabilities
Harness implementation:
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:
"""Analyze security vulnerabilities."""
state["security_issues"] = ["SQL injection in login.py:42"]
return state
def analyze_performance(state: ReviewState) -> ReviewState:
"""Analyze performance issues."""
state["performance_issues"] = ["N+1 query in users.py:87"]
return state
def generate_review(state: ReviewState) -> ReviewState:
"""Generate the final review."""
issues = state["security_issues"] + state["performance_issues"]
state["review_summary"] = f"Found {len(issues)} issues"
return state
# Build the graph
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
Strengths:
- Intuitive multi-agent model (role-based)
- "Agent = Role + Goal + Backstory" pattern
- Natural inter-agent collaboration
Harness implementation:
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. Harness Design Patterns
Let us examine the key patterns you can apply when designing an AI harness.
6-1. Router Pattern
Analyze the input and route it to the appropriate specialized agent.
User Input
|
v
[Router Agent] -> "code review request" -> Code Review Agent
-> "data analysis request" -> Data Analysis Agent
-> "documentation request" -> Documentation Agent
-> "general question" -> General Assistant
Best for:
- Handling diverse request types
- When specialized agents are optimized for each domain
- When a single agent cannot cover all domains
6-2. Orchestrator-Worker Pattern
A central orchestrator splits work and distributes it to workers.
[Orchestrator]
|
+--- "Review file A" -> [Worker 1] -> Result A
|
+--- "Review file B" -> [Worker 2] -> Result B
|
+--- "Review file C" -> [Worker 3] -> Result C
|
v
[Orchestrator] -> Integrate A + B + C -> Final Review
Best for:
- Processing large tasks in parallel
- When work can be split into independent units
- When integration logic is needed for the results
6-3. Pipeline Pattern
Sequential processing where each stage's output becomes the next stage's input.
[Analysis Agent] -> Code analysis results
|
v
[Planning Agent] -> Refactoring plan
|
v
[Execution Agent] -> Code modifications
|
v
[Validation Agent] -> Test execution + result verification
Best for:
- Tasks with a clear sequence
- When each step depends on the previous step's output
- When stage-by-stage quality validation is needed
6-4. Evaluator-Optimizer Pattern
Evaluate results and retry if they fall below standards.
[Generator Agent] -> Draft
|
v
[Evaluator Agent] -> Quality score (1-10)
|
+--- Score >= 8 -> Done
|
+--- Score < 8 -> Send feedback to Generator for revision
|
v
[Generator Agent] -> Revised draft
|
v
[Evaluator Agent] -> Re-evaluate
...
Best for:
- When output quality is paramount
- When quality can be objectively measured
- When iterative improvement is possible
6-5. Guardrails Pattern
Ensure safe AI behavior through input/output validation.
User Input
|
v
[Input Guardrail] -> Harmful content filtering
| Prompt injection detection
| Input normalization
v
[AI Agent] -> Perform task
|
v
[Output Guardrail] -> PII masking
| Hallucination detection
| Format validation
v
Final Response
Implementation example:
class InputGuardrail:
"""Validate and sanitize inputs."""
def validate(self, user_input: str) -> tuple:
# Detect prompt injection
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"
# Input length limit
if len(user_input) > 10000:
return False, "Input too long"
return True, "Valid input"
class OutputGuardrail:
"""Validate and sanitize outputs."""
def validate(self, output: str) -> str:
import re
# Mask email addresses
output = re.sub(
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
"[EMAIL_REDACTED]",
output,
)
# Mask phone numbers
output = re.sub(
r"\b\d{3}[-.]?\d{4}[-.]?\d{4}\b",
"[PHONE_REDACTED]",
output,
)
return output
7. Hands-On: Building Your Own Code Review Harness
Let us build a working code review harness from scratch.
7-1. Project Structure
code-review-harness/
src/
agent.py # Agent loop
tools.py # Tool definitions and execution
permissions.py # Permissions model
guardrails.py # Input/output validation
memory.py # Memory management
monitor.py # Monitoring
skills/
code-review.md # Code review skill
security-audit.md # Security audit skill
config/
settings.json # Configuration file
tests/
test_agent.py # Agent tests
README.md
requirements.txt
7-2. System Prompt Design
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. Tool Implementation
# tools.py
import subprocess
import os
def git_diff(base_branch: str = "main") -> str:
"""Get the diff between the current branch and the base branch."""
result = subprocess.run(
["git", "diff", f"{base_branch}...HEAD"],
capture_output=True,
text=True,
)
return result.stdout
def read_file(path: str) -> str:
"""Read file contents with line numbers."""
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:
"""Search for a pattern in the codebase."""
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:
"""Run the test suite."""
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:
"""Get the git history of a file."""
result = subprocess.run(
["git", "log", f"-{count}", "--oneline", "--", path],
capture_output=True,
text=True,
)
return result.stdout
7-4. Skill Definition
# 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 Model
# permissions.py
class ReviewPermissions:
"""Define permissions for the code review agent."""
# Read-only: no code modification allowed
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. Harness Evaluation and Monitoring
Harness Quality Metrics
Key metrics for measuring AI harness quality:
| Metric | Description | Target |
|---|---|---|
| Tool use accuracy | Correct tool called with correct parameters | 95%+ |
| Task completion rate | Successfully completing user requests | 90%+ |
| Safety violation frequency | Bypassing guardrails or violating permissions | 0% |
| Average turns | Mean agent-loop iterations to completion | 5 or fewer |
| Cost efficiency | Average token usage and API cost per task | Varies by task |
LLM-as-Judge
Use another LLM to evaluate the quality of the agent's output:
def evaluate_review_quality(
original_code: str,
review_output: str,
evaluator_model: str = "claude-sonnet-4-20250514"
) -> dict:
"""Evaluate code review quality using an 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
"""
# Call LLM for evaluation
# ... (actual API call code)
return evaluation_result
A/B Testing
Compare versions when changing prompts or tools:
- Establish baseline: Measure current harness performance
- Apply changes: New system prompt, tools, or skills
- Test with identical inputs: Run both versions on the same test cases
- Compare metrics: Accuracy, completion rate, cost
- Statistical significance: Verify the difference is not due to chance
Cost Tracking
class CostTracker:
"""Track API call costs for the agent."""
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 Harness Trends
Model-Native Tool Calling
Early LLMs needed to be taught tool usage through prompts. The latest 2025 models have internalized tool usage during training. Claude Sonnet 4 and GPT-4o natively support Function Calling.
Impact on harnesses:
- Accurate tool usage even with concise descriptions
- Autonomous planning of complex tool combinations
- Improved error recovery
The Rise of Autonomous Agents
2025 is the breakout year for autonomous agents:
- Devin: An AI software engineer by Cognition
- Claude Code: Anthropic's CLI-based coding agent
- OpenAI Codex (CLI): OpenAI's coding agent
- GitHub Copilot Agent Mode: GitHub's agent mode
All of these use sophisticated harnesses. The differentiator is not the model's capability but the harness design.
Multimodal Harnesses
Harnesses that once handled only text are expanding to images, audio, and video:
- Screenshot analysis: UI bug detection, design review
- Diagram understanding: Convert architecture diagrams to code
- Voice interfaces: Coding instructions via voice
- Video analysis: Analyze user session recordings
Agent-to-Agent Protocols
MCP (Model Context Protocol) is evolving into a standard communication protocol between AI agents:
- Agent A uses Agent B's tools
- Task delegation and result sharing between agents
- Heterogeneous agent collaboration (Claude + GPT + Gemini)
Harness Standardization
Currently each framework implements harnesses differently, but standardization efforts are underway:
- MCP: Tool connection standard
- Agent Protocol: Agent interface standard
- OpenAPI for Agents: API-based agent definitions
By 2026, a "harness standard" is expected to emerge, allowing identical building blocks across any framework.
10. Quiz
Q1: In the AI harness metaphor, what corresponds to the "wild horse"?
Answer: The raw LLM (foundation models like GPT-4, Claude, Gemini)
A wild horse has tremendous power (intelligence) but without a harness, that power cannot be directed. Similarly, a raw LLM cannot perform practical tasks without tools, context, permissions, skills, and other harness components.
Q2: List all 7 building blocks of an AI harness.
Answer:
- System Prompt
- Tools
- Context
- Skills
- Hooks
- Permissions
- Memory
These seven components combine to transform a raw LLM into a practical AI agent.
Q3: What is the difference between PreToolUse hooks and PostToolUse hooks?
Answer:
- PreToolUse: Runs before tool execution. Used for input validation, permission checking, and blocking dangerous commands. Can abort tool execution on validation failure.
- PostToolUse: Runs after tool execution. Used for result validation, auto-formatting (prettier, eslint), and auto-testing.
This is analogous to Git's pre-commit (validate before commit) and post-commit (notify after commit) hooks.
Q4: What is the difference between the Orchestrator-Worker pattern and the Pipeline pattern?
Answer:
- Orchestrator-Worker pattern: A central orchestrator splits work and distributes it to multiple workers in parallel. Each worker operates independently, and the orchestrator integrates the results. Example: reviewing multiple files simultaneously.
- Pipeline pattern: Work is processed sequentially. Each stage's output becomes the next stage's input. Example: analysis then planning then execution then validation.
The key difference is parallel execution (Orchestrator-Worker) vs sequential execution (Pipeline).
Q5: What did the center of gravity in AI engineering shift to in 2025, away from "model training"? And why?
Answer: Model Orchestration
Reasons:
- Foundation models became powerful enough that fine-tuning is unnecessary for most tasks
- The same model produces vastly different results depending on harness design
- Enterprise needs for security, auditing, permission management, and cost tracking became essential
- Autonomous agents like Devin and Claude Code differentiate through sophisticated harness design
The key question shifted from "which model to use?" to "how to wrap and control the model?"
11. References
- Anthropic, "Claude Agent SDK Documentation," 2025
- Anthropic, "Model Context Protocol (MCP) Specification," 2025
- Anthropic, "Claude Code: An Agentic Coding Tool," 2025
- Harrison Chase, "LangGraph: Building Stateful Agent Workflows," LangChain Blog, 2025
- CrewAI, "Multi-Agent Orchestration Framework Documentation," 2025
- Microsoft, "AutoGen: Enabling Next-Gen LLM Applications," 2025
- Microsoft, "Semantic Kernel Documentation," 2025
- OpenAI, "Function Calling Guide," 2025
- Anthropic, "Building Effective Agents," Research Blog, 2025
- Lilian Weng, "LLM Powered Autonomous Agents," OpenAI Blog, 2024
- Shunyu Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models," ICLR, 2023
- Andrew Ng, "Agentic Design Patterns," DeepLearning.AI, 2025
- Simon Willison, "Building AI-Powered Tools with LLMs," Blog, 2025
- Chip Huyen, "Building LLM Applications for Production," 2025
- Devin AI, "How Devin Works: Architecture and Design," Cognition Blog, 2025
- GitHub, "Copilot Agent Mode: Architecture Deep Dive," GitHub Blog, 2025