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

따라하기 — 모델이 무엇을 부르려 했는지 들여다보기

한 줄 요약

자동 실행을 꺼 놓고 호출하면, 모델의 요청서 그 자체를 볼 수 있습니다. 2절에서 말로만 설명한 경계선을 눈으로 확인하는 절입니다.


1. 코드 — peek()

아래는 code/ch03_order_agent.py 의 일부입니다 — 위에서 이미 붙여 넣었으니 다시 넣지 마세요.

def peek(question: str):
    """모델이 '무엇을 부르려 했는지'만 들여다본다(실행은 하지 않는다)."""
    resp = client.models.generate_content(
        model=GEMINI_MODEL,
        contents=question,
        config=types.GenerateContentConfig(
            tools=[get_order_status],
            system_instruction=SYSTEM,
            temperature=0,
            # 자동 실행을 꺼서, 모델의 '결정'만 확인한다
            automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
        ),
    )
    return resp.function_calls or []

ask()두 곳이 다릅니다.

ask() peek()
자동 실행 켜짐(기본) disable=True 로 끔
돌려주는 것 resp.text (최종 답변) resp.function_calls (요청서)

2. 실행 — [2] 부분

==============================================================
[2] 모델은 무엇을 부르려 하는가 (아직 실행 전)
   부르려는 함수: get_order_status
   채워 넣은 값 : {'order_id': 'O001203'}
  → 모델은 '이 함수를 이 값으로 불러 달라'고 요청만 합니다. 실행은 우리 쪽 몫입니다.

이것이 2절에서 말한 요청서의 실제 모습입니다.

  • get_order_status — 함수 이름
  • {'order_id': 'O001203'} — 채워 넣은 인자

orders.csv 는 아직 읽히지 않았습니다. 아무것도 실행되지 않았습니다. 모델은 여기까지만 합니다.


3. 이 두 줄을 만들어 낸 것

모델이 이 요청서를 만들 수 있었던 이유를 되짚어 봅시다.

"주문 O001203 상태 알려주세요""주문 … 상태"↳ 독스트링의 설명과 맞는다이 도구를 쓰자"O001203"↳ 독스트링의 예시 형식과 맞는다order_id 자리에 넣자모델은 '어떤 도구를 호출할지'와 '어떤 인자를 전달할지'를 따로 고릅니다. 둘 다 근거는 독스트링입니다.
모델은 '무엇을 부를지'와 '무엇을 넣을지'를 따로 고른다 — 근거는 둘 다 독스트링이다

모델은 독스트링 한 줄을 근거로 두 가지 판단을 모두 내렸습니다. 3절에서 "독스트링이 곧 사용 설명서"라고 한 것이 이런 뜻입니다.


4. 여러 개를 한 번에 요청하기도 합니다

peek() 가 리스트를 돌려주는 이유가 있습니다. 모델이 여러 도구를 한꺼번에 요청할 수 있기 때문입니다.

[2] 의 질문을 이렇게 바꿔 보세요.

직접 고쳐 보기agentic_ai/code/ch03_order_agent.py 를 열어 아래처럼 바꾸거나 덧붙인 뒤 다시 실행하세요.

for fc in peek("주문 O001203 과 O000533 둘 다 상태 알려주세요"):
    print(f"   부르려는 함수: {fc.name}")
    print(f"   채워 넣은 값 : {dict(fc.args)}")
   부르려는 함수: get_order_status
   채워 넣은 값 : {'order_id': 'O001203'}
   부르려는 함수: get_order_status
   채워 넣은 값 : {'order_id': 'O000533'}

요청이 두 개 나왔습니다. 4장에서 루프를 직접 짤 때, 이 목록을 전부 돌면서 실행하게 됩니다.

아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.

for fc in resp.function_calls:          # 4장에서 쓸 형태
    result = TOOLS[fc.name](**dict(fc.args))

**dict(fc.args) 는 딕셔너리를 인자로 펼쳐 넣는 문법입니다. {'order_id': 'O001203'}get_order_status(order_id='O001203') 이 됩니다.


5. 도구를 안 부를 때는 빈 목록

아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.

for fc in peek("오늘 날씨 어때요?"):
    ...

아무것도 출력되지 않습니다. resp.function_calls 가 비어 있기 때문입니다.

아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.

return resp.function_calls or []      # None 일 수도 있어서 or [] 로 감쌌다

"부를 도구가 없다 = 이제 답할 수 있다" — 이 판정이 4장 루프의 종료 조건이 됩니다. 미리 봐 두세요.

아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.

# 4장에서 쓸 형태
if not resp.function_calls:
    return resp.text.strip()      # 도구 요청이 없으면 최종 답변이다

6. 언제 자동을 끄고 언제 켜나

상황 자동 함수 호출 이유
도구 하나, 단순 조회 (3장 ask) 짧고 편하다
도구 여럿, 순서가 궁금 (4장) 어떤 순서로 쓰는지 봐야 한다
실행 전 검사가 필요 위험한 요청을 걸러야 한다
학습·디버깅 안이 보여야 한다

5장부터는 이 선택 자체가 사라집니다. LangChain을 쓰면 루프를 라이브러리가 돌리기 때문입니다. 그래서 4장까지 손으로 해 보는 것입니다. 5장에서 create_agent 한 줄을 쓸 때, 그 안에서 무슨 일이 일어나는지 이미 봤기 때문에 안심하고 쓸 수 있습니다.


스스로 해 보기

  1. 독스트링에서 예: 'O001203' 를 지우고 peek("주문 O001203 상태 알려주세요") 를 실행해 보세요. 여전히 인자를 정확히 뽑나요?
  2. 독스트링을 """상태 조회""" 로 확 줄이고 실행해 보세요. 어떻게 되나요? 왜 그런지 한 문장으로 적어 보세요.
  3. peek("어제 주문한 거 어떻게 됐어요?") — 주문번호가 없는 질문입니다. 모델은 무엇을 요청할까요? 예측하고 확인하세요.

핵심 정리

  • automatic_function_calling=...(disable=True)자동 실행을 끄면 모델의 요청서를 직접 볼 수 있습니다.
  • 요청서는 resp.function_calls 에 들어 있고, fc.namefc.args 로 꺼냅니다.
  • 이 시점에는 아무것도 실행되지 않았습니다. 모델은 여기까지만 합니다.
  • 요청서는 여러 개일 수 있습니다. 4장에서 전부 돌면서 실행하게 됩니다.
  • function_calls 가 비어 있으면 "이제 답할 수 있다" 는 뜻입니다. 이것이 4장 루프의 종료 조건입니다.
  • 도구가 하나면 자동에 맡기고, 여럿이거나 검사가 필요하면 끕니다.
← 이전 절05. 따라하기 도구를 붙여 자동으로 호출시키기다음 절 →07. 없는 주문번호가 들어왔을 때