가이드 투어 · 퀵스타트 (Guided tour)
가이드 투어 · 퀵스타트
이 투어에서는 에이전트를 어떻게 만들고 실행하며, 내 용도에 맞게 커스터마이즈하는 방법까지 차근차근 살펴볼게요.
에이전트 유형 고르기: CodeAgent vs ToolCallingAgent
smolagents에는 CodeAgent와 ToolCallingAgent 두 클래스가 있어요. 둘은 에이전트가 도구와 상호작용하는 방식이 서로 다른 두 패러다임을 나타내요. 핵심 차이는 액션을 코드 생성으로 표현하느냐, 구조화된 툴 콜로 표현하느냐예요.
CodeAgent는 툴 콜을 파이썬 코드 스니펫으로 생성해요.
- 코드는 로컬(보안이 낮을 수 있음) 또는 보안 샌드박스에서 실행돼요.
- 도구는 파이썬 함수(바인딩)로 노출돼요.
- 툴 콜 예시:
result = search_docs("What is the capital of France?") print(result) - 장점:
- 표현력이 높아요 — 복잡한 로직·제어 흐름이 가능하고, 도구를 조합·반복·변환·추론할 수 있어요.
- 유연해요 — 모든 액션을 미리 정의할 필요 없이 필요할 때 새 도구·액션을 동적으로 만들 수 있어요.
- 창발적 추론 — 멀티스텝 문제나 동적 로직에 이상적이에요.
- 한계:
- 오류 위험이 있어요 — 문법 오류·예외를 처리해야 해요.
- 예측이 어려워요 — 예상 밖이거나 안전하지 않은 출력이 나올 수 있어요.
- 안전한 실행 환경이 필요해요.
ToolCallingAgent는 툴 콜을 구조화된 JSON으로 작성해요.
- OpenAI API 같은 많은 프레임워크에서 쓰는 일반적인 형식이라, 코드 실행 없이 구조화된 툴 상호작용이 가능해요.
- 도구는 JSON 스키마(이름, 설명, 파라미터 타입 등)로 정의돼요.
- 툴 콜 예시:
{ "tool_call": { "name": "search_docs", "arguments": { "query": "What is the capital of France?" } } } - 장점:
- 신뢰도가 높아요 — 환각 가능성이 낮고, 출력이 구조화되고 검증돼요.
- 안전해요 — 인자가 엄격히 검증되고 임의 코드 실행 위험이 없어요.
- 상호운용성이 좋아요 — 외부 API·서비스에 매핑하기 쉬워요.
- 한계:
- 표현력이 낮아요 — 결과를 동적으로 조합·변환하거나 복잡한 로직·제어 흐름을 다루기 어려워요.
- 유연하지 않아요 — 모든 가능한 액션을 미리 정의해야 하고 미리 정의된 도구에 한정돼요.
- 코드 합성이 없어요 — 도구 기능에만 제한돼요.
언제 어떤 걸 골라야 할까요? 추론·체이닝·동적 조합이 필요하고 도구를 함수처럼 조합(파싱+수학+조회)해야 할 문제 해결사·프로그래머 역할이라면 CodeAgent. 단순하고 원자적인 도구(API 호출, 문서 조회)를 쓰고 높은 신뢰도·명확한 검증을 원하는 디스패처·컨트롤러 역할이라면 ToolCallingAgent를 고르면 돼요.
CodeAgent
CodeAgent는 액션을 수행하고 작업을 풀기 위해 파이썬 코드 스니펫을 생성해요.
기본적으로 코드 실행은 로컬 환경에서 이뤄져요. 이게 안전한 이유는 호출할 수 있는 함수가 우리가 넣어준 도구(특히 Hugging Face 도구라면 더)와 print, math 모듈 함수 같은 사전 정의된 안전 함수 몇 가지뿐이라, 실행 가능한 범위가 이미 제한돼 있기 때문이에요.
파이썬 인터프리터도 기본적으로 안전 목록 밖의 import를 허용하지 않아서, 가장 흔한 공격들은 문제가 되지 않아요. 추가 import를 허용하려면 CodeAgent 초기화 시 additional_authorized_imports 인자에 허용할 모듈을 문자열 리스트로 넘기면 돼요.
model = InferenceClientModel()
agent = CodeAgent(tools=[], model=model, additional_authorized_imports=['requests', 'bs4'])
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")
추가 보안 계층으로, 하위 모듈(submodule) 접근도 기본적으로 금지돼요. 예를 들어 numpy.random 하위 모듈을 쓰려면 'numpy.random'을 additional_authorized_imports에 추가해야 해요. numpy.*처럼 쓰면 numpy와 numpy.random 같은 모든 하위 패키지를 허용하게 돼요.
[!WARNING] LLM이 생성한 임의 코드가 실행될 수 있어요. 절대 안전하지 않은 import를 추가하지 마세요!
불법 연산을 시도하는 코드나 에이전트가 생성한 코드에 일반 파이썬 오류가 나면 실행은 그 지점에서 멈춰요.
로컬 파이썬 인터프리터 대신 Blaxel, E2B, Docker를 쓸 수도 있어요. Blaxel은 먼저 BL_API_KEY, BL_WORKSPACE 환경변수를 설정하고 에이전트 초기화 시 executor_type="blaxel"을 넘겨요. E2B는 E2B_API_KEY를 설정하고 executor_type="e2b"를, Docker는 executor_type="docker"를 넘기면 돼요.
[!TIP] 코드 실행에 대해 더 알고 싶다면 보안 코드 실행 튜토리얼을 읽어보세요.
ToolCallingAgent
ToolCallingAgent는 JSON 툴 콜을 출력해요. 코드 실행 없이 구조화된 툴 상호작용이 가능한 OpenAI API 같은 여러 프레임워크의 공통 형식이죠. smolagents toolkit 엑스트라의 내장 WebSearchTool을 이용해 웹 검색을 수행하게 만들어볼게요.
CodeAgent와 동작은 비슷하지만 코드를 실행하지 않으니 additional_authorized_imports는 필요 없어요.
from smolagents import ToolCallingAgent, WebSearchTool
agent = ToolCallingAgent(tools=[WebSearchTool()], model=model)
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")
CLI로 시작하기
커맨드라인 인터페이스로도 빠르게 시작할 수 있어요.
# 프롬프트와 옵션을 직접 지정해 실행
smolagent "Plan a trip to Tokyo, Kyoto and Osaka between Mar 28 and Apr 7." --model-type "InferenceClientModel" --model-id "Qwen/Qwen2.5-Coder-32B-Instruct" --imports "pandas numpy" --tools "web_search"
# 대화형 모드: 프롬프트 없이 실행하면 인자 선택을 안내
smolagent
에이전트 만들기
최소한의 에이전트를 초기화하려면 두 인자가 필요해요.
model— 에이전트를 구동하는 텍스트 생성 모델. 에이전트는 단순한 LLM이 아니라 LLM을 엔진으로 쓰는 시스템이에요. 다음 중 골라 쓸 수 있어요.TransformersModel— 사전 초기화된transformers파이프라인으로 로컬 추론.InferenceClientModel—huggingface_hub.InferenceClient기반. 허브의 모든 Inference Provider(Cerebras, Cohere, Fal, Fireworks, HF-Inference, Hyperbolic, Nebius, Novita, Replicate, SambaNova, Together 등)를 지원해요.LiteLLMModel— LiteLLM으로 100개 넘는 모델·프로바이더를 호출.AzureOpenAIModel— Azure에 배포된 OpenAI 모델.AmazonBedrockModel— AWS의 Amazon Bedrock.MLXModel— mlx-lm 파이프라인으로 로컬 추론.
tools— 에이전트가 작업을 풀 때 쓸Tools목록. 빈 리스트여도 되고,add_base_tools=True를 넘기면 내tools위에 기본 툴박스까지 더할 수 있어요.
이 두 인자만 있으면 에이전트를 만들고 실행할 수 있어요. LLM은 Inference Providers, transformers, ollama, LiteLLM, Azure OpenAI, Amazon Bedrock, mlx-lm 중 어떤 걸 쓰든 상관없어요.
모든 모델 클래스는 초기화 시점에 temperature, max_tokens, top_p 같은 추가 키워드 인자를 받아요. 이 파라미터들은 내부 모델의 completion 호출로 자동 전달되어 창의성, 응답 길이, 샘플링 전략 같은 동작을 설정할 수 있어요.
모델 파라미터 관리
모델을 초기화할 때 추론 중 밑단 모델 API로 전달될 completion 파라미터를 키워드 인자로 넘길 수 있어요. 세밀한 제어가 필요할 땐 REMOVE_PARAMETER 센티넬 값으로, 기본 설정되거나 다른 곳에서 전달될 수도 있는 파라미터를 명시적으로 제외할 수 있어요.
from smolagents import OpenAIModel, REMOVE_PARAMETER
# "stop" 파라미터 제거
model = OpenAIModel(
model_id="gpt-5",
stop=REMOVE_PARAMETER, # API 호출에 "stop"이 포함되지 않게 함
temperature=0.7
)
agent = CodeAgent(tools=[], model=model, add_base_tools=True)
이는 이런 경우에 특히 유용해요. 자동으로 적용될 기본 파라미터를 덮어쓰고 싶을 때, 특정 파라미터를 API 호출에서 완전히 제외해야 할 때, 특정 파라미터는 모델 프로바이더 기본값을 그대로 쓰게 하고 싶을 때요.
에이전트 고급 설정
종료 조건 커스터마이즈
기본적으로 에이전트는 final_answer 함수를 호출하거나 최대 스텝 수에 도달할 때까지 계속 실행돼요. final_answer_checks 파라미터로 에이전트가 언제·어떻게 종료할지 더 제어할 수 있어요.
from smolagents import CodeAgent, InferenceClientModel
# 사용자 정의 최종 답변 검사 함수
def is_integer(final_answer: str, agent_memory=None) -> bool:
"""final_answer가 정수면 True를 반환."""
try:
int(final_answer)
return True
except ValueError:
return False
# 사용자 정의 최종 답변 검사로 에이전트 초기화
agent = CodeAgent(
tools=[],
model=InferenceClientModel(),
final_answer_checks=[is_integer]
)
agent.run("Calculate the least common multiple of 3 and 7")
final_answer_checks는 함수 리스트를 받는데, 각 함수는 에이전트의 final_answer와 에이전트 자신을 인자로 받고 final_answer가 유효한지(True) 아닌지(False)를 불리언으로 반환해요. 어떤 함수라도 False를 반환하면 에이전트는 오류 메시지를 기록하고 실행을 계속해요. 이 검증 메커니즘 덕분에 출력 형식을 강제하고(예: 수학 문제에 숫자 답 요구), 도메인별 검증 규칙을 넣고, 자신의 출력을 스스로 검증하는 더 견고한 에이전트를 만들 수 있어요.
에이전트 실행 결과 들여다보기
실행 후 무슨 일이 있었는지 확인하는 데 유용한 속성들이 있어요.
agent.logs— 에이전트의 세밀한 로그를 저장해요. 실행의 각 스텝마다 모든 것이 딕셔너리에 저장되고agent.logs에 누적돼요.agent.write_memory_to_messages()— 에이전트의 메모리를 Model이 볼 수 있는 채팅 메시지 목록으로 써줘요. 각 스텝의 로그를 훑으며 관심 있는 내용만 메시지로 저장하죠. 예를 들어 시스템 프롬프트와 작업을 별도 메시지로 저장하고, 각 스텝의 LLM 출력과 툴 콜 출력을 각각 메시지로 저장해요. 더 높은 수준의 관점이 필요할 때 쓰면 되지만, 모든 로그가 이 메서드로 기록되진 않아요.
도구(Tools)
도구는 에이전트가 사용하는 원자적인 함수예요. LLM이 사용하려면 해당 도구를 어떻게 호출할지 설명해 주는 API 속성이 필요해요.
- 이름(name)
- 설명(description)
- 입력 타입과 설명
- 출력 타입(output type)
예를 들어 PythonInterpreterTool은 이름, 설명, 입력 설명, 출력 타입, 그리고 액션을 수행하는 forward 메서드를 가져요. 에이전트 초기화 시 이 속성들로 도구 설명이 생성되어 에이전트의 시스템 프롬프트에 포함돼요. 덕분에 에이전트는 어떤 도구를 왜 쓸 수 있는지 알게 돼요.
스키마 정보: output_schema가 정의된 도구(구조화된 출력을 가진 MCP 도구 같은)라면 CodeAgent 시스템 프롬프트에 자동으로 JSON 스키마 정보가 포함돼요. 이 덕분에 에이전트는 도구 출력의 예상 구조를 이해하고 데이터를 적절히 접근할 수 있어요.
기본 툴박스
smolagents를 "toolkit" 엑스트라로 설치하면 에이전트를 강화하는 기본 툴박스가 제공돼요. 초기화 시 add_base_tools=True로 추가하면 돼요.
- DuckDuckGo 웹 검색 — DuckDuckGo의 브라우저 기반 검색으로 웹 검색 수행.
- 파이썬 코드 인터프리터 — LLM이 생성한 파이썬 코드를 안전한 환경에서 실행. 코드 기반 에이전트는 이미 파이썬 코드를 네이티브로 실행할 수 있으므로, 이 도구는
ToolCallingAgent를add_base_tools=True로 초기화할 때만 추가돼요. - Transcriber — Whisper-Turbo 기반의 음성→텍스트 파이프라인.
도구를 수동으로 쓸 수도 있어요.
# !pip install 'smolagents[toolkit]'
from smolagents import WebSearchTool
search_tool = WebSearchTool()
print(search_tool("Who's the current president of Russia?"))
새 도구 만들기
Hugging Face 기본 도구로 커버되지 않는 용례는 나만의 도구를 만들 수 있어요. 예를 들어 특정 작업(task)에 대해 허브에서 가장 많이 다운로드된 모델을 반환하는 도구를 만들어볼게요. 먼저 아래 코드로 시작해요.
from huggingface_hub import list_models
task = "text-classification"
most_downloaded_model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
print(most_downloaded_model.id)
이 코드는 함수로 감싸고 tool 데코레이터만 붙이면 바로 도구로 바뀌어요. 그 외에 Tool을 서브클래스로 만들어 직접 정의하는 방법도 있는데, 이 경우 무거운 클래스 속성을 초기화할 수 있어 더 유연해요. 두 방법을 모두 볼게요.
함수에 @tool 데코레이터 붙이기
from smolagents import tool
@tool
def model_download_tool(task: str) -> str:
"""
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.
Args:
task: The task for which to get the download count.
"""
most_downloaded_model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
return most_downloaded_model.id
이 함수에는 어떤 게 필요할까요? 먼저 명확한 이름이 필요해요. 이름은 이 도구가 무엇을 하는지 에이전트의 두뇌인 LLM이 알 수 있을 만큼 설명적이어야 해요. 특정 작업에서 가장 많이 다운로드된 모델을 반환하니 model_download_tool이라고 할게요. 그리고 입력·출력에 타입 힌트, 각 인자를 설명하는 Args: 부분을 포함한 설명이 필요해요 (이번엔 타입 표기는 없어도 돼요, 타입 힌트에서 가져오거든요). 도구 이름과 마찬가지로 이 설명도 에이전트를 구동하는 LLM을 위한 사용 설명서라서 소홀히하면 안 돼요. 이 모든 요소는 초기화 시 자동으로 시스템 프롬프트에 들어가니 최대한 명확하게 만들어야 해요.
[!TIP] 이 정의 형식은
apply_chat_template에서 쓰는 tool 스키마와 동일하고,tool데코레이터만 추가된 것이에요. 자세한 내용은 tool use API 문서를 읽어보세요.
그리고 바로 에이전트를 초기화할 수 있어요.
from smolagents import CodeAgent, InferenceClientModel
agent = CodeAgent(tools=[model_download_tool], model=InferenceClientModel())
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?"
)
Tool 서브클래스로 만들기
from smolagents import Tool
class ModelDownloadTool(Tool):
name = "model_download_tool"
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 for which to get the download count."}}
output_type = "string"
def forward(self, task: str) -> str:
most_downloaded_model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
return most_downloaded_model.id
서브클래스에는 명확한 name, description, 입력 타입·설명, 출력 타입이 필요해요. 이 속성들 역시 에이전트 초기화 시 시스템 프롬프트에 자동으로 들어가니 최대한 명확하게 만들어야 해요.
도구를 실행하면 아래 같은 로그가 나와요.
╭──────────────────────────────────────── New 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? │
│ │
╰─ InferenceClientModel - Qwen/Qwen2.5-Coder-32B-Instruct ───────────────────────────────────────────╯
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
╭─ Executing this code: ───────────────────────────────────────────────────────────────────╮
│ 1 model_name = model_download_tool(task="text-to-video") │
│ 2 print(model_name) │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
Execution logs:
ByteDance/AnimateDiff-Lightning
Out: None
[Step 0: Duration 0.27 seconds| Input tokens: 2,069 | Output tokens: 60]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
╭─ Executing this code: ───────────────────────────────────────────────────────────────────╮
│ 1 final_answer("ByteDance/AnimateDiff-Lightning") │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
Out - Final answer: ByteDance/AnimateDiff-Lightning
[Step 1: Duration 0.10 seconds| Input tokens: 4,288 | Output tokens: 148]
Out[20]: 'ByteDance/AnimateDiff-Lightning'
[!TIP] 도구에 대해 더 자세히 알고 싶다면 도구 심화 전용 튜토리얼을 읽어보세요.
멀티에이전트
멀티에이전트 시스템은 Microsoft의 Autogen 프레임워크에서 시작됐어요. 이 방식은 하나가 아니라 여러 에이전트가 함께 작업해서 문제를 풀어요. 대부분의 벤치마크에서 경험적으로 더 좋은 성능을 내는데, 그 이유는 개념적으로 단순해요. 많은 작업에서 만능 시스템을 쓰기보다 서브태스크에 특화된 유닛을 두는 편이 낫기 때문이에요. 여기서 각 에이전트가 분리된 도구 세트와 메모리를 갖는 게 효율적인 특화를 가능하게 해요. 예를 들어 코드 생성 에이전트의 메모리를 웹 검색 에이전트가 방문한 웹페이지 내용으로 채울 필요가 없잖아요? 분리해 두는 게 낫죠.
smolagents로 계층적 멀티에이전트 시스템을 쉽게 만들 수 있어요. 에이전트에 name과 description 속성을 주면(도구와 마찬가지로) 이 속성이 매니저 에이전트의 시스템 프롬프트에 포함되어 매니저가 이 관리 에이전트를 어떻게 호출할지 알게 돼요. 그리고 매니저 에이전트 초기화 시 managed_agents 파라미터에 이 관리 에이전트를 넘기면 돼요.
네이티브 WebSearchTool을 쓰는 특정 웹 검색 에이전트를 관리하는 에이전트를 만들어볼게요.
from smolagents import CodeAgent, InferenceClientModel, WebSearchTool
model = InferenceClientModel()
web_agent = CodeAgent(
tools=[WebSearchTool()],
model=model,
name="web_search_agent",
description="Runs web searches for you. Give it your query as an argument."
)
manager_agent = CodeAgent(
tools=[], model=model, managed_agents=[web_agent]
)
manager_agent.run("Who is the CEO of Hugging Face?")
[!TIP] 효율적인 멀티에이전트 구현의 심화 예시로 GAIA 리더보드 1위를 차지한 멀티에이전트 시스템 글을 참고하세요.
Gradio UI로 에이전트와 대화하기
GradioUI를 쓰면 에이전트에 작업을 대화형으로 제출하고 그 사고와 실행 과정을 멋진 인터페이스로 관찰할 수 있어요.
from smolagents import (
load_tool,
CodeAgent,
InferenceClientModel,
GradioUI
)
# 허브에서 도구 불러오기
image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True)
model = InferenceClientModel(model_id=model_id)
# 이미지 생성 도구를 가진 에이전트 초기화
agent = CodeAgent(tools=[image_generation_tool], model=model)
GradioUI(agent).launch()
내부적으로 사용자가 새 답변을 입력하면 에이전트가 agent.run(user_request, reset=False)로 실행돼요. reset=False 플래그는 새 작업을 시작하기 전에 에이전트 메모리를 비우지 않게 해서 대화가 이어질 수 있게 해요. 이 reset=False 인자는 다른 에이전트 애플리케이션에서도 대화를 이어갈 때 쓸 수 있어요.
Gradio UI에서 사용자가 실행 중인 에이전트를 중단할 수 있게 하려면, agent.interrupt() 메서드를 호출하는 버튼을 만들면 돼요. 이러면 현재 스텝이 끝나는 시점에 에이전트가 멈추고 오류를 발생시켜요.
다음 단계
에이전트를 내 필요에 맞게 구성했다면 허브에 공유할 수 있어요.
agent.push_to_hub("m-ric/my_agent")
반대로 허브에 올려둔 에이전트를 불러오려면, 그 도구 코드를 신뢰한다는 전제 하에 이렇게 해요.
agent.from_hub("m-ric/my_agent", trust_remote_code=True)
더 깊이 있게 쓰려면 이런 튜토리얼을 확인하면 돼요.
출처 인용
- 원문: smolagents - Guided tour (Hugging Face Docs)
- 원본 파일: huggingface/smolagents - docs/source/en/guided_tour.md