Anthropic 코드의 90%는 Claude 에이전트가 작성합니다. 엔지니어가 채팅 창에 입력하는 방식이 아닙니다. 팀이 잠든 동안 자율적으로 루프를 실행하고, 도구를 호출하며, 코드를 배포하는 에이전트가 작성합니다.
제 Substack을 팔로우하여 최신 AI 알파를 받아보세요:
이것이 바로 그 설정입니다. 단계별로 설명합니다. 첫 번째 API 호출부터 어떤 작업에든 사용할 수 있는 작동하는 에이전트까지.
이 글에서 다룰 내용:
1 - 대부분의 사람들이 만드는 "에이전트"가 실제 에이전트가 아닌 이유
2 - 모든 작동하는 에이전트에 필요한 5가지 구성 요소
3 - Claude로 각 부분을 코드와 함께 구축하는 방법
4 - 에이전트를 배포 전에 망가뜨리는 실수들
이 글을 북마크하세요. 아래 모든 코드 블록은 작동합니다.
01. 대부분의 "AI 에이전트"는 에이전트가 아닙니다
저는 셀 수 없이 많은 에이전트를 만들고 부숴봤습니다. 밤새 토큰을 태우고 아무것도 만들어내지 못하는 모습을 지켜봤습니다. 같은 파일을 30번 다시 쓰는 모습을 봤습니다. 테스트를 삭제해서 자신의 테스트를 통과하는 모습도 봤습니다.

모든 실패는 같은 교훈을 가르쳐줬습니다: 모델이 문제가 아닙니다. 그 주변의 아키텍처가 문제입니다. 이 가이드는 제가 배운 모든 것을 가장 짧은 경로로 압축한 것입니다.
사람들이 "AI 에이전트"라고 말할 때 대부분이 만드는 것은 이것입니다:
1while True:2 user_input = input("> ")3 response = call_claude(user_input)4 print(response)
그것은 챗봇입니다. 사용자를 기다립니다. 사용자가 시키는 대로 합니다. 세션 사이에 모든 것을 잊어버립니다. 탭을 닫으면 멈춥니다.
에이전트는 사용자가 앞에 앉아 있지 않아도 목표를 향해 작업하는 시스템입니다. 무엇을 해야 하는지 발견하고, 계획을 세우고, 실행하고, 결과를 확인하고, 완료되지 않았다면 다시 시도합니다. 사용자가 방향을 설정합니다. 에이전트가 작업을 수행합니다.
"Claude Code는 몇 달 만에 수익이 0에서 4억 달러로 성장했습니다. 해커톤 프로젝트로 시작했습니다. 여전히 공개 API만 사용합니다." -
Boris Cherny, Claude Code 헤드
여러분이 지금 바로 사용할 수 있는 동일한 API입니다. 동일한 모델입니다. 차이는 모델을 둘러싼 아키텍처에 있습니다.

02. 실제 에이전트의 5가지 구성 요소
모든 작동하는 에이전트(Claude Code, Devin, Codex, 또는 직접 구축하는 모든 것)는 다섯 가지 부분으로 조립됩니다. 하나라도 빠지면 작동하지 않습니다.

03. API 레이어
모든 것은 여기서 시작됩니다. Claude를 호출하면 Claude가 응답합니다. 하지만 호출하는 방식에 따라 챗봇을 얻을지 에이전트를 얻을지가 결정됩니다.

세 가지가 중요합니다: 시스템 프롬프트, 구조화된 출력, 그리고 temperature입니다.
시스템 프롬프트는 인사말이 아닙니다. 에이전트의 작동 매뉴얼입니다. 모든 규칙, 제약 조건, 행동 방식이 여기에 들어갑니다. 이것이 없으면 Claude는 사용자가 원하는 바를 추측합니다. 이것이 있으면 Claude는 사용자의 사양을 따릅니다.
1import anthropic23client = anthropic.Anthropic()45response = client.messages.create(6 model="claude-sonnet-4-6",7 max_tokens=4096,8 system="""당신은 코드 리뷰 에이전트입니다.910규칙:11- 코멘트를 남기기 전에 전체 diff를 읽으십시오12- 스타일 선호사항이 아닌 실제 버그만 지적하십시오13- 문제가 없으면 "LGTM"이라고 말하고 중단하십시오14- 정신적으로 테스트하지 않은 변경사항은 절대 제안하지 마십시오15- 출력 형식: {file, line, issue, fix}의 JSON 배열""",16 messages=[{"role": "user", "content": diff_content}]17)
구조화된 출력은 에이전트의 응답을 기계가 읽을 수 있게 만듭니다. Claude가 자유 텍스트를 반환하면 코드가 이를 파싱해야 합니다. Claude가 JSON을 반환하면 코드가 직접 사용할 수 있습니다.
1# 정확한 형태를 Claude에 알려줘서 JSON 출력 강제2system = """유효한 JSON만 반환하세요. 마크다운 없음. 설명 없음.3스키마:4{5 "status": "pass" | "fail",6 "issues": [{"file": str, "line": int, "issue": str}],7 "summary": str8}"""
Temperature. 결정론적 에이전트에는 0으로 설정하세요. 창의적인 작업에는 0.3-0.5로 설정하세요. 기본값(1.0)은 에이전트에서 거의 원하지 않는 무작위성을 추가합니다.
04. 도구
도구가 없는 모델은 추론할 수 있지만 행동할 수는 없습니다. 어떤 파일을 편집해야 하는지 말해줄 수 있지만 실제로 편집할 수는 없습니다. 쿼리를 설명할 수 있지만 실행할 수는 없습니다.

