
Your Agent's Actions Are Python, Not JSON: Build a Code Agent With HuggingFace smolagents
Chris Harper
3 min read
Aug 15, 2026 · 04:07 UTC
smolagents (HuggingFace) lets your LLM write actual Python to call tools instead of JSON blobs — in benchmarks, code agents complete complex multi-step tasks up to ~20% more often in fewer steps.
What you'll be able to do after this:
- Understand why code agents outperform JSON tool-calling agents on multi-step tasks
- Build a working
CodeAgentwith real tools in under 30 lines of Python - Know when to pick
CodeAgent(default) vsToolCallingAgent(structured API calls)
Why code over JSON?
Most agent frameworks represent tool calls as JSON: {"tool": "search", "args": {"query": "..."}}. The model names the tool; the JSON is dispatched; repeat. smolagents instead gives the model a Python interpreter: it writes real code, with your tools available as callables. The model can loop, branch, compose multiple tools in one step, and define helper functions on the fly. Research shows this raises task success rates by up to ~20% on complex benchmarks and often reduces total steps.
smolagents ships two agent types:
CodeAgent(default) — generates Python, executes it in a sandboxed subprocessToolCallingAgent— generates JSON tool calls (backward-compatible with traditional tool-calling setups)
For most multi-step work, CodeAgent is the right choice.
Quick start
pip install smolagents
from smolagents import CodeAgent, HfApiModel, DuckDuckGoSearchTool
model = HfApiModel(model_id="Qwen/Qwen2.5-72B-Instruct")
agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=model)
result = agent.run(
"Find the Python package for the largest open-source vector database "
"by GitHub stars and show me the install command."
)
print(result)
To use Claude Sonnet 4.6 instead:
from smolagents import CodeAgent, LiteLLMModel, DuckDuckGoSearchTool
model = LiteLLMModel(model_id="anthropic/claude-sonnet-4-6")
agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=model)
smolagents uses LiteLLM under the hood for provider routing, so any Anthropic model string works.
Add a custom tool with a decorator
from smolagents import tool
@tool
def count_words(text: str) -> int:
"""Counts the number of words in the given text."""
return len(text.split())
agent = CodeAgent(tools=[count_words], model=model)
The function name becomes the tool name; the docstring becomes the model's description; type hints define the schema. No boilerplate.
When to choose each agent type
| Scenario | Agent type |
|---|---|
| Multi-step research with branching | CodeAgent |
| Loop over a list until condition met | CodeAgent |
| Single structured API call | ToolCallingAgent |
| Can't execute arbitrary Python safely | ToolCallingAgent |
CodeAgent runs code in a sandboxed local interpreter by default. For production, swap it for the DockerExecutor or E2BExecutor to confine execution to a container. Tools are allowlisted — the model can only call functions you explicitly passed in.
Where to go deeper
The HuggingFace Agents Course Unit 2 (huggingface.co/learn/agents-course) is the free, official walkthrough — covers CodeAgent, ToolCallingAgent, multi-agent orchestration, and the secure execution layer. The DeepLearning.AI short course (taught by Thomas Wolf and Aymeric Roucher, HuggingFace) goes from scratch to production in about two hours.
Sources: Building Agents That Use Code — HuggingFace Agents Course · Building Code Agents with HuggingFace smolagents — DeepLearning.AI · smolagents guided tour — HuggingFace Docs · smolagents GitHub — huggingface/smolagents