📚 Agentic AI - 기업용 자율 에이전트 개발 3장 · 에이전트 ① 사내 데이터 조회 Agentic AI란

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 · 구글 서버. 앞의 둘은 둘 다 내 컴퓨터 안에 있고, 구글 서버만 인터넷 너머에 있습니다.

내 컴퓨터내가 쓴 코드ask() / peek()get_order_status()SDKgoogle-genai(구글이 배포한 라이브러리)구글 서버모델 (Gemini)SDK도 내 컴퓨터에서 실행됩니다. 구글이 만들어 배포했을 뿐입니다. 오른쪽 두 화살표만 인터넷을 통한 통신입니다.
SDK 도 내 컴퓨터에서 돈다 — 인터넷을 건너가는 것은 오른쪽 화살표 두 개뿐이다

왼쪽 큰 상자 안이 전부 내 컴퓨터입니다. SDK도 내 컴퓨터에서 돕니다 — 구글이 만들어 배포했을 뿐이죠. 인터넷을 건너가는 것은 오른쪽 화살표 두 개뿐입니다.

한 번 물어보면 다섯 단계를 거칩니다.

무슨 일이 어디서
질문 + 도구 목록을 보낸다 내 코드 → SDK → 구글 서버
"get_order_statusorder_id='O001203' 로 불러 줘" 구글 서버 → SDK
get_order_status("O001203") 실행 · orders.csv 를 읽는다 내 컴퓨터 안 (SDK가 내 함수를 부른다)
실행 결과 "주문 O001203 \| ... \| 상태 '배송완료'" 를 전달 SDK → 구글 서버
"주문 O001203은 배송완료 상태입니다" 구글 서버 → SDK → 내 코드
인터넷내 컴퓨터내가 쓴 코드SDK구글 서버시간 ↓① 질문 + 도구 명세₩ 요금② 도구 호출 요청get_order_status()③ 함수 실행orders.csv를 읽는 곳이 화살표는 테두리 밖으로 나가지 않습니다④ 실행 결과₩ 요금⑤ 최종 답변점선을 건너간 것은 ①②④⑤ 네 번이지만 요금이 부과되는 모델 호출은 ①과 ④ 두 번입니다 — 도구 한 번에 호출 두 번.
다섯 단계가 세 칸을 오가는 모습 — ③만 내 컴퓨터 테두리 안에 머문다

③이 헷갈리는 자리입니다. ③을 실제로 "부르는" 것은 SDK지만, 불리는 함수는 내가 쓴 함수이고 실행되는 곳은 내 컴퓨터 안입니다. 구글 서버는 orders.csv 를 본 적이 없습니다. 그러니까 구분해서 봐야 할 기준은 두 가지입니다.

갈라지는 것
어디서 도는가 내 컴퓨터(내 코드 + SDK) ↔ 구글 서버
누가 쓴 코드인가 내가 쓴 코드 ↔ 구글이 만든 SDK

"실행은 우리 쪽"이라고 할 때의 "우리 쪽"은 첫 번째 축입니다.

②③④는 눈에 잘 안 띕니다. SDK가 대신해 주기 때문입니다.

내 코드 ──▶ SDK ──▶ 구글 서버구글 서버 ──▶ SDKSDK ──▶ 내 함수 실행 ──▶ 결과 문자열SDK ──▶ 구글 서버구글 서버 ──▶ SDK ──▶ 내 코드 (최종 답변)AFC를 켜면이 세 단계를SDK가 대신합니다disable=True로 끄면 ②에서 멈추고, 호출 요청(function_calls)이 내 코드로 그대로 돌아옵니다.
AFC 를 켜면 가운데 세 단계를 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) 로 끄면 모델의 요청서를 직접 볼 수 있습니다.
  • 실행 지점이 우리 쪽이라서 — 루프를 짤 수 있고, 안전장치를 걸 수 있고, 도구를 나눠 줄 수 있습니다.
← 이전 절01. 두뇌에 손발 달기 도구란 그냥 파이썬 함수다음 절 →03. 독스트링과 타입힌트가 모델에게 주는 설명서