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

멀티 에이전트

이 페이지에 묻기

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

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

MULTI · AGENT

멀티 에이전트

생성 수정

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

26개 문서 · 2일차 · 12 / 26

11차시 · 프레임워크 기반 에이전트 구성

지도 위치 : 2일차 · README 4장의 ② 행동 층 — 직접 만든 루프를 표준 부품 으로 옮기고, 행동을 기록 하는 자리 | 이전: 10차시 루프 제어와 가드레일 | 다음: 12차시 문서 로딩과 청킹 키워드 : LangChain, 도구 바인딩, 트레이싱, 회귀 테스트

이 차시를 다 읽으면

  1. 프레임워크를 유와 종차로 정의하고, 직접 구현과 비교해 얻는 것과 잃는 것 을 말할 수 있다.
  2. 축 ②로 "프레임워크의 공통 인터페이스 ↔ 모델 제공자"를 가르고, 제공자를 바꿀 때 바뀌지 않는 것과 바뀌는 것을 구별할 수 있다.
  3. ChatAnthropic · @tool · bind_tools 코드에서 9차시 수동 루프의 어느 부분이 어디로 갔는지 짚을 수 있다.
  4. 트레이싱을 정의하고, 추적 기록이 외부로 나갈 때 무엇을 가려야 하는지 말할 수 있다.
  5. 회귀 테스트로 "옮긴 뒤에도 전과 같이 동작한다"는 것을 증거로 보일 수 있다.

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_schema dict를 대신 만들어 줍니다. ⭐ 그래서 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=toolsbind_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
5bind_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문제를 내줘. 비전공자 기준으로.
"프레임워크 ↔ 라이브러리", "추상 ↔ 구상으로 본 제공자 교체", "트레이싱과 외부 전송 마스킹", "회귀 테스트와 증거 ↔ 주장"은 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.