5장. LLM 외부 도구 연동
모델에게 도구가 필요한 까닭
여행 상담원이 여행지 이야기는 술술 하는데, "P01 상품 남은 자리가 몇 개예요?"라는 질문에는 답하지 못한다고 해 봅시다. 남은 자리는 상담원의 머릿속이 아니라 예약 시스템에 있기 때문입니다. 상담원이 아무리 똑똑해도 예약 화면을 열어 보거나 계산기를 쓰지 않으면, 그럴듯한 숫자를 지어낼 수밖에 없습니다. 모델도 같습니다. 모델이 배운 것은 학습할 때 읽은 글뿐이라서, 우리 회사의 상품 표와 오늘의 남은 자리는 모릅니다.
왼쪽 파랑 상자는 모델이 잘하는 일, 오른쪽 보라 상자는 모델이 모르거나 틀리기 쉬운 일입니다. 오른쪽의 일을 함수로 만들어 모델 곁에 두고, 모델에게는 "언제 무엇을 부를지"만 고르게 하는 것이 이 장의 방법입니다. 여기서 상담원이 모델, 예약 화면과 계산기가 도구입니다. 비유와 다른 점은, 상담원은 스스로 예약 화면을 열지만 모델은 화면을 열 손이 없어서 "열어 달라"는 부탁을 우리 코드에 돌려준다는 것입니다.
모델이 모르는 것과 맡길 도구
| 모델이 모르는 것 | 까닭 | 맡길 도구의 예 |
|---|---|---|
| 우리 회사의 상품, 가격, 남은 자리 | 학습한 글에 없다 | 상품 표를 찾는 함수 |
| 오늘 날짜, 방금 바뀐 값 | 학습이 끝난 뒤의 일이다 | 시계, 예약 시스템을 묻는 함수 |
| 틀리면 안 되는 계산 | 글을 이어 쓰는 방식이라 숫자를 어림한다 | 요금 계산 함수 |
| 파일 안의 글 | 파일을 열 수 없다 | 파일을 읽는 함수 |
짧은 예시
가짜 모델에게 요금을 물은 답과, 상품 표(trip_products.csv, 지어낸 상품 12개)로 계산한 값을 견줍니다. 가짜 모델의 답은 "표를 보지 못한 모델이 할 법한 답"으로 우리가 지어 넣은 것입니다.
import pandas as pd
from langchain_core.language_models.fake_chat_models import FakeListChatModel
# 구름다리여행의 가상 상품 표. 가격은 어른 1명 기준(만 원)
products = pd.read_csv("trip_products.csv")
# 표를 보지 못한 모델이 할 법한 답을 지어 넣은 가짜 모델
model = FakeListChatModel(responses=["P01 상품에 어른 2명, 아이 1명이면 117만 원쯤입니다. 자리는 넉넉합니다."])
print("모델의 답:", model.invoke("P01 상품에 어른 2명, 아이 1명이면 얼마예요?").content)
# 표에서 찾아 계산하면: 어른 요금 x 2 + 아동 요금(어른의 80%) x 1
row = products[products["상품코드"] == "P01"].iloc[0]
price = int(row["가격"])
print("표로 계산:", round(price * 2 + price * 0.8 * 1, 1), "만 원 / 남은 자리:", int(row["남은자리"]))모델의 답: P01 상품에 어른 2명, 아이 1명이면 117만 원쯤입니다. 자리는 넉넉합니다.
표로 계산: 109.2 만 원 / 남은 자리: 6모델의 답은 말투가 자연스러워도 숫자가 표와 다릅니다. 표로 계산하면 어른 39만 원 둘에 아동 요금(39만 원의 80%) 하나를 더해 109.2만 원이고, 남은 자리는 6개입니다. 이 숫자는 표에만 있으므로, 표를 읽고 계산하는 일은 함수(도구)에게 맡겨야 합니다.
코드가 의미하는 것
이 코드는 예약 화면을 보지 못한 상담원과 예약 화면을 연 상담원을 나란히 세운 것입니다. 한쪽은 가짜 모델이 지어낸 답을 그대로 읽고, 다른 쪽은 표에서 행을 찾아 계산합니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
FakeListChatModel(responses=[...]) | 정해 둔 답을 돌려주는 가짜 모델을 만든다 | 표를 보지 못한 모델이 할 법한 답을 흉내 냅니다. 진짜 모델도 표가 없으면 말투만 그럴듯한 숫자를 낼 수 있습니다 |
model.invoke(...).content | 질문을 넣고 답의 글을 꺼낸다 | 1장에서 본 모델 부르기와 같습니다. 도구가 없으니 모델이 가진 것은 질문 한 줄뿐입니다 |
products[products["상품코드"] == "P01"].iloc[0] | 표에서 P01 행 하나를 꺼낸다 | 도구가 맡을 일의 앞 절반, 곧 "회사 데이터 찾기"입니다 |
price * 2 + price * 0.8 * 1 | 어른 둘과 아동 하나의 요금을 계산한다 | 도구가 맡을 일의 뒤 절반, "틀리면 안 되는 계산"입니다. 앞의 표에서 요금 계산 함수에 맡기자고 한 일입니다 |
결과에서 읽을 것은 두 가지입니다.
- 말투와 숫자는 따로 놉니다. 가짜 모델의 답(117만 원, "자리는 넉넉합니다")은 자연스럽지만, 표로 계산한 109.2만 원과 남은 자리 6과 다릅니다. 자연스러운 문장이 맞는 숫자를 보장하지 않습니다.
- 맞는 숫자는 코드 두 줄에서 나왔습니다. 표에서 행 하나를 찾고 곱셈 두 번을 하면 끝나는 일입니다. 이 두 줄을 함수로 묶어 모델 곁에 두는 것이 이 장에서 만드는 도구입니다.
주의할 점
- 도구가 있다고 모든 질문에 도구를 쓰지는 않습니다. "제주도는 어떤 곳이에요?"처럼 모델이 아는 것으로 답할 질문에는 도구를 부르지 않는 것이 맞습니다.
- 도구가 틀린 값을 돌려주면 모델은 그 값을 그대로 믿고 답합니다. 도구의 코드와 데이터가 맞는지는 우리가 따로 확인합니다.