Smolagents Guide: Code-First AI Agents & Execution Sandbox

The Problem: The Latency and Expressiveness Wall of JSON Tool Calling
In modern generative AI workflows, automated market research systems, and autonomous desktop platforms like AiFetchly, developers rely on agents to interact with external databases, APIs, and the web.
For years, the industry standard for agentic execution has been JSON Tool Calling (popularized by OpenAI Function Calling, LangChain, and ReAct). In this paradigm, when a Large Language Model (LLM) needs to execute an external tool, it outputs a rigid JSON structure specifying the tool name and an arguments dictionary:
{
"name": "web_search",
"arguments": {
"query": "Q3 2026 enterprise GPU pricing"
}
} The host application parses the JSON, dispatches the network request, serializes the observation into text, and feeds it back into the LLM’s conversation context for the next turn.
While straightforward for isolated lookups, deploying JSON tool calling on complex, multi-step engineering and extraction workflows reveals three crippling architectural bottlenecks:
1. The Schema Injection Tax and Context Saturation
To enable JSON tool calling, the agent system must serialize the full specification of every available tool—including parameter types, docstrings, required fields, and nested object definitions—into the system prompt as an OpenAPI or Pydantic JSON schema.
- If an agent platform provides 25 specialized enterprise tools, the schema definitions alone consume over 4,000 to 8,000 prompt tokens.
- Because every subsequent step repeats this full context, token consumption grows quadratically.
- The high token volume increases Time-to-First-Token (TTFT) and accelerates context window exhaustion on smaller local models (such as 3B to 8B parameter models).
2. The Ping-Pong Roundtrip Bottleneck
In JSON tool calling, tools can only be invoked through a strictly serialized, turn-by-turn “ping-pong” loop:
The Traditional JSON Ping-Pong Tax:
[User Prompt] ──► LLM Call 1 ──► Emit JSON {"tool": "search"}
│
▼ External Execution
[Tool Result: 10 URLs] ──► LLM Call 2 ──► Emit JSON {"tool": "fetch_url_1"}
│
▼ External Execution
[HTML Content 1] ──► LLM Call 3 ──► Emit JSON {"tool": "fetch_url_2"}
│
▼ (Repeats 5 to 10 times!) If an agent needs to search for five articles, extract three financial metrics from each, and calculate their average, JSON tool calling requires at least 8 to 12 complete LLM inference passes.
Each turn incurs network latency, queueing time, and autoregressive generation delays. A task that should execute in two seconds stretches across 45 seconds of continuous API round trips.
3. The Inability to Maintain State or Express Control Flow
JSON is a static data serialization format, not a Turing-complete programming language:
- No Variables: An agent cannot store the result of Tool A in an intermediate variable
data = tool_a()and pass a subsetdata["items"][0]directly to Tool B. The raw output must travel all the way back into the LLM context. - No Native Control Flow: JSON cannot express basic loops (
for item in results:), conditional branches (if status == 200:), or error handling (try/except). - Hallucinated Formats: When small or fine-tuned open-source models attempt to generate complex JSON schemas, missing commas, escaped quotes, and type mismatches cause JSON parse errors, stalling the entire agent loop.
The AI engineering ecosystem faced a fundamental question: Why force language models to communicate with code environments using brittle JSON dictionaries, when modern models are already pre-trained on billions of lines of Python code?
The Architecture: CodeAgent and the Code-First Execution Paradigm
To eliminate the JSON serialization tax and unlock expressive, low-latency agent workflows, Hugging Face released smolagents (2025/2026).
At the center of smolagents is a conceptual shift: Code-First Agents (CodeAgent). Instead of emitting fragmented JSON blobs, the model writes its actions directly as executable blocks of Python code.
The Code-First Execution Shift:
JSON Agent Action: {"name": "calculator", "arguments": {"expression": "24 * 365"}}
CodeAgent Action: hours = 24 * 365
print(f"Total hours: {hours}") Why Foundation Models Naturally Excel at Code Actions
Modern Large Language Models (such as Claude 3.5 Sonnet, DeepSeek-Coder, Qwen2.5-Coder, and Llama 3.3) are pre-trained on massive open-source code repositories (The Stack, GitHub). Writing valid Python syntax—assigning variables, importing functions, creating loops, and manipulating dictionaries—is second nature to these architectures.
When prompted to write Python code rather than JSON:
- Syntax Robustness: Code generation syntax error rates are significantly lower than JSON schema failure rates.
- Logic Density: A single block of Python code can express multi-step algorithms, filtering, string transformations, and error handling without requiring intermediate LLM intervention.
- Token Efficiency: Writing
res = [f(x) for x in data if x > 0]requires fewer than 20 tokens, whereas expressing the equivalent logic in JSON tool calling requires multiple LLM turns and thousands of context tokens.
Single-Turn Composition and Programmatic Flow
In smolagents, the model can chain multiple tools, iterate across lists, and compute statistics in a single generation turn:
# A single CodeAgent action step executing a complete research workflow:
query_results = web_search(query="semiconductor revenue growth 2026")
filtered_articles = [doc for doc in query_results if "Q2" in doc["title"]]
summaries = []
for doc in filtered_articles[:3]:
text = fetch_webpage(url=doc["url"])
key_metrics = extract_metrics(content=text)
summaries.append({"title": doc["title"], "metrics": key_metrics})
final_report = format_markdown_table(data=summaries)
print(final_report) In traditional JSON tool calling, the sequence above would require over 8 separate LLM inference turns. In smolagents, it executes in one single turn.
Architectural Comparison: JSON Tool Calling vs. CodeAgent
Comparison: ToolCallingAgent vs. smolagents CodeAgent
┌─────────────────────────┬─────────────────────────────┬─────────────────────────────┐
│ Architectural Dimension │ Traditional JSON (ReAct) │ smolagents (CodeAgent) │
├─────────────────────────┼─────────────────────────────┼─────────────────────────────┤
│ Action Format │ JSON schema dictionary │ Valid Executable Python │
│ Compositionality │ 1 tool per LLM turn │ Unlimited tools per turn │
│ Control Flow │ None (External host logic) │ Native loops, if/else, try │
│ Intermediate State │ Echoed back into LLM prompt │ Stored in local memory dict │
│ Token Consumption │ High (Repeats full schemas) │ Up to 70% lower │
│ Framework Footprint │ Heavy (>20,000 LOC) │ Ultra-lightweight (~1k LOC) │
│ Execution Runtime │ External API orchestrator │ Secure AST-sandboxed runner │
└─────────────────────────┴─────────────────────────────┴─────────────────────────────┘ Security Mechanics: How the AST-Based Python Sandbox Operates
Allowing an autonomous language model to generate and execute arbitrary code on a production host raises an obvious security question: How do you prevent malicious code execution, file system tampering, and shell injection?
Rather than relying on dangerous functions like raw exec() or eval(), smolagents incorporates an AST-based (Abstract Syntax Tree) Secure Python Interpreter.
smolagents Execution Security Pipeline:
[Model Output] ──► ast.parse(code)
│
▼ Node-Level Inspection
┌──────────────────────────────────────────────┐
│ AST Gatekeeper Checks: │
│ • Is module in allowed_imports? │
│ • Does Call target banned builtins (eval)? │
│ • Are dunder attributes accessed (__sub__)? │
│ • Are iteration loops under MAX_OPERATIONS? │
└──────────────────────┬───────────────────────┘
│ (Passes All Audits)
▼
[Custom Virtual Environment]
(Isolated locals & tool mapping)
│
▼
[Controlled Safe Execution] 1. Static AST Node Traversal and Allowlist Filtering
Before any code executes, smolagents compiles the text string into an Abstract Syntax Tree using Python’s native ast module. It traverses every syntax node using an AST visitor pattern:
ast.Importandast.ImportFrom: Only modules explicitly defined inadditional_authorized_imports(e.g.,math,datetime,re,pandas) are permitted. Attemptingimport os,import subprocess, orimport socketimmediately raises anInterpreterErrorbefore runtime.ast.Call: Function invocations are checked against an internal registry. Unsafe Python built-ins—such asopen(),eval(),exec(),compile(), andglobals()—are completely stripped from the execution scope.- Tools as Functions: Allowed tools are injected directly as callable Python functions within the interpreter’s isolated scope.
2. Guardrails Against Dunder Inspection and Introspection Attacks
Advanced prompt injection attacks frequently attempt to bypass Python sandboxes using object model introspection (e.g., ''.__class__.__mro__[1].__subclasses__() to locate the os module).
smolagents explicitly neutralizes introspection attacks:
- The AST visitor inspects all
ast.Attributenodes. - Any attempt to access attributes beginning with double underscores (
__) triggers an immediate authorization error. - Private variable inspection and class hierarchy crawling are structurally blocked at the parser level.
3. Deterministic Resource and Execution Limits
To prevent accidental or malicious infinite loops (while True: pass) from freezing the server process:
- Operation Budget (
max_operations): Every loop iteration and function call decrements a deterministic counter. If total executed operations exceed the configured budget (e.g., 10,000 operations), execution terminates. - Execution Timeouts: CPU wall-clock timeout gates prevent runaway algorithmic complexity.
- Memory Bounds: Objects created in the virtual scope are tracked to prevent heap exhaustion.
End-to-End Implementation: Building Multi-Tool CodeAgents with smolagents
Let us walk through the complete implementation of an autonomous data intelligence agent using smolagents, integrating custom typed tools and local execution.
1. Defining Typed Tools with the @tool Decorator
In smolagents, defining a tool requires minimal boilerplate. You write a clean Python function with complete type hints and a docstring detailing inputs and outputs:
from smolagents import tool
from typing import Dict, Any, List
@tool
def fetch_financial_metrics(ticker: str, fiscal_year: int) -> Dict[str, Any]:
"""
Retrieves key financial fundamentals for a given publicly traded stock ticker.
Args:
ticker: The stock ticker symbol (e.g., 'AAPL', 'NVDA', 'MSFT').
fiscal_year: The four-digit fiscal year to retrieve (e.g., 2025, 2026).
"""
# Simulated high-precision data retrieval
mock_data = {
"NVDA": {"revenue_billions": 130.5, "gross_margin_pct": 75.2, "ebitda_growth": 122.4},
"MSFT": {"revenue_billions": 245.1, "gross_margin_pct": 69.8, "ebitda_growth": 18.6},
"GOOGL": {"revenue_billions": 307.4, "gross_margin_pct": 57.4, "ebitda_growth": 24.1}
}
company_data = mock_data.get(ticker.upper(), {"revenue_billions": 0.0, "gross_margin_pct": 0.0, "ebitda_growth": 0.0})
return {"ticker": ticker.upper(), "year": fiscal_year, "metrics": company_data}
@tool
def calculate_growth_score(revenue: float, margin: float, growth: float) -> float:
"""
Calculates a normalized enterprise financial efficiency score.
Args:
revenue: Total revenue in billions USD.
margin: Operating gross margin percentage (0 to 100).
growth: Year-over-year EBITDA growth percentage.
"""
score = (revenue * 0.2) + (margin * 0.4) + (growth * 0.4)
return round(score, 2) 2. Instantiating and Running a Production CodeAgent
With tools defined, we instantiate a CodeAgent. Notice how clean the setup is:
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel
# 1. Initialize the Foundation Model Backend
# smolagents supports Hugging Face Inference API, Ollama, vLLM, and OpenAI-compatible endpoints
model = HfApiModel(
model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
token="YOUR_HUGGINGFACE_TOKEN"
)
# 2. Instantiate the CodeAgent with Sandbox Permissions
agent = CodeAgent(
tools=[DuckDuckGoSearchTool(), fetch_financial_metrics, calculate_growth_score],
model=model,
additional_authorized_imports=["math", "statistics", "datetime"],
max_steps=4,
verbosity_level=2
)
# 3. Execute the Multi-Step Instruction
prompt = (
"Compare the financial metrics for NVDA and MSFT for 2026. "
"Fetch their revenue and gross margin, calculate their growth scores, "
"and return a comparative summary indicating which company shows higher capital efficiency."
)
result = agent.run(prompt)
print("\nAgent Final Answer:\n", result) 3. Local Model Execution with Ollama and vLLM
For organizations running private, 100% on-premise infrastructure without third-party API dependencies, smolagents integrates seamlessly with local inference engines like Ollama and vLLM via LiteLLMModel:
from smolagents import CodeAgent, LiteLLMModel
# Point smolagents to a locally running vLLM or Ollama instance
local_model = LiteLLMModel(
model_id="ollama/qwen2.5-coder:14b",
api_base="http://localhost:11434"
)
local_agent = CodeAgent(
tools=[fetch_financial_metrics, calculate_growth_score],
model=local_model,
additional_authorized_imports=["math"]
)
response = local_agent.run("Calculate the growth score for GOOGL in 2026.") Empirical Benchmarks: GAIA, SWE-Bench, and Token Efficiency
How do Code-First agents perform when tested against traditional JSON-based agents on rigorous real-world benchmarks?
1. GAIA Benchmark Performance
The GAIA (General AI Assistants) benchmark evaluates AI assistants on complex, multi-modal, multi-tool tasks requiring web browsing, code execution, spreadsheet reasoning, and file manipulation.
GAIA Benchmark (Level 1 - Level 3 Combined Success Rate):
┌─────────────────────────────────┬───────────────────┬────────────────────────┐
│ Agent Framework & Backend │ Action Paradigm │ Overall GAIA Accuracy │
├─────────────────────────────────┼───────────────────┼────────────────────────┤
│ ReAct (LangChain + GPT-4o) │ JSON Tool Calling │ 38.4% │
│ ToolCallingAgent (smolagents) │ JSON Tool Calling │ 42.1% │
│ CodeAgent (smolagents + GPT-4o) │ Executable Python │ 55.2% (SOTA Baseline) │
│ CodeAgent + Agent-KB (Pass@3) │ Executable Python │ 73.9% (State-of-the-Art)│
└─────────────────────────────────┴───────────────────┴────────────────────────┘ By allowing the model to write code to manipulate arrays, parse regex patterns, and handle mathematical transformations internally, CodeAgent delivers a +13.1 percentage point increase in accuracy over equivalent JSON tool-calling implementations.
2. Token Consumption and Wall-Clock Latency Reductions
Measuring resource efficiency across multi-step data extraction workflows demonstrates the operational advantages of code-first execution:
| Benchmark Dimension | JSON Tool Calling | smolagents (CodeAgent) | System Efficiency Gain |
|---|---|---|---|
| Average LLM Turns per Task | 6.8 turns | 2.1 turns | 3.2x Fewer Turns |
| Total Prompt Tokens Used | ~14,200 tokens | ~4,650 tokens | 67.3% Token Reduction |
| Wall-Clock Completion Time | 38.4 seconds | 11.2 seconds | 3.4x Faster Execution |
| Tool Argument Parse Failures | 8.4% of steps | 0.8% of steps | 10.5x Error Reduction |
By consolidating multiple sequential tool operations into a single programmatic block, smolagents slashes cloud inference billing and reduces latency variance.
Strategic Value: Empowering 100% Local AI Autonomous Agents
The architectural shift to Code-First agents mirrors the foundational philosophy of modern private desktop intelligence platforms like AiFetchly.
When running continuous agentic workflows—such as local web extraction, cross-referencing multi-thousand-row spreadsheets, or monitoring system telemetry—traditional JSON tool calling creates unsustainable friction:
- Token Explosion in Local VRAM: Small local language models (1B to 8B parameters) possess smaller effective attention context windows. Feeding massive JSON schemas on every step degrades local reasoning.
- Latency Multipliers on Consumer Hardware: Running 10 sequential inference passes on a workstation GPU or Apple Silicon chip creates severe user-facing delays.
- Fragile Output Contracts: Open-source models frequently drop punctuation in JSON schemas, causing unrecoverable agent crashes.
By adopting smolagents CodeAgent patterns:
- Local Python Execution: The local host environment handles filtering, sorting, and arithmetic at native CPU speeds rather than wasting neural network tokens.
- Minimalist System Footprint: The entire agent library is clean and lightweight (~1,000 lines of code), free from bloated abstraction layers.
- Robust Local Subagents: Desktop agents can reliably chain file system reads, local database queries, and regex extraction in single, sandboxed programmatic passes.
By replacing the brittle JSON ping-pong with expressive, AST-sandboxed Python code, smolagents represents the modern standard for fast, reliable, and token-efficient autonomous intelligence.