페이지 목차
이 페이지에서 검색
멀티 에이전트
이 페이지에 묻기
멀티 에이전트 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
MULTI · AGENT
멀티 에이전트
생성 수정
도구 실행·검색·기억·그래프 흐름·전문 에이전트의 협업을 구성하고 권한·평가·사람의 확인으로 통제합니다.
26개 문서 · 1일차 · 3 / 26
2차시 · 개발 환경 구성과 LLM API 호출
지도 위치 : 1일차 · README 4장 ① 호출 층 — 모델을 처음 부른다 | 이전: 1차시 에이전트 요구사항과 아키텍처 설계 | 다음: 3차시 프롬프트 설계 키워드 : 가상환경, API 키 관리, SDK, 토큰과 비용
이 차시를 다 읽으면
- 가상환경과 SDK를 유와 종차로 정의하고, 왜 프로젝트마다 가상환경을 따로 두는지 말할 수 있다.
- API 키를 환경변수 ·
.env·.env.example·.gitignore로 관리하는 이유를 설명할 수 있다. - 응답의
content블록,stop_reason,usage를 읽고 무엇이 일어났는지 말할 수 있다. - 토큰 수와 가격표로 한 건의 비용 을 계산하고, 용도에 맞는 모델(Opus · Sonnet · Haiku)을 고를 수 있다.
- ⭐ "프롬프트는 외부로 나간다"(축 ④)와 "API는 지난번을 기억하지 않는다"(축 ⑦)를 설명할 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | ⭐ 어디서 처리되는가 | 내 컴퓨터 (코드 · 키 · 사내 데이터) | Anthropic 서버 (모델) | 무엇이 인터넷을 건너 밖으로 나가는가 | ④ 로컬 ↔ 원격 |
| 2 | ⭐ 지난번을 기억하는가 | API 호출 (매번 처음 보는 요청) | 대화 기억 (16차시에 직접 만듦) | 대화를 이으려면 누가 이력을 보내는가 | ⑦ 무상태 ↔ 상태 |
| 3 | 비밀은 어디에 두는가 | 디스크의 코드 파일 (git에 영구히 남음) | 실행 중인 환경변수 | API 키가 새어 나가는 길 | ① 디스크 ↔ 메모리 |
| 4 | 비용을 어디서 보는가 | 호출 한 번의 토큰 | 서비스 전체의 토큰 | 몇 원짜리 호출이 한 달에 얼마가 되는가 | 한 건의 비용 ↔ 전체 비용 |
이 차시의 질문은 "모델을 한 번 부르면, 무엇이 어디로 가고, 무엇이 돌아오고, 얼마가 드는가?" 입니다. 앞에서 그린 여섯 층 설계도(1차시)의 맨 아래 칸 ① 호출을 실제로 놓는 차시입니다.
1. 개발 환경 — 가상환경과 SDK
1.1 가상환경
가상환경 (Virtual Environment) — [파이썬 실행 환경]인데, [ 프로젝트 폴더마다 패키지를 따로 설치해 서로 섞이지 않게 한다 ]는 점에서 컴퓨터 전체가 함께 쓰는 시스템 파이썬과 구별된다.
python -m venv .venv # 가상환경 만들기 (폴더 .venv 생성)
source .venv/bin/activate # 켜기 (Windows: .venv\Scripts\activate)
pip install anthropic python-dotenv # 이 프로젝트에만 설치
pip freeze > requirements.txt # 설치 목록 기록 → 다른 사람이 같은 환경을 재현
🔍 읽는 포인트 : .venv는 이 프로젝트 전용 상자 입니다. 켜진 상태에서 설치한 것은 이 상자 안에만 들어갑니다. requirements.txt가 "이 상자에 무엇이 들어 있나"의 목록 입니다.
반례 — 가상머신(VM)은 운영체제 전체를, 도커 컨테이너는 프로그램과 실행 환경 전체를 나눕니다. 가상환경은 파이썬 패키지만 나눕니다. 💡 uv 같은 더 빠른 도구도 원리는 같습니다.
1.2 SDK
SDK (Software Development Kit) — [라이브러리]인데, [ 특정 서비스의 API 호출을 언어에 맞는 함수로 감싸 준다 ]는 점에서 HTTP 요청을 직접 조립하는 방식과 구별된다.
API — [프로그램 사이의 약속]인데, [ 정해진 주소와 형식으로 요청하면 정해진 형식으로 답해 준다 ]는 점에서 사람이 쓰는 화면(UI)과 구별된다.
| 직접 HTTP 호출 | SDK (anthropic) | |
|---|---|---|
| 요청 | 주소 · 헤더 · JSON 본문을 직접 조립 | client.messages.create(...) 한 줄 |
| 실패 대응 · 응답 | 재시도 · 파싱을 직접 구현 | 재시도 · 타임아웃 내장(5절), response.content 같은 속성으로 접근 |
2. API 키 관리
2.1 정의와 원칙
API 키 — [인증 정보]인데, [ 요청이 누구의 계정에서 온 것인지 증명하고 그 계정에 비용을 청구하게 하는 비밀 문자열 ]이라는 점에서 공개해도 되는 설정값과 구별된다.
⚠️ 키가 새면 남이 내 계정으로 모델을 부르고, 청구서는 나에게 옵니다.
| 어디에 두나 | 판정 | 이유 |
|---|---|---|
코드에 직접 api_key="sk-..." | ❌ | git에 커밋되면 이력에 영구히 남음 (축 ①의 디스크 쪽) |
| 채팅 · 코딩 에이전트 대화창에 붙여 넣기 | ❌ | 대화 기록이 어디에 저장되는지 내가 통제하지 못함 |
환경변수 ANTHROPIC_API_KEY | ⭕ | 실행 중인 프로세스에만 있음. SDK가 자동으로 읽음 |
.env 파일 + .gitignore에 등록 | ⭕ | 로컬 개발용. git에는 올라가지 않음 |
.env.example (값은 비우고 이름만) | ⭕ | "이 프로젝트는 이런 변수가 필요하다"를 팀에 알리는 견본 |
# .env (git에 올리지 않음) → ANTHROPIC_API_KEY=여기에_실제_키
# .env.example (git에 올림) → ANTHROPIC_API_KEY=
# .gitignore → .env 와 .venv/ 두 줄
🔍 읽는 포인트 : .env와 .env.example은 이름이 같고 값만 다릅니다. 실제 값이 들어간 쪽은 .gitignore에 적혀 git이 무시합니다.
2.2 코드에서 키를 읽는 법
from dotenv import load_dotenv
import anthropic
load_dotenv() # .env 내용을 환경변수로 올림
client = anthropic.Anthropic() # ANTHROPIC_API_KEY 를 자동으로 읽음
🔍 읽는 포인트 : 코드 어디에도 키 값이 없습니다. 이것이 정답의 모양입니다. AI가 만든 코드에 api_key="sk- 같은 문자열이 보이면 바로 돌려보내세요. 💡 키가 샌 것 같으면 git 이력을 지우려 애쓰기보다 콘솔에서 그 키를 폐기하고 새로 발급 하는 쪽이 확실합니다.
3. 첫 호출과 응답 구조
3.1 기본 호출
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
system="너는 사내 도우미 오피스봇이다. 한국어로 짧게 답한다.",
messages=[{"role": "user", "content": "연차는 언제부터 생기나요?"}],
)
for block in response.content:
if block.type == "text":
print(block.text)
print(response.stop_reason, response.usage.input_tokens, response.usage.output_tokens)
🔍 읽는 포인트 : 네 줄만 보면 됩니다. model=(어느 모델), system=(역할 지시, 3차시), messages=(대화 내용), max_tokens=(답의 길이 상한 — 넘으면 잘림).
3.2 응답 구조 — 세 가지만 읽기
| 필드 | 무엇인가 | 왜 보는가 |
|---|---|---|
response.content | 블록의 리스트 . 각 블록에 .type이 있음: text(글), tool_use(도구 호출 요청, 5차시), thinking(생각, 3차시) | 답이 여러 조각일 수 있음. .type을 확인하고 꺼냄 |
response.stop_reason | 모델이 왜 멈췄는가 | 아래 표 |
response.usage | input_tokens, output_tokens | 비용 의 원천 (4절) |
stop_reason | 뜻 | 코드가 할 일 |
|---|---|---|
end_turn | 정상적으로 답을 마침 | 답을 씀 |
max_tokens | max_tokens에 걸려 잘림 | ⚠️ 답이 중간에 끊겼음. 상한을 늘리거나 짧게 요청 |
tool_use | 도구를 불러 달라고 요청 | 도구 실행 (5차시) |
refusal | 안전상 이유로 거절 | ⚠️ content를 읽기 전에 확인. 사용자에게 안내 |
pause_turn | 긴 작업 중 잠시 멈춤 | 이어서 다시 호출 (세부는 공식 문서 확인) |
⚠️ response.content[0].text처럼 첫 블록을 무조건 글이라고 가정하면 언젠가 깨집니다. 생각 블록이나 도구 호출 블록이 먼저 올 수 있습니다.
3.3 ⭐ 축 ④ — 프롬프트는 밖으로 나간다
내 컴퓨터 (로컬) 인터넷 너머 (원격)
┌──────────────────────┐ HTTPS 요청 ┌─────────────────────┐
│ 코드 · .env(키) │ ─────────────▶ │ Anthropic API 서버 │
│ system + messages │ system, │ 모델이 읽고 답을 만듦 │
│ (사번? 연락처? 연봉?) │ messages 전부 │ │
└──────────────────────┘ ◀───────────── └─────────────────────┘
응답
system과 messages에 넣은 글은 전부 인터넷을 건너 외부 서버로 갑니다. 지금은 "연차는 언제 생기나요?"뿐이지만, 6차시에 도구 결과(직원 데이터)가 실리기 시작하면 이야기가 달라집니다. 데이터 보관 · 학습 사용 정책은 계약과 설정에 따라 다르므로 회사 보안 담당과 공식 문서로 확인 하세요.
앞으로 다시 만납니다 — 8차시에서 외부로 나가기 전에 개인정보를 가리는 마스킹 을, 17차시에서 장기 기억에 무엇을 저장하지 말지를 이 그림 위에서 정합니다.
3.4 ⭐ 축 ⑦ — API는 지난번을 기억하지 않는다
client.messages.create(..., messages=[{"role": "user", "content": "내 이름은 민수야"}])
client.messages.create(..., messages=[{"role": "user", "content": "내 이름이 뭐라고?"}])
# 두 번째 호출의 모델은 "민수"를 모른다 — 첫 번째 호출을 본 적이 없다
🔍 읽는 포인트 : 두 호출은 완전히 별개 입니다. 대화를 이어 가려면 앞의 질문과 답을 messages에 매번 다시 넣어 보내야 합니다.
무상태 (Stateless) — [시스템의 성질]인데, [ 각 요청을 이전 요청과 무관하게 처리한다 ]는 점에서 이전 요청을 기억하는 상태 유지 시스템과 구별된다.
⚠️ 그래서 대화가 길어질수록 매번 보내는 토큰이 늘고 비용도 늡니다. "챗봇이 기억한다"는 것은 사실 앱이 이력을 모아 다시 보내 주는 것 입니다.
앞으로 다시 만납니다 — 9차시 에이전트 루프는 이 성질 때문에 매 반복마다 대화 전체를 다시 보내고, 16차시에서 세션별로 이력을 저장 · 정리하는 법을 직접 만듭니다.
4. 토큰과 비용
4.1 토큰
토큰 (Token) — [글의 단위]인데, [ 모델의 토크나이저가 글을 자른 조각으로, 단어보다 작을 수 있다 ]는 점에서 단어 · 글자 수와 구별된다. ⚠️ 로그인에 쓰는 인증 토큰 과는 같은 말, 다른 범주입니다.
| 알아 둘 점 | 내용 |
|---|---|
| 모델마다 자르는 법이 다름 | 다른 회사 도구(예: tiktoken)로 센 숫자는 맞지 않음 |
| 한국어 | 같은 뜻이라도 영어보다 토큰이 더 많이 드는 경우가 많음 → 직접 세어 비교 |
| 입력과 출력 | 입력(내가 보낸 것)과 출력(모델이 만든 것)은 단가가 다름 |
4.2 보내기 전에 세기
count = client.messages.count_tokens(
model="claude-opus-5-5",
system="너는 사내 도우미 오피스봇이다.",
messages=[{"role": "user", "content": "연차는 언제부터 생기나요?"}],
)
print(count.input_tokens)
🔍 읽는 포인트 : create 대신 count_tokens — 모델을 부르지 않고 입력 토큰 수만 알려 줍니다. 같은 문장을 한국어 · 영어로 넣어 숫자를 비교해 보세요.
4.3 비용 계산
한 건 비용 = 입력 토큰 × 입력 단가 + 출력 토큰 × 출력 단가 (단가는 100만 토큰당)
| 모델 (2026-10 기준) | 입력 / 100만 토큰 | 출력 / 100만 토큰 | 주 용도 |
|---|---|---|---|
claude-opus-5-5 | $4 | $20 | 기본 · 고성능. 어려운 판단, 에이전트의 중심 |
claude-sonnet-5-5 | $2 | $10 | 속도와 성능의 균형 |
claude-haiku-5-5 | $0.10 | $0.50 | 가볍고 대량 — 분류 · 라우팅 · 워커 |
⚠️ 가격과 모델 이름은 바뀝니다. 실제 계산 전에 반드시 공식 가격 페이지를 확인하세요. 모델 ID는 날짜가 붙지 않은 위 이름을 씁니다.
계산 예시 (실측 아님) — 입력 3,000 토큰, 출력 300 토큰인 질문 한 건:
| 모델 | 입력 | 출력 | 한 건 합계 | 하루 1만 건이면 |
|---|---|---|---|---|
| Opus | 3,000 × $4 / 100만 = $0.012 | 300 × $20 / 100만 = $0.006 | $0.018 | $180 |
| Haiku | 3,000 × $0.10 / 100만 = $0.0003 | 300 × $0.50 / 100만 = $0.00015 | $0.00045 | $4.5 |
⭐ 한 건은 몇 원이지만 곱하기 건수, 곱하기 반복 횟수 가 됩니다. 9차시 에이전트 루프는 한 질문에 여러 번 호출하고, 21차시 멀티에이전트는 그것을 다시 여러 배로 만듭니다. 이것이 축 4 "한 건의 비용 ↔ 전체 비용"입니다.
앞으로 다시 만납니다 — 4차시에서 "분류는 Haiku로 충분한가"를 이 표로 판단하고, 10차시에서 누적 토큰에 상한을 거는 가드레일을 만듭니다.
4.4 프롬프트 캐싱 — 개요
프롬프트 캐싱 — [비용 절감 기능]인데, [ 요청의 앞부분(시스템 프롬프트 · 도구 목록)이 이전 요청과 같으면 그 부분을 재사용해 싸게 처리한다 ]는 점에서 답 자체를 저장해 두는 응답 캐시와 구별된다.
response = client.messages.create(
model="claude-opus-5-5", max_tokens=16000,
system=LONG_SYSTEM_PROMPT,
messages=messages,
cache_control={"type": "ephemeral"}, # 앞부분 재사용 표시
)
print(response.usage.cache_read_input_tokens) # 캐시에서 읽은 토큰 수
🔍 읽는 포인트 : cache_control 한 줄과, 효과를 확인하는 cache_read_input_tokens 한 줄. 두 번째 호출부터 이 숫자가 0보다 커지면 캐시가 동작한 것입니다.
⚠️ 앞부분이 한 글자라도 바뀌면 캐시가 깨집니다. 시스템 프롬프트에 "현재 시각: 14:03:27"을 넣으면 매번 캐시가 무효가 됩니다. 너무 짧은 프롬프트는 캐시되지 않으며, 최소 길이와 할인율은 공식 문서를 확인하세요.
5. 에러 처리와 재시도
에러 (anthropic. 아래) | 상황 | 재시도 |
|---|---|---|
RateLimitError (429) | 너무 자주 호출 | ⭕ 잠시 기다렸다가 |
APIStatusError (5xx) | 서버 쪽 일시 문제 | ⭕ |
APIConnectionError | 네트워크 끊김 | ⭕ |
BadRequestError (400) | 요청이 잘못됨 (잘못된 파라미터 등) | ❌ 다시 보내도 같음. 요청을 고쳐야 함 |
client = anthropic.Anthropic(max_retries=4, timeout=60.0) # 기본은 재시도 2회, 타임아웃 10분
try:
response = client.messages.create(...)
except anthropic.BadRequestError:
raise # 요청 자체를 고쳐야 함 — 재시도 무의미
except anthropic.RateLimitError:
return "지금 요청이 많습니다. 잠시 후 다시 시도해 주세요."
except anthropic.APIConnectionError:
return "AI 서비스에 연결하지 못했습니다."
🔍 읽는 포인트 : SDK가 재시도를 이미 기본으로 합니다(2회). 그 위에서 코드는 "재시도해도 소용없는 에러"(400)를 따로 잡아 사용자에게 무엇을 보여 줄지 정합니다. 💡 대화형 서비스에서 기본 타임아웃 10분은 너무 깁니다. timeout=으로 줄이세요.
6. 실습 — 맡기고 검수하기
지시
오피스봇 프로젝트의 개발 환경과 첫 API 호출을 만들어줘.
참고 공식 문서: Anthropic Python SDK 문서(설치, Messages API, 토큰 세기, 에러 처리)
목표:
1. 가상환경(.venv) 생성, anthropic·python-dotenv 설치, requirements.txt 생성
2. .env.example, .gitignore(.env와 .venv 포함) 생성. .env는 내가 직접 만든다 — 키 값을 묻거나 출력하지 말 것
3. hello.py: claude-opus-5-5에 "연차는 언제부터 생기나요?"를 보내고
text 블록, stop_reason, input_tokens, output_tokens를 출력
4. count_tokens.py: 같은 뜻의 한국어/영어 문장 3쌍의 입력 토큰 수를 표로 출력.
usage와 가격표(상수로 분리, "공식 가격 페이지 확인" 주석)로 한 건 비용도 계산
5. 에러 처리: BadRequestError는 재시도하지 않고, RateLimitError·APIConnectionError는 사용자 메시지 반환
제약: 코드에 키 문자열 금지. 모델 이름은 상수 하나로.
추측하지 말고 실행 명령과 실행 출력 원문을 붙여.
보고 형식: [파일 | 역할 | 실행 명령 | 출력 요약] 표 + 출력 원문
받을 증거
git status출력 —.env가 추적 목록에 없어야 함hello.py실행 출력 원문 (stop_reason, 토큰 수 포함)- 한국어/영어 토큰 비교표와 비용 계산 결과
내가 직접 확인할 것
| 볼 것 | 이걸로 아는 것 |
|---|---|
프로젝트 폴더에서 sk- 같은 키 문자열을 검색해 0건 인가 | 2.1 — 키가 코드에 박히지 않았는가 |
.env를 잠시 다른 이름으로 바꾸고 실행하면 인증 에러 가 나는가 | 2.2 — 키를 정말 환경변수에서만 읽는가 |
출력의 stop_reason이 end_turn인가, max_tokens를 50으로 줄이면 max_tokens로 바뀌는가 | 3.2 — 잘림을 감지할 수 있는가 |
| 한국어/영어 토큰 수 차이 | 4.1 — 우리 서비스의 비용이 영어 기준 예상보다 클 수 있는가 |
| 비용 계산이 가격표 × 토큰과 손계산으로 맞는가 | 4.3 — 비용 숫자를 믿을 수 있는가 |
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | 가상환경을 유와 종차로 정의하면? 도커와 무엇이 다른가? | 1.1 |
| 2 | SDK가 직접 HTTP 호출보다 대신해 주는 것 세 가지는? | 1.2 |
| 3 | .env와 .env.example의 차이와 각각 git에 올리는가? | 2.1 |
| 4 | content[0].text로 바로 꺼내면 위험한 이유와, stop_reason이 max_tokens · refusal일 때 할 일은? | 3.2 |
| 5 | 프롬프트에 넣은 글은 어디로 가는가? | 3.3 |
| 6 | 두 번째 호출이 첫 번째 대화를 모르는 이유와 대처는? | 3.4 |
| 7 | 한 건 비용 계산식과, 하루 건수를 곱해 봐야 하는 이유는? | 4.3 |
| 8 | 프롬프트 캐싱이 깨지는 대표적인 실수와, 재시도해도 소용없는 에러는? | 4.4, 5 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
코드에 api_key="sk-..." | ❌ 환경변수 ANTHROPIC_API_KEY. SDK가 자동으로 읽음 (2.2) |
.env를 git에 커밋 | ❌ .gitignore에 등록. 이미 올렸으면 키를 폐기 · 재발급 (2.1) |
| 다른 회사 토크나이저로 토큰 계산 | ❌ count_tokens로 (4.2) |
| "모델이 앞 대화를 기억한다" | ❌ API는 무상태. 앱이 이력을 다시 보냄 (3.4) |
| 400 에러를 계속 재시도 | ❌ 요청을 고쳐야 함 (5) |
| 시스템 프롬프트에 현재 시각 | ⚠️ 캐시가 매번 깨짐 (4.4) |
부록 C. NotebookLM 복습 프롬프트
이 2차시 노트만 근거로 6문제를 내줘. 비전공자 기준으로.
"API 키를 두는 곳", "stop_reason의 뜻", "프롬프트는 외부로 나간다(로컬↔원격)", "API는 무상태", "한 건 비용 계산"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor