도구(Tools) 심화
도구(Tools)
여기서는 심화 도구 사용법을 다뤄 볼게요.
[!TIP] 에이전트를 처음 만들어 보는 분이라면 먼저 에이전트 소개와 smolagents 둘러보기를 읽고 오시는 게 좋아요.
도구란 무엇이고, 어떻게 만들까요?
도구는 기본적으로 에이전트 시스템에서 LLM이 사용할 수 있는 함수예요.
하지만 LLM이 도구를 사용하려면 API를 줘야 해요. 이름, 도구 설명, 입력 타입과 설명, 출력 타입이 필요하죠.
그래서 도구는 단순한 함수일 수 없어요. 클래스여야 해요.
핵심적으로 도구는 LLM이 어떻게 사용해야 하는지 이해하도록 돕는 메타데이터로 함수를 감싼 클래스예요.
이렇게 생겼어요:
from smolagents import Tool
class HFModelDownloadsTool(Tool):
name = "model_download_counter"
description = """
This is a tool that returns the most downloaded model of a given task on the Hugging Face Hub.
It returns the name of the checkpoint."""
inputs = {
"task": {
"type": "string",
"description": "the task category (such as text-classification, depth-estimation, etc)",
}
}
output_type = "string"
def forward(self, task: str):
from huggingface_hub import list_models
model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
return model.id
model_downloads_tool = HFModelDownloadsTool()
커스텀 도구는 Tool을 서브클래싱해 유용한 메서드를 상속받아요. 자식 클래스는 다음도 정의해요:
- 속성
name: 도구 자체의 이름에 해당해요. 이름은 보통 도구가 무엇을 하는지 설명해요. 코드가 한 작업에 대해 가장 많이 다운로드된 모델을 반환하니까model_download_counter라고 이름 붙여 볼게요. - 속성
description: 에이전트의 시스템 프롬프트를 채우는 데 사용돼요. - 속성
inputs: 키가"type"과"description"인 딕셔너리예요. 파이썬 인터프리터가 입력에 대해 현명한 선택을 하도록 돕는 정보를 담아요. - 속성
output_type: 출력 타입을 지정해요.inputs와output_type의 타입 모두 Pydantic 형식이어야 하고,["string", "boolean","integer", "number", "image", "audio", "array", "object", "any", "null"]중 하나가 될 수 있어요. - 메서드
forward: 실행될 인퍼런스 코드를 담아요.
이것만 있으면 에이전트에서 사용할 수 있어요!
도구를 만드는 또 다른 방법도 있어요. 둘러보기에서 우리는 @tool 데코레이터로 도구를 만들었죠. tool() 데코레이터는 간단한 도구를 정의하는 권장 방법이지만, 때로는 이것보다 더 필요할 수 있어요. 명확성을 위해 클래스에서 여러 메서드를 쓰거나, 추가 클래스 속성을 쓰는 경우죠.
이런 경우에는 위에서 설명한 대로 Tool을 서브클래싱해 도구를 만들면 돼요.
도구를 허브에 공유하기
도구에서 push_to_hub()를 호출해 커스텀 도구를 허브에 Space 저장소로 공유할 수 있어요. 허브에 저장소를 만들었고 읽기 권한이 있는 토큰을 쓰고 있는지 확인하세요.
model_downloads_tool.push_to_hub("{your_username}/hf-model-downloads", token="<YOUR_HUGGINGFACEHUB_API_TOKEN>")
허브로 푸시가 동작하려면 도구가 몇 가지 규칙을 지켜야 해요:
- 모든 메서드는 자급자족적이어야 해요. 예를 들어 자신의 인자에서 오는 변수만 사용하세요.
- 위 내용에 이어서, 모든 임포트는 도구의 함수 안에서 직접 정의해야 해요. 그렇지 않으면 커스텀 도구로 save()나 push_to_hub()를 호출할 때 오류가 나요.
__init__메서드를 서브클래싱한다면,self말고 다른 인자를 주면 안 돼요. 특정 도구 인스턴스의 초기화 중 설정된 인자는 추적하기 어려워서 허브로 제대로 공유되지 못하기 때문이에요. 어차피 구체적인 클래스를 만든다는 발상 자체가 하드코딩하고 싶은 모든 것에 클래스 속성을 이미 설정할 수 있다는 뜻이에요(class YourTool(Tool):줄 바로 아래에your_variable=(...)을 설정하면 됩니다). 물론 코드 어디서든self.your_variable에 값을 할당해 클래스 속성을 만들 수도 있어요.
도구가 허브로 푸시되면 시각화할 수 있어요. 여기가 제가 푸시한 model_downloads_tool이에요. 멋진 gradio 인터페이스가 있어요.
도구 파일을 들여다보면 도구의 모든 로직이 tool.py 아래에 있는 걸 발견할 수 있어요. 여기서 다른 사람이 공유한 도구를 검사할 수 있어요.
그런 다음 그 도구를 load_tool()로 불러오거나 from_hub()로 만들고 에이전트의 tools 파라미터에 넘기면 돼요.
도구를 실행한다는 건 커스텀 코드를 실행한다는 뜻이니 저장소를 신뢰할 수 있는지 확인해야 해요. 그래서 허브에서 도구를 불러오려면 trust_remote_code=True를 넘기도록 요구해요.
from smolagents import load_tool, CodeAgent
model_download_tool = load_tool(
"{your_username}/hf-model-downloads",
trust_remote_code=True
)
MCP 서버에서 도구 사용하기
우리의 MCPClient는 MCP 서버에서 도구를 불러오고, 연결과 도구 관리를 완전히 제어할 수 있게 해줘요:
stdio 기반 MCP 서버의 경우:
from smolagents import MCPClient, CodeAgent
from mcp import StdioServerParameters
import os
server_parameters = StdioServerParameters(
command="uvx", # Using uvx ensures dependencies are available
args=["--quiet", "[email protected]"],
env={"UV_PYTHON": "3.12", **os.environ},
)
with MCPClient(server_parameters) as tools:
agent = CodeAgent(tools=tools, model=model, add_base_tools=True)
agent.run("Please find the latest research on COVID-19 treatment.")
Streamable HTTP 기반 MCP 서버의 경우:
from smolagents import MCPClient, CodeAgent
with MCPClient({"url": "http://127.0.0.1:8000/mcp", "transport": "streamable-http"}) as tools:
agent = CodeAgent(tools=tools, model=model, add_base_tools=True)
agent.run("Please find a remedy for hangover.")
try...finally 패턴으로 연결 생명주기를 수동으로 관리할 수도 있어요:
from smolagents import MCPClient, CodeAgent
from mcp import StdioServerParameters
import os
# Initialize server parameters
server_parameters = StdioServerParameters(
command="uvx",
args=["--quiet", "[email protected]"],
env={"UV_PYTHON": "3.12", **os.environ},
)
# Manually manage the connection
try:
mcp_client = MCPClient(server_parameters)
tools = mcp_client.get_tools()
# Use the tools with your agent
agent = CodeAgent(tools=tools, model=model, add_base_tools=True)
result = agent.run("What are the recent therapeutic approaches for Alzheimer's disease?")
# Process the result as needed
print(f"Agent response: {result}")
finally:
# Always ensure the connection is properly closed
mcp_client.disconnect()
서버 파라미터 목록을 넘겨 여러 MCP 서버에 한 번에 연결할 수도 있어요:
from smolagents import MCPClient, CodeAgent
from mcp import StdioServerParameters
import os
server_params1 = StdioServerParameters(
command="uvx",
args=["--quiet", "[email protected]"],
env={"UV_PYTHON": "3.12", **os.environ},
)
server_params2 = {"url": "http://127.0.0.1:8000/sse"}
with MCPClient([server_params1, server_params2]) as tools:
agent = CodeAgent(tools=tools, model=model, add_base_tools=True)
agent.run("Please analyze the latest research and suggest remedies for headaches.")
[!WARNING] 보안 경고: 특히 프로덕션 환경에서는 연결하기 전에 MCP 서버의 출처와 무결성을 항상 검증하세요. MCP 서버 사용에는 보안 위험이 따릅니다:
- 신뢰가 핵심입니다: 신뢰할 수 있는 출처의 MCP 서버만 사용하세요. 악성 서버는 여러분의 머신에서 유해한 코드를 실행할 수 있어요.
- stdio 기반 MCP 서버는 항상 여러분의 머신에서 코드를 실행해요(그게 의도된 기능이에요).
- Streamable HTTP 기반 MCP 서버: 원격 MCP 서버는 여러분의 머신에서 코드를 실행하지 않지만, 그래도 주의해서 진행하세요.
구조화 출력과 출력 스키마 지원
최신 MCP 사양(2025-06-18+)은 outputSchema를 지원해, 도구가 정의된 스키마를 가진 구조화 데이터를 반환할 수 있게 해줘요. smolagents는 이 구조화 출력 능력을 활용해서, 에이전트가 복잡한 데이터 구조, JSON 객체, 기타 구조화 형식을 반환하는 도구와 작업할 수 있게 해요. 이 기능 덕분에 에이전트의 LLM은 도구를 호출하기 전에 도구 출력의 구조를 "볼" 수 있어서, 더 지능적이고 컨텍스트를 고려한 상호작용이 가능해져요.
구조화 출력을 활성화하려면 MCPClient를 초기화할 때 structured_output=True를 전달하세요:
from smolagents import MCPClient, CodeAgent
# Enable structured output support
with MCPClient(server_parameters, structured_output=True) as tools:
agent = CodeAgent(tools=tools, model=model, add_base_tools=True)
agent.run("Get weather information for Paris")
structured_output=True일 때 다음 기능이 활성화돼요:
- 출력 스키마 지원: 도구가 출력에 대한 JSON 스키마를 정의할 수 있어요
- 구조화 콘텐츠 처리: MCP 응답의
structuredContent지원 - JSON 파싱: 도구 응답에서 구조화 데이터를 자동으로 파싱해요
구조화 출력을 쓰는 날씨 MCP 서버 예시가 있어요:
# demo/weather.py - Example MCP server with structured output
from pydantic import BaseModel, Field
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Weather Service")
class WeatherInfo(BaseModel):
location: str = Field(description="The location name")
temperature: float = Field(description="Temperature in Celsius")
conditions: str = Field(description="Weather conditions")
humidity: int = Field(description="Humidity percentage", ge=0, le=100)
@mcp.tool(
name="get_weather_info",
description="Get weather information for a location as structured data.",
# structured_output=True is enabled by default in FastMCP
)
def get_weather_info(city: str) -> WeatherInfo:
"""Get weather information for a city."""
return WeatherInfo(
location=city,
temperature=22.5,
conditions="partly cloudy",
humidity=65
)
출력 스키마와 구조화 출력을 쓰는 에이전트:
from smolagents import MCPClient, CodeAgent
# Using the weather server with structured output
from mcp import StdioServerParameters
server_parameters = StdioServerParameters(
command="python",
args=["demo/weather.py"]
)
with MCPClient(server_parameters, structured_output=True) as tools:
agent = CodeAgent(tools=tools, model=model)
result = agent.run("What is the temperature in Tokyo in Fahrenheit?")
print(result)
구조화 출력이 활성화되면 CodeAgent 시스템 프롬프트가 도구의 JSON 스키마 정보를 포함하도록 강화되어, 에이전트가 도구 출력의 기대 구조를 이해하고 데이터를 적절히 접근할 수 있게 돼요.
하위 호환성: structured_output 파라미터는 현재 하위 호환성을 위해 기본값이 False예요. 기존 코드는 변경 없이 계속 동작하고, 이전처럼 단순한 텍스트 출력을 받아요.
향후 변경: 향후 릴리스에서는 structured_output의 기본값이 False에서 True로 바뀔 거예요. structured_output=True를 명시적으로 설정해서 향상된 기능(더 나은 도구 출력 처리와 개선된 에이전트 성능)을 선택하는 걸 권장해요. 현재의 텍스트 전용 동작을 꼭 유지해야 하는 경우에만 structured_output=False를 사용하세요.
Space를 도구로 가져오기
Tool.from_space() 메서드로 허브의 Gradio Space를 직접 도구로 가져올 수 있어요!
허브에 있는 Space의 id, 이름, 그리고 에이전트가 도구가 무엇을 하는지 이해하는 데 도움이 될 설명만 제공하면 돼요. 내부적으로는 gradio-client 라이브러리를 사용해 Space를 호출해요.
예를 들어 허브의 FLUX.1-dev Space를 가져와 이미지를 생성해 볼게요.
image_generation_tool = Tool.from_space(
"black-forest-labs/FLUX.1-schnell",
name="image_generator",
description="Generate an image from a prompt"
)
image_generation_tool("A sunny beach")
짜잔, 여기 여러분의 이미지가 있어요! 🏖️
그런 다음 이 도구를 다른 도구들과 똑같이 사용할 수 있어요. 예를 들어 a rabbit wearing a space suit 프롬프트를 개선해 이미지를 생성해 볼게요. 이 예시는 에이전트에 추가 인자를 넘길 수도 있음을 보여줘요.
from smolagents import CodeAgent, InferenceClientModel
model = InferenceClientModel(model_id="Qwen/Qwen3-Next-80B-A3B-Thinking")
agent = CodeAgent(tools=[image_generation_tool], model=model)
agent.run(
"Improve this prompt, then generate an image of it.", additional_args={'user_prompt': 'A rabbit wearing a space suit'}
)
=== Agent thoughts:
improved_prompt could be "A bright blue space suit wearing rabbit, on the surface of the moon, under a bright orange sunset, with the Earth visible in the background"
Now that I have improved the prompt, I can use the image generator tool to generate an image based on this prompt.
>>> Agent is executing the code below:
image = image_generator(prompt="A bright blue space suit wearing rabbit, on the surface of the moon, under a bright orange sunset, with the Earth visible in the background")
final_answer(image)
이거 정말 멋지지 않아요? 🤩
LangChain 도구 사용하기
우리는 Langchain을 좋아하고 도구 모음이 매우 매력적이라고 생각해요.
LangChain에서 도구를 가져오려면 from_langchain() 메서드를 사용하세요.
소개의 검색 결과를 LangChain 웹 검색 도구로 재현하는 방법은 이래요.
이 도구가 제대로 동작하려면 pip install langchain google-search-results -q가 필요해요.
from langchain.agents import load_tools
search_tool = Tool.from_langchain(load_tools(["serpapi"])[0])
agent = CodeAgent(tools=[search_tool], model=model)
agent.run("How many more blocks (also denoted as layers) are in BERT base encoder compared to the encoder from the architecture proposed in Attention is All You Need?")
에이전트의 도구 모음 관리하기
agent.tools 속성이 표준 딕셔너리이므로, 거기에 도구를 추가하거나 교체해 에이전트의 도구 모음을 관리할 수 있어요.
기본 도구 모음만으로 초기화된 기존 에이전트에 model_download_tool을 추가해 볼게요.
from smolagents import InferenceClientModel
model = InferenceClientModel(model_id="Qwen/Qwen3-Next-80B-A3B-Thinking")
agent = CodeAgent(tools=[], model=model, add_base_tools=True)
agent.tools[model_download_tool.name] = model_download_tool
이제 새 도구를 활용할 수 있어요:
agent.run(
"Can you give me the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub but reverse the letters?"
)
[!TIP] 에이전트에 도구를 너무 많이 추가하지 않도록 주의하세요. 약한 LLM 엔진을 압도할 수 있어요.
도구 모음 사용하기
ToolCollection로 도구 모음을 활용할 수 있어요. 허브의 컬렉션이나 MCP 서버의 도구 중 하나를 불러올 수 있어요.
임의의 MCP 서버에서 온 도구 모음
glama.ai나 smithery.ai에 있는 수백 개의 MCP 서버에서 도구를 활용하세요.
MCP 서버 도구는 ToolCollection.from_mcp()로 불러올 수 있어요.
[!WARNING] 보안 경고: 특히 프로덕션 환경에서는 연결하기 전에 MCP 서버의 출처와 무결성을 항상 검증하세요. MCP 서버 사용에는 보안 위험이 따릅니다:
- 신뢰가 핵심입니다: 신뢰할 수 있는 출처의 MCP 서버만 사용하세요. 악성 서버는 여러분의 머신에서 유해한 코드를 실행할 수 있어요.
- stdio 기반 MCP 서버는 항상 여러분의 머신에서 코드를 실행해요(그게 의도된 기능이에요).
- Streamable HTTP 기반 MCP 서버: 원격 MCP 서버는 여러분의 머신에서 코드를 실행하지 않지만, 그래도 주의해서 진행하세요.
stdio 기반 MCP 서버의 경우 서버 파라미터를 mcp.StdioServerParameters 인스턴스로 전달하세요:
from smolagents import ToolCollection, CodeAgent
from mcp import StdioServerParameters
server_parameters = StdioServerParameters(
command="uvx",
args=["--quiet", "[email protected]"],
env={"UV_PYTHON": "3.12", **os.environ},
)
with ToolCollection.from_mcp(server_parameters, trust_remote_code=True) as tool_collection:
agent = CodeAgent(tools=[*tool_collection.tools], model=model, add_base_tools=True)
agent.run("Please find a remedy for hangover.")
ToolCollection에서 구조화 출력을 활성화하려면 structured_output=True 파라미터를 추가하세요:
with ToolCollection.from_mcp(server_parameters, trust_remote_code=True, structured_output=True) as tool_collection:
agent = CodeAgent(tools=[*tool_collection.tools], model=model, add_base_tools=True)
agent.run("Please find a remedy for hangover.")
Streamable HTTP 기반 MCP 서버의 경우 mcp.client.streamable_http.streamablehttp_client에 파라미터가 담긴 dict를 전달하고 transport 키에 "streamable-http" 값을 추가하기만 하면 돼요:
from smolagents import ToolCollection, CodeAgent
with ToolCollection.from_mcp({"url": "http://127.0.0.1:8000/mcp", "transport": "streamable-http"}, trust_remote_code=True) as tool_collection:
agent = CodeAgent(tools=[*tool_collection.tools], add_base_tools=True)
agent.run("Please find a remedy for hangover.")
허브의 컬렉션에서 온 도구 모음
사용하려는 컬렉션의 slug로 활용할 수 있어요. 그런 다음 목록으로 에이전트를 초기화하고 사용을 시작하면 돼요!
from smolagents import ToolCollection, CodeAgent
image_tool_collection = ToolCollection.from_hub(
collection_slug="huggingface-tools/diffusion-tools-6630bb19a942c2306a2cdb6f",
token="<YOUR_HUGGINGFACEHUB_API_TOKEN>"
)
agent = CodeAgent(tools=[*image_tool_collection.tools], model=model, add_base_tools=True)
agent.run("Please draw me a picture of rivers and lakes.")
시작을 빨리 하기 위해, 도구는 에이전트가 호출할 때만 로드돼요.