수료 — 무엇을 만들었고 다음에 무엇을 할 수 있나
한 줄 요약
8장이 끝났습니다. 파이썬 파일 8개로 에이전트 네 종류를 만들고, 기억을 붙이고, 하나의 팀으로 묶었습니다. 이 절은 무엇을 손에 넣었는지와 다음에 무엇을 할 수 있는지를 정리합니다.
1. 만든 것
8개 파일. 그리고 뒤의 것이 앞의 것을 가져다 씁니다.
ch08 ──import──▶ ch03 (get_order_status)
──import──▶ ch04 (search_web)
──import──▶ ch05 (run_pandas)
──import──▶ ch06 (policy_tool)
ch07 ──import──▶ ch05 (run_pandas)
버리는 코드가 없었습니다. 1장에서 만든 에이전트 네 종류가 전부 8장 팀에 들어갔습니다.
2. 에이전트 네 종류 — 최종 정리
| 에이전트 | 무엇에 연결 | 핵심 기술 | 대표 질문 |
|---|---|---|---|
| ① 사내 데이터 조회 | CSV의 정해진 값 | Function Calling | "주문 O001203 어디까지 왔어요?" |
| ② 웹검색 | 인터넷 (실시간·외부) | 외부 API 도구 + ReAct | "요즘 스마트워치 시장 어때요?" |
| ③ CSV 데이터분석 | 표 (자유로운 집계) | 코드 생성·실행 | "카테고리별 매출 상위 3개는?" |
| ④ RAG 문서검색 | 문서 PDF (긴 글) | 임베딩·벡터검색 | "불량 반품 기간이 며칠이죠?" |
전부 같은 LLM(gemini-2.5-flash)을 씁니다. 다른 것은 바깥세상과 연결되는 방식뿐입니다.
3. 8장을 관통한 원칙들
장마다 나왔던 것을 한자리에 모읍니다. 이것이 이 과정의 알맹이입니다.
① 도구는 그냥 파이썬 함수다
특별한 상속·등록이 없습니다. AI가 하는 일은 "언제 어느 도구를 쓸지 고르는 것" 뿐입니다.
② LLM은 함수를 직접 실행하지 못한다
요청서를 만들 뿐이고 실행은 우리 코드가 합니다. 이 경계선이 루프·안전장치·역할 분리를 전부 가능하게 합니다.
③ 모델은 함수 본문을 볼 수 없다
이름·독스트링·타입힌트만 봅니다. 그래서 그 셋이 모델이 읽는 설명서입니다.
④ 실패했을 때도 상황을 설명하는 문장을 돌려준다
예외를 던지면 멈추고, 문장을 돌려주면 모델이 읽고 대응합니다. 도구·담당·종합 모든 층에서 같은 원칙입니다.
⑤ 환각은 빈칸을 메우려는 성질이다
대책은 부탁이 아니라 빈칸을 채워 주는 것입니다. 그리고 모를 때 할 말을 정해 두는 것.
⑥ 숫자는 코드에게, 문장은 문서에서
LLM은 판단과 표현을 하고, 사실·숫자·규정은 맡기지 않습니다.
⑦ 모델이 알 수 없는 것은 사람이 알려 준다
"매출 집계에서 취소·환불 제외", "재고 수치는 문서에 없다" — 데이터를 아는 사람만 쓸 수 있는 문장입니다.
⑧ 기억은 모델의 능력이 아니라 우리가 만드는 구조다
history.append 든 checkpointer 든, 쌓아서 다시 보내는 것이 전부입니다.
⑨ 도구를 한 명에게 몰면 판단이 나빠진다
그래서 나눕니다. 멀티에이전트는 멋있어서가 아니라 필요해서 씁니다.
⑩ 에이전트가 틀리는 방식은 대개 "오류 없이 나오는 오답"이다
오류가 안 납니다. 답도 그럴듯합니다. 그래서 대조하고 검증합니다.
4. 라이브러리가 대신해 준 것과 안 해 준 것
| 라이브러리가 | 여전히 사람이 |
|---|---|
ReAct 루프 (create_agent) |
어떤 도구를 만들 것인가 |
대화 저장·복원 (checkpointer) |
도구 안의 로직 |
사용자 분리 (thread_id) |
독스트링과 설명 |
벡터 검색 (FAISS) |
chunk_size·k 같은 값 |
계획의 모양 강제 (with_structured_output) |
역할을 어떻게 나눌지 |
상태 이어 붙이기 (add_messages) |
안전장치와 실패 처리 |
| 결과 검증 |
오른쪽이 이 과정에서 배운 것입니다. 왼쪽은 한 줄씩 쓰면 되는 것들이고요.
"돌아가는데 왜 알아야 하나"에 대한 답 — 라이브러리는 연결 작업을 대신해 줍니다. 판단은 대신해 주지 않습니다. 도구를 잘못 만들면 잘못된 답이 아주 매끄럽게 나오고, 그건 라이브러리 탓이 아닙니다.
5. 다음에 할 수 있는 것
① 내 데이터로 바꾸기 — 가장 먼저 해 볼 것
data/ 폴더의 파일만 바꾸면 됩니다.
data/orders.csv → 내 회사의 주문/거래 데이터
data/docs/*.pdf → 내 회사의 규정·매뉴얼
그리고 두 곳을 고칩니다.
run_pandas의 독스트링 — 표 이름, 열 이름, 규칙policy_tool의 설명 — 어떤 문서가 들어 있는지
나머지 코드는 그대로입니다. 5장 3절에서 독스트링을 길게 쓴 이유가 여기 있습니다.
② 담당자 추가하기
세 곳을 함께 고쳐야 합니다. 지금 팀은 주문담당 · 데이터분석가 · 규정담당 · 시장조사원 넷이고, 여기에 하나를 더하는 예입니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
# ① 워커 추가
WORKERS["재무담당"] = create_agent(llm, tools=[accounting_tool], system_prompt="...")
# ② 역할 설명 추가
ROLE_DESC["재무담당"] = "회계 데이터를 조회한다 — 매입, 원가, 마진. 매출·재고는 여기 없다."
# ③ 계획의 Literal 에도 추가 ← 이걸 빠뜨리면 플래너가 영영 못 부른다
class Task(BaseModel):
worker: Literal["주문담당", "데이터분석가", "규정담당",
"시장조사원", "재무담당"] = Field(...)
③의 목록에 기존 넷이 전부 들어 있는지 확인하세요.
Literal은 여기 적힌 이름만 허용합니다. 새 이름을 넣으면서 기존 이름을 하나라도 빠뜨리면 그 담당은 그 순간부터 못 불립니다.
③이 함정입니다. 5절에서 배운 대로 Literal 은 정해진 값만 허용합니다. 새 담당을 여기 안 넣으면 플래너의 출력 스키마가 그 이름을 아예 허용하지 않아, 아무리 필요해도 부르지 못합니다.
팀원 목록은 안 고쳐도 됩니다 — 시스템 지시를 상수가 아니라 함수(
planner_prompt()) 로 짜 두었기 때문입니다.
python def planner_prompt() -> str: return ("너는 승승장구몰 대응팀의 팀장이다. ...\n팀원:\n" + "\n".join(f" - {k}: {v}" for k, v in ROLE_DESC.items()) + "\n규칙:\n...")
부를 때마다ROLE_DESC을 새로 펼치므로 ②만 고치면 팀원 목록이 자동으로 갱신됩니다.만약 상수(
PLANNER_SYSTEM = "...")로 짰다면 파일이 처음 읽힐 때 딱 한 번 만들어져서, 나중에 딕셔너리에 키를 추가해도 이미 만들어진 문자열은 안 바뀝니다. 담당을 늘릴 일이 있는 코드에서는 함수 쪽이 안전합니다. 8장 5절에서 짚은 그 이야기입니다.
③ 저장을 영구적으로
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
team = graph.compile(checkpointer=SqliteSaver(sqlite3.connect("chat.db", check_same_thread=False)))
체크포인터를 만드는 줄만 바꾸면 됩니다. (별도 패키지 설치 필요 — 7장 4절 참조)
더 중요한 것 —
InMemorySaver는 프로세스 메모리입니다. 웹 서버를 여러 프로세스로 띄우면 같은thread_id라도 어느 프로세스가 받느냐에 따라 대화가 갈립니다. 재배포하면 전부 사라지고요. 화면을 붙이는 순간 파일이나 DB 체크포인터로 바꿔야 합니다.
④ 화면 붙이기
지금은 터미널에서만 돌아갑니다. 웹 화면을 붙이려면 —
- Streamlit — 파이썬만으로 몇십 줄이면 챗봇 화면이 됩니다. 가장 빠릅니다
- FastAPI + 프런트엔드 — 제대로 만들 때
- Gradio — 데모용
에이전트 코드는 그대로 두고 화면만 얹으면 됩니다. ask(question, thread_id) 를 부르기만 하면 되니까요.
⑤ 동시 실행으로 빨라지기
8장 6절에서 본 그것입니다. 담당들이 독립이므로 동시에 실행하면 시간이 크게 줄어듭니다.
6. 더 배우면 좋은 것
이 과정에서 일부러 안 다룬 것들입니다. 필요해지면 찾아보세요.
| 주제 | 무엇 | 언제 필요한가 |
|---|---|---|
| 조건부 엣지 | 그래프에서 갈라지는 흐름 | Supervisor 방식을 만들 때 |
| 중간 승인 (human-in-the-loop) | 실행 전에 사람이 확인 | 돈이 오가는 작업 |
| 스트리밍 | 답이 한 글자씩 나오게 | 사용자 경험 |
| 평가 (evaluation) | 에이전트가 잘하는지 자동 측정 | 개선하려면 필수 |
| 관측 (observability) | 호출·비용·오류를 추적 | 운영할 때 |
| 하이브리드 검색 | 키워드 + 벡터 검색 | RAG 정확도를 더 올릴 때 |
| MCP | 도구를 표준 방식으로 연결 | 도구를 여러 앱에서 공유할 때 |
가장 먼저 볼 것은 "평가" 입니다. 이 과정에서는 답을 눈으로 확인했지만, 실제로 쓰려면 질문 50개를 자동으로 돌려 정답률을 재는 장치가 필요합니다. 그게 있어야 "고쳤더니 좋아졌다"를 말할 수 있습니다.
7. 마지막으로 — 판단은 여전히 사람의 일입니다
이 과정에서 만든 것은 "조사해서 정리해 주는 팀" 입니다.
[AI가 한 것]
· 어느 자료를 봐야 할지 판단
· 도구를 써서 사실을 모음
· 사실을 읽을 수 있는 문장으로 정리
[사람이 할 것]
· 27개를 정말 발주할지
· 상세페이지를 언제 어떻게 고칠지
· 이 상품을 계속 팔지
8장의 최종 답변이 "① 확인된 사실 → ② 권고" 로 나뉘어 있던 이유가 이것입니다. ①은 근거가 있고, ②는 사람이 검토할 것이라는 표시죠.
실제로 실행해 보면 모델이 "단종을 검토하십시오" 같은 중대한 권고를 쉽게 하기도 합니다. 재고 3개와 평점 3.8점만 보고요. 그 판단이 맞는지 아닌지는 회사 사정을 아는 사람이 정할 일입니다.
에이전트를 잘 만든다는 것은 모델을 더 똑똑하게 만드는 일이 아니라, 어디까지를 AI에게 맡기고 어디부터를 사람이 볼지 선을 긋는 일입니다. 이 과정에서 반복해서 그 선을 그어 왔습니다.
8. 체크리스트 — 이 과정을 마쳤다면
- [ ] 파이썬 파일 8개가 전부 내 PC에서 돌아간다
- [ ] 에이전트 네 종류의 차이를 설명할 수 있다
- [ ] "도구는 그냥 파이썬 함수"라고 설명할 수 있다
- [ ] "LLM은 함수를 직접 실행하지 못한다" 가 왜 중요한지 설명할 수 있다
- [ ] 독스트링을 고치면 에이전트의 판단이 달라지는 것을 직접 봤다
- [ ] 표의 숫자와 문서의 문장을 언제 다르게 다뤄야 하는지 설명할 수 있다
- [ ]
checkpointer와thread_id가 무엇을 대신해 주는지 설명할 수 있다 - [ ] 왜 도구를 나눠 주는지 설명할 수 있다
- [ ] 8장 최종 답변에서 어느 사실이 어디서 왔는지 짚을 수 있다
수고하셨습니다
1장에서 이렇게 시작했습니다.
Q: 승승장구몰에서 불량 상품 반품은 며칠 이내에 신청해야 하나요?
A: 일반적으로 전자상거래법에 따르면 상품을 받은 날로부터 3개월 이내 ...
그리고 8장에서 여기까지 왔습니다.
담당자 ▸ "승승 스마트워치 Fit 5(P0003), 지금 어떻게 대응해야 할까요?"
[계획] → 데이터분석가 · 규정담당 · 시장조사원 (이어 묻기에서는 주문담당까지)
[보고] 재고 3개 / 평점 3.8 / "GPS 켜면 하루도 못 갑니다"
배터리 일반 7일 / GPS 18시간 / 불량 반품 30일 이내
시장은 성장세, 배터리가 경쟁 요소
[최종 답변]
고객 불만은 제품 사양과 실제 기대치의 괴리에서 옵니다.
상세 페이지에 GPS 사용 시 배터리(약 18시간)를 명확히 고지하십시오.
재고가 3개뿐이므로 27개 발주가 필요합니다.
불만 고객은 불량 기준(30일)으로 안내하십시오.
같은 모델입니다. 달라진 것은 여러분이 만든 구조입니다.