Claude의 도구 사용 기능을 사용하면 모델이 호출할 수 있는 함수를 정의할 수 있습니다. 함수를 설명합니다. Claude가 호출 시점을 결정합니다. 사용자가 실행하고 결과를 반환합니다. Claude는 결과를 사용하여 계속 추론합니다.
1tools = [{2 "name": "run_sql",3 "description": "데이터베이스에 대해 읽기 전용 SQL 쿼리 실행",4 "input_schema": {5 "type": "object",6 "properties": {7 "query": {8 "type": "string",9 "description": "실행할 SQL SELECT 쿼리"10 }11 },12 "required": ["query"]13 }14},15{16 "name": "write_file",17 "description": "디스크의 파일에 콘텐츠 쓰기",18 "input_schema": {19 "type": "object",20 "properties": {21 "path": {"type": "string"},22 "content": {"type": "string"}23 },24 "required": ["path", "content"]25 }26}]
도구 설명은 생각보다 중요합니다. Claude는 이를 읽고 도구를 언제, 어떻게 사용할지 결정합니다. 모호한 설명은 잘못된 호출로 이어집니다. 정확한 설명은 정확한 호출로 이어집니다.
3-5개의 도구로 시작하세요. 파일 읽기, 파일 쓰기, 명령 실행, 검색, 그리고 사용 사례에 맞는 하나의 도메인 특화 도구입니다. 이것으로 에이전트 작업의 90%를 처리할 수 있습니다.

05. 루프
이것이 스크립트를 에이전트로 바꾸는 부분입니다. 루프가 없으면 코드가 Claude를 한 번 호출하고 멈춥니다. 루프가 있으면 코드가 Claude를 호출하고, 결과를 확인하고, 작업이 완료될 때까지 다시 호출합니다.

세 가지 구성 요소:
- 검증자(Verifier). 출력이 좋은지 확인하는 무언가. 테스트 스위트, 타입 체커, 린터, 엄격한 기준을 가진 두 번째 Claude 호출. 이것이 없으면 에이전트가 계속해서 자기 자신과 동의하게 됩니다.
- 상태(State). 무슨 일이 일어났는지에 대한 기록. 무엇이 작동했고, 무엇이 실패했으며, 다음에 무엇을 시도할지. 상태가 없으면 에이전트는 매번 같은 실수를 반복합니다.
- 중단 조건(Stop condition). 목표가 달성되었거나, "N번 시도 후 중단하고 보고"라는 하드 리미트. 이것이 없으면 루프가 영원히 실행되어 계정 잔액을 소진시킵니다.
1import json2from pathlib import Path34def run_agent(task: str, max_attempts: int = 5):5 state = {"task": task, "attempts": [], "done": False}67 for i in range(max_attempts):8 # 상태로부터 컨텍스트 구축9 context = build_prompt(state)1011 # 도구와 함께 Claude 호출12 result = call_claude(context, tools)1314 # 모든 도구 호출 실행15 output = execute_tools(result)1617 # 결과 검증18 check = verify(output)1920 # 상태 업데이트21 state["attempts"].append({22 "attempt": i + 1,23 "action": result.summary,24 "passed": check.passed,25 "reason": check.reason26 })2728 if check.passed:29 state["done"] = True30 break3132 # 다음 실행을 위해 상태 저장33 Path("state.json").write_text(json.dumps(state, indent=2))34 return state
이것이 완전한 뼈대입니다. 모든 프로덕션 에이전트는 이 패턴의 변형입니다. 세부 사항은 바뀝니다. 형태는 바뀌지 않습니다.
06. 메모리
메모리가 없으면 모든 세션이 처음부터 시작됩니다. 에이전트가 프로젝트 구조를 다시 발견합니다. 컨벤션을 다시 배웁니다. 어제 했던 실수를 다시 합니다.

