← Hub ← 07 09 →
08
Chapter 08 · Tool Calling

생각하고,
행동하고,
관찰한다.

Chapter 7의 JSON 패턴을 API가 직접 지원한다.
tools 파라미터로 스펙 전달 → LLM은 별도 필드로 호출 결정.
그리고 그 반복이 ReAct.

·
정의

실행 결과를 보고
다음을 정한다. 반복.

사용자 요청 LLM 툴 선택 툴 실행 결과 보고 → 다시 결정
·
JSON 방식 vs Tool Calling

파싱 위험을
API가 책임진다.

구분
Chapter 7 JSON
Chapter 8 Tool Calling
툴 목록 전달
시스템 프롬프트 텍스트
API 파라미터로
툴 선택 결과
JSON 텍스트
별도 필드 (tool_calls)
파싱
json.loads 실패 가능
불필요, 안정적
결과 저장
user/assistant role
role=tool + tool_call_id
병렬 호출
한 호출에 한 툴
여러 툴 동시 가능
·
Code · 툴 스펙

툴 스펙은
API가 읽는 계약서.

tools = [
    {
        "type": "function",
        "function": {
            "name": "execute_sql",
            "description": "DB에서 이력서 또는 채용공고 조회.
                            ID/부서명 등 조건이 명확할 때 사용",
            "parameters": {
                "type": "object",
                "properties": {
                    "sql_query": {"type": "string", "description": "실행할 SQL"},
                },
                "required": ["sql_query"],
            },
        },
    },
    # ... retrieve_from_rag, get_confluence, save_excel, send_email
]

LLM은 description 만 보고 어떤 툴을 부를지 판단한다. 모호하면 잘못 부른다.
— 좋은 description이 곧 좋은 디스패치.

·
ReAct Pattern

💭 생각 →
⚡ 행동 →
👀 관찰. 반복.

💭 Reasoning

생각

LLM의 내부 추론. message.reasoning으로 노출됨. reasoning 모델에서만 의미 있음.

⚡ Acting

행동

툴 호출 결정. message.tool_calls에 함수 이름과 파라미터가 담겨 있다.

👀 Observing

관찰

실행 결과를 role=tool로 history에. tool_call_id로 어떤 호출의 응답인지 매핑.

·
Code · 피드백 루프

루프 하나가
에이전트다.

def handle_request(user_prompt, history, tools, max_steps=10):
    history.append({"role": "user", "content": user_prompt})

    for step in range(max_steps):
        message = generate_response(history, tools)

        # 종료: 툴 호출 없으면 최종 답변
        if not message.tool_calls:
            history.append({"role": "assistant", "content": message.content})
            return history

        history.append({"role": "assistant", "content": message.content,
                        "tool_calls": [tc.model_dump() for tc in message.tool_calls]})

        # 툴 실행 + 결과 저장
        for tc in message.tool_calls:
            params = json.loads(tc.function.arguments)
            try:
                result = TOOLS[tc.function.name](**params)
            except Exception as e:
                result = f"툴 실행 실패: {e}"
            history.append({"role": "tool",
                            "tool_call_id": tc.id,
                            "content": json.dumps(result)})
·
Fallback in action

실패해도 멈추지 않는다.
다음 방법을 찾는다.

SYSTEM평가기준표는 1) 위키 → 2) 엑셀 순으로 시도. 실패하면 다음 소스로.
USER이력서 평가해줘 (기준은 위키에서 찾아)
LLM💭 위키부터 시도
get_confluence_page_content("00000000")
TOOL"툴 실행 실패: page not found"
LLM💭 위키 실패. 엑셀로 폴백.
read_excel()
TOOL{"content": "직무적합성 40% / 자격요건 30% / ..."}
LLM👀 기준 확보. 평가 시작.
Chapter 08 · 마무리

반복이 작동한다.
다음은 먼저 계획.

피드백 루프는 매 스텝 결정. 복잡한 목표일수록 LLM 호출이 늘어난다.
다음 챕터에선 '시작 전에 한 번 계획'.

Next · Chapter 09
Planning