ai-agent-deep-dive는 프로덕션급 AI 에이전트(Claude Code류)를 역설계해, 핵심 원리만 남긴 교육용 미니 에이전트와 17편의 설계 문서로 정리한 학습 레포다. "코딩 에이전트가 마법처럼 코드를 고치는데, 안에서 뭐가 도는 거지?"라는 궁금증에 코드로 답한다.
복잡해 보이는 Claude Code도 본질은 단순한 반복문 하나다. LLM에게 질문하고 → 도구를 호출하고 → 결과를 받아 → 다시 LLM에게 보낸다. 끝날 때까지. 이 골격 위에 메모리 관리·권한·검증 에이전트·컨텍스트 압축 같은 9개의 층을 쌓아올린 게 프로덕션 에이전트다.
이 레포는 그 9개 층을 차례로 해부한 역설계 보고서 + 동작하는 Python 코드(src/agt/)다. 챗봇과 에이전트의 차이를 한 줄로 — 챗봇은 말만 하지만, 에이전트는 일을 한다.
실제 Claude Code·Cursor는 비공개거나 거대해서 초보가 읽을 수 없다. LangChain 같은 프레임워크는 추상화가 두꺼워 "그래서 루프가 어디 있지?"가 안 보인다. 정작 에이전트가 내부에서 어떻게 도는지 평이하게 알려주는 자료가 드물었다.
핵심 로직을 Python ~500줄(교육용 주석 포함 485줄)로 압축하고(src/agt/agent.py), FakeLLM 인터페이스로 API 키 없이도 돌려볼 수 있게 했다. 거기에 요구사항 명세 스타일의 17편 문서(docs/00~16)로 "왜 이렇게 설계했는지"까지 붙였다. 코드와 설계 의도를 같이 주는 게 핵심.
| 비교 대상 | 이 레포의 위치 |
|---|---|
| 실제 Claude Code / Cursor | 읽기엔 너무 크고 비공개. 이 레포는 같은 패턴을 축소 모델로 보여줌 |
| LangChain / LlamaIndex | 프레임워크는 추상화가 두껍다. 여기는 프레임워크 없이 맨손 while 루프라 골격이 드러남 |
| 블로그 글 "에이전트란" | 글은 개념만. 이 레포는 실행되는 코드 + 설계 명세서로 손에 잡힘 |
| 논문 (ReAct 등) | 이론. 이 레포는 그 이론을 Python ~500줄(교육용 주석 포함 485줄)로 구현해 체득시킴 |
실제 양산차 엔진은 뜯어보기 너무 복잡하다. 이 레포는 투명 케이스에 담긴 1기통 데모 엔진 같다 — 실린더 하나가 어떻게 폭발해서 바퀴를 돌리는지 눈으로 따라갈 수 있다.
원리를 잡고 나면, 12기통 양산차(Claude Code)를 봐도 "아, 이게 그거 여러 개구나"가 보인다.
의도적으로 Python 단일 언어다. 학습 대상이 "에이전트의 구조"이지 "다양한 스택"이 아니므로, 곁가지를 쳐냈다.
| 기술 | 역할 | 상세 |
|---|---|---|
| Python 3 | 구현 언어 | 전체 코드가 Python. 타입 힌트 + Protocol 적극 사용 |
| Poetry | 의존성 관리 | pyproject.toml + poetry.lock. pip 아님 — poetry install로 설치 |
| FakeLLM 인터페이스 | 모델 자리표시자 | 실제 API 없이 정해진 답을 스트리밍으로 반환. 나중에 진짜 LLM으로 교체 |
agt CLI | 실행 진입점 | poetry run agt "..."로 에이전트 실행. src/agt/cli.py |
| Skills 디스커버리 | 능력 확장 | --skills-dir로 스킬 폴더 지정, --list-skills로 목록 확인 |
| 축 | 역할 | 한 줄 정의 |
|---|---|---|
| Skill | 절차/지식 패키지 | SKILL.md 한 장으로 "이 작업은 이렇게 해"를 가르침 |
| Plugin | 묶음 배포 단위 | 여러 스킬·커맨드·설정을 한 번에 설치/제거 (npm 패키지처럼) |
| MCP | 외부 연결 표준 | Slack·GitHub·DB 등을 잇는 "AI용 USB 포트" 프로토콜 |
| 분류 | 층 | 대응 문서 |
|---|---|---|
| 토대 | 진입점 · 프롬프트 편성 · 도구 실행 | 00 · 01 · 02 |
| 두뇌 | 확장 생태계(Skills/Plugins/MCP) · 메모리·세션 · 아키텍처 맵 | 03 · 04 · 07 |
| 운영 | 태스크·백그라운드 · 품질 보증 · 운영자 경험 | 05 · 06 · 11~14 |
max_turns를 넘으면 종료. src/agt/agent.py의 Agent.run()이 바로 이것.에이전트는 새 요리에 도전하는 요리사 같다. 레시피 한 줄을 읽고(생각), 재료를 자르고(행동), 맛을 보고(관찰), 다음 단계를 정한다(다시 생각). 맛이 이상하면 재시도, 좋으면 다음으로.
이 단순한 반복이 복잡한 요리를 완성하듯, while 루프가 리팩토링·디버깅 같은 복잡한 작업을 해낸다.
벽 콘센트는 정해진 모양이다. 그 모양만 맞으면 충전기든 주전자든 다 꽂힌다. 콘센트는 안에 뭐가 꽂힐지 모르고, 신경도 안 쓴다.
Agent 코어가 콘센트라면 LLM 클라이언트는 플러그다. OpenAI든 Anthropic이든 로컬 Ollama든 — stream_text() 같은 메서드만 있으면 끼울 수 있다.
실제 LLM 없이 테스트된다. FakeLLMClient가 정해진 답만 반환하면 네트워크·API 키 없이 로직 검증 가능 — 비용도 응답 시간도 0. 나중에 진짜 LLM으로 바꿀 때 Agent 코드는 한 줄도 안 건드리고 클라이언트만 교체한다.
윈도우가 커져도 긴 컨텍스트일수록 집중력이 떨어진다("lost in the middle"). 중간 정보를 무시하기 시작하고, 비용도 토큰 수에 비례해 늘어난다.
Level 1 경량 트리밍: 중복 진행 표시·과한 도구 출력을 잘라낸다.
Level 2 요약: 오래된 메시지를 "사용자가 X 요청 → Y, Z 단계 거침" 식 한 단락으로 압축.
Level 3 경계 마킹: 압축 지점에 표식을 남기고 정말 중요한 것(현재 목표·안전 규칙·사용자 선호)만 남긴다.
같은 LLM에게 "방금 한 거 잘했어?"라고 물으면 대개 "네"라고 한다. 객관적 검증이 안 된다. 그대로 두면 컴파일도 안 되는 코드를 자랑스럽게 보고한다.
작업 에이전트와 분리된 검증 에이전트를 둔다. 그의 일은 하나 — build·test·lint 실행 후 PASS/FAIL 판정. 작업 에이전트의 자기 평가는 무시. FAIL이면 결과를 돌려보내 재시도시킨다. 장애 복구도 같은 사상(멱등성: 같은 작업을 두 번 해도 결과가 같게 설계)이다.
읽는 순서 힌트: 먼저 src/agt/agent.py 한 파일로 골격을 잡고 → docs/07(9계층 맵)·08(루프)·10(컨텍스트) 세 편으로 "왜 그렇게 설계했나"를 채우면, 나머지 문서는 곁가지로 빠르게 읽힌다.
docs/ 문서들은 코드를 줄별로 설명하는 게 아니라, 프로덕션 에이전트가 갖춰야 할 요구사항을 모듈별로 정리한 스펙에 가깝다. 그래서 일부 항목(원격 태스크·백그라운드 에이전트 등)은 미니 코드에는 아직 없고 "이렇게 해야 한다"는 설계 의도로만 존재할 수 있다 — 코드와 1:1로 매칭하려 들지 말 것.
배울 것: 화려한 기능을 다 걷어내면 에이전트는 "생각→행동→관찰"의 반복문 하나라는 사실. Message(대화 단위)·Tool(도구)·Agent(루프) 세 클래스의 관계만 잡으면, 이후 어떤 에이전트 코드도 무섭지 않다.
배울 것: 코어가 구현체를 모르게 만드는 설계. FakeLLM ↔ 실제 LLM 교체가 코드 한 줄 안 바꾸고 되는 이유. 테스트 용이성과 교체 가능성을 동시에 얻는 실전 예제다.
배울 것: 트리밍 → 요약 → 경계 마킹의 3단계 압축. "윈도우만 키우면 된다"가 왜 함정인지("lost in the middle", 비용 폭증)와, 토큰 예산을 관리하는 사고법.
배울 것: 셋이 어떻게 다른지. Skill(절차서) ⊂ Plugin(묶음 배포) / MCP(외부 연결 표준)의 역할 분담. --skills-dir·--list-skills로 스킬 디스커버리를 직접 만져볼 수 있다.
배울 것: 자기 검증의 함정과, 별도 검증 에이전트로 build/test/lint를 돌려 객관적 PASS/FAIL을 내는 패턴. 멀티 에이전트의 가장 실용적인 첫걸음.
배울 것: 진입점·프롬프트·도구·스케줄링·확장·메모리·태스크·품질·운영자경험의 9층. 각 층이 레고처럼 독립 모듈이라, 1~3층만 먼저 짜고 위로 쌓는 식으로 미니 Claude Code를 단계적으로 만들 수 있다.
| 항목 | 최소 | 권장 |
|---|---|---|
| OS | macOS / Linux / Windows (어디든) | 상관없음 (순수 Python) |
| Python | 3.11+ (pyproject.toml: >=3.11,<4.0) | 3.11+ |
| 패키지 관리 | Poetry | Poetry 최신 |
| RAM | 1GB (FakeLLM은 추론 안 함) | 2GB+ |
| 네트워크 | 불필요 (FakeLLM이면 오프라인 OK) | 실제 LLM 연결 시에만 필요 |
| API 키 | 불필요 (기본 FakeLLM) | 실제 LLM 교체 시 OpenAI/Anthropic 키 |
FakeLLM이 정해진 응답을 스트리밍으로 흉내내기 때문에, 설치만 하면 바로 루프가 도는 걸 볼 수 있다. 학습 비용이 0에 수렴한다.
# 1. 의존성 설치 (Poetry)
poetry install
# 2. 에이전트 실행 (FakeLLM이라 키 불필요)
poetry run agt "你好"
# 3. 스킬 목록 확인
poetry run agt --skills-dir ./skills --list-skills
※ 저자가 중국어권이라 예제에 "你好"(안녕)가 쓰인다. 한국어/영어 입력으로 바꿔도 동일하게 동작한다.
git clone 후 src/agt/agent.py 하나만 끝까지 읽는다(교육용 주석 포함 485줄, 핵심 로직은 그보다 적음). Message·Tool·Agent 세 클래스의 관계와 while 루프의 종료 조건을 그림으로 그려본다.
# 설치 후 바로 실행 (키 불필요)
poetry install
poetry run agt "파일 목록 보여줘"
# 스킬 디스커버리 체험
poetry run agt --skills-dir ./skills --list-skills
FakeLLM이 반환하는 가짜 응답이 루프를 어떻게 한 바퀴 돌리는지 콘솔 출력으로 관찰한다.
read_file_tool, write_file_tool을 직접 정의해 도구 레지스트리에 등록한다. LLM이 tool_call을 어떻게 파싱하고 결과를 메시지에 되먹이는지 체득한다.
Protocol 패턴의 위력을 체감하는 핵심 과제. FakeLLMClient 자리에 OpenAI API를 호출하는 OpenAIClient를 만들어 끼운다. Agent 코드는 한 줄도 안 바꾼다.
# openai_client.py — Protocol만 맞추면 끝
class OpenAIClient:
def stream_text(self, history):
# OpenAI ChatCompletion 스트리밍 호출
for chunk in openai_stream(history):
yield chunk
멀티 에이전트의 가장 단순한 형태. 작업 에이전트가 코드를 수정하면, 검증 에이전트가 pytest를 돌려 결과를 돌려준다. FAIL이면 피드백 후 재시도. 이걸 만들면 멀티 에이전트의 본질을 잡는다.
| 요일 | 학습 내용 | 실습 |
|---|---|---|
| 월 | 레포 클론 & 설치 | poetry install → agt 실행 |
| 화 | agent.py 정독 | Message·Tool·Agent 클래스 관계도 그리기 |
| 수 | while 루프 추적 | docs/08 루프 명세와 코드 대조 |
| 목 | 메시지 모델 | docs/09 — 대화 상태가 어떻게 쌓이나 |
| 금 | FakeLLM 이해 | 가짜 응답이 루프를 도는 과정 디버깅 |
| 요일 | 학습 내용 | 실습 |
|---|---|---|
| 월 | 도구 시스템 개념 | docs/02 — 도구·권한·실행 체인 |
| 화 | 커스텀 도구 작성 | read_file/write_file 도구 추가 |
| 수 | Protocol 의존성 역전 | FakeLLM ↔ 실제 LLM 인터페이스 비교 |
| 목 | OpenAI 클라이언트 교체 | OpenAIClient 만들어 끼우기 |
| 금 | 권한 & 보안 | docs/02 권한 모델 정리 |
| 요일 | 학습 내용 | 실습 |
|---|---|---|
| 월 | 컨텍스트 압축 | docs/10 — 3단계 압축 구현 스케치 |
| 화 | 메모리 & 세션 | docs/04 — 세션 요약·복구 |
| 수 | Skill 디스커버리 | --skills-dir로 스킬 추가/조회 |
| 목 | Plugin vs MCP | docs/03 — 3축 차이 정리 |
| 금 | 설정 시스템 | docs/14 — 설정 소스·우선순위 |
| 요일 | 학습 내용 | 실습 |
|---|---|---|
| 월 | 에이전트 스케줄링 | docs/01 역할 편성 + 자식 에이전트 |
| 화 | 검증 에이전트 | docs/06 — pytest 검증 페어 구현 |
| 수 | 장애 복구 & 멱등성 | docs/13 — 세션 트랜스크립트 복구 |
| 목 | 워크스페이스 격리 | docs/12 — 작업 격리 경계 |
| 금 | 종합 — 미니 Claude Code | 9계층 중 1~6층을 결합한 작은 에이전트 완성 |
| 키워드 | 설명 |
|---|---|
| AI 에이전트 | LLM에 도구 사용 능력을 붙여 스스로 다음 단계를 정하는 시스템. 챗봇은 말만, 에이전트는 일을 한다 |
| Agent Loop | "생각→행동→관찰"을 반복하는 while 루프. 에이전트의 본체 |
| Tool Calling | LLM이 "이 도구를 이 인자로 실행"이라 요청하는 것. 에이전트가 세상과 상호작용하는 유일한 통로 |
| Protocol | "이런 메서드를 가진 객체"라는 약속. 상속 없이 모양만 맞으면 끼워지는 인터페이스 |
| 의존성 역전 | 코어가 구현체를 모르게 만드는 설계. LLM 교체가 코드 변경 없이 되는 이유 |
| FakeLLM | 실제 모델 대신 정해진 답을 스트리밍으로 반환하는 테스트용 클라이언트. API 키·비용 0 |
| Context Window | LLM이 한 번에 볼 수 있는 텍스트 최대 길이(토큰 단위). 차면 앞부분을 잊는다 |
| 컨텍스트 압축 | 트리밍→요약→경계 마킹 3단계로 토큰 예산을 관리해 긴 대화를 버티는 기법 |
| lost in the middle | 컨텍스트가 길수록 중간 정보를 무시하는 LLM의 집중력 저하 현상 |
| Skill | SKILL.md 한 장으로 "이 작업은 이렇게 해"를 가르치는 절차서 |
| Plugin | 여러 스킬·커맨드·설정을 한 묶음으로 배포하는 단위 (npm 패키지처럼) |
| MCP | Model Context Protocol. 외부 서비스를 잇는 "AI용 USB 포트" 표준 |
| 검증 에이전트 | 작업 에이전트와 분리되어 build/test/lint로 객관적 PASS/FAIL을 내는 에이전트 |
| 멱등성 | 같은 작업을 두 번 해도 결과가 같게 설계하는 것. 장애 복구의 토대 |
| 9계층 아키텍처 | 진입점·프롬프트·도구·스케줄링·확장·메모리·태스크·품질·운영자경험의 모듈 지도 |
| max_turns | 에이전트 루프가 무한 반복하지 않도록 거는 최대 반복 횟수 안전장치 |
| Poetry | Python 의존성·패키징 도구. pyproject.toml+poetry.lock으로 환경 재현 |
| 스킬 디스커버리 | --skills-dir로 지정한 폴더에서 스킬을 자동 탐색·목록화하는 메커니즘 |