본문으로 건너뛰기
페이지 목차
이 페이지에서 검색

멀티 에이전트

이 페이지에 묻기

멀티 에이전트 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.

생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.

MULTI · AGENT

멀티 에이전트

생성 수정

도구 실행·검색·기억·그래프 흐름·전문 에이전트의 협업을 구성하고 권한·평가·사람의 확인으로 통제합니다.

26개 문서 · 1일차 · 3 / 26

2차시 · 개발 환경 구성과 LLM API 호출

지도 위치 : 1일차 · README 4장 ① 호출 층 — 모델을 처음 부른다 | 이전: 1차시 에이전트 요구사항과 아키텍처 설계 | 다음: 3차시 프롬프트 설계 키워드 : 가상환경, API 키 관리, SDK, 토큰과 비용

이 차시를 다 읽으면

  1. 가상환경과 SDK를 유와 종차로 정의하고, 왜 프로젝트마다 가상환경을 따로 두는지 말할 수 있다.
  2. API 키를 환경변수 · .env · .env.example · .gitignore 로 관리하는 이유를 설명할 수 있다.
  3. 응답의 content 블록, stop_reason, usage 를 읽고 무엇이 일어났는지 말할 수 있다.
  4. 토큰 수와 가격표로 한 건의 비용 을 계산하고, 용도에 맞는 모델(Opus · Sonnet · Haiku)을 고를 수 있다.
  5. ⭐ "프롬프트는 외부로 나간다"(축 ④)와 "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.usageinput_tokens, output_tokens비용 의 원천 (4절)
stop_reason뜻코드가 할 일
end_turn정상적으로 답을 마침답을 씀
max_tokensmax_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만 건이면
Opus3,000 × $4 / 100만 = $0.012300 × $20 / 100만 = $0.006$0.018$180
Haiku3,000 × $0.10 / 100만 = $0.0003300 × $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
2SDK가 직접 HTTP 호출보다 대신해 주는 것 세 가지는?1.2
3.env와 .env.example의 차이와 각각 git에 올리는가?2.1
4content[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는 무상태", "한 건 비용 계산"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.