AI Sparkup

최신 AI 쉽게 깊게 따라잡기⚡

Gemma 튜토리얼 – 오류 복구 기능을 갖춘 멀티툴 AI 에이전트 구축하기

단순한 툴 호출 데모에서 진짜 에이전트로 넘어가는 결정적 차이는 오류 처리 방식이다. 이 튜토리얼은 Gemma 4와 Ollama를 사용해 도구 실패를 모델이 스스로 복구하는 반복 에이전트 루프를 Python으로 구현한다.

전제 조건

핵심 아이디어: 오류를 메시지로 변환

기존 방식은 오류가 나면 스크립트가 멈추거나 try/except로 에러 메시지를 출력하고 끝낸다. 이 튜토리얼은 다른 접근을 취한다:

오류 발생
    → 오류를 모델이 읽을 수 있는 메시지로 변환
    → 컨텍스트에 추가해 모델에 전달
    → 모델이 재시도·우회·설명 중 선택

이 패턴이 “툴 호출 데모”를 “에이전트”로 만드는 핵심이다.

에이전트가 처리하는 오류 유형

  • 존재하지 않는 함수명 호출 (할루시네이션)
  • 잘못된 인자 타입 전달 (문자열 vs. 숫자)
  • 알 수 없는 데이터 요청 (예: 존재하지 않는 도시)
  • 업스트림 API 타임아웃
  • 필수 인자 누락

구현

1단계: 도구 정의

import ollama
import json

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "도시의 현재 날씨 반환",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "도시명"}
                },
                "required": ["city"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "수식 계산",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {"type": "string", "description": "계산할 수식"}
                },
                "required": ["expression"],
            },
        },
    },
]

2단계: 툴 실행 래퍼 (오류 처리 포함)

TOOL_REGISTRY = {
    "get_weather": get_weather,
    "calculate": calculate,
}

def execute_tool(tool_call):
    name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)

    if name not in TOOL_REGISTRY:
        raise ValueError(f"Unknown tool: {name}")

    return TOOL_REGISTRY[name](**args)

3단계: 반복 에이전트 루프

MAX_ITERATIONS = 10

def run_agent(user_message):
    messages = [{"role": "user", "content": user_message}]

    for i in range(MAX_ITERATIONS):
        response = ollama.chat(
            model="gemma4",
            messages=messages,
            tools=TOOLS,
        )
        messages.append(response.message)

        if not response.message.tool_calls:
            # 툴 호출 없음 = 최종 응답
            return response.message.content

        for tool_call in response.message.tool_calls:
            try:
                result = execute_tool(tool_call)
                content = str(result)
            except Exception as e:
                # 오류도 일반 툴 결과와 동일한 형식으로 전달
                content = f"Error executing {tool_call.function.name}: {str(e)}"

            messages.append({
                "role": "tool",
                "content": content,
            })

    return "최대 반복 횟수 초과"

핵심 포인트: 오류도 일반 툴 결과와 동일한 형식으로 컨텍스트에 추가한다. 모델은 오류 메시지를 읽고 다음 행동(재시도·다른 툴 사용·설명)을 스스로 결정한다.

4단계: 실행

result = run_agent("서울 날씨 알려줘, 그리고 (15 * 4) + 7 계산해줘")
print(result)

동작 원리

사용자: "서울 날씨 알려줘"
  → 에이전트: get_weather("서울") 호출
  → 오류: "Seoul not found"
  → 에이전트: 오류 메시지를 컨텍스트에 추가
  → 에이전트: get_weather("Seoul") 재시도 또는 대안 설명
  → 최종 응답

전체 코드

GitHub: mmmayo13/gemma_4_tool_calling

마무리

이 패턴의 핵심은 단순하다: 오류도 데이터다. 오류를 숨기거나 포기하는 대신, 모델의 컨텍스트 창에 넣어 주면 모델이 스스로 전략을 바꾼다. 반복 횟수 상한(MAX_ITERATIONS)은 무한 루프를 방지하는 최소한의 안전망이다.

참고 자료



AI Sparkup 구독하기

최신 게시물 요약과 더 심층적인 정보를 이메일로 받아 보세요! (무료)