Agents Course documentation
Building Agents That Use Code
Building Agents That Use Code
Code agents are the default agent type in smolagents
. They generate Python tool calls to perform actions, achieving action representations that are efficient, expressive, and accurate.
Their streamlined approach reduces the number of required actions, simplifies complex operations, and enables reuse of existing code functions. smolagents
provides a lightweight framework for building code agents, implemented in approximately 1,000 lines of code.
Graphic from the paper Executable Code Actions Elicit Better LLM Agents
Why Code Agents?
In a multi-step agent process, the LLM writes and executes actions, typically involving external tool calls. Traditional approaches use a JSON format to specify tool names and arguments as strings, which the system must parse to determine which tool to execute.
However, research shows that tool-calling LLMs work more effectively with code directly. This is a core principle of smolagents
, as shown in the diagram above from Executable Code Actions Elicit Better LLM Agents.
Writing actions in code rather than JSON offers several key advantages:
- Composability: Easily combine and reuse actions
- Object Management: Work directly with complex structures like images
- Generality: Express any computationally possible task
- Natural for LLMs: High-quality code is already present in LLM training data
How Does a Code Agent Work?
The diagram above illustrates how CodeAgent.run()
operates, following the ReAct framework we mentioned in Unit 1. The main abstraction for agents in smolagents
is a MultiStepAgent
, which serves as the core building block. CodeAgent
is a special kind of MultiStepAgent
, as we will see in an example below.
A CodeAgent
performs actions through a cycle of steps, with existing variables and knowledge being incorporated into the agentβs context, which is kept in an execution log:
The system prompt is stored in a
SystemPromptStep
, and the user query is logged in aTaskStep
.Then, the following while loop is executed:
2.1 Method
agent.write_memory_to_messages()
writes the agentβs logs into a list of LLM-readable chat messages.2.2 These messages are sent to a
Model
, which generates a completion.2.3 The completion is parsed to extract the action, which, in our case, should be a code snippet since weβre working with a
CodeAgent
.2.4 The action is executed.
2.5 The results are logged into memory in an
ActionStep
.
At the end of each step, if the agent includes any function calls (in agent.step_callback
), they are executed.
Letβs See Some Examples
Alfred is planning a party at the Wayne family mansion and needs your help to ensure everything goes smoothly. To assist him, weβll apply what weβve learned about how a multi-step CodeAgent
operates.

If you havenβt installed smolagents
yet, you can do so by running the following command:
pip install smolagents -U
Letβs also login to the Hugging Face Hub to have access to the Serverless Inference API.
from huggingface_hub import login
login()
Selecting a Playlist for the Party Using smolagents
Music is an essential part of a successful party! Alfred needs some help selecting the playlist. Luckily, smolagents
has got us covered! We can build an agent capable of searching the web using DuckDuckGo. To give the agent access to this tool, we include it in the tool list when creating the agent.

For the model, weβll rely on InferenceClientModel
, which provides access to Hugging Faceβs Serverless Inference API. The default model is "Qwen/Qwen2.5-Coder-32B-Instruct"
, which is performant and available for fast inference, but you can select any compatible model from the Hub.
Running an agent is quite straightforward:
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel
agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())
agent.run("Search for the best music recommendations for a party at the Wayne's mansion.")
When you run this example, the output will display a trace of the workflow steps being executed. It will also print the corresponding Python code with the message:
β Executing parsed code: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
results = web_search(query="best music for a Batman party")
print(results)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
After a few steps, youβll see the generated playlist that Alfred can use for the party! π΅
Using a Custom Tool to Prepare the Menu

