The Core Difference
Smolagents ships two agent types with fundamentally different execution models. Most frameworks hide this choice. Smolagents makes it explicit.
| CodeAgent | ToolCallingAgent | |
|---|---|---|
| How it calls tools | Writes and executes Python code | Calls tools via structured JSON (like OpenAI function calling) |
| Flexibility | Very high — can compose tools in loops, conditionals, list comprehensions | Moderate — one tool per step, structured inputs only |
| Security risk | Code execution — requires sandboxing in production | Low — no arbitrary code execution |
| Model requirement | Works best with models that can write clean Python | Works with any instruction-following model |
| Debugging | Inspect the generated code directly | Inspect the tool call JSON |
CodeAgent: What It Actually Does
CodeAgent asks the model to write a Python snippet that calls your tools, then executes that snippet in a local Python interpreter. The model can use loops, string manipulation, and multi-step logic in a single turn — something JSON-based tool calling cannot do without multiple API calls.
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel
model = HfApiModel('meta-llama/Meta-Llama-3.1-70B-Instruct')
agent = CodeAgent(
tools=[DuckDuckGoSearchTool()],
model=model,
)
result = agent.run(
'Find the 3 most recent Python releases and return their version numbers and dates'
)
# The model writes a Python loop to search, parse, and format the results
The generated code is something like:
# Model-generated code (you can inspect this with agent.logs)
results = []
for query in ['Python 3.13 release date', 'Python 3.12 release date', 'Python 3.11 release date']:
r = search(query)
results.append(r)
final_answer(', '.join(results))
CodeAgent executes Python in the same process by default. In production, enable the sandboxed executor: CodeAgent(..., executor_type='e2b') to run code in an E2B sandbox, or use the 'local' executor only behind strict input validation.ToolCallingAgent: When to Use It
ToolCallingAgent uses the model's native function-calling capability. One tool per turn, structured JSON inputs. Safer, more predictable, slower for multi-step tasks that need iteration.
from smolagents import ToolCallingAgent, DuckDuckGoSearchTool, HfApiModel
from smolagents import tool
@tool
def get_stock_price(ticker: str) -> str:
'''Get the current stock price for a given ticker symbol.
Args:
ticker: The stock ticker symbol (e.g. AAPL, GOOG)
'''
# Your implementation here
return f'${ticker}: $150.00'
agent = ToolCallingAgent(
tools=[DuckDuckGoSearchTool(), get_stock_price],
model=HfApiModel('meta-llama/Meta-Llama-3.1-70B-Instruct'),
)
result = agent.run('What is Apple\'s current stock price?')
The @tool Decorator
Both agent types use the same @tool decorator to define custom tools. The docstring is critical — Smolagents uses it to build the tool description the model sees. Write it clearly.
from smolagents import tool
@tool
def query_database(sql: str, database: str = 'production') -> str:
'''Execute a read-only SQL query and return results as JSON.
Only SELECT statements are allowed. Use this to look up customer records,
order history, or product inventory.
Args:
sql: The SELECT query to run
database: Database name: 'production' or 'staging'
'''
if not sql.strip().upper().startswith('SELECT'):
return 'Error: Only SELECT queries are allowed'
# ... execute query ...
return '{"rows": [...]}'
For CodeAgent, write tool docstrings as if explaining to a programmer. For ToolCallingAgent, write them as if explaining to a helpful assistant who needs to decide when to use the tool. The audiences are slightly different.Model Selection by Agent Type
| Agent Type | Recommended Models | Avoid |
|---|---|---|
| CodeAgent | Llama 3.1 70B+, Qwen 2.5 Coder, GPT-4o, Claude Sonnet | Small models (< 7B) — code quality degrades quickly |
| ToolCallingAgent | Any model with function calling support — Llama 3.1 8B works well | Models without native function calling |
Decision Guide
Use CodeAgent when:
- Your task requires iterating over a list and calling a tool once per item
- You want the agent to do post-processing (filter, sort, format) without an extra tool
- You are prototyping and want maximum flexibility
- You can sandbox code execution (E2B, container, or trusted internal environment)
Use ToolCallingAgent when:
- You are exposing the agent to untrusted inputs (user-submitted queries)
- You need predictable, auditable tool calls (compliance, logging requirements)
- Your task is straightforward single-tool lookups
- You are using a smaller or less capable model