Claude 에이전트는 세 가지 메모리 레이어를 사용합니다:
CLAUDE.md는 프로젝트 루트에 있는 마크다운 파일입니다. Claude Code가 모든 세션 시작 시 자동으로 읽습니다. 여러분의 규칙, 스택, 컨벤션을 담습니다. 한 번 작성하면 영원히 읽힙니다.
1# CLAUDE.md23## 프로젝트4태스크 관리 API. Python 3.12, FastAPI, PostgreSQL.56## 규칙7- 모든 응답: {data, error, meta} 스키마8- 모든 새 엔드포인트에 테스트 필수9- 커밋 메시지: type(scope): description10- 로깅에 print() 사용 금지. structlog 사용.1112## 알려진 이슈13- Auth 미들웨어는 Authorization이 아닌 x-auth-token을 기대함14- 테스트 스위트 전체 실행 45초 소요. 반복 작업 시 --filter 사용.
스킬(Skills)은 전체 워크플로우를 캡처합니다. 단순한 프롬프트가 아닌 전체 형태: 입력 형식, 단계, 출력 형식, 검증 규칙. 첫 실행은 20분 걸립니다. 재실행은 30초 걸립니다.
학습 파일(Learnings file)은 실수의 실행 기록입니다. 에이전트가 매 세션 후에 기록합니다. 다음 세션에서 읽습니다. 실수는 기록될 때까지 반복됩니다. 기록되면 멈춥니다.
1# learnings.md23- 결제 API는 본문이 아닌 헤더에 멱등성 키를 기대함4- PostgreSQL NOTIFY는 연결 풀에 명시적 LISTEN이 필요함5- Rate limiter는 IP별이 아닌 키별로 카운트함. 테스트는 고유 키가 필요함.
07. 검증 게이트
게이트는 구축하기 가장 어렵고 건너뛰기 가장 쉬운 부분입니다. 대부분의 사람들이 건너뜁니다. 그래서 대부분의 에이전트가 프로덕션에서 망가집니다.

