AI 에이전트·MCP·A2A를 처음 접하는 입문자를 위한 완전판 가이드. LLM 기초부터 에이전트 아키텍처, 도구 호출, MCP 서버 제작, A2A 프로토콜, 멀티에이전트·보안까지 한 편에 담았습니다.
Data DynamicsJune 21, 2026202 min read
This post is not yet translated. The original Korean version is shown below.
이 글은 AI 에이전트(AI Agent), MCP(Model Context Protocol), **A2A(Agent-to-Agent)**를 처음 접하는 입문자를 위한 완전판 가이드입니다. 개념 → 동작 원리 → 직접 만들어보기까지 단계적으로 따라올 수 있도록 구성했고, 글보다 그림으로 먼저 이해할 수 있게 다이어그램을 적극적으로 사용했습니다.
Loading diagram…
📌 읽기 전 참고: AI 에이전트·MCP·A2A는 매우 빠르게 변하는 분야입니다. 이 글은 변하지 않는 핵심 개념과 원리에 집중했으며, 구체적인 모델명·수치·API 세부사항은 시점에 따라 달라질 수 있으니 실제 적용 시 공식 문서를 함께 확인하세요.
authentication_error 가 나오면 → API 키가 환경변수에 제대로 설정됐는지 확인.
ModuleNotFoundError 가 나오면 → pip install anthropic 다시 확인.
비용의 기본: 토큰 과금
2장에서 자세히 배우지만, 미리 한 가지만 알아둡시다. API는 보통 토큰 수로 요금이 매겨집니다.
Loading diagram…
입력 토큰(우리가 보낸 것) + 출력 토큰(모델이 답한 것) 합으로 과금됩니다.
같은 작업이라도 더 작은(빠른) 모델을 쓰면 저렴합니다. 학습·실험엔 가벼운 모델로 시작해도 충분합니다.
에이전트는 LLM을 여러 번 호출하므로 비용이 쌓입니다(7장에서 다룸). 처음엔 max_tokens를 작게 두고 실험하세요.
모델 이름에 대하여
이 책의 예제는 claude-sonnet-4-6 같은 모델 이름을 사용합니다. 모델 라인업과 정확한 이름은 계속 업데이트되므로, 최신 모델·가격은 공식 문서에서 확인하는 것을 권합니다. 예제의 모델 이름은 필요에 따라 최신 것으로 바꿔도 됩니다.
📌 이 책을 읽는 분께: AI 에이전트·MCP·A2A는 매우 빠르게 변하는 분야입니다. 이 책은 변하지 않는 핵심 개념과 원리에 집중했습니다. 구체적인 모델명·수치·API 세부사항은 시점에 따라 달라질 수 있으니, 실제 적용 시 공식 문서를 함께 확인하세요.
핵심 요약
준비물: Python 3.10+, anthropic·mcp 패키지.
API 키는 환경변수로 안전하게 설정하고, 절대 코드/저장소에 노출하지 않는다.
첫 호출(hello.py)로 환경을 검증한다.
비용은 입력+출력 토큰으로 과금되며, 에이전트는 호출이 많아 비용이 쌓인다.
이 분야는 빠르게 변하므로 개념 중심으로 익히고 세부사항은 공식 문서로 확인한다.
연습 문제
hello.py를 실행해 Claude의 답변을 받아보세요. 환경이 정상인지 확인합니다.
max_tokens를 10으로 줄여 실행하면 답이 어떻게 달라지나요? 왜 그럴까요?
(생각해보기) 입력 토큰과 출력 토큰 중 무엇이 비용에 더 큰 영향을 줄지, 작업 종류에 따라 달라질지 생각해보세요.
📦 Part 1 · 기초 다지기
1장. AI Agent란 무엇인가
이 장에서 배우는 것
"AI 에이전트"가 정확히 무엇을 의미하는지
일반 챗봇과 AI 에이전트의 결정적 차이
에이전트가 작동하는 기본 루프(생각 → 행동 → 관찰)
왜 지금 에이전트가 중요한 기술이 되었는지
들어가며
여러분이 똑똑한 비서를 한 명 고용했다고 상상해 봅시다. 이 비서에게 두 가지 방식으로 일을 시킬 수 있습니다.
질문에만 답하는 비서: "내일 서울 날씨 어때?"라고 물으면 자기가 아는 지식으로 대답합니다. 모르면 "모르겠어요"라고 합니다.
스스로 일하는 비서: "내일 서울 날씨 알아보고, 비가 오면 우산 챙기라고 알림 보내줘"라고 하면, 직접 날씨 사이트를 확인하고 판단한 뒤 알림 앱을 실행합니다.
첫 번째가 우리가 흔히 쓰는 챗봇이고, 두 번째가 바로 AI 에이전트(AI Agent) 입니다.
한 문장으로: AI 에이전트는 목표를 받으면, 스스로 계획하고 도구를 사용해 그 목표를 달성하는 AI 시스템입니다.
챗봇 vs AI 에이전트
Loading diagram…
구분
챗봇
AI 에이전트
입력
질문
목표/작업
행동
말(텍스트)만 함
도구를 사용해 실제 행동을 함
반복
한 번 답하고 끝
목표 달성까지 여러 단계 반복
외부 연결
없음(아는 것만 대답)
검색·API·파일·코드 실행 등 연결
판단
거의 없음
다음에 무엇을 할지 스스로 결정
핵심 차이는 "행동(action)" 과 "반복(loop)" 입니다. 챗봇은 한 번 말하고 끝나지만, 에이전트는 목표를 이룰 때까지 생각과 행동을 반복합니다.
에이전트의 기본 동작 루프
AI 에이전트의 심장은 아주 단순한 반복 루프입니다. 흔히 "생각(Think) → 행동(Act) → 관찰(Observe)" 루프라고 부릅니다.
Loading diagram…
예를 들어 "오늘 환율로 100달러가 몇 원인지 알려줘"라는 목표가 주어지면:
생각: "환율을 알아야 하는데 내 머릿속 정보는 오래됐어. 환율 API를 호출하자."
행동: 환율 조회 도구를 호출한다 → get_exchange_rate("USD", "KRW")
관찰: 결과를 받는다 → 1380
생각: "이제 계산만 하면 돼. 100 × 1380 = 138,000원"
종료: "100달러는 약 138,000원입니다."
이 단순한 루프가 에이전트의 본질입니다. 뒤에서 배울 ReAct 같은 패턴도 결국 이 루프의 정교한 버전입니다(4장).
에이전트가 똑똑해 보이는 비결: LLM
이 루프에서 "생각"과 "결정"을 담당하는 두뇌가 바로 LLM(거대 언어 모델, Large Language Model) 입니다. Claude, GPT 같은 모델이죠.
LLM은 글을 이해하고 생성하는 능력이 뛰어나지만, 혼자서는 한계가 있습니다.
Loading diagram…
에이전트는 "판단은 잘하지만 손발이 없는 LLM" 에게 "손발(도구)" 을 달아준 것입니다. 그 손발을 표준화된 방식으로 연결하는 기술이 바로 이 책의 핵심 주제인 MCP(3부)이고, 에이전트끼리 협업하게 하는 것이 A2A(4부)입니다.
왜 지금 AI 에이전트인가?
Loading diagram…
세 가지가 맞물리면서 에이전트가 현실이 되었습니다.
LLM의 추론 능력이 충분히 좋아짐 — 복잡한 작업을 단계로 쪼개 계획할 수 있게 됨
도구 호출(Tool Calling) 표준화 — LLM이 "이 함수를 이렇게 불러줘"라고 말할 수 있게 됨
MCP 같은 연결 표준 등장 — 도구를 매번 새로 만들지 않고 재사용·공유 가능
핵심 요약
AI 에이전트는 목표를 받아 스스로 계획하고 도구를 사용해 달성하는 시스템이다.
챗봇과의 결정적 차이는 행동(도구 사용) 과 반복(루프) 이다.
에이전트의 심장은 생각 → 행동 → 관찰 루프이며, 그 두뇌는 LLM이다.
LLM은 똑똑하지만 손발이 없고, 도구(MCP) 와 협업(A2A) 이 그 손발이 되어 준다.
연습 문제
주변에서 본 '챗봇'과 'AI 에이전트'의 예를 각각 하나씩 들어보세요. 무엇이 둘을 가르나요?
"내일 비가 오면 우산 챙기라고 알려줘"라는 목표를 생각 → 행동 → 관찰 루프로 나눠 적어보세요.
(생각해보기) "LLM은 똑똑하지만 손발이 없다"는 말의 의미를 한 문장으로 설명해보세요.
2장. LLM 기초 이해하기
이 장에서 배우는 것
LLM이 글자를 어떻게 다루는지(토큰)
프롬프트, 시스템 프롬프트, 컨텍스트 윈도우의 의미
LLM이 "기억"하지 못하는 이유와 대화가 이어지는 원리
환각(hallucination)이 무엇이고 왜 도구가 필요한지
들어가며
AI 에이전트의 두뇌는 LLM이라고 1장에서 배웠습니다. 두뇌를 제대로 다루려면 두뇌가 어떻게 생각하는지 알아야겠죠. 이 장에서는 수식 없이, 에이전트 개발에 꼭 필요한 만큼만 LLM의 동작 원리를 살펴봅니다.
LLM은 "다음 단어 맞히기" 기계다
LLM의 본질은 놀랍도록 단순합니다. 앞의 글을 보고 다음에 올 가장 그럴듯한 단어를 예측하는 것입니다.
Loading diagram…
"오늘 날씨가 너무"라는 문장 뒤에 올 단어로 LLM은 여러 후보의 확률을 계산하고, 그중 하나를 골라 이어 붙입니다. 이 과정을 한 단어씩 반복하면서 문장을 완성합니다. 챗봇이 글자를 하나씩 흘려보내며 답하는 모습이 바로 이 과정입니다.
중요한 직관: LLM은 "정답을 안다"기보다 "그럴듯한 말을 이어 붙인다". 이 점이 뒤에 나올 환각과 도구의 필요성으로 이어집니다.
토큰(Token): LLM이 글을 세는 단위
LLM은 글자나 단어가 아니라 토큰(token) 이라는 조각 단위로 글을 처리합니다. 토큰은 대략 단어보다 작고 글자보다 큰 덩어리입니다.
Loading diagram…
영어는 보통 1단어 ≈ 1~2토큰, 한국어는 더 잘게 쪼개집니다.
대략 영어 1토큰 ≈ 4글자, 한국어는 1글자에 1토큰 이상이 되기도 합니다.
토큰이 중요한 이유는 두 가지입니다.
비용: API 요금은 보통 토큰 수로 매겨집니다(입력 토큰 + 출력 토큰).
한계: 한 번에 다룰 수 있는 토큰 수에 상한이 있습니다 → 이것이 컨텍스트 윈도우.
프롬프트와 컨텍스트 윈도우
프롬프트(prompt) 는 LLM에게 주는 입력 전체입니다. 에이전트에서는 보통 여러 역할의 메시지로 구성됩니다.
Loading diagram…
시스템 프롬프트(system prompt): "너는 친절한 여행 가이드야"처럼 역할과 규칙을 정해 주는 설정값.
사용자 메시지(user message): 이번 차례의 실제 질문/요청.
대화 기록(history): 이전에 주고받은 메시지들.
이 모든 것을 합친 길이가 컨텍스트 윈도우(context window) 안에 들어가야 합니다. 컨텍스트 윈도우는 LLM이 한 번에 "볼 수 있는" 최대 토큰 수입니다.
Loading diagram…
비유: 컨텍스트 윈도우는 책상 크기입니다. 책상이 크면 더 많은 자료를 펼쳐 놓고 작업할 수 있지만, 무한정은 아닙니다. 넘치면 오래된 자료를 치워야 합니다.
플랜에 따라 컨텍스트 윈도우가 다르다고요?
"Claude를 무료로 쓸 때랑 회사에서 쓸 때랑 한 번에 처리하는 양이 다른 것 같다"는 이야기를 들어봤을 수 있습니다. 맞습니다. 그런데 여기엔 초보자가 자주 헷갈리는 함정이 하나 있습니다.
컨텍스트 윈도우 크기는 "하나의 숫자"가 아닙니다. 어디서 보느냐에 따라 세 개의 층위로 나뉩니다.
Loading diagram…
이건 마치 자동차 엔진의 최고 출력(모델 능력) 과 도로의 제한 속도(상품 정책) 가 다른 것과 같습니다. 엔진이 아무리 좋아도 도로마다 허용 속도가 다르듯이, 모델 능력이 같아도 상품(플랜)이 실제로 열어주는 사용량은 다를 수 있습니다.
일반 사용자(Pro)와 기업용(Enterprise)은 왜 다를까?
구분
누가 / 어디서
유효 컨텍스트 경향
함께 고려할 점
Free
무료 사용자
가장 작음
전체 사용량 제한도 큼
Pro
유료 개인
중간
Team · Enterprise
기업·조직
더 큼(확장 제공)
보안·관리자 제어·SSO·데이터 학습 제외 등
API
개발자가 직접 호출
모델 최대치(최신 모델 최대 100만 토큰)
에이전트 개발은 보통 여기
기업용(Enterprise)이 더 큰 컨텍스트를 제공하는 경향이 있는 이유는 단순합니다. 기업은 긴 계약서, 대규모 코드베이스, 방대한 사내 문서를 한 번에 다뤄야 하기 때문입니다. 또한 Enterprise의 진짜 차별점은 컨텍스트 크기뿐 아니라 데이터 보안·관리자 제어·우리 데이터를 학습에 쓰지 않는 정책 같은 부분에 있습니다.
⚠️ 구체적인 토큰 숫자는 자주 바뀝니다. 플랜별 정확한 수치는 제품 정책이라 시점에 따라 달라지므로, 실제 값은 항상 공식 문서에서 최신 정보를 확인하세요.
에이전트 개발자에게 주는 시사점
이 부분이 이 책에서 가장 중요합니다.
Loading diagram…
우리가 만드는 AI 에이전트는 대부분 ②API를 사용합니다. 따라서 모델이 지원하는 최대 컨텍스트(최신 모델 기준 최대 100만 토큰) 를 그대로 쓸 수 있습니다.
즉, "내가 Claude.ai Pro 화면에서 느낀 한계"와 "내가 API로 만드는 에이전트의 한계"는 다릅니다. 소비자 제품의 제약을 에이전트의 한계로 오해하지 마세요.
LLM은 기억하지 못한다 (Stateless)
많은 입문자가 오해하는 부분입니다. LLM은 이전 대화를 스스로 기억하지 못합니다. 매 호출은 백지 상태에서 시작합니다.
그런데 챗봇과 대화가 자연스럽게 이어지는 이유는? 이전 대화 내용을 매번 통째로 다시 보내주기 때문입니다.
Loading diagram…
이 사실은 에이전트 개발에서 매우 중요합니다. 대화나 작업 기록을 어떻게 관리하고, 무엇을 컨텍스트에 넣을지가 곧 메모리 설계입니다(6장).
환각(Hallucination): 그럴듯한 거짓말
LLM은 "그럴듯한 말 잇기" 기계이므로, 모르는 것도 자신 있게 지어냅니다. 이를 환각(hallucination) 이라고 합니다.
Loading diagram…
환각을 줄이는 가장 강력한 방법이 바로 도구를 주는 것입니다. "모르면 지어내지 말고 도구로 확인해"라고 하는 셈이죠. 이것이 에이전트와 MCP가 필요한 근본 이유 중 하나입니다.
작은 코드로 감 잡기 (Python)
다음은 Claude API로 LLM을 한 번 호출하는 가장 단순한 예제입니다. 아직 에이전트가 아니라 "두뇌 한 번 부르기"입니다.
# pip install anthropicimport anthropicclient = anthropic.Anthropic() # API 키는 환경변수 ANTHROPIC_API_KEY 사용response = client.messages.create( model="claude-sonnet-4-6", max_tokens=300, system="너는 초보자에게 친절히 설명하는 IT 강사야.", # 시스템 프롬프트 messages=[ {"role": "user", "content": "토큰이 뭔지 한 문장으로 설명해줘."} ],)print(response.content[0].text)
여기서 system이 시스템 프롬프트, messages가 대화 기록 + 사용자 메시지에 해당합니다. 다음 장부터는 여기에 도구와 루프를 더해 진짜 에이전트로 발전시킵니다.
글자만? 아니, 그림도 본다 — 멀티모달
최신 LLM은 텍스트뿐 아니라 이미지·PDF 같은 자료도 입력으로 받을 수 있습니다. 이를 멀티모달(multimodal) 이라고 합니다.
Loading diagram…
덕분에 에이전트는 "이 스크린샷에서 에러 메시지를 읽어줘", "이 영수증 PDF에서 금액을 뽑아줘" 같은 일을 할 수 있습니다. 입력에 이미지/문서 조각을 함께 담아 보내면 됩니다(세부 방법은 공식 문서 참고). 입문 단계에서는 "LLM이 글 말고 그림·문서도 본다" 정도만 기억하면 충분합니다.
답이 한 글자씩 나오는 이유 — 스트리밍
챗봇이 답을 한 번에 툭 주지 않고 타이핑하듯 흘려보내는 것을 본 적 있죠? LLM이 토큰을 하나씩 생성하기 때문인데, 이를 그대로 실시간으로 받아 보여주는 것을 스트리밍(streaming) 이라고 합니다.
Loading diagram…
스트리밍 없이: 답이 다 완성될 때까지 빈 화면을 기다린다.
스트리밍: 생성되는 대로 보여주니 반응이 빠른 느낌을 준다. 긴 답변일수록 효과가 크다.
에이전트처럼 긴 작업에서는 스트리밍이 사용자 경험에 중요합니다. 구현은 SDK가 도와주며(messages.stream(...)), 자세한 사용은 실전에서 다룹니다.
핵심 요약
LLM은 다음 토큰을 확률로 예측하는, "그럴듯한 말 잇기" 기계다.
토큰은 처리·과금·한계의 기본 단위이며, 컨텍스트 윈도우는 한 번에 볼 수 있는 토큰 상한이다.
LLM은 스스로 기억하지 못한다. 대화는 이전 내용을 매번 다시 보내서 이어진다.
환각은 LLM의 본질적 한계이며, 도구를 주는 것이 가장 강력한 해법이다.
LLM은 텍스트뿐 아니라 이미지·문서(멀티모달) 도 입력으로 받을 수 있다.
답변은 토큰 단위로 생성되므로 스트리밍으로 실시간 표시할 수 있다.
연습 문제
좋아하는 한국어 문장 하나가 대략 몇 토큰일지 추측해보고, 왜 한국어가 영어보다 토큰이 많아지는지 설명해보세요.
"컨텍스트 윈도우 = 책상 크기" 비유를, 자신만의 다른 비유로 바꿔보세요.
(생각해보기) 환각을 줄이는 방법으로 '도구 제공' 외에 또 무엇이 있을까요?
3장. AI Agent의 구성 요소
이 장에서 배우는 것
에이전트를 이루는 네 가지 핵심 부품: 두뇌·계획·메모리·도구
각 부품이 하는 일과 서로 어떻게 맞물리는지
전체 그림을 한눈에 보는 아키텍처 다이어그램
들어가며
1장에서 에이전트의 동작 루프를, 2장에서 두뇌인 LLM을 배웠습니다. 이제 에이전트라는 "로봇"을 분해해 어떤 부품으로 이루어져 있는지 살펴봅시다. 부품을 알면 나중에 직접 만들 때(7장) 무엇을 준비해야 할지 분명해집니다.
에이전트의 4대 구성 요소
대부분의 AI 에이전트는 네 가지 핵심 부품으로 설명할 수 있습니다.
Loading diagram…
부품
역할
비유
🧠 두뇌(LLM)
이해·추론·결정
사람의 뇌
🗺️ 계획(Planning)
목표를 단계로 쪼개기
할 일 목록 작성
💾 메모리(Memory)
정보 기억·회상
수첩과 장기 기억
🔧 도구(Tools)
실제 행동 수행
손과 발, 연장
하나씩 살펴봅시다.
1. 두뇌 (LLM)
모든 판단의 중심입니다. 사용자의 목표를 이해하고, 다음에 무엇을 할지 결정하며, 도구의 결과를 해석합니다. 2장에서 본 그 LLM입니다.
두뇌의 성격은 주로 시스템 프롬프트로 정합니다.
SYSTEM_PROMPT = """너는 여행 일정을 짜주는 AI 에이전트야.- 사용자의 예산과 일정을 먼저 확인해.- 모르는 정보(날씨, 가격 등)는 반드시 도구로 확인하고 지어내지 마.- 최종 일정은 표로 정리해서 보여줘."""
좋은 시스템 프롬프트의 4가지 요령
두뇌의 성격을 정하는 시스템 프롬프트는 에이전트 품질을 좌우합니다. 입문자가 지키면 좋은 요령을 정리합니다.
Loading diagram…
역할을 분명히: "너는 친절한 여행 가이드야"처럼 정체성을 준다.
규칙은 구체적으로: "모르는 정보는 도구로 확인하고 지어내지 마"처럼 행동 지침을 명확히.
출력 형식을 지정: "표로", "3문장 이내로"처럼 원하는 형태를 알려준다.
과한 강요는 자제: "반드시! 무조건!"을 남발하면 오히려 엉뚱하게 과잉 반응할 수 있다. 차분하고 명확한 지시가 더 잘 통한다.
비유: 시스템 프롬프트는 신입에게 주는 업무 매뉴얼입니다. 역할·규칙·결과물 형식을 분명히 적을수록 일을 잘합니다.
2. 계획 (Planning)
복잡한 목표는 한 번에 못 풉니다. 에이전트는 목표를 작은 단계로 쪼개고, 순서를 정합니다.
Loading diagram…
계획에는 여러 스타일이 있습니다(자세히는 4장).
한 걸음씩(ReAct): 한 단계 하고, 결과 보고, 다음 단계 정하기 — 즉흥적이고 유연함
미리 다 짜기(Plan-and-Execute): 전체 계획을 먼저 세우고 순서대로 실행 — 체계적
반성(Reflection): 결과를 스스로 점검하고 부족하면 다시 시도
3. 메모리 (Memory)
2장에서 LLM은 스스로 기억하지 못한다고 했습니다. 그래서 에이전트는 별도의 메모리가 필요합니다. 메모리는 보통 두 종류로 나뉩니다.
Loading diagram…
단기 기억(short-term): 지금 진행 중인 대화·작업 내용. 컨텍스트 윈도우 안에 담깁니다.
장기 기억(long-term): 윈도우에 다 담을 수 없는 방대한 지식. 외부 저장소에 두고 필요할 때 검색해서 가져옵니다. 이 검색-주입 방식이 바로 RAG(6장)입니다.
4. 도구 (Tools)
에이전트의 손발입니다. 도구가 있어야 LLM이 실제 행동을 할 수 있습니다.
Loading diagram…
도구의 예:
웹 검색, 날씨 조회, 환율 조회 (정보 가져오기)
파일 읽기/쓰기, 데이터베이스 조회 (데이터 다루기)
이메일 보내기, 일정 등록 (행동하기)
계산기, 코드 실행 (정확한 처리)
여기서 MCP가 등장합니다. 도구를 매번 직접 만들면 번거롭고 재사용이 어렵습니다. MCP는 이런 도구들을 표준 방식으로 연결·공유하게 해주는 규약입니다(3부에서 본격적으로 다룹니다).
전체 그림: 부품들이 함께 도는 모습
네 부품이 1장의 "생각 → 행동 → 관찰" 루프 안에서 어떻게 협력하는지 봅시다.
Loading diagram…
이 그림 한 장이 이 책 1~2부의 요약입니다. 앞으로 각 부품을 깊이 파고들 것입니다.
핵심 요약
에이전트는 두뇌(LLM)·계획·메모리·도구 네 부품으로 이루어진다.
두뇌는 결정하고, 계획은 목표를 단계로 쪼개고, 메모리는 정보를 기억하며, 도구는 실제 행동을 한다.
메모리는 단기(컨텍스트) 와 장기(외부 저장소+검색) 로 나뉜다.
도구를 표준으로 연결하는 것이 MCP, 에이전트끼리 협업하는 것이 A2A다.
연습 문제
4대 구성요소(두뇌·계획·메모리·도구)를 '요리사'에 비유해 각각 무엇에 해당하는지 매핑해보세요.
0장에서 만든 자기소개 호출에 시스템 프롬프트를 추가해 말투(예: 해적 말투)를 바꿔보세요.
(생각해보기) 단기 기억과 장기 기억이 둘 다 필요한 작업의 예를 들어보세요.
📦 Part 2 · AI Agent 만들기
4장. Agent 아키텍처와 동작 패턴
이 장에서 배우는 것
에이전트가 목표를 향해 나아가는 대표적인 "사고 패턴" 3가지
ReAct: 한 걸음씩 생각하고 행동하기
Plan-and-Execute: 먼저 계획을 세우고 실행하기
Reflection: 자기 결과를 점검하고 다시 시도하기
언제 어떤 패턴을 쓰면 좋은지
들어가며
1장에서 에이전트의 심장은 "생각 → 행동 → 관찰" 루프라고 배웠습니다. 그런데 이 루프를 어떻게 돌리느냐에는 여러 스타일이 있습니다. 사람도 일하는 방식이 다르듯이요.
어떤 사람은 일단 한 단계 해보고 결과를 보며 다음을 정합니다. (즉흥형)
어떤 사람은 전체 계획을 먼저 쫙 세우고 그대로 실행합니다. (계획형)
어떤 사람은 결과물을 다시 검토하고 부족하면 고칩니다. (검토형)
에이전트도 똑같습니다. 이 세 가지 대표 패턴을 알아봅시다.
패턴 1. ReAct — 생각하고, 행동하고, 반복하기
ReAct는 Reasoning(추론) + Acting(행동) 을 합친 말입니다. 가장 널리 쓰이는 기본 패턴으로, 1장에서 본 루프 그 자체입니다.
한 단계마다 LLM이 "지금 무슨 생각을 하고, 그래서 어떤 행동을 할지" 를 정하고, 도구 결과를 본 뒤 다음 단계를 다시 정합니다.
Loading diagram…
ReAct가 머릿속으로 진행되는 모습을 글로 풀면 이렇습니다.
질문: "서울과 부산 중 지금 더 따뜻한 도시는?"Thought: 두 도시의 현재 기온을 알아야 한다. 먼저 서울부터.Action: get_weather("서울")Observation: 서울 12도Thought: 이제 부산도 확인하자.Action: get_weather("부산")Observation: 부산 16도Thought: 16 > 12 이므로 부산이 더 따뜻하다. 답할 수 있다.Answer: 지금은 부산(16도)이 서울(12도)보다 따뜻합니다.
장점: 단순하고 유연하다. 중간 결과에 따라 방향을 바꿀 수 있다.
단점: 단계가 많아지면 길을 잃거나 같은 행동을 반복할 수 있다.
언제: 대부분의 일반적인 작업. 가장 먼저 시도해 볼 기본값.
좋은 소식: 5장에서 배울 Tool Calling을 쓰면, 이 ReAct 루프가 사실상 자동으로 굴러갑니다. Claude 같은 모델이 "이 도구를 이렇게 부르겠다"를 알아서 정해주거든요.
패턴 2. Plan-and-Execute — 먼저 계획, 그다음 실행
복잡하고 단계가 많은 작업은, 매 단계 즉흥적으로 정하면 비효율적일 수 있습니다. 그래서 계획(Plan)을 먼저 통째로 세우고, 그 계획을 순서대로 실행(Execute) 하는 패턴입니다.
Loading diagram…
예: "다음 주 제주도 2박 3일 여행 계획 짜줘"
[Planner가 먼저 세운 계획]1. 제주 날씨 확인2. 일자별 추천 명소 검색3. 각 지역 맛집 검색4. 이동 동선 정리5. 표로 일정 완성[Executor가 1번부터 차례로 실행]
장점: 큰 그림이 명확하고, 단계가 많아도 체계적이다. 토큰·비용 예측이 쉽다.
단점: 중간에 상황이 바뀌면 계획을 다시 짜야 한다(유연성↓).
언제: 단계가 많고 순서가 중요한 복잡한 작업.
ReAct가 즉흥 연주라면, Plan-and-Execute는 악보를 먼저 그리고 연주하는 셈입니다. 실무에서는 둘을 섞기도 합니다(큰 계획 + 각 단계는 ReAct).
패턴 3. Reflection — 스스로 검토하고 고치기
사람도 글을 쓰면 다시 읽고 고칩니다. Reflection(반성/성찰) 패턴은 에이전트가 자기 결과물을 스스로 평가하고, 부족하면 다시 시도하게 합니다.
Loading diagram…
예: "이 함수의 버그를 고쳐줘"
1. 생성: 수정안을 만든다.2. 검토: "이 수정이 정말 버그를 고치나? 새 버그는 없나?"3. 부족하면: 문제점을 정리해 다시 수정.4. 통과하면: 최종안 제출.
종종 생성하는 역할과 검토하는 역할을 분리합니다(검토자에게 "비판적으로 보라"고 시킴). 그러면 더 엄격하게 잡아냅니다 — 이는 17장의 품질·안정성 이야기와도 이어집니다.
장점: 결과 품질이 크게 올라간다. 실수를 잡아낸다.
단점: 호출이 늘어 비용·시간이 더 든다.
언제: 정확성이 중요한 작업(코드, 분석, 보고서).
세 패턴 한눈에 비교
Loading diagram…
패턴
한 줄 요약
강점
적합한 상황
ReAct
한 걸음씩 생각·행동
유연·단순
일반적인 작업(기본값)
Plan-and-Execute
계획 먼저, 실행 나중
체계적
복잡·다단계 작업
Reflection
결과를 스스로 검토
고품질
정확성이 중요한 작업
실전에서는 한 가지만 쓰지 않습니다. 예를 들어 "Plan으로 큰 틀을 잡고 → 각 단계는 ReAct로 실행 → 마지막에 Reflection으로 검수" 처럼 조합합니다.
핵심 요약
에이전트의 루프를 어떻게 굴리느냐에 따라 대표 패턴이 나뉜다.
ReAct: 생각→행동→관찰을 반복하는 유연한 기본 패턴.
Plan-and-Execute: 전체 계획을 먼저 세우고 순서대로 실행하는 체계적 패턴.
Reflection: 결과를 스스로 검토·개선해 품질을 높이는 패턴.
실무에서는 여러 패턴을 조합하며, 어떤 패턴이든 핵심 동력은 도구(다음 장) 다.
연습 문제
"이메일 100통을 읽고 주제별로 분류해 정리하라"는 작업에 어떤 패턴이 적합할지 고르고 이유를 적어보세요.
ReAct와 Plan-and-Execute의 차이를 각각 한 문장으로 구분해보세요.
(생각해보기) Reflection은 비용이 더 듭니다. 그럼에도 꼭 써야 하는 작업의 예를 들어보세요.
5장. 도구 사용과 Function/Tool Calling
이 장에서 배우는 것
LLM이 외부 기능을 호출하는 원리(Tool Calling)
도구를 "정의"한다는 것의 의미 (이름·설명·입력 형식)
LLM이 도구를 부르고 결과를 받는 전체 흐름
Python + Claude로 실제 도구 호출 코드 따라 하기
들어가며
3장에서 도구는 에이전트의 손발이라고 했습니다. 그런데 LLM은 글만 생성할 수 있는데, 어떻게 "함수를 실행"할까요?
비밀은 의외로 단순합니다. LLM이 직접 함수를 실행하는 게 아닙니다. LLM은 그저 "이 도구를, 이런 입력으로 불러주세요" 라고 말(JSON) 할 뿐이고, 실제 실행은 우리 코드가 합니다.
비유: LLM은 요리를 시키는 셰프(머리), 우리 코드는 실제로 칼질하는 손입니다. 셰프가 "양파를 썰어라"라고 말하면, 손이 그 일을 실행하고 결과를 셰프에게 돌려줍니다.
도구를 "정의"한다는 것
LLM이 도구를 쓰려면, 먼저 어떤 도구가 있는지 알려줘야 합니다. 도구 하나를 정의할 때 보통 3가지를 적습니다.
Loading diagram…
이름: 도구를 식별하는 짧은 이름.
설명: 이 도구가 무엇을, 언제 쓰는 것인지. LLM은 이 설명을 보고 언제 부를지 판단하므로 가장 중요합니다.
입력 형식: 어떤 값을 어떤 타입으로 받는지 (JSON Schema).
핵심 팁: 설명은 "무엇을 하는지"뿐 아니라 "언제 불러야 하는지" 까지 적으면 LLM이 훨씬 정확하게 호출합니다. 예: "사용자가 현재 날씨나 기온을 물을 때 호출하라."
Tool Calling의 전체 흐름
도구를 정의해 주면, LLM과 우리 코드 사이에 다음과 같은 "왕복 대화"가 일어납니다.
Loading diagram…
흐름의 핵심은 공이 LLM과 우리 코드 사이를 오간다는 점입니다.
우리가 질문 + 도구 목록을 LLM에 보낸다.
LLM이 "이 도구를 이렇게 불러줘"라고 응답한다. (아직 실행 안 됨!)
우리 코드가 그 도구를 실제로 실행한다.
결과를 다시 LLM에 보낸다.
LLM이 결과를 보고 최종 답을 만든다. (또는 도구를 더 부른다 → 2번으로)
이 반복이 바로 4장에서 배운 ReAct 루프입니다. Tool Calling이 그 루프를 자연스럽게 굴려줍니다.
Python으로 직접 해보기
이제 Claude로 실제 도구 호출을 구현해 봅시다. 날씨를 알려주는 가짜 도구를 만들어 볼게요.
messages = [{"role": "user", "content": "서울 날씨 어때?"}]while True: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages, ) # LLM이 도구를 부르지 않고 그냥 답했다면 종료 if response.stop_reason != "tool_use": break # LLM의 응답(도구 요청 포함)을 대화에 추가 messages.append({"role": "assistant", "content": response.content}) # 요청된 도구들을 실제로 실행 tool_results = [] for block in response.content: if block.type == "tool_use": if block.name == "get_weather": result = get_weather(**block.input) # 실제 함수 실행! tool_results.append({ "type": "tool_result", "tool_use_id": block.id, # 어떤 요청에 대한 결과인지 짝 맞추기 "content": result, }) # 도구 결과를 사용자 메시지로 다시 LLM에 전달 messages.append({"role": "user", "content": tool_results})# 최종 답변 출력for block in response.content: if block.type == "text": print(block.text)
코드가 길어 보여도, 하는 일은 위 시퀀스 다이어그램 그대로입니다.
stop_reason == "tool_use" → "LLM이 도구를 부르고 싶어 한다"는 신호
block.id와 tool_use_id를 짝지어, 어떤 요청의 결과인지 알려줌
도구를 안 부르고 그냥 답하면 루프 종료
더 쉬운 길: 자동 루프 도우미
위 루프를 매번 손으로 짜는 게 번거롭다면, SDK가 제공하는 도구 실행 도우미(tool runner) 를 쓸 수 있습니다. 함수를 등록하면 위 반복을 알아서 처리해 줍니다. (입문 단계에서는 위 수동 루프로 원리를 이해한 뒤, 익숙해지면 도우미를 쓰는 것을 권합니다.)
도구를 여러 개 줄 때
도구가 여러 개면 LLM이 상황에 맞는 도구를 골라서 부릅니다. 때로는 한 번에 여러 도구를 동시에 부르기도 합니다.
Loading diagram…
주의: 한 번에 여러 도구 결과를 돌려줄 때는 하나의 사용자 메시지에 모든 tool_result를 모아서 보내야 합니다. 결과를 여러 메시지로 쪼개면 LLM이 혼란스러워합니다.
(보너스) 구조화된 출력: JSON으로 받기
도구 호출과 짝을 이루는 유용한 기능이 하나 더 있습니다. 바로 구조화된 출력(structured output) 입니다.
문제 상황: LLM에게 "이름과 이메일을 뽑아줘"라고 하면, 답이 매번 다른 형태로 옵니다. 그러면 우리 코드가 그 결과를 자동으로 처리하기 어렵습니다.
Loading diagram…
이때 원하는 형식(스키마)을 정해주고 그 형식으로만 답하게 강제할 수 있습니다. 그러면 항상 똑같은 구조의 JSON으로 받아, 코드에서 곧바로 쓸 수 있습니다.
# 개념 예시: 원하는 JSON 구조를 지정response = client.messages.create( model="claude-sonnet-4-6", max_tokens=300, messages=[{"role": "user", "content": "다음에서 정보 추출: 홍길동 (hong@example.com), 프로 플랜"}], output_config={ "format": { "type": "json_schema", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "email": {"type": "string"}, "plan": {"type": "string"}, }, "required": ["name", "email", "plan"], "additionalProperties": False, }, } },)# → 항상 {"name": "...", "email": "...", "plan": "..."} 형태로 반환
Tool Calling 이 "행동"을 시키는 것이라면, 구조화된 출력은 "결과를 정해진 형식으로" 받는 것입니다.
분류 결과, 추출 데이터, 점수 등 코드가 후속 처리할 값을 받을 때 특히 유용합니다.
입문 단계에서는 "원하는 형식을 강제해 JSON으로 받을 수 있다" 정도만 기억하면 됩니다. 세부 사용법은 공식 문서를 참고하세요.
좋은 도구를 만드는 팁
이름은 구체적으로: weather보다 get_current_weather.
설명에 "언제 쓰는지"를 명시: LLM의 호출 판단이 정확해진다.
입력은 단순하게: 필요한 값만, 명확한 타입으로.
에러도 결과로: 실패 시 "Error: 도시를 찾을 수 없음" 같은 메시지를 돌려주면 LLM이 다른 방법을 시도한다.
도구 개수는 적당히: 너무 많으면 LLM이 헷갈린다. → 이 문제를 표준으로 푸는 게 다음 파트의 MCP입니다.
핵심 요약
LLM은 도구를 직접 실행하지 않는다. "이렇게 불러줘"라고 말하면 우리 코드가 실행한다.
도구 정의는 이름·설명·입력 형식 3가지. 특히 설명("언제 쓰는지") 이 중요하다.
흐름: 질문+도구 → LLM이 호출 요청 → 코드가 실행 → 결과 전달 → LLM이 답(또는 더 호출). 이것이 ReAct 루프다.
tool_use_id로 요청과 결과를 짝 지어야 한다.
도구를 표준 방식으로 연결·재사용하는 기술이 다음에 배울 MCP다.
연습 문제
get_weather 도구의 description을 "언제 사용하는지"까지 포함해 더 좋게 다시 써보세요.
도구를 하나 더 추가한다면 무엇을 만들지, 이름·설명·입력 형식을 정의해보세요.
(생각해보기) 같은 기능을 다음 장에서 배울 'Tool'과 'Resource' 중 무엇으로 제공할지 판단하는 기준은 무엇일까요?
6장. 메모리와 컨텍스트 관리
이 장에서 배우는 것
에이전트의 단기 기억과 장기 기억의 차이
컨텍스트 윈도우가 가득 찰 때 대처하는 법(요약·정리)
장기 기억의 핵심 기술 RAG(검색 증강 생성) 맛보기
임베딩과 벡터 검색이 무엇인지 직관적으로 이해하기
들어가며
2장에서 충격적인 사실을 배웠죠. LLM은 스스로 기억하지 못합니다. 대화가 이어지는 건 이전 내용을 매번 다시 보내주기 때문이었습니다.
그런데 대화가 길어지거나, 회사 문서 1만 페이지를 참고해야 한다면? 그 모든 걸 매번 다 보낼 수는 없습니다(컨텍스트 윈도우 한계!). 그래서 에이전트에는 메모리 설계가 필요합니다.
두 가지 기억: 단기 vs 장기
3장에서 잠깐 봤듯이, 메모리는 두 종류로 나뉩니다.
Loading diagram…
구분
단기 기억
장기 기억
위치
컨텍스트 윈도우 안
외부 저장소(DB·파일·벡터DB)
크기
제한적
사실상 무제한
비유
책상 위에 펼친 종이
책장에 꽂힌 책들
접근
항상 보임
필요할 때 찾아서 꺼냄
핵심 아이디어: 모든 걸 책상(컨텍스트)에 올릴 수 없으니, 책장(장기 기억)에 두고 필요한 페이지만 꺼내 오자.
단기 기억이 넘칠 때: 요약과 정리
대화가 아주 길어지면 단기 기억(컨텍스트)이 가득 찹니다. 이때 흔히 쓰는 두 가지 방법이 있습니다.
Loading diagram…
요약(Compaction): 오래된 대화를 짧게 압축해서 핵심만 남긴다. "지금까지 줄거리" 같은 것.
정리(Context Editing): 더 이상 필요 없는 오래된 도구 결과 등을 비워서 공간을 확보한다.
비유: 책상이 꽉 차면 ① 오래된 서류를 한 장 요약본으로 바꾸거나 ② 안 쓰는 서류를 치웁니다. 둘 다 작업 공간을 되찾는 방법입니다.
장기 기억의 핵심: RAG
회사 문서, 제품 매뉴얼, 위키처럼 방대하고 고정된 지식은 어떻게 활용할까요? 정답은 RAG(Retrieval-Augmented Generation, 검색 증강 생성) 입니다.
이름이 어렵지만 개념은 단순합니다.
질문과 관련된 부분만 검색해서, 그 내용을 LLM에게 같이 건네주고 답하게 한다.
Loading diagram…
RAG의 흐름:
사용자가 질문한다.
질문과 관련된 문서 조각을 저장소에서 검색한다.
검색된 조각 + 질문을 함께 LLM에 보낸다.
LLM은 그 내용을 근거로 답한다.
이렇게 하면 2장에서 배운 환각(없는 사실 지어내기) 을 크게 줄일 수 있습니다. LLM이 "기억"이 아니라 실제 문서를 보고 답하니까요.
RAG는 오픈북 시험과 같습니다. 모든 걸 외울(컨텍스트에 담을) 필요 없이, 필요할 때 책에서 해당 페이지를 펴 보고 답하는 거죠.
그런데 "관련된 조각"을 어떻게 찾지? — 임베딩
여기서 한 가지 의문이 생깁니다. 질문 "환불 정책"과 관련된 문서를, 단순 키워드 검색만으로 찾기는 어렵습니다(문서엔 "반품 규정"이라고 써 있을 수도 있으니까요).
그래서 의미로 검색하는 기술, 임베딩(embedding) 을 씁니다.
Loading diagram…
임베딩: 글을 숫자 목록(벡터) 으로 바꾸는 것. 의미가 비슷한 글은 비슷한 숫자가 된다.
벡터 검색: 질문을 벡터로 바꾼 뒤, 가장 가까운(의미가 비슷한) 문서 조각을 찾는다.
이렇게 의미가 가까운 것을 저장·검색하는 데 특화된 DB가 벡터 데이터베이스(Vector DB) 입니다.
비유: 임베딩은 모든 글에 GPS 좌표를 붙이는 것과 같습니다. "환불 정책"과 "반품 규정"은 좌표가 가깝고, "점심 메뉴"는 멀리 있습니다. 질문 근처에 있는 문서를 집어 오면 됩니다.
RAG 전체 그림
문서를 미리 준비하는 단계와, 질문에 답하는 단계로 나뉩니다.
Loading diagram…
이 책은 입문서이므로 RAG의 구현 코드까지 다루지는 않지만, "검색해서 LLM에 같이 넣어준다" 는 핵심만 기억하면 충분합니다. 실제로는 에이전트가 RAG를 하나의 도구(5장)로 갖는 경우가 많습니다 — 예: search_documents("환불 정책").
조각으로 나누기(청킹)가 중요한 이유
RAG 준비 단계에서 문서를 작은 조각으로 나눈다(chunking, 청킹) 고 했습니다. 이 "조각 크기"가 검색 품질을 크게 좌우합니다.
Loading diagram…
너무 크면: 한 조각에 여러 주제가 섞여, 검색해도 군더더기가 따라온다.
너무 작으면: 문장이 중간에 잘려 의미가 깨진다.
적당히: 보통 "한 단락 = 한 주제"가 담기도록 나누고, 조각끼리 약간 겹치게(overlap) 해서 경계에서 문맥이 끊기지 않게 합니다.
비유: 책을 포스트잇 단위로 찢어 보관하는 것과 같습니다. 너무 크게 찢으면 한 장에 여러 주제가, 너무 잘게 찢으면 문장이 토막 납니다. 한 생각이 온전히 담길 만큼이 좋습니다.
청킹 외에도 검색 품질을 높이는 기법(키워드+의미 혼합 검색, 재정렬 등)이 많지만, 입문 단계에서는 "조각을 잘 나누는 것이 RAG 품질의 출발점" 이라는 점만 기억하면 됩니다.
정리: 에이전트의 기억 전략
Loading diagram…
가까운 정보는 단기 기억에,
넘치면 요약·정리로,
방대한 지식은 RAG(장기 기억) 로.
핵심 요약
에이전트 메모리는 단기(컨텍스트 안) 와 장기(외부 저장소) 로 나뉜다.
컨텍스트가 넘치면 요약(Compaction) 이나 정리(Context Editing) 로 공간을 확보한다.
RAG는 질문과 관련된 내용을 검색해서 LLM에 함께 주는 기법으로, 환각을 줄인다.
관련 내용은 임베딩(글→벡터) 과 벡터 검색으로 의미 기반으로 찾는다.
RAG는 흔히 에이전트의 하나의 도구로 구현된다.
연습 문제
회사 위키 5,000페이지를 활용하는 챗봇을 만든다면, 단기 기억과 장기 기억을 각각 어떻게 쓸지 설계해보세요.
"환불 정책"과 의미가 가까운 문구 3개, 먼 문구 3개를 적어보세요(임베딩 직관 연습).
(생각해보기) 청크(조각)가 너무 크거나 너무 작을 때 각각 어떤 문제가 생기나요?
7장. 첫 AI Agent 직접 만들기
이 장에서 배우는 것
지금까지 배운 개념을 하나로 합쳐 작동하는 에이전트 만들기
도구 2개(계산기·날씨)를 가진 미니 에이전트 완성
코드를 한 줄씩 따라 하며 ReAct 루프 직접 굴려 보기
잘 안 될 때 점검할 것들
들어가며
드디어 직접 만들어 볼 시간입니다! 1~6장에서 배운 것을 모아 작동하는 AI 에이전트를 손으로 만들어 봅시다. 목표는 화려함이 아니라 "아, 에이전트가 이렇게 도는구나" 를 몸으로 느끼는 것입니다.
우리가 만들 에이전트는 두 가지 도구를 가집니다.
🧮 계산기: 정확한 수식 계산 (LLM은 계산을 종종 틀리므로 도구로 맡김)
🌦️ 날씨 조회: 도시의 현재 날씨 (예시용 가짜 데이터)
Loading diagram…
준비물
pip install anthropic
그리고 API 키를 환경변수로 설정합니다(키는 코드에 직접 적지 마세요).
export ANTHROPIC_API_KEY="당신의-API-키"
API 키 발급이나 로그인이 필요하면, 프롬프트에 ! 명령어 형태로 직접 실행해 안내받을 수 있습니다.
1단계: 실제 도구 함수 만들기
먼저 에이전트의 "손발"이 될 실제 함수 두 개를 만듭니다.
import anthropicclient = anthropic.Anthropic()# 🧮 계산기: 안전하게 사칙연산만 평가def calculator(expression: str) -> str: try: # 보안을 위해 사용할 수 있는 문자를 제한 (입문용 간단 버전) allowed = set("0123456789+-*/(). ") if not set(expression) <= allowed: return "Error: 허용되지 않은 문자가 포함됨" return str(eval(expression)) except Exception as e: return f"Error: {e}"# 🌦️ 날씨: 예시용 가짜 데이터def get_weather(city: str) -> str: fake = {"서울": "12도, 맑음", "부산": "16도, 흐림", "제주": "18도, 비"} return fake.get(city, "정보 없음")
실제 서비스라면 eval은 위험하므로 쓰면 안 됩니다(17장 보안에서 다룹니다). 여기서는 학습용으로 문자 제한을 두었습니다.
2단계: 도구 정의(LLM에게 알려줄 설명서)
5장에서 배운 대로, 각 도구를 이름·설명·입력 형식으로 정의합니다.
tools = [ { "name": "calculator", "description": "사칙연산 수식을 정확히 계산한다. " "숫자 계산이 필요할 때 항상 이 도구를 사용하라.", "input_schema": { "type": "object", "properties": { "expression": { "type": "string", "description": "계산할 수식, 예: (12 + 3) * 2", } }, "required": ["expression"], }, }, { "name": "get_weather", "description": "도시의 현재 날씨를 조회한다. " "사용자가 날씨나 기온을 물을 때 사용하라.", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "도시 이름"} }, "required": ["city"], }, },]# 도구 이름 → 실제 함수 연결 (실행 시 사용)TOOL_FUNCS = {"calculator": calculator, "get_weather": get_weather}
3단계: 에이전트 루프 만들기
이제 핵심입니다. 4장의 ReAct 루프를 함수 하나로 구현합니다.
def run_agent(user_message: str) -> str: messages = [{"role": "user", "content": user_message}] while True: # 목표를 달성할 때까지 생각↔행동 반복 response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, system="너는 도구를 활용하는 친절한 비서야. " "계산이나 날씨는 반드시 도구로 확인하고 지어내지 마.", tools=tools, messages=messages, ) # 1) 도구를 안 부르고 그냥 답했다면 → 끝 if response.stop_reason != "tool_use": break # 2) LLM의 응답(도구 요청 포함)을 대화 기록에 추가 messages.append({"role": "assistant", "content": response.content}) # 3) 요청된 도구들을 실제로 실행 tool_results = [] for block in response.content: if block.type == "tool_use": func = TOOL_FUNCS[block.name] # 이름으로 함수 찾기 result = func(**block.input) # 실제 실행! print(f" 🔧 {block.name}({block.input}) → {result}") tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": result, }) # 4) 도구 결과를 다시 LLM에 전달 (하나의 메시지에 모아서) messages.append({"role": "user", "content": tool_results}) # 최종 텍스트 답변 모으기 return "".join(b.text for b in response.content if b.type == "text")
4단계: 실행해 보기
if __name__ == "__main__": 질문들 = [ "서울 날씨 알려줘", "(15 + 7) * 3 은 얼마야?", "서울이 18도이고 제주가 18도면 기온 차이는 몇 도야?", ] for q in 질문들: print(f"\n🙋 {q}") print(f"🤖 {run_agent(q)}")
🔧 표시가 바로 에이전트가 도구를 부른 순간입니다. LLM이 직접 계산하지 않고 계산기 도구를 거쳤다는 게 보이죠?
방금 무슨 일이 일어났나?
우리가 만든 작은 코드 안에서 1~6장의 개념이 모두 작동했습니다.
Loading diagram…
두뇌(LLM): 어떤 도구를 부를지 판단 (3장)
계획: 여러 도구를 순서대로/동시에 사용 (3·4장)
도구: 실제 행동 수행 (5장)
루프: ReAct로 목표까지 반복 (4장)
메모리: messages에 대화가 누적됨 (단기 기억, 6장)
잘 안 될 때 점검표
Loading diagram…
인증 오류: API 키가 환경변수에 제대로 설정됐는지 확인.
도구를 안 부름: 설명에 "언제 사용하라"를 추가. 시스템 프롬프트에 "지어내지 말고 도구로 확인"을 명시.
무한 루프 걱정: 안전하게 while 대신 최대 횟수(예: 10회) 제한을 두는 것도 좋습니다.
결과가 꼬임: tool_use_id와 block.id가 올바르게 짝지어졌는지 확인.
안전장치 추천: 실전에서는 for _ in range(10): 처럼 최대 반복 횟수를 정해 무한 루프를 막습니다.
실전에서 한 걸음 더
우리 미니 에이전트는 학습용으로 충분하지만, 실제 서비스로 가려면 몇 가지를 더 챙겨야 합니다. 깊이 들어가진 않고, "이런 걸 고려해야 한다" 는 감만 잡아 봅시다.
Loading diagram…
1) 멀티턴 대화 유지하기
지금 run_agent는 질문 하나가 끝나면 기억이 사라집니다. 2장에서 배웠듯, 대화를 이어가려면 messages를 계속 누적하면 됩니다.
messages = [] # 대화 전체를 함수 밖에서 유지def chat(user_text: str) -> str: messages.append({"role": "user", "content": user_text}) while True: resp = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages, ) messages.append({"role": "assistant", "content": resp.content}) if resp.stop_reason != "tool_use": break # (도구 실행 후 결과를 messages에 append — 7장 루프와 동일) ... return "".join(b.text for b in resp.content if b.type == "text")chat("내 이름은 민수야")chat("내 이름이 뭐랬지?") # 이전 대화를 기억! ("민수")
핵심은 대화 기록(messages)을 호출 사이에 보존하는 것입니다.
2) 비용과 지연 관리
에이전트는 한 번 답하는 데 LLM을 여러 번 부릅니다(루프마다 호출!). 그래서 비용·시간이 쌓입니다.
가벼운 작업엔 작은 모델을 쓴다.
max_tokens를 필요한 만큼만 둔다.
같은 큰 문맥을 반복해 보낸다면 프롬프트 캐싱으로 비용을 줄일 수 있다(공식 문서 참고).
3) 재시도와 오류 대응
네트워크 오류나 일시적 한도 초과(rate limit)는 언제든 생깁니다.
SDK는 보통 일시 오류를 자동 재시도해 준다.
도구 실행이 실패하면 결과에 "Error: ..."를 담아 LLM이 다른 방법을 시도하게 한다(5장).
4) 평가와 테스트
"내 에이전트가 잘 작동하나?"는 생각보다 답하기 어렵습니다.
대표적인 질문-기대답변 쌍을 모아 두고, 바꿀 때마다 통과하는지 확인한다.
위험한 행동(삭제·결제 등)은 사람의 승인을 거치게 한다(17장에서 자세히).
이 주제들은 17장(보안·모범 사례) 과 18장(사례·로드맵) 에서 더 다룹니다. 지금은 "에이전트는 비용·안정성·검증을 함께 고려해야 한다"는 점만 기억하세요.
더 해보기 (도전 과제)
도구를 하나 더 추가해 보세요. 예: 현재 시각 조회, 간단한 메모 저장.
시스템 프롬프트를 바꿔 에이전트의 말투/역할을 바꿔 보세요.
사용자와 계속 대화하도록 input()으로 반복 입력을 받아 보세요.
이렇게 직접 도구를 만들다 보면 곧 이런 생각이 듭니다. "좋은 도구를 매번 새로 만들기 번거로운데, 남이 만든 도구를 가져다 쓸 수는 없을까?" — 바로 그것이 다음 파트의 주제, MCP입니다.
핵심 요약
1~6장 개념을 모아 도구를 가진 작동하는 에이전트를 만들었다.
구조는 단순하다: 실제 함수 → 도구 정의 → ReAct 루프.
핵심 신호는 stop_reason == "tool_use", 핵심 연결은 tool_use_id 짝 맞춤.
실전에서는 최대 반복 제한과 보안(다음 파트·17장)을 꼭 챙긴다.
도구를 재사용·공유하고 싶다는 욕구가 곧 MCP로 이어진다.
📦 Part 3 · MCP (Model Context Protocol)
8장. MCP란 무엇이고 왜 필요한가
이 장에서 배우는 것
MCP(Model Context Protocol)가 무엇인지 한 문장으로
MCP가 풀려는 문제: 도구 연결의 "M×N 지옥"
MCP를 "AI의 USB-C"라고 부르는 이유
MCP가 있을 때와 없을 때가 어떻게 다른지
들어가며
2부에서 우리는 직접 도구를 만들어 에이전트에 붙였습니다(5·7장). 그런데 7장 마지막에 이런 생각이 들었죠.
"좋은 도구를 매번 새로 만드는 게 번거로운데, 남이 만든 도구를 그냥 가져다 쓸 수는 없을까?"
바로 이 문제를 푸는 것이 MCP(Model Context Protocol) 입니다.
한 문장으로: MCP는 AI 애플리케이션과 외부 도구·데이터를 연결하는 "표준 규약(약속)" 입니다.
MCP가 없으면 생기는 문제: M×N 지옥
도구를 연결하는 방식이 표준화되어 있지 않다고 상상해 봅시다. 우리에게는 여러 AI 앱(Claude 데스크톱, 우리가 만든 에이전트, IDE 등)이 있고, 연결하고 싶은 여러 도구·데이터(GitHub, 데이터베이스, 구글 드라이브, 슬랙 등)가 있습니다.
표준이 없으면, 앱마다 도구마다 따로따로 연결 코드를 만들어야 합니다.
Loading diagram…
앱이 3개, 도구가 3개면 연결선이 3 × 3 = 9개. 앱 10개 × 도구 100개라면 1,000개의 연결을 각자 만들고 유지해야 합니다. 이것이 M×N 문제입니다. 도구 하나가 바뀌면 모든 앱을 고쳐야 하죠.
MCP가 있으면: M+N으로 단순화
MCP는 공통 규약을 정해서, 이 복잡함을 풀어 줍니다.
도구를 만드는 쪽은 MCP 서버로 한 번만 만들면 된다.
앱을 만드는 쪽은 MCP를 말할 줄 아는 클라이언트만 갖추면 된다.
그러면 어떤 앱이든 어떤 MCP 서버든 서로 꽂아 쓸 수 있다.
Loading diagram…
연결선이 3 + 3 = 6개로 줄었습니다. 규모가 커질수록 효과는 극적입니다. M×N → M+N.
비유: AI의 USB-C
MCP를 흔히 "AI의 USB-C" 라고 부릅니다.
Loading diagram…
예전엔 기기마다 전용 충전기·케이블이 따로 필요했습니다. USB-C라는 공통 단자가 생기자 하나의 케이블로 여러 기기를 연결하게 됐죠.
MCP도 똑같습니다. AI 앱과 도구 사이에 표준 단자를 두어, 한 번 만든 도구(서버)를 여러 AI 앱이 그대로 쓸 수 있게 합니다.
MCP가 주는 이점
Loading diagram…
재사용: 도구를 MCP 서버로 한 번 만들면, 여러 AI 앱에서 그대로 쓴다.
호환: "MCP를 말할 줄 아는" 앱과 서버는 서로 꽂아 쓸 수 있다.
생태계: 이미 공개된 MCP 서버(GitHub, 파일시스템, 구글 드라이브 등)를 가져다 바로 쓸 수 있다.
관심사 분리: 도구 개발자와 AI 앱 개발자가 독립적으로 일할 수 있다.
5장에서 우리가 손으로 만든 도구는 "그 에이전트 전용"이었습니다. MCP 서버로 만들면 누구나, 어떤 앱에서나 쓸 수 있는 도구가 됩니다.
MCP는 누가 만들었나?
MCP는 Anthropic이 2024년에 공개한 오픈 표준입니다. 특정 회사 제품에 묶이지 않은 공개 규약이라, 다양한 AI 앱과 도구 제작자들이 함께 채택해 생태계가 빠르게 커지고 있습니다.
⚠️ 편리함에는 책임이 따른다: 외부 서버를 꽂아 쓰는 만큼, 신뢰할 수 없는 서버를 연결하면 위험할 수 있습니다(악성 도구, 정보 유출 등). MCP·A2A의 보안 위험과 대처는 17장에서 통합해 다룹니다.
정리: MCP를 한 그림으로
Loading diagram…
다음 장에서는 이 그림을 더 자세히 뜯어봅니다. "앱"과 "서버" 사이에 정확히 무엇이 있고, 어떻게 대화하는지(Host·Client·Server)를 살펴보겠습니다.
핵심 요약
MCP는 AI 앱과 외부 도구·데이터를 연결하는 표준 규약이다.
표준이 없으면 앱마다 도구마다 따로 연결해야 하는 M×N 지옥에 빠진다.
MCP는 이를 M+N으로 단순화한다 — "AI의 USB-C".
이점: 재사용·호환·생태계·관심사 분리.
Anthropic이 공개한 오픈 표준이며, 한 번 만든 서버를 여러 앱이 공유한다.
연습 문제
회사에 AI 앱 5개와 연결할 도구 8개가 있다면, 표준이 없을 때와 MCP가 있을 때 필요한 연결 수를 각각 계산해보세요.
MCP를 'USB-C'에 빗댄 이유를 자신의 말로 설명해보세요.
(생각해보기) 내가 만들거나 가져다 쓰고 싶은 MCP 서버(도구)는 무엇인가요?
9장. MCP 아키텍처: Host·Client·Server
이 장에서 배우는 것
MCP의 세 등장인물: Host, Client, Server
셋이 어떻게 연결되고 대화하는지
로컬 연결(stdio)과 원격 연결(HTTP)의 차이
연결이 시작될 때 일어나는 "인사(handshake)"
들어가며
8장에서 "AI 앱이 MCP로 서버와 대화한다"고 했습니다. 그런데 그 "AI 앱" 안을 들여다보면 사실 두 개의 역할로 나뉩니다. 그래서 MCP에는 세 명의 등장인물이 있습니다.
command와 args: Host가 이 명령으로 서버 프로그램을 실행한다는 뜻 (9장의 stdio 방식).
앱을 다시 시작하면, Host가 자동으로 서버를 띄우고 연결합니다.
이제 대화창에서 "2와 3을 더해줘"라고 하면, Claude가 우리 서버의 add 도구를 호출합니다!
코드 한 줄 없이, 설정만으로 우리 도구를 Claude에 붙였습니다. 이것이 MCP 표준의 힘입니다(8장).
방법 ②: Python으로 클라이언트 직접 만들기
원리를 이해하려면 클라이언트를 직접 만들어 보는 게 최고입니다. MCP SDK로 서버에 연결해 도구를 호출해 봅시다.
# client.pyimport asynciofrom mcp import ClientSession, StdioServerParametersfrom mcp.client.stdio import stdio_client# 어떤 서버를 어떻게 실행할지 (9장의 stdio 방식)server_params = StdioServerParameters( command="python", args=["server.py"],)async def main(): # 1) 서버를 실행하고 연결 통로(read, write)를 얻는다 async with stdio_client(server_params) as (read, write): # 2) 세션(대화)을 연다 async with ClientSession(read, write) as session: # 3) initialize 핸드셰이크 (9장) await session.initialize() # 4) 어떤 도구가 있는지 물어본다 tools = await session.list_tools() print("사용 가능한 도구:", [t.name for t in tools.tools]) # 5) 도구를 실제로 호출한다 result = await session.call_tool("add", {"a": 2, "b": 3}) print("add(2, 3) =", result.content[0].text)asyncio.run(main())
실행하면:
사용 가능한 도구: ['add', 'multiply']add(2, 3) = 5
이 코드가 9장의 흐름 그대로 동작합니다.
Loading diagram…
stdio_client: 서버를 실행하고 통신 통로를 연다.
session.initialize(): 9장의 핸드셰이크.
list_tools(): 도구 목록 조회.
call_tool(이름, 입력): 도구 실행.
여기엔 아직 LLM이 없습니다. 사람이 직접 add를 호출했죠. 이제 여기에 LLM의 판단을 더하면 진짜 에이전트가 됩니다.
방법 ③: 우리 에이전트(7장)에 MCP 붙이기
7장에서 만든 에이전트는 도구가 코드에 내장돼 있었습니다. 이번엔 그 도구를 MCP 서버에서 가져오게 바꿉니다. 구조는 이렇습니다.
Loading diagram…
핵심 아이디어: MCP 서버의 도구 목록을 가져와, Claude에게 줄 도구 형식으로 변환한 뒤, 5장의 Tool Calling 루프를 돌립니다.
Loading diagram…
개념 코드로 보면 다음과 같습니다(요점만).
import anthropicfrom mcp import ClientSession, StdioServerParametersfrom mcp.client.stdio import stdio_clientllm = anthropic.Anthropic()server_params = StdioServerParameters(command="python", args=["server.py"])async def run_agent(user_message: str): async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() # (1) MCP 도구 목록 → Claude가 이해하는 도구 형식으로 변환 mcp_tools = (await session.list_tools()).tools claude_tools = [ { "name": t.name, "description": t.description or "", "input_schema": t.inputSchema, # MCP가 준 스키마 그대로 } for t in mcp_tools ] messages = [{"role": "user", "content": user_message}] while True: resp = llm.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=claude_tools, messages=messages, ) if resp.stop_reason != "tool_use": break messages.append({"role": "assistant", "content": resp.content}) # (2) Claude가 부른 도구를 MCP 서버로 실행 results = [] for block in resp.content: if block.type == "tool_use": out = await session.call_tool(block.name, block.input) results.append({ "type": "tool_result", "tool_use_id": block.id, "content": out.content[0].text, }) messages.append({"role": "user", "content": results}) return "".join(b.text for b in resp.content if b.type == "text")
7장 코드와 거의 같습니다! 딱 두 군데만 바뀌었죠.
도구 정의: 손으로 쓰지 않고 MCP 서버의 list_tools() 에서 가져와 변환.
도구 실행: 내장 함수 대신 session.call_tool() 로 MCP 서버에 위임.
MCP 도구를 Claude 형식으로 바꾸는 작업은 SDK가 도와주기도 합니다. Anthropic SDK에는 MCP 도구를 자동 변환해 주는 보조 기능(anthropic.lib.tools.mcp)이 있어, 위 변환을 더 간단히 처리할 수 있습니다. 입문 단계에서는 위처럼 직접 변환해 원리를 익힌 뒤, 익숙해지면 보조 기능을 쓰는 걸 권합니다.
세 방법 비교
Loading diagram…
방법
난이도
언제
① 기존 앱 연결
⭐
내 도구를 Claude 등에서 바로 쓰고 싶을 때
② 직접 클라이언트
⭐⭐
MCP 동작 원리를 이해하고 싶을 때
③ 에이전트에 붙이기
⭐⭐⭐
내 에이전트에 MCP 도구를 통합할 때
공개 MCP 서버 가져다 쓰기 (직접 안 만들어도 된다!)
8장에서 MCP의 큰 장점이 "이미 만들어진 서버를 가져다 쓰는 것" 이라고 했죠. 실제로 파일시스템, GitHub, 데이터베이스 등 공개된 MCP 서버가 많습니다. 우리가 서버를 안 만들어도 바로 연결해 쓸 수 있습니다.
Loading diagram…
연결 방법은 11장에서 만든 내 서버와 똑같습니다. 설정 파일에 "어떤 명령으로 그 서버를 실행할지"만 적어주면 됩니다. 예를 들어 공개 서버는 보통 이런 식으로 등록합니다(서버마다 실행 명령은 문서에 안내됨).
(생각해보기) 7장 에이전트에 MCP를 붙일 때, 7장 코드에서 바뀌는 부분은 정확히 어디인가요?
📦 Part 4 · A2A (Agent-to-Agent)
13장. A2A란 무엇인가
이 장에서 배우는 것
A2A(Agent-to-Agent)가 무엇인지 한 문장으로
에이전트끼리 협업이 왜 필요한지
MCP(도구 연결)와 A2A(에이전트 연결)의 근본적 차이
A2A를 "에이전트들의 공용어"로 이해하기
들어가며
지금까지 우리는 에이전트가 도구를 쓰는 법을 배웠습니다. 5장에서 직접 도구를 붙였고, 3부에서는 MCP로 도구를 표준 연결했죠.
그런데 현실의 복잡한 일은 혼자 다 하기 어렵습니다. 사람도 큰 프로젝트는 여러 전문가가 나눠 맡습니다. 에이전트도 마찬가지예요.
한 문장으로: A2A(Agent-to-Agent)는 서로 다른 에이전트끼리 대화하고 협업하게 해주는 표준 규약입니다.
왜 에이전트끼리 협업해야 하나?
복잡한 작업을 하나의 거대한 에이전트가 다 하려 하면 문제가 생깁니다.
Loading diagram…
그래서 전문 에이전트로 나누는 것이 자연스럽습니다.
Loading diagram…
예: "제주 여행 예약해줘"라는 요청을 받으면,
총괄 에이전트가 일을 쪼개서
항공 에이전트, 호텔 에이전트, 맛집 에이전트에게 각각 맡기고
결과를 모아 사용자에게 전달합니다.
이렇게 각자 잘하는 일에 집중하는 전문 에이전트들이 협력하면, 더 똑똑하고 관리하기 쉬운 시스템이 됩니다. (이런 구성을 멀티 에이전트라 하며, 16장에서 자세히 다룹니다.)
그런데 협업에도 표준이 필요하다
문제는, 이 에이전트들이 서로 다른 회사·다른 프레임워크로 만들어졌을 수 있다는 점입니다. 항공 에이전트는 A사가, 호텔 에이전트는 B사가 만들었다면, 서로 말이 안 통합니다.
이건 8장에서 본 도구 연결의 M×N 문제와 똑같은 상황입니다. 에이전트마다 대화 방식이 제각각이면 협업이 지옥이 되죠.
Loading diagram…
A2A는 에이전트들의 공용어(공통 언어) 입니다. 누가 만들었든, A2A를 말할 줄 알면 서로 협업할 수 있습니다.
MCP vs A2A: 무엇이 다른가?
여기서 가장 중요한 구분이 나옵니다. MCP와 A2A는 둘 다 표준이지만, 연결하는 대상이 다릅니다.
Loading diagram…
구분
MCP
A2A
연결 대상
에이전트 ↔ 도구·데이터
에이전트 ↔ 다른 에이전트
방향
수직(아래로: 도구를 부린다)
수평(옆으로: 동료와 협력)
관계
주인-도구
동료-동료
비유
사람이 연장을 쓰는 것
사람이 다른 사람과 협업하는 것
핵심: MCP는 도구를 연결하고, A2A는 에이전트를 연결합니다. 둘은 경쟁이 아니라 상호 보완입니다(자세한 비교는 15장).
함께 쓰는 그림
실제로는 둘을 같이 씁니다. 각 전문 에이전트는 A2A로 협업하면서, 자기 일은 MCP로 도구를 써서 처리합니다.
Loading diagram…
A2A는 누가 만들었나?
A2A는 2025년에 공개된 오픈 프로토콜로, 처음 Google이 주도해 제안했고 이후 여러 기업·커뮤니티가 참여하는 공개 표준(리눅스 재단 산하)으로 발전하고 있습니다. MCP와 마찬가지로 특정 제품에 묶이지 않은 공개 규약이라, 서로 다른 진영의 에이전트들이 함께 협업하는 길을 엽니다.
⚠️ 협업에는 신뢰 문제가 따른다: 다른 에이전트에게 일을 맡긴다는 것은, 그 상대를 어디까지 믿을지 결정해야 한다는 뜻입니다(권한·인증·악의적 에이전트 등). A2A의 인증은 14장에서, 보안 위험과 대처는 17장에서 다룹니다.
A2A의 핵심 약속들 (맛보기)
다음 장에서 자세히 보겠지만, A2A는 크게 이런 것들을 약속합니다.
Loading diagram…
Agent Card: 에이전트가 "나는 이런 능력이 있어"라고 알리는 자기소개서.
Task: "이 일을 해달라"는 하나의 작업 단위(상태를 가짐).
Message / Artifact: 주고받는 대화와, 그 결과로 만들어지는 산출물.
핵심 요약
A2A는 서로 다른 에이전트끼리 협업하게 하는 표준 규약이다.
복잡한 일은 전문 에이전트로 나눠 협력하는 것이 효율적이다.
에이전트마다 대화 방식이 다르면 협업이 어려우므로, 공용어(A2A) 가 필요하다.
MCP는 도구 연결(수직), A2A는 에이전트 연결(수평) — 둘은 상호 보완.
A2A는 2025년 공개된 오픈 프로토콜이다.
연습 문제
하나의 만능 에이전트 대신 전문 에이전트로 나누면 좋은 점 2가지를 적어보세요.
"회사"에 빗대어, A2A와 MCP가 각각 무엇에 해당하는지 설명해보세요.
(생각해보기) 내가 떠올린 시스템에서 'A2A로 연결할 상대'와 'MCP로 연결할 도구'를 하나씩 들어보세요.
14장. A2A 프로토콜 들여다보기
이 장에서 배우는 것
Agent Card: 에이전트의 자기소개서
Client Agent와 Remote Agent의 역할
Task(작업)의 생애주기(상태 변화)
Message·Part·Artifact의 구조
에이전트끼리 일을 주고받는 전체 흐름
들어가며
13장에서 A2A가 "에이전트들의 공용어"라고 했습니다. 이제 그 공용어의 문법을 들여다봅시다. 어렵지 않습니다 — 사람이 일을 의뢰하고 처리하는 과정과 거의 똑같거든요.
두 역할: Client Agent와 Remote Agent
A2A에서 협업하는 두 에이전트는 상황에 따라 역할이 나뉩니다.
Loading diagram…
역할
하는 일
비유
Client Agent
작업을 요청하는 쪽
일을 의뢰하는 고객
Remote Agent
작업을 수행하는 쪽
일을 받는 전문가
13장의 여행 예시에서 총괄 에이전트는 Client, 항공 에이전트는 Remote가 됩니다. (역할은 고정이 아니라, 누가 누구에게 요청하느냐에 따라 정해집니다.)
1. Agent Card — 에이전트의 자기소개서
협업하려면 먼저 상대가 누구이고 뭘 할 수 있는지 알아야 합니다. 그 정보를 담은 것이 Agent Card입니다.
Loading diagram…
Agent Card는 보통 정해진 위치(예: /.well-known/agent-card.json)에 공개되어, 다른 에이전트가 찾아 읽을 수 있습니다. JSON으로 대략 이런 모습입니다.