LLM은 함수를 직접 실행하지 못한다 — 가장 중요한 개념
한 줄 요약
모델이 하는 일은 "이 함수를 이 값으로 불러 주세요"라는 요청서를 만드는 것까지입니다. 실제 실행은 우리 코드가 합니다. 이 경계선 하나로 4장의 루프, 5장의 안전장치, 8장의 멀티에이전트가 전부 설명됩니다.
1. 흔한 오해
"LLM에게 함수를 주면 알아서 실행한다"고 생각하기 쉽습니다. 틀렸습니다.
모델은 인터넷 너머 구글 서버에서 돌아갑니다. 여러분 컴퓨터의 orders.csv 를 읽을 수도, 파이썬 함수를 실행할 수도 없습니다. 애초에 불가능합니다.
그럼 무엇을 하느냐 — 글자를 만듭니다. 모델이 할 줄 아는 건 그것뿐입니다.
Function Calling이란 "모델이 만든 글자를 우리가 함수 호출로 해석해서 대신 실행해 주는 것" 입니다.
2. 실제로 오가는 것
모델이 만들어 보내는 것은 이런 모양의 데이터입니다.
name: "get_order_status"
args: {"order_id": "O001203"}
함수 이름과 인자값입니다. 실행 결과가 아닙니다. 주문할 메뉴를 적은 주문서라고 보면 정확합니다. 주문서를 받아 요리하는 건 주방(우리 코드)입니다.
아래는 4절에서 만들 code/ch03_order_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
resp = client.models.generate_content(
model=GEMINI_MODEL, contents=question,
config=types.GenerateContentConfig(
tools=[get_order_status],
# ↓ 이 줄이 있어야 요청서가 그대로 남는다
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
),
)
print(resp.function_calls)
[FunctionCall(name='get_order_status', args={'order_id': 'O001203'})]
disable=True가 없으면None이 나옵니다. SDK가 요청서를 받아 실행까지 끝내 버리기 때문에, 돌아온 응답에는 요청서가 남아 있지 않습니다. 왜 그런지는 다음 §4에서 다룹니다. 6절의peek()함수에 이 줄이 들어 있는 것도 그래서입니다 — 선택이 아니라 필수입니다.
6절에서 이걸 직접 눈으로 확인합니다.
3. 전체 흐름 다시 보기
칸을 세 개로 나눠서 봐야 합니다 — 내가 쓴 코드 · SDK · 구글 서버. 앞의 둘은 둘 다 내 컴퓨터 안에 있고, 구글 서버만 인터넷 너머에 있습니다.
왼쪽 큰 상자 안이 전부 내 컴퓨터입니다. SDK도 내 컴퓨터에서 돕니다 — 구글이 만들어 배포했을 뿐이죠. 인터넷을 건너가는 것은 오른쪽 화살표 두 개뿐입니다.
한 번 물어보면 다섯 단계를 거칩니다.
| 무슨 일이 | 어디서 | |
|---|---|---|
| ① | 질문 + 도구 목록을 보낸다 | 내 코드 → SDK → 구글 서버 |
| ② | "get_order_status 를 order_id='O001203' 로 불러 줘" |
구글 서버 → SDK |
| ③ | get_order_status("O001203") 실행 · orders.csv 를 읽는다 |
내 컴퓨터 안 (SDK가 내 함수를 부른다) |
| ④ | 실행 결과 "주문 O001203 \| ... \| 상태 '배송완료'" 를 전달 |
SDK → 구글 서버 |
| ⑤ | "주문 O001203은 배송완료 상태입니다" | 구글 서버 → SDK → 내 코드 |
③이 헷갈리는 자리입니다. ③을 실제로 "부르는" 것은 SDK지만, 불리는 함수는 내가 쓴 함수이고 실행되는 곳은 내 컴퓨터 안입니다. 구글 서버는
orders.csv를 본 적이 없습니다. 그러니까 구분해서 봐야 할 기준은 두 가지입니다.
축 갈라지는 것 어디서 도는가 내 컴퓨터(내 코드 + SDK) ↔ 구글 서버 누가 쓴 코드인가 내가 쓴 코드 ↔ 구글이 만든 SDK "실행은 우리 쪽"이라고 할 때의 "우리 쪽"은 첫 번째 축입니다.
②③④는 눈에 잘 안 띕니다. SDK가 대신해 주기 때문입니다.
점선 안이 AFC(자동 함수 호출)를 켜면 SDK가 대신해 주는 구간입니다. 6절의 peek() 는 이 점선을 걷어내고 ②를 눈으로 보는 것입니다.
구글 서버로 가는 화살표가 두 개라는 점을 눈여겨보세요. ①이 한 번, ④가 한 번 — 요금도 두 번 나갑니다.
① ──▶ 구글 서버 💲 1회차 호출 ④ ──▶ 구글 서버 💲 2회차 호출
도구를 한 번 쓰는 데 LLM 호출이 두 번 일어납니다. 2장에서 본 비용 이야기가 여기서 현실이 됩니다.
4. "그런데 코드에는 그런 게 안 보이는데요?"
맞습니다. 이번 장 실습 코드는 이렇게 짧습니다.
아래는 4절에서 만들 code/ch03_order_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
resp = client.models.generate_content(
model=GEMINI_MODEL,
contents=question,
config=types.GenerateContentConfig(
tools=[get_order_status],
system_instruction=SYSTEM,
temperature=0,
),
)
return resp.text.strip()
②③④⑤가 어디에도 안 보입니다. SDK가 대신 해 주고 있기 때문입니다.
이 기능을 자동 함수 호출(Automatic Function Calling, AFC) 이라고 합니다. 구글 SDK에서는 이 기능이 기본으로 켜져 있어서, 모델이 함수를 요청하면 알아서 실행하고 결과를 다시 보내고 최종 답변까지 받아 옵니다.
이름 세 개가 같은 과정의 서로 다른 범위를 가리킵니다. 1장 2절 표의 함수 도구, 이 절의 Function Calling, 그리고 방금 나온 자동 함수 호출(AFC) — 헷갈리기 쉬우니 여기서 정리해 둡니다.
이름 무엇을 가리키나 함수 도구 · Function Calling 파이썬 함수를 모델에게 도구로 쥐여 주는 방식 전체. 같은 것의 한국어·영어 표기입니다. 자동 함수 호출(AFC) 그 방식 안에서, ②③④를 SDK가 대신 처리해 주는 하위 기능. 끄고 켤 수 있습니다. 즉 AFC는 Function Calling의 일부이지 다른 기술이 아닙니다.
편합니다. 그런데 편한 만큼 안 보입니다.
그래서 이 장에서 두 가지 방식을 다 해 봅니다.
-ask()— 자동에 맡기고 결과만 본다 (편한 쪽)
-peek()— 자동을 꺼서 모델의 요청서를 직접 들여다본다 (보이는 쪽)
peek()로 한 번 열어 보고 나면,ask()안에서 무슨 일이 일어나는지 알고 쓰게 됩니다.
자동을 끄는 방법은 이 한 줄입니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True)
4장에서는 이걸 계속 꺼 놓고 루프를 직접 돌립니다. 도구가 여러 개일 때 순서를 우리가 보고 싶기 때문입니다.
5. 이 경계선이 만드는 것들
이 선 하나가 앞으로의 모든 내용을 설명합니다.
① 4장 — ReAct 루프
ReAct 라는 이름과 그 뜻은 4장에서 제대로 다룹니다. 지금은 구조만 보세요.
도구를 한 번 쓰고 끝이 아니라, 결과를 보고 또 다른 도구를 쓰고 싶어 할 수 있습니다. 그러면 ②③④를 반복해야 합니다. 그 반복문을 우리가 짜는 것이 4장입니다.
② 요청 → ③ 실행 → ④ 전달 → 또 요청? → ③ 실행 → ④ 전달 → 이제 답변
② 5장 — 안전장치
실행이 우리 쪽 일이므로, 실행하기 전에 검사할 수 있습니다. 5장에서 만드는 도구는 모델이 만든 코드를 실행하는데, 그 앞에 안전 검사를 끼워 넣습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
result = eval(expr, {"__builtins__": SAFE_BUILTINS}, ...) # 허용된 것만 실행
모델을 믿는 게 아니라 실행 지점을 통제하는 것 — 이게 가능한 이유가 바로 이 경계선입니다.
③ 8장 — 멀티에이전트
에이전트 여러 개를 팀으로 묶을 때, 각 에이전트의 도구 목록을 다르게 줍니다. "이 담당자는 이 도구만 쓸 수 있다"를 우리가 정하는 것이죠. 실행 지점이 우리 쪽이니 가능한 일입니다.
6. 정리 — AI가 한 것과 사람이 한 것
이 과정에서 계속 물을 질문입니다. 이번 장에서는 이렇게 갈립니다.
| 누가 | 무엇을 |
|---|---|
| AI(모델) | 질문을 읽고 "주문 조회 도구를 써야겠다" 고 판단 |
| AI(모델) | 질문에서 O001203 을 뽑아 인자로 채움 |
| 사람(우리 코드) | 도구를 어떤 것들로 구성할지 결정 |
| 사람(우리 코드) | 도구 안의 실제 로직을 작성 |
| 사람(우리 코드) | 요청서를 받아 실행 |
| 사람(우리 코드) | 없는 주문번호일 때 무엇을 돌려줄지 결정 |
AI가 한 일은 위 두 줄뿐입니다. 나머지는 전부 평범한 프로그래밍입니다.
이 표를 매 장 다시 그려 볼 것을 권합니다. 에이전트를 만들다 보면 "AI가 알아서 해 주겠지"라고 넘기게 되는 지점이 생기는데, 거기가 대개 버그가 나는 자리입니다.
예측해 보기
다음 절로 넘어가기 전에 예측을 적어 보세요.
도구
get_order_status를 쥐여 준 상태에서, "오늘 날씨 어때요?" 라고 물으면 모델은 어떻게 할까요?
- ① 도구를 부른다 (
order_id="오늘 날씨"같은 식으로) - ② 도구를 안 부르고 그냥 답한다
- ③ 오류가 난다
실습에서 직접 확인해 보고, 예측이 틀렸다면 왜 틀렸는지 적어 보세요.
핵심 정리
- 모델은 함수를 실행하지 못합니다. 구글 서버에 있고, 할 줄 아는 건 글자를 만드는 일뿐입니다.
- 모델이 만드는 것은 함수 이름과 인자값이 담긴 요청서입니다 —
name,args. - 실행은 우리 코드가 합니다. 이 경계선이 이 과정 전체의 뼈대입니다.
- 구글 SDK는 자동 함수 호출(AFC) 로 이 과정을 대신 해 줍니다. 편한 대신 안 보입니다.
automatic_function_calling=...(disable=True)로 끄면 모델의 요청서를 직접 볼 수 있습니다.- 실행 지점이 우리 쪽이라서 — 루프를 짤 수 있고, 안전장치를 걸 수 있고, 도구를 나눠 줄 수 있습니다.