검증 게이트는 에이전트가 스스로를 평가하지 않고 에이전트의 작업을 확인하는 것입니다. 코드를 작성한 모델은 자신의 숙제를 채점할 때 너무 관대합니다. 두 번째 확인이 필요합니다.
세 가지 작동하는 패턴:
1. 자동화된 테스트. 에이전트가 코드를 작성합니다. 테스트 스위트가 실행됩니다. 테스트가 실패하면 에이전트는 오류 출력을 받고 다시 시도합니다. 이것이 Claude Code가 내부적으로 작동하는 방식입니다.
1def verify(output):2 # 테스트 스위트 실행3 result = subprocess.run(4 ["pytest", "tests/", "-x", "--tb=short"],5 capture_output=True, text=True6 )7 return {8 "passed": result.returncode == 0,9 "reason": result.stdout if result.returncode != 0 else "모든 테스트 통과"10 }
2. 타입 체커 / 린터. 모든 변경 후 mypy, ruff 또는 tsc --noEmit을 실행합니다. 단일 테스트를 작성하지 않고도 전체 버그 범주를 잡아냅니다.
3. 리뷰어로서의 두 번째 모델. 문제만 찾는 엄격한 시스템 프롬프트를 가진 별도의 Claude 호출을 사용합니다. 작성자는 빠르고 저렴합니다. 리뷰어는 느리고 엄격합니다. 이 분리가 품질의 대부분을 결정합니다.
1# 리뷰어 프롬프트 - 빌더와 분리2reviewer_system = """당신은 엄격한 코드 리뷰어입니다.3당신의 유일한 임무는 문제를 찾는 것입니다.45확인 사항:6- 코드가 스펙과 일치합니까?7- 잡히지 않은 엣지 케이스가 있습니까?8- 모든 테스트가 실제로 올바른 것을 테스트합니까?910모든 것이 정확하면 응답: {"passed": true}11무엇이든 잘못되면 응답: {"passed": false, "issues": [...]}1213개선 사항을 제안하지 마십시오. 실제 버그만 지적하십시오."""
작성자는 빠르고 저렴합니다. 리뷰어는 느리고 엄격합니다. 이 분리가 품질의 대부분을 결정합니다.
08. 모든 것을 하나로 합치기
다음은 GitHub 이슈 URL을 받아서, 이슈를 읽고, 코드를 작성하고, 테스트를 실행하고, PR을 여는 완전한 에이전트입니다. 다섯 부분이 함께 작동합니다.
1import anthropic, subprocess, json2from pathlib import Path34client = anthropic.Anthropic()5CLAUDE_MD = Path("CLAUDE.md").read_text()6LEARNINGS = Path("learnings.md").read_text()78SYSTEM = f"""당신은 코딩 에이전트입니다.9이슈를 읽고, 수정 사항을 작성하고, 테스트를 실행하세요.1011프로젝트 컨텍스트:12{CLAUDE_MD}1314알려진 이슈:15{LEARNINGS}1617규칙:18- 변경하기 전에 전체 코드베이스를 읽으십시오19- 모든 변경사항에 대한 테스트를 작성하십시오20- 테스트가 실패하면 테스트가 아닌 코드를 수정하십시오21- 모든 테스트가 통과하면 중단하십시오"""2223TOOLS = [24 read_file_tool,25 write_file_tool,26 run_command_tool,27 search_codebase_tool,28]2930def run(issue_text, max_attempts=5):31 messages = [{"role": "user", "content": issue_text}]3233 for attempt in range(max_attempts):34 # Claude 호출35 response = client.messages.create(36 model="claude-sonnet-4-6",37 max_tokens=8192,38 system=SYSTEM,39 tools=TOOLS,40 messages=messages41 )4243 # 도구 호출 실행44 messages = handle_tool_use(response, messages)4546 # 검증: 테스트 실행47 test_result = subprocess.run(48 ["pytest", "-x", "--tb=short"],49 capture_output=True, text=True50 )5152 if test_result.returncode == 0:53 print(f"{attempt + 1}회 시도 후 완료")54 return True5556 # 실패를 루프에 다시 피드백57 messages.append({58 "role": "user",59 "content": f"테스트 실패:\n{test_result.stdout}\n수정 후 재시도."60 })6162 return False
이것이 작동하는 에이전트입니다. 시스템 프롬프트와 CLAUDE.md가 있는 API 레이어. 파일 작업을 위한 도구. 재시도가 있는 루프. learnings.md의 메모리. pytest를 통한 검증 게이트.
50줄 미만. Claude Code가 내부적으로 사용하는 것과 동일한 아키텍처입니다.
**
09. 모든 에이전트를 망가뜨리는 5가지 실수
- 검증 게이트 없음. 에이전트가 자신의 숙제를 스스로 채점합니다. 코드를 작성하고 "괜찮아 보인다"고 말하고 넘어갑니다. 출력은 맞아 보이지만 프로덕션에서 망가집니다.
- 중단 조건 없음. API 요금이 $200가 될 때까지 루프가 실행됩니다. 하드 리미트가 없으면 에이전트가 영원히 재시도하며 같은 파일을 40번 다시 씁니다. 항상 max_attempts를 설정하세요. 항상.
- 상태 파일 없음. 1번 시도와 50번 시도에서 같은 실수를 반복합니다. 에이전트는 자신이 이미 무엇을 시도했는지 알지 못합니다. 실패를 기록하는 것이 없기 때문에 같은 고장난 수정을 세 번 연속 제안합니다.
- 너무 많은 도구. Claude에게 20개의 도구를 주면 잘못된 도구를 선택합니다. 5개의 명확한 도구를 가진 모델이 20개의 중복되는 도구를 가진 모델보다 더 나은 선택을 합니다. 작게 시작하세요. 에이전트가 벽에 부딪힐 때만 도구를 추가하세요.
- 모호한 시스템 프롬프트. "좋은 코딩 어시스턴트가 되어줘"는 일반적인 출력을 제공합니다. "모든 응답은 유효한 JSON이어야 하며, 모든 변경사항에 테스트가 필요하며, /src 외부의 파일을 절대 수정하지 마십시오"는 규칙을 따르는 에이전트를 제공합니다.
결론:
작동하는 에이전트는 더 나은 프롬프트가 아닙니다. 시스템입니다: API + 도구 + 루프 + 메모리 + 검증 게이트. 다섯 부분. 하나라도 빠지면 망가집니다.
대부분의 사람들은 이 글을 읽고, 북마크하고, 계속해서 Claude를 챗봇으로 사용할 것입니다. 한 번에 하나의 질문을 붙여넣고 응답을 수동으로 코드베이스에 복사할 것입니다.
루프를 구축하는 사람들은 잠자는 동안에도 작업을 배포할 것입니다. 같은 모델. 같은 API. 같은 가격. 다른 아키텍처.
위의 모든 코드 블록은 작동합니다. 복사하세요. 실행하세요. 사용 사례에 맞게 수정하세요.
이번 주에 에이전트 하나를 구축하세요. 매일 하는 작업을 가리키게 하세요. 실행하게 두세요.





