페이지 목차
이 페이지에서 검색
멀티 에이전트
이 페이지에 묻기
멀티 에이전트 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
MULTI · AGENT
멀티 에이전트
생성 수정
도구 실행·검색·기억·그래프 흐름·전문 에이전트의 협업을 구성하고 권한·평가·사람의 확인으로 통제합니다.
26개 문서 · 2일차 · 12 / 26
11차시 · 프레임워크 기반 에이전트 구성
지도 위치 : 2일차 · README 4장의 ② 행동 층 — 직접 만든 루프를 표준 부품 으로 옮기고, 행동을 기록 하는 자리 | 이전: 10차시 루프 제어와 가드레일 | 다음: 12차시 문서 로딩과 청킹 키워드 : LangChain, 도구 바인딩, 트레이싱, 회귀 테스트
이 차시를 다 읽으면
- 프레임워크를 유와 종차로 정의하고, 직접 구현과 비교해 얻는 것과 잃는 것 을 말할 수 있다.
- 축 ②로 "프레임워크의 공통 인터페이스 ↔ 모델 제공자"를 가르고, 제공자를 바꿀 때 바뀌지 않는 것과 바뀌는 것을 구별할 수 있다.
ChatAnthropic·@tool·bind_tools코드에서 9차시 수동 루프의 어느 부분이 어디로 갔는지 짚을 수 있다.- 트레이싱을 정의하고, 추적 기록이 외부로 나갈 때 무엇을 가려야 하는지 말할 수 있다.
- 회귀 테스트로 "옮긴 뒤에도 전과 같이 동작한다"는 것을 증거로 보일 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | ⭐ 추상 ↔ 구상 | 프레임워크의 공통 인터페이스 (invoke, bind_tools) | 모델 제공자별 구현 (ChatAnthropic 등) | 무엇을 바꿔도 앱 코드가 그대로인가 | ② 추상 ↔ 구상 |
| 2 | ⭐ 증거 ↔ 주장 | 추적 기록 · 평가셋 결과가 붙은 보고 | "옮겼고 잘 됩니다" | 이전이 성공했는가 | 파생 축 (부모 ⑧) |
| 3 | 직접 구현 ↔ 프레임워크 | 루프를 내가 씀 — 다 보임 | 뼈대를 프레임워크가 쥠 — 코드 짧음 | 통제 ↔ 편의 | 이 차시의 축 |
| 4 | 로컬 ↔ 원격 | 추적 기록을 내 서버에 | 외부 추적 서비스로 전송 | 무엇이 밖으로 나가는가 | ④ 로컬 ↔ 원격 |
이 차시의 질문 — 9 · 10차시에서 루프와 가드레일을 손으로 짰습니다. 실무에서는 이런 뼈대를 매번 새로 짜지 않고 프레임워크 를 씁니다. 그렇다면 직접 짠 것은 헛수고였을까요? 아닙니다. 이 차시는 같은 오피스봇을 LangChain으로 옮기면서, 무엇을 얻고 무엇이 가려지는지 , 그리고 옮긴 뒤에도 같다는 것을 어떻게 증명하는지 를 다룹니다.
1. 프레임워크 — 무엇에 속하는가
1.1 정의와 경계
프레임워크 (Framework) — [재사용 소프트웨어]인데, [ 전체 흐름의 뼈대를 프레임워크가 쥐고, 개발자는 정해진 자리에 부품(모델 · 도구 · 프롬프트)을 끼운다 ]는 점에서 개발자가 흐름을 쥐고 필요할 때 불러 쓰는 라이브러리와 구별된다.
LangChain — [LLM 응용 프레임워크]인데, [ 모델 · 도구 · 프롬프트 · 메시지를 제공자에 상관없는 공통 인터페이스로 감싸 조립하게 한다 ]는 점에서 한 회사의 API만 감싸는 제공자 SDK(anthropic)와 구별된다.
라이브러리 (anthropic SDK) | 프레임워크 (LangChain) | |
|---|---|---|
| 흐름을 쥔 쪽 | 내 코드 (9차시의 for 루프) | 프레임워크 (미리 만든 에이전트) |
| 내가 쓰는 것 | 루프 · 메시지 조립 · 분기 | 도구 함수 · 프롬프트 · 설정값 |
| 제공자 | 한 회사 | 여러 회사를 같은 모양으로 |
1.2 직접 구현 ↔ 프레임워크 — 무엇을 얻고 잃나
| 직접 구현 (9 · 10차시) | 프레임워크 | |
|---|---|---|
| ⭕ 얻는 것 | 루프 안이 한 줄씩 보임, 가드레일을 원하는 곳에 | 코드가 짧음, 제공자 교체 쉬움, 트레이싱 · 그래프 · 메모리 등 주변 부품 |
| ❌ 잃는 것 | 코드가 길고 반복됨, 부품을 다 직접 | 루프 안이 감춰짐, 프레임워크 규칙을 배워야 함 |
| ⚠️ 위험 | 내가 만든 버그 | 버전이 바뀌면 이름 · 위치가 바뀜 (AI가 옛 이름을 자신 있게 씀) |
1.3 반례 — 이건 프레임워크가 아닙니다
| 대상 | 왜 아닌가 |
|---|---|
anthropic SDK | 흐름을 내 코드가 쥠. 라이브러리 |
| LangSmith | 프레임워크가 아니라 추적 · 평가 서비스 (4절) |
| 9차시에서 Tool Runner로 돌린 루프 | SDK가 제공하는 도우미 . 한 제공자 안의 편의 기능 |
2. 축 ② — 공통 인터페이스 ↔ 모델 제공자
앞에서 배운 도구 선언(5차시)은 축 ②의 왼쪽 — "무엇을 할 수 있다"는 약속 — 이었고, 실제 함수가 오른쪽이었습니다. 프레임워크는 같은 축을 모델 쪽에 적용합니다.
앱 코드: llm.bind_tools(tools).invoke(messages) ← 추상 (약속: 모델이면 이렇게 부른다)
│
┌────────────────┼────────────────┐
▼ ▼ ▼
ChatAnthropic 다른 제공자 클래스 로컬 모델 클래스 ← 구상 (각 회사 API로 번역)
| 제공자를 바꿀 때 | 그대로인 것 | 바뀌는 것 |
|---|---|---|
| 코드 | 도구 함수, invoke 호출, 루프 구조 | 모델 클래스 한 줄, 패키지 |
| 동작 | — | ⚠️ 도구 선택 성향, 답의 길이 · 말투, 지원 옵션 |
| 제약 | — | ⚠️ 예: 최신 Claude 모델은 temperature를 직접 받지 않고 effort로 조절 (3차시) |
⭐ "코드가 그대로"와 "동작이 그대로"는 다릅니다. 인터페이스가 같아도 모델이 다르면 도구 선택이 달라집니다. 그래서 제공자를 바꾸면 반드시 5절의 회귀 테스트를 다시 돌립니다.
3. 오피스봇을 LangChain으로 옮기기
3.1 모델과 도구 바인딩
# 버전에 따라 패키지 · 이름이 다를 수 있음 — 공식 문서 확인
from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
@tool
def get_leave_balance(year: int) -> str:
"""직원의 남은 연차 일수를 조회한다. 휴가 잔여를 물을 때 사용."""
return leave_db.balance(employee_id=current_session().employee_id, year=year)
llm = ChatAnthropic(model="claude-opus-5-5")
llm_with_tools = llm.bind_tools([get_leave_balance, get_leave_policy])
ai_msg = llm_with_tools.invoke("다음 주 월~수 휴가 쓸 수 있어?")
print(ai_msg.tool_calls) # 예: [{'name': 'get_leave_balance', 'args': {'year': 2026}, 'id': '...'}]
🔍 읽는 포인트
@tool— 함수의 docstring이 도구 설명 , 인자 타입(year: int)이 인자 형식 이 됩니다. 9차시에서 손으로 쓰던name·description·input_schemadict를 대신 만들어 줍니다. ⭐ 그래서 docstring이 모델의 도구 선택 근거가 됩니다(7차시) — 대충 쓰면 안 됩니다.current_session().employee_id— 프레임워크로 옮겨도 권한 경계는 도구 함수 안에 그대로 둡니다(8차시).bind_tools— 도구 바인딩 . 모델에 도구 목록을 묶어 두는 것뿐, 실행하지도 반복하지도 않습니다.ai_msg.tool_calls는 9차시의tool_use블록과 같은 "요청"입니다.
도구 바인딩 (Tool Binding) — [모델 설정]인데, [ 이후의 모든 호출에 같은 도구 목록이 붙어 가도록 모델 객체에 묶어 둔다 ]는 점에서 매 호출마다 tools=를 넘기는 방식과 구별된다.
3.2 미리 만든 에이전트 — 루프까지 맡기기
# 버전에 따라 위치 · 이름이 다름 — 공식 문서 확인
from langgraph.prebuilt import create_react_agent # 예: 이전 버전
# from langchain.agents import create_agent # 예: 새 버전
agent = create_react_agent(llm, tools=[get_leave_balance, get_leave_policy])
result = agent.invoke(
{"messages": [("user", "다음 주 월~수 휴가 쓸 수 있어?")]},
config={"recursion_limit": 10}, # 단계 수 상한
)
print(result["messages"][-1].content)
🔍 읽는 포인트
create_react_agent— 이름 그대로 9차시의 ReAct 루프 를 미리 만들어 둔 함수입니다.recursion_limit— 9차시MAX_STEPS의 자리입니다. 다만 세는 단위(모델 호출과 도구 실행을 각각 한 단계로 세는지 등)가 다를 수 있으니 문서로 확인합니다.result["messages"]— 9차시에서 쌓던messages리스트가 여기 들어 있습니다. 상태는 여전히 메시지 누적 입니다(축 ⑦).
3.3 9차시의 부품은 어디로 갔나
| 9 · 10차시 직접 구현 | LangChain | 내가 여전히 챙길 것 |
|---|---|---|
| 도구 선언 dict | @tool + docstring | 설명 문장의 품질 |
tools=tools | bind_tools([...]) | 역할별로 다른 도구 목록 (10차시 허용 목록) |
for step in range(MAX_STEPS) | 미리 만든 에이전트 내부 | recursion_limit 설정 |
block.type == "tool_use" | ai_msg.tool_calls | — |
tool_result (+ is_error) | 도구 메시지 (ToolMessage) | 도구 실패 시 오류 문장 |
| 누적 비용 계산 | 응답 메시지의 토큰 정보(usage_metadata 등, 버전 확인) | ⚠️ 비용 상한은 여전히 내가 걸어야 함 |
| 권한 · 마스킹 | — (프레임워크가 해 주지 않음) | ⭐ 도구 함수와 입력 단계에 그대로 |
⚠️ 표의 마지막 두 줄이 핵심입니다. 프레임워크는 흐름의 뼈대 를 대신할 뿐, 권한 · 마스킹 · 비용 상한 같은 우리 회사의 선 은 대신 지켜 주지 않습니다.
4. 트레이싱 — 에이전트의 행동을 기록하기
4.1 정의
트레이싱 (Tracing) — [관측 기법]인데, [ 한 요청이 거친 단계(모델 호출 · 도구 호출)를 부모-자식 순서와 입력 · 출력 · 토큰 · 시간까지 한 묶음으로 기록한다 ]는 점에서 줄 단위로 흩어져 쌓이는 일반 로그와 구별된다.
요청 "다음 주 월~수 휴가 쓸 수 있어?" 총 4.1초 · in 4,340 · out 423 (예시 — 실측 아님)
├─ 모델 호출 #1 1.2초 → tool_calls: get_leave_balance(year=2026)
│ └─ 도구 get_leave_balance 0.05초 → "남은 연차: 5일"
├─ 모델 호출 #2 1.1초 → tool_calls: get_leave_policy(topic="연차 신청 기한")
│ └─ 도구 get_leave_policy 0.08초 → "사용 3일 전까지 신청"
└─ 모델 호출 #3 1.6초 → 최종 답
9차시 6.2의 루프 로그를 나무 모양으로 묶고 화면으로 볼 수 있게 한 것입니다. 어느 단계에서 시간이 걸렸는지, 어떤 입력을 보고 어떤 도구를 골랐는지 클릭해서 따라갈 수 있습니다.
4.2 LangSmith로 켜기
LangChain 쪽의 추적 서비스가 LangSmith 입니다. 코드를 고치지 않고 환경변수로 켭니다.
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=... # 키는 환경변수로만. 코드 · 대화에 붙여 넣지 않음
🔍 읽는 포인트 : 이 두 줄이 켜져 있으면 각 단계의 입력 · 출력 · 토큰 · 시간이 기록됩니다. 변수 이름은 버전에 따라 다를 수 있으니 공식 문서 확인.
4.3 ⚠️ 추적 데이터는 밖으로 나간다 (축 ④)
추적 기록에는 프롬프트 원문, 도구 결과 원문 이 그대로 담깁니다. 오피스봇이라면 직원 사번 · 잔여 연차 · 질문 내용이 외부 서비스로 전송됩니다.
| 무엇이 나가는가 | 대응 |
|---|---|
| 사용자 질문 원문 | 앞에서 배운 마스킹 (8차시)을 추적 전송 전에 적용 |
| 도구 결과 (사번 · 개인 데이터) | 도구 결과도 마스킹, 또는 민감 도구는 추적에서 입력 · 출력 숨기기 (지원 여부 문서 확인) |
| 전체 | 사내 정책상 외부 전송이 안 되면 자체 호스팅 하거나, OpenTelemetry 같은 표준 형식으로 내 서버에 수집 |
⭐ 이미 외부 LLM API로 나가는 데이터와 별개로, 또 하나의 출구 가 생긴다는 점을 기억하세요. "무엇이 밖으로 나가는가" 목록에 추적 서비스를 추가해야 합니다.
5. 회귀 테스트 — 옮긴 뒤에도 같은가
5.1 정의
회귀 테스트 (Regression Test) — [소프트웨어 시험]인데, [ 고정해 둔 평가셋을 변경 전후에 똑같이 돌려, 이전에 되던 것이 나빠지지 않았는지 비교한다 ]는 점에서 새 기능이 동작하는지만 보는 기능 시험과 구별된다.
"회귀"는 뒤로 돌아간다 는 뜻입니다. 프레임워크로 옮기기, 모델 바꾸기, 프롬프트 한 줄 고치기 — 전부 변경이고, 전부 회귀를 일으킬 수 있습니다.
5.2 오피스봇 평가셋과 비교표
평가셋은 한 번 만들면 고정 합니다. 바꾸면 전후 비교가 의미를 잃습니다.
| # | 질문 | 기대 도구 | 답에 꼭 있어야 할 것 |
|---|---|---|---|
| 1 | 올해 연차 며칠 남았어? | get_leave_balance | 잔여 일수 |
| 2 | 다음 주 월~수 휴가 쓸 수 있어? | get_leave_balance, get_leave_policy | 가능 여부, 신청 기한 |
| 3 | 안녕 | (없음) | 인사 |
| 4 | 김과장 연차 며칠 남았어? | (권한 거부) | 본인 것만 조회 가능 안내 |
| … | (실제 질문 20~30개) |
| 지표 | 9차시 직접 구현 | 11차시 LangChain | 차이 |
|---|---|---|---|
| 기대 도구를 부른 비율 | |||
| 답에 필수 내용이 있는 비율 | |||
| 권한 거부가 지켜진 비율 | |||
| 평균 바퀴 수 · 평균 비용 |
(빈칸 양식 — 실습에서 채웁니다)
⚠️ 같은 질문도 돌릴 때마다 결과가 조금씩 다를 수 있습니다(결정론 ↔ 확률론, 3차시). 질문마다 몇 번씩 돌려 비율로 비교하고, 한두 개 차이로 결론 내리지 않습니다.
6. ⭐ 증거 ↔ 주장 — 추적 기록이 증거다
| 주장 | 증거 |
|---|---|
| "옮겼고 잘 동작합니다" | 5.2 비교표 — 전후 수치 |
| "규정을 확인하고 답했습니다" | 추적 기록에 get_leave_policy 호출과 그 결과가 있음 |
| "다른 사람 정보는 안 보여 줍니다" | 평가셋 4번의 추적 기록 — 도구가 권한 거부를 반환 |
| "비용은 문제없습니다" | 추적 기록의 토큰 합계 × 단가 |
README 문장 3 — 근거와 측정이 없으면 믿지 않는다. 에이전트는 경로가 매번 다를 수 있으므로(9차시 6.1), 답만 보고는 어떻게 그 답에 도달했는지 알 수 없습니다. 추적 기록은 에이전트의 행동에 대한 영수증 입니다.
앞으로 다시 만납니다 — 14차시에서 같은 축이 답의 내용 에 적용됩니다(출처가 붙은 답 ↔ 근거 없는 답). 24차시 통합 테스트에서는 이 평가셋을 서비스 전체에 다시 돌립니다.
7. 왜 직접 구현을 먼저 해 봤는가
| 상황 | 직접 구현을 해 본 사람 | 프레임워크만 써 본 사람 |
|---|---|---|
| 추적 기록에 같은 도구가 다섯 번 | "반복 감지가 없구나" (10차시 3.2) | "프레임워크 버그인가?" |
tool_use / tool_result 짝 오류 | 메시지 순서를 의심 (9차시 3.1) | 오류 문장을 검색 |
| AI가 사라진 함수 이름을 씀 | 원리로 대체 함수를 찾게 지시 | 함께 헤맴 |
| 비용 상한을 어디에 둘까 | 루프의 어느 지점인지 앎 | 설정 옵션이 있기를 기대 |
⭐ 프레임워크는 이미 아는 것을 짧게 쓰는 도구 일 때 가장 안전합니다. 그리고 루프는 결국 9차시의 몇 줄이라는 것을 알면, 프레임워크 버전이 바뀌어도 흔들리지 않습니다.
앞으로 다시 만납니다 — 18차시에서 같은 생태계의 LangGraph로 루프 대신 그래프 (큰 흐름은 코드가, 칸 안의 판단은 모델이)를 그립니다. 3.2의 미리 만든 에이전트도 내부는 그래프입니다.
8. 실습 — 맡기고 검수하기
지시
9·10차시의 오피스봇 루프를 LangChain으로 옮겨줘. 기존 코드는 지우지 말고 나란히 둘 것.
- 참고: LangChain · langchain-anthropic · LangSmith 공식 문서. 설치한 패키지 버전과 확인한 문서 주소를 보고에 적을 것
(함수 이름·위치가 버전마다 다르니 기억에 의존하지 말고 설치된 버전 기준으로)
- ChatAnthropic(model="claude-opus-5-5"), 도구는 @tool로, 신원은 세션에서 (모델 인자 무시)
- 미리 만든 ReAct 에이전트 사용, 단계 상한 설정. 비용 상한(요청당 0.20달러)은 직접 계산해 유지
- LangSmith 트레이싱을 켜되, 전송 전에 사번·전화번호를 마스킹할 것. 마스킹 방법을 보고에 설명
- 평가셋 20문항(내가 준 파일, 수정 금지)을 9차시 버전과 LangChain 버전에 각각 3번씩 돌려 비교
추측하지 말고 실행 출력 원문을 붙여. API 키는 환경변수로만.
보고 형식: [지표 | 직접 구현 | LangChain | 차이] 표 + 결과가 달라진 문항 목록과 추적 기록 링크
받을 증거
- 설치된 패키지 버전(
pip show출력)과 참고한 문서 주소 - 회귀 비교표와 달라진 문항의 추적 기록
- 추적 화면에서 사번이 가려진 화면 캡처 또는 기록 원문
내가 직접 확인할 것
| 볼 것 | 이걸로 아는 것 |
|---|---|
| 추적 화면에서 휴가 질문 하나를 열어 도구 호출 순서를 따라가 봄 | 트레이싱이 9차시 로그를 대신하는가 (4.1) |
| 추적 기록 어디에도 사번 · 전화번호 원문이 없는가 | 외부 전송 전 마스킹 (4.3) |
| "김과장 연차" 문항이 두 버전 모두 거부됐는가 | 권한이 프레임워크로 옮겨도 도구 안에 남았는가 (3.3) |
| 비교표에서 나빠진 지표가 있는가, 있다면 이유가 보고됐는가 | 회귀 여부 (5.2) |
| 보고서의 패키지 버전과 코드의 import가 일치하는가 | AI가 옛 이름을 쓰지 않았는가 (1.2) |
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | 프레임워크와 라이브러리는 무엇으로 갈리나? | 1.1 |
| 2 | 직접 구현 ↔ 프레임워크에서 얻는 것과 잃는 것은? | 1.2 |
| 3 | 제공자를 바꾸면 코드가 그대로여도 다시 시험해야 하는 이유는? | 2절 |
| 4 | @tool의 docstring이 중요한 이유는? | 3.1 |
| 5 | bind_tools만으로 루프가 도는가? | 3.1, 3.2 |
| 6 | 프레임워크가 대신 지켜 주지 않는 것 두 가지는? | 3.3 |
| 7 | 트레이싱과 일반 로그의 차이는? | 4.1 |
| 8 | 추적 기록을 외부로 보낼 때의 위험과 대응은? | 4.3 |
| 9 | 회귀 테스트에서 평가셋을 고정해야 하는 이유는? | 5.2 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
| "LangChain으로 옮겼으니 권한 · 비용은 프레임워크가 알아서" | ❌ 우리 회사의 선은 직접 도구 · 루프에 |
bind_tools 했는데 도구가 실행 안 된다 | ⚠️ 바인딩은 묶기만 함. 실행 · 반복은 에이전트(루프)가 |
| AI가 쓴 import를 그대로 믿음 | ⚠️ 버전마다 이름 · 위치가 다름. 설치 버전과 공식 문서로 확인 |
| 트레이싱을 켜고 개인정보 그대로 전송 | ❌ 또 하나의 외부 출구. 전송 전 마스킹 |
| 이전 후 예시 질문 두세 개로 "잘 됩니다" | ❌ 고정 평가셋으로 전후 비교 |
| 제공자를 바꾸고 회귀 테스트 생략 | ⚠️ 인터페이스가 같아도 동작은 다름 |
docstring을 비워 둔 @tool | ❌ 모델이 도구를 고를 근거가 사라짐 |
부록 C. NotebookLM 복습 프롬프트
이 11차시 노트만 근거로 6문제를 내줘. 비전공자 기준으로.
"프레임워크 ↔ 라이브러리", "추상 ↔ 구상으로 본 제공자 교체", "트레이싱과 외부 전송 마스킹", "회귀 테스트와 증거 ↔ 주장"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor