Agentic AI/Google ADK
6. G-ADK Tool 활용[4.1] - Declarative OpenAPI Spec Tool (OpenAPI Spec 명세서 자동 바인딩)
아톨
2026. 10. 10. 14:24
개발자가 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. 작동 흐름 요약
- 사용자가 Dev UI에 "서울 내일 날씨 어때?" 입력
- openapi_agent가 프롬프트를 분석하고, weather_tool 내부의 OpenAPI Spec을 참조
- LLM이 /forecast 엔드포인트를 사용해야 함을 스스로 판단
- 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 요청을 전송
- 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

반응형