Now that we have selected a playlist, we need to organize the menu for the guests. Again, Alfred can take advantage of smolagents
to do so. Here, we use the @tool
decorator to define a custom function that acts as a tool. Weβll cover tool creation in more detail later, so for now, we can simply run the code.
As you can see in the example below, we will create a tool using the @tool
decorator and include it in the tools
list.
from smolagents import CodeAgent, tool, InferenceClientModel
# Tool to suggest a menu based on the occasion
@tool
def suggest_menu(occasion: str) -> str:
"""
Suggests a menu based on the occasion.
Args:
occasion (str): The type of occasion for the party. Allowed values are:
- "casual": Menu for casual party.
- "formal": Menu for formal party.
- "superhero": Menu for superhero party.
- "custom": Custom menu.
"""
if occasion == "casual":
return "Pizza, snacks, and drinks."
elif occasion == "formal":
return "3-course dinner with wine and dessert."
elif occasion == "superhero":
return "Buffet with high-energy and healthy food."
else:
return "Custom menu for the butler."
# Alfred, the butler, preparing the menu for the party
agent = CodeAgent(tools=[suggest_menu], model=InferenceClientModel())
# Preparing the menu for the party
agent.run("Prepare a formal menu for the party.")
The agent will run for a few steps until finding the answer. Precising allowed values in the docstring helps direct agent to occasion
argument values which exist and limit hallucinations.
The menu is ready! π₯
Using Python Imports Inside the Agent
We have the playlist and menu ready, but we need to check one more crucial detail: preparation time!
Alfred needs to calculate when everything would be ready if he started preparing now, in case they need assistance from other superheroes.
smolagents
specializes in agents that write and execute Python code snippets, offering sandboxed execution for security.
Code execution has strict security measures - imports outside a predefined safe list are blocked by default. However, you can authorize additional imports by passing them as strings in additional_authorized_imports
.
For more details on secure code execution, see the official guide.
When creating the agent, weβll use additional_authorized_imports
to allow for importing the datetime
module.
from smolagents import CodeAgent, InferenceClientModel
import numpy as np
import time
import datetime
agent = CodeAgent(tools=[], model=InferenceClientModel(), additional_authorized_imports=['datetime'])
agent.run(
"""
Alfred needs to prepare for the party. Here are the tasks:
1. Prepare the drinks - 30 minutes
2. Decorate the mansion - 60 minutes
3. Set up the menu - 45 minutes
4. Prepare the music and playlist - 45 minutes
If we start right now, at what time will the party be ready?
"""
)
These examples are just the beginning of what you can do with code agents, and weβre already starting to see their utility for preparing the party. You can learn more about how to build code agents in the smolagents documentation.
In summary, smolagents
specializes in agents that write and execute Python code snippets, offering sandboxed execution for security. It supports both local and API-based language models, making it adaptable to various development environments.
Sharing Our Custom Party Preparator Agent to the Hub
Wouldnβt it be amazing to share our very own Alfred agent with the community? By doing so, anyone can easily download and use the agent directly from the Hub, bringing the ultimate party planner of Gotham to their fingertips! Letβs make it happen! π
The smolagents
library makes this possible by allowing you to share a complete agent with the community and download others for immediate use. Itβs as simple as the following:
# Change to your username and repo name
agent.push_to_hub('sergiopaniego/AlfredAgent')
To download the agent again, use the code below:
# Change to your username and repo name
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("Give me the best playlist for a party at Wayne's mansion. The party idea is a 'villain masquerade' theme")
Whatβs also exciting is that shared agents are directly available as Hugging Face Spaces, allowing you to interact with them in real-time. You can explore other agents here.
For example, the AlfredAgent is available here. You can try it out directly below:
You may be wonderingβhow did Alfred build such an agent using smolagents
? By integrating several tools, he can generate an agent as follows. Donβt worry about the tools for now, as weβll have a dedicated section later in this unit to explore that in detail:
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, Tool, tool, VisitWebpageTool
@tool
def suggest_menu(occasion: str) -> str:
"""
Suggests a menu based on the occasion.
Args:
occasion: The type of occasion for the party.
"""
if occasion == "casual":
return "Pizza, snacks, and drinks."
elif occasion == "formal":
return "3-course dinner with wine and dessert."
elif occasion == "superhero":
return "Buffet with high-energy and healthy food."
else:
return "Custom menu for the butler."
@tool
def catering_service_tool(query: str) -> str:
"""
This tool returns the highest-rated catering service in Gotham City.
Args:
query: A search term for finding catering services.
"""
# Example list of catering services and their ratings
services = {
"Gotham Catering Co.": 4.9,
"Wayne Manor Catering": 4.8,
"Gotham City Events": 4.7,
}
# Find the highest rated catering service (simulating search query filtering)
best_service = max(services, key=services.get)
return best_service
class SuperheroPartyThemeTool(Tool):
name = "superhero_party_theme_generator"
description = """
This tool suggests creative superhero-themed party ideas based on a category.
It returns a unique party theme idea."""
inputs = {
"category": {
"type": "string",
"description": "The type of superhero party (e.g., 'classic heroes', 'villain masquerade', 'futuristic Gotham').",
}
}
output_type = "string"
def forward(self, category: str):
themes = {
"classic heroes": "Justice League Gala: Guests come dressed as their favorite DC heroes with themed cocktails like 'The Kryptonite Punch'.",
"villain masquerade": "Gotham Rogues' Ball: A mysterious masquerade where guests dress as classic Batman villains.",
"futuristic Gotham": "Neo-Gotham Night: A cyberpunk-style party inspired by Batman Beyond, with neon decorations and futuristic gadgets."
}
return themes.get(category.lower(), "Themed party idea not found. Try 'classic heroes', 'villain masquerade', or 'futuristic Gotham'.")
# Alfred, the butler, preparing the menu for the party
agent = CodeAgent(
tools=[
DuckDuckGoSearchTool(),
VisitWebpageTool(),
suggest_menu,
catering_service_tool,
SuperheroPartyThemeTool()
],
model=InferenceClientModel(),
max_steps=10,
verbosity_level=2
)
agent.run("Give me the best playlist for a party at the Wayne's mansion. The party idea is a 'villain masquerade' theme")
As you can see, weβve created a CodeAgent
with several tools that enhance the agentβs functionality, turning it into the ultimate party planner ready to share with the community! π
Now, itβs your turn: build your very own agent and share it with the community using the knowledge weβve just learned! π΅οΈββοΈπ‘
Inspecting Our Party Preparator Agent with OpenTelemetry and Langfuse π‘
As Alfred fine-tunes the Party Preparator Agent, heβs growing weary of debugging its runs. Agents, by nature, are unpredictable and difficult to inspect. But since he aims to build the ultimate Party Preparator Agent and deploy it in production, he needs robust traceability for future monitoring and analysis.
Once again, smolagents
comes to the rescue! It embraces the OpenTelemetry standard for instrumenting agent runs, allowing seamless inspection and logging. With the help of Langfuse and the SmolagentsInstrumentor
, Alfred can easily track and analyze his agentβs behavior.
Setting it up is straightforward!
First, we need to install the necessary dependencies:
pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents
Next, Alfred has already created an account on Langfuse and has his API keys ready. If you havenβt done so yet, you can sign up for Langfuse Cloud here or explore alternatives.
Once you have your API keys, they need to be properly configured as follows:
import os
import base64
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_AUTH=base64.b64encode(f"{LANGFUSE_PUBLIC_KEY}:{LANGFUSE_SECRET_KEY}".encode()).decode()
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://cloud.langfuse.com/api/public/otel" # EU data region
# os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://us.cloud.langfuse.com/api/public/otel" # US data region
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
Finally, Alfred is ready to initialize the SmolagentsInstrumentor
and start tracking his agentβs performance.
from opentelemetry.sdk.trace import TracerProvider
from openinference.instrumentation.smolagents import SmolagentsInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
trace_provider = TracerProvider()
trace_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))
SmolagentsInstrumentor().instrument(tracer_provider=trace_provider)
Alfred is now connected π! The runs from smolagents
are being logged in Langfuse, giving him full visibility into the agentβs behavior. With this setup, heβs ready to revisit previous runs and refine his Party Preparator Agent even further.
from smolagents import CodeAgent, InferenceClientModel
agent = CodeAgent(tools=[], model=InferenceClientModel())
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("Give me the best playlist for a party at Wayne's mansion. The party idea is a 'villain masquerade' theme")
Alfred can now access these logs here to review and analyze them.
Meanwhile, the suggested playlist sets the perfect vibe for the party preparations. Cool, right? πΆ
Now that we have created our first Code Agent, letβs learn how we can create Tool Calling Agents, the second type of agent available in smolagents
.
Resources
- smolagents Blog - Introduction to smolagents and code interactions
- smolagents: Building Good Agents - Best practices for reliable agents
- Building Effective Agents - Anthropic - Agent design principles
- Sharing runs with OpenTelemetry - Details about how to setup OpenTelemetry for tracking your agents.