본문 바로가기
Agentic AI/Google ADK

6. G-ADK Tool 활용[4.1] - Declarative OpenAPI Spec Tool (OpenAPI Spec 명세서 자동 바인딩)

by 아톨 2026. 10. 10.

개발자가 API 호출 함수를 일일이 파이썬 코드로 작성하지 않고, OpenAPI/Swagger JSON 명세서 파일을 그대로 읽어 들여 툴 세트로 자동 변환 및 등록하는 고급 방식입니다. 이 코드는 OpenAPI 규격문서(JSON)를 파싱하여, 개발자가 일일이 Python 함수를 만들지 않고도 OpenWeatherMap API를 LLM 도구(Tool)로 자동으로 변환 및 바인딩하는 최신 ADK 툴셋 구현 코드입니다. TypeError: the JSON object must be str... not NoneType 에러를 방지하기 위한 안전장치(read_file_as_string)와 API Key 인증 헬퍼(auth_helpers)까지 깔끔하게 포함되어 있습니다.

I. 주요 동작 방식

OpenWeatherMap의 API 사양(Specification)이 적힌 OpenWeatherMap_OpenAPI_명세서.json 파일을 읽은 뒤, OpenAPIToolset 모듈을 이용해 규격에 맞춰 인증 정보와 엔드포인트들을 한 번에 도구화합니다. 에이전트는 명세서 내용을 바탕으로 어떤 엔드포인트를 호출해야 할지 스스로 결정합니다.

II. 코드 라인별 주요 사항

  • auth_helpers.token_to_scheme_credential(...)
    • OpenAPI 명세서 기반 호출 시 필요한 인증(Authentication) 방식을 정의합니다.
    • token_type="apikey", location="query", name="appid"로 지정하여, API 호출 시 URL 쿼리 파라미터 형태로 appid={YOUR_API_KEY}가 자동 결합되도록 구성합니다.
  • OpenAPIToolset(...)
    • OpenAPI JSON 문자열(spec_str)과 인증 객체(auth_scheme, auth_credential)를 넘겨받아 OpenAPI 명세서에 정의된 모든 엔드포인트(/weather, /forecast 등)를 LLM이 즉시 사용할 수 있는 Tool 집합으로 자동 변환합니다.
  • openapi_agent 및 tools=[weather_tool]
    • OpenAPIToolset으로 만들어진 객체를 tools 배열에 그대로 전달합니다. Agent는 내부 구현 방식(파이썬 함수인지, OpenAPI Spec인지)에 상관없이 사용 가능한 도구 목록으로 받아들입니다.
  • instruction (상황별 엔드포인트 선택 지침)
    • 명세서 안에 여러 기능이 포함되어 있으므로, 질문의 맥락에 맞게 엔드포인트를 선택하도록 가이드합니다.
      • '내일 날씨' 질문 → /forecast 엔드포인트 선택
      • '현재 날씨' 질문 → /weather 엔드포인트 선택
      • 파라미터 강제: units 파라미터를 'metric'으로 설정하여 화씨가 아닌 섭씨 온도로 응답받도록 보장합니다.

III. 코드 라인별 상세

1. 모듈 임포트 (Import)

import os
import json
from typing import Optional
from dotenv import load_dotenv

# ADK 필수 모듈 Import
from google.adk.agents import Agent
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
from google.adk.tools.openapi_tool.auth import auth_helpers
  • import os, json: 파일 경로 탐색, 환경 변수 접근, JSON 처리를 위한 Python 표준 라이브러리입니다.
  • from typing import Optional: 변수나 함수의 반환값이 특정 타입일 수도 있고 None일 수도 있음을 명시하는 타입 힌트입니다.
  • load_dotenv: .env 파일에 저장된 API 키와 설정값을 환경 변수로 로드합니다.
  • OpenAPIToolset: ADK 핵심 클래스로, OpenAPI Spec(JSON/YAML) 문자열을 입력받아 LLM이 호출할 수 있는 도구(Tool) 세트로 자동 변환해 줍니다.
  • auth_helpers: OpenAPI 호출 시 필요한 인증 방식(API Key, OAuth2, Bearer Token 등)을 ADK가 이해할 수 있는 객체 규격으로 생성해 주는 인증 도구 모듈입니다.

2. 경로 설정 및 파일 안전하게 읽기 (방어적 코드)

# 2. 파일 경로 설정
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
JSON_FILE_PATH = os.path.join(CURRENT_DIR, "OpenWeatherMap_OpenAPI_명세서.json")

# 3. 파일을 문자열(String)로 읽는 함수
def read_file_as_string(file_path: str) -> Optional[str]:
    if not os.path.exists(file_path):
        print(f" 파일을 찾을 수 없습니다: {file_path}")
        return None
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            return f.read()
    except Exception as e:
        print(f" 파일 읽기 오류: {e}")
        return None
  • CURRENT_DIR & JSON_FILE_PATH: 현재 파이썬 파일이 위치한 절대 경로를 기준으로 OpenWeatherMap_OpenAPI_명세서.json 파일의 정확한 경로를 조합합니다. 실행 위치에 따라 파일 경로를 못 찾는 오류를 예방합니다.
  • read_file_as_string():
    • 이전 트레이스백에서 보셨던 NoneType 에러를 막아주는 방어 코드입니다.
    • os.path.exists()로 파일 존재 여부를 먼저 검사합니다. 파일이 없거나 읽기 실패 시 None을 반환하고 터미널에 에러 메시지를 출력합니다.

3. API Key 인증 객체 생성 (auth_helpers)

# [Step 1] 파일 내용을 문자열로 가져옴
openapi_spec_string = read_file_as_string(JSON_FILE_PATH)

# --- API Key 설정 ---
API_KEY = os.getenv("OPEN_WEATHER_API_KEY", "")

auth_scheme, auth_credential = auth_helpers.token_to_scheme_credential(
    token_type="apikey",        # "apikey" 또는 "oauth2Token"
    location="query",          # "header", "query", "cookie" 중 하나 (API 명세에 따름)
    name="appid",               # 실제 API 헤더 이름 (예: "Authorization", "api-key" 등)
    credential_value=API_KEY    # 실제 키 값
)
  • API_KEY = os.getenv(...): .env 파일에서 OPEN_WEATHER_API_KEY 값을 읽어옵니다.
  • auth_helpers.token_to_scheme_credential(...): OpenWeatherMap API는 HTTP 요청 URL 뒤에 ?appid=YOUR_KEY 형태로 쿼리 파라미터 인증을 사용합니다.
    • token_type="apikey": 인증 방식이 API Key임을 명시합니다.
    • location="query": 키를 HTTP URL의 쿼리 스트링(?appid=...)으로 전달하겠다고 설정합니다.
    • name="appid": OpenWeatherMap이 요구하는 쿼리 파라미터 이름인 appid를 지정합니다.
    • credential_value=API_KEY: 실제 발급받은 API Key 문자열을 주입합니다.
    • 결과: auth_scheme과 auth_credential이라는 2개의 인증 객체가 반환됩니다.

4. OpenAPIToolset 객체 생성

weather_tool = OpenAPIToolset(
    spec_str=openapi_spec_string,
    spec_str_type="json",
    auth_scheme=auth_scheme,          # 객체 전달
    auth_credential=auth_credential   # 객체 전달 (문자열 아님!)
)
  • spec_str=openapi_spec_string: JSON 파일에서 읽어온 OpenAPI 명세서 문자열입니다. (이 값이 None이면 아까 보신 TypeError가 발생합니다.)
  • spec_str_type="json": 명세서 포맷이 JSON임을 명시합니다 (yaml도 지원).
  • auth_scheme, auth_credential: 앞에서 만든 인증 객체들을 전달하여, ADK가 OpenWeatherMap REST API를 직접 호출할 때 자동으로 appid 쿼리 파라미터를 붙여서 전송하도록 바인딩합니다.

5. 에이전트 등록 및 진입점 설정 (root_agent)

openapi_agent = Agent(
    name="openapi_weather_agent",
    model=os.getenv("GOOGLE_MODEL", "gemini-2.5-flash"), # 실시간 운영용 Gemini 모델
    description="OpenWeather API Spec으로 날씨 정보를 조회하는 에이전트",

    # [핵심] 파이썬 함수 대신, OpenAPI Tool 객체를 통째로 전달
    tools=[weather_tool],

    instruction="""
    당신은 기상 데이터 전문가입니다.
    제공된 OpenAPI 도구를 사용하여 전 세계 날씨 정보를 조회하세요.
    사용자가 '내일 날씨'를 물으면 /forecast 엔드포인트를,
    '현재 날씨'를 물으면 /weather 엔드포인트를 적절히 선택하여 사용하세요.
    날씨 API를 호출할 때는 반드시 'units' 파라미터를 'metric' 으로 설정해서 섭씨 온도로 답해줘.
    """
)

# adk web 명령어가 실행할 진입점
root_agent = openapi_agent
  • tools=[weather_tool]: 개발자가 def get_weather(...) 같은 함수를 일일이 만들지 않고, OpenAPI 명세서로 만든 weather_tool 객체 하나만 등록하면, OpenAPI JSON 문서 안에 정의된 /weather 및 /forecast 엔드포인트가 각각 독립된 도구로 에이전트에 자동 등록됩니다.
  • instruction 내 가이드라인:
    • LLM이 OpenAPI Spec의 description과 파라미터를 참고하여, 사용자의 의도가 '현재'인지 '내일/미래'인지에 따라 /weather 또는 /forecast 도구를 자율적으로 선택해 호출하도록 유도합니다.
    • 섭씨 온도 표시를 위해 units='metric' 파라미터 적용을 강제했습니다.
  • root_agent = openapi_agent: ADK Web Dev UI 서버가 실행될 때 메인 진입점으로 사용할 에이전트를 지정합니다.

6. 작동 흐름 요약

  1. 사용자가 Dev UI에 "서울 내일 날씨 어때?" 입력
  2. openapi_agent가 프롬프트를 분석하고, weather_tool 내부의 OpenAPI Spec을 참조
  3. LLM이 /forecast 엔드포인트를 사용해야 함을 스스로 판단
  4. ADK 엔진이 auth_credential(appid)을 부착하여 [https://api.openweathermap.org/data/2.5/forecast?lat=...&lon=...&units=metric&appid=](https://api.openweathermap.org/data/2.5/forecast?lat=...&lon=...&units=metric&appid=)...에 실제 HTTP GET 요청을 전송
  5. API가 반환한 날씨 JSON 데이터를 LLM이 전달받아 사용자에게 자연스러운 한글 문장으로 변환하여 출력
 
    import os
    import json
    from typing import Optional
    from dotenv import load_dotenv

    # ADK 필수 모듈 Import
    from google.adk import Agent
    from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
    from google.adk.tools.openapi_tool.auth import auth_helpers

    # 1. 환경 설정
    load_dotenv(override=True)

    # 2. 파일 경로 설정
    CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
    JSON_FILE_PATH = os.path.join(CURRENT_DIR, "OpenWeatherMap_OpenAPI_명세서.json")

    # 3. 파일을 문자열(String)로 읽는 함수
    def read_file_as_string(file_path: str) -> Optional[str]:
        if not os.path.exists(file_path):
            print(f" 파일을 찾을 수 없습니다: {file_path}")
            return None
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            print(f" 파일 읽기 오류: {e}")
            return None

    # 4. OpenAPIToolset 생성
    # [Step 1] 파일 내용을 문자열로 가져옴
    openapi_spec_string = read_file_as_string(JSON_FILE_PATH)

    # --- API Key 설정 ---
    API_KEY = os.getenv("OPEN_WEATHER_API_KEY", "")

    auth_scheme, auth_credential = auth_helpers.token_to_scheme_credential(
        token_type="apikey",        # "apikey" 또는 "oauth2Token"
        location="query",          # "header", "query", "cookie" 중 하나 (API 명세에 따름)
        name="appid",           # 실제 API 헤더 이름 (예: "Authorization", "api-key" 등)
        credential_value=API_KEY    # 실제 키 값
    )

    weather_tool = OpenAPIToolset(
        spec_str=openapi_spec_string,
        spec_str_type="json",
        auth_scheme=auth_scheme,          # 객체 전달
        auth_credential=auth_credential   # 객체 전달 (문자열 아님!)
    )
    # 5. Agent에 등록
    # Agent는 이것이 OpenAPI Tool인지 Custom Tool인지 구분하지 않습니다.
    # 그저 '사용 가능한 도구'로 인식하고 활용합니다.
    openapi_agent = Agent(
        name="openapi_weather_agent",
        model=os.getenv("GOOGLE_MODEL", "gemini-3.1-flash-lite"),  # OpenAPI Spec은 내용이 방대하므로 Pro 모델 권장
        description="OpenWeather API Spec으로 날씨 정보를 조회하는 에이전트",

        # [핵심] 함수가 아닌, OpenAPI Tool 객체를 통째로 전달
        tools=[weather_tool],

        instruction="""
        당신은 기상 데이터 전문가입니다.
        제공된 OpenAPI 도구를 사용하여 전 세계 날씨 정보를 조회하세요.
        사용자가 '내일 날씨'를 물으면 /forecast 엔드포인트를,
        '현재 날씨'를 물으면 /weather 엔드포인트를 적절히 선택하여 사용하세요.
        날씨 API를 호출할 때는 반드시 'units' 파라미터를 'metric' 으로 설정해서 섭씨 온도로 답해줘.
        """
    )
    # adk web 명령어가 실행할 진입점
    root_agent = openapi_agent
 

 

 

반응형