따라하기 — 도구를 붙여 자동으로 호출시키기
한 줄 요약
tools=[get_order_status] 한 줄로 도구를 쥐여 줍니다. 그러면 1장에서 못 풀던 질문에 답합니다. 2장 코드와 딱 한 줄 차이입니다.
1. 코드 — ask()
아래는 code/ch03_order_agent.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.
def ask(question: str) -> str:
"""도구를 쥐여 주고 질문한다. 도구 실행까지 SDK 가 알아서 처리한다."""
resp = client.models.generate_content(
model=GEMINI_MODEL,
contents=question,
config=types.GenerateContentConfig(
tools=[get_order_status], # ← 이 한 줄이 전부
system_instruction=SYSTEM,
temperature=0, # 도구 선택은 매번 같아야 하므로 0
),
)
return resp.text.strip()
2장의
(resp.text or "")가 여기에도 그대로 있습니다. 도구를 쓰면 빈 답변이 나올 여지가 줄기는 하지만, 안전 필터에 걸리면 도구를 썼든 안 썼든.text가 비어 옵니다. 짧게 덧붙이기만 하면 되므로, 이 과정의 실습 파일은 전부 이 안전장치를 넣어 두었습니다.
2장의 chat() 과 나란히 놓고 보세요.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
# 2장
config=types.GenerateContentConfig(
system_instruction=system,
temperature=temperature,
)
# 3장
config=types.GenerateContentConfig(
tools=[get_order_status], # ← 추가된 것
system_instruction=SYSTEM,
temperature=0,
)
tools 한 줄. 이것으로 LLM 애플리케이션이 에이전트가 됩니다.
2. 시스템 지시가 하는 일
아래는 code/ch03_order_agent.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.
SYSTEM = ("너는 승승장구몰의 CS 상담원이다. 주문 관련 질문에는 반드시 get_order_status "
"도구로 조회한 결과만 근거로 답한다. 존댓말로 간결하게 답한다.")
"반드시 ... 도구로 조회한 결과만 근거로 답한다" — 이 문장이 핵심입니다.
이게 없으면 모델이 도구를 안 쓰고 자기 지식으로 답해 버릴 수 있습니다. 1장에서 반품 기간을 지어냈던 것처럼요. "도구 결과만 근거로" 라고 못을 박아 두면 지어낼 여지가 크게 줄어듭니다.
2장에서 배운 원칙이 여기서 쓰입니다 — 모를 때 어떻게 할지를 미리 정해 둔다. 여기서는 "모르면 도구를 써라"입니다.
temperature=0 인 이유도 명확합니다. 같은 질문에는 같은 도구를 골라야 하기 때문입니다. 도구 선택이 실행할 때마다 달라지면 원인을 추적할 수 없습니다.
3. 실행 — [3] 부분
터미널에서 실행 — 프로젝트 폴더
agentic_ai에서,(agentic)표시를 확인한 뒤.
python code/ch03_order_agent.py
==============================================================
[3] 1장에서 못 풀던 질문
고객 : 승승장구몰 주문 O001203은 지금 어떤 상태인가요?
상담원: 주문 O001203은 배송완료 상태입니다.
고객 : 주문 O999999 어떻게 됐나요?
상담원: 주문번호 O999999를 찾을 수 없습니다. 번호를 다시 확인해 주세요.
==============================================================
관찰: 없는 주문번호에도 지어내지 않고 '찾을 수 없다'고 답합니다.
도구가 사실을 돌려주기 때문입니다.
1장과 비교해 보세요.
| 1장 (도구 없음) | 3장 (도구 있음) | |
|---|---|---|
| 답 | "내부 시스템에 접근할 수 없습니다" | "배송완료 상태입니다" |
| 근거 | 없음 | orders.csv 의 실제 행 |
| 없는 번호 | (물어본 적 없음) | "찾을 수 없습니다" — 지어내지 않음 |
4. 겉으로 안 보이는 일 — 안에서 벌어진 것
이 짧은 코드 뒤에서 실제로는 이런 일이 있었습니다.
2절의 세 칸 그림을 그대로 다시 살펴봅니다 — 내가 쓴 코드 · SDK · 구글 서버.
① 내 코드 → SDK → 구글 서버 질문 + 도구 명세 💲 1회차 호출
② 구글 서버 → SDK "get_order_status(order_id='O001203')
불러 주세요"
③ SDK → 내 함수 get_order_status("O001203") 실행
→ "주문 O001203 | 고객 오지우 | ...
| 상태 '배송완료'"
④ SDK → 구글 서버 실행 결과 전달 💲 2회차 호출
⑤ 구글 서버 → SDK → 내 코드 "주문 O001203은 배송완료 상태입니다."
②③④를 SDK가 대신 했습니다. 이것이 자동 함수 호출(AFC) 입니다. 구글 SDK에서는 이 기능이 기본으로 켜져 있습니다.
③에서 "SDK가 실행했다"와 2절의 "실행은 우리 쪽"이 어긋나 보인다면, 2절의 두 축을 다시 보세요. 부른 것은 SDK지만 불린 함수는 내가 쓴 함수이고 실행된 곳은 내 컴퓨터 안입니다. 구글 서버는
orders.csv를 여전히 본 적이 없습니다.
편리하지만, LLM 호출이 두 번 일어났다는 사실은 알아 두세요. 위 그림의 💲 표시 두 개가 그것입니다. ask() 를 한 번 부르면 요금은 두 번 나갑니다. 회사에 적용할지 판단할 때 바로 쓰게 될 숫자입니다.
5. 도구가 필요 없는 질문을 하면?
앞 절에서 예측해 보라고 했던 문제입니다. 직접 확인해 봅시다.
직접 고쳐 보기 —
agentic_ai/code/ch03_order_agent.py를 열어 아래처럼 바꾸거나 덧붙인 뒤 다시 실행하세요.
for q in ["승승장구몰 주문 O001203은 지금 어떤 상태인가요?",
"주문 O999999 어떻게 됐나요?",
"오늘 날씨 어때요?"]: # ← 추가
고객 : 오늘 날씨 어때요?
상담원: 죄송하지만 저는 주문 관련 문의만 도와드릴 수 있습니다.
도구를 부르지 않습니다. 모델은 "이 질문에는 주문 조회 도구가 안 맞는다"고 판단할 수 있습니다. 도구를 쥐여 준다고 해서 아무 데나 쓰는 게 아닙니다.
이것이 "스스로 쓴다"의 의미입니다. 우리는
if "주문" in question:같은 분기를 짜지 않았습니다. 언제 쓸지 판단하는 것 자체가 모델의 일입니다. 그 판단의 근거가 앞 절에서 본 독스트링이고요.
6. 도구를 여러 개 주면?
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
tools=[get_order_status, get_customer_info, get_product_info]
목록에 넣기만 하면 됩니다. 모델이 독스트링을 보고 고릅니다.
4장에서 이걸 합니다. 도구가 둘 이상이면 새로운 문제가 생깁니다 — 두 개를 순서대로 써야 하는 질문이 나오는 것이죠. 그때 자동 호출로는 부족해지고, 우리가 직접 루프를 돌려야 합니다.
스스로 해 보기
SYSTEM에서 "반드시 get_order_status 도구로 조회한 결과만 근거로 답한다" 를 지우고 실행해 보세요. 답이 달라지나요?- 질문을
"O001203 배송됐어?"처럼 짧고 구어체로 바꿔 보세요. 여전히 도구를 부르나요? "주문번호 없이 그냥 제 주문 상태 알려주세요"라고 물어 보세요. 모델은 어떻게 반응할까요? 예측하고 확인하세요.
핵심 정리
tools=[함수]한 줄로 도구를 쥐여 줍니다. 2장 코드와의 유일한 차이입니다.- 시스템 지시에 "도구로 조회한 결과만 근거로 답한다" 를 넣어 지어내는 것을 막습니다.
temperature=0— 같은 질문에는 같은 도구를 골라야 하기 때문입니다.- SDK의 자동 함수 호출(AFC) 이 실행과 재전송을 대신 해 줍니다. 그래서 LLM 호출이 두 번 일어납니다. (도구를 두 번 이상 쓰는 4장에서는 도구 N번에 호출 N+1번이 됩니다.)
- 도구가 안 맞는 질문에는 모델이 알아서 도구를 안 부릅니다. 판단 근거는 독스트링입니다.
- 다음 절에서 이 자동 처리를 꺼서, 모델의 요청서를 직접 들여다봅니다.