HuggingFace Smolagents Observability
HuggingFace Smolagents is a lightweight agent framework — a minimal API for tool-calling and code-executing agents. This guide shows how to add full distributed tracing to Smolagents runs with Nexus: per-run traces, step-level spans for CodeAgent, and tool call spans for ToolCallingAgent.
Install
Install both the Smolagents framework and the Nexus Python SDK:
pip install smolagents nexus-agent
Get your API key from the dashboard and set it as an environment variable:
export NEXUS_API_KEY="nxs_your_key_here"
Basic trace wrapping
The simplest integration wraps the entire agent run in a trace. Every run appears in the dashboard with its status and duration:
from smolagents import ToolCallingAgent, HfApiModel, tool
from nexus_agent import NexusClient
import os
nexus = NexusClient(
api_key=os.environ["NEXUS_API_KEY"],
agent_id="my-smolagent",
)
@tool
def get_weather(location: str) -> str:
"""Get current weather for a location."""
return f"Sunny, 22C in {location}"
model = HfApiModel(model_id="Qwen/Qwen2.5-72B-Instruct")
agent = ToolCallingAgent(tools=[get_weather], model=model)
task = "What is the weather in Paris?"
trace = nexus.start_trace(
name=f"agent: {task[:60]}",
metadata={"task": task, "model": "Qwen2.5-72B-Instruct"},
)
try:
result = agent.run(task)
nexus.end_trace(trace["id"], status="success", output={"result": result})
except Exception as e:
nexus.end_trace(trace["id"], status="error", error=str(e))
raise
Step-level spans (CodeAgent)
For CodeAgent, each reasoning step is the interesting unit. Subclass CodeAgent and override step() to create per-step spans:
from smolagents import CodeAgent, HfApiModel
from nexus_agent import NexusClient
import os
nexus = NexusClient(
api_key=os.environ["NEXUS_API_KEY"],
agent_id="code-agent",
)
class TracedCodeAgent(CodeAgent):
def __init__(self, *args, trace_id=None, **kwargs):
super().__init__(*args, **kwargs)
self._trace_id = trace_id
self._step_count = 0
def step(self, memory):
self._step_count += 1
span = nexus.add_span(
trace_id=self._trace_id,
name=f"step-{self._step_count}",
metadata={"step": self._step_count},
) if self._trace_id else None
try:
result = super().step(memory)
if span:
nexus.end_span(span["id"], status="success")
return result
except Exception as e:
if span:
nexus.end_span(span["id"], status="error", error=str(e))
raise
model = HfApiModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct")
task = "Write a function to sort a list of dicts by a given key."
trace = nexus.start_trace(name=f"code-agent: {task[:60]}", metadata={"task": task})
agent = TracedCodeAgent(tools=[], model=model, trace_id=trace["id"])
try:
result = agent.run(task)
nexus.end_trace(trace["id"], status="success", output={"result": result})
except Exception as e:
nexus.end_trace(trace["id"], status="error", error=str(e))
raise
Tool call spans (ToolCallingAgent)
For ToolCallingAgent, wrap individual tool functions to get per-tool spans. A decorator makes this reusable across all your tools:
import functools
from smolagents import tool
from nexus_agent import NexusClient
import os
nexus = NexusClient(
api_key=os.environ["NEXUS_API_KEY"],
agent_id="tool-calling-agent",
)
def traced_tool(trace_id: str):
"""Decorator: wraps a smolagents tool with a Nexus span."""
def decorator(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
span = nexus.add_span(
trace_id=trace_id,
name=f"tool:{fn.__name__}",
input={"args": list(args), "kwargs": kwargs},
metadata={"tool_name": fn.__name__},
)
try:
result = fn(*args, **kwargs)
nexus.end_span(span["id"], status="success", output={"result": result})
return result
except Exception as e:
nexus.end_span(span["id"], status="error", error=str(e))
raise
return wrapper
return decorator
# Wrap tools at trace creation time:
trace = nexus.start_trace(name="weather-lookup")
@tool
@traced_tool(trace["id"])
def get_weather(location: str) -> str:
"""Get current weather for a location."""
return f"Sunny, 22C in {location}"
Metadata best practices
Use trace metadata to make runs searchable and comparable in the dashboard:
nexus.start_trace(
name=f"agent: {task[:60]}",
metadata={
"task": task,
"model_id": "Qwen/Qwen2.5-72B-Instruct",
"agent_type": "ToolCallingAgent", # or CodeAgent
"step_budget": 10, # max_steps passed to agent.run()
"tool_count": len(tools),
"environment": "production", # or "staging", "dev"
},
)
With agent_type and model_id in metadata, you can filter traces in the Nexus dashboard to compare different agent configurations on the same tasks.
Ready to instrument your Smolagents?
Free tier, no credit card. Add tracing to your first Smolagents run in minutes.