A data-analysis agent takes a business question, plans an approach, writes and executes analysis code, and returns a chart plus a narrative — the pattern behind every "talk to your data" product.
Tier: Advanced · Workflow area: Agentic systems
The difference between a demo and something you can trust is twofold: the code runs in a sandbox that cannot touch production, and every headline number is re-computed once through an independent path before it ships.
Skip the agent for one-off analyses — driving an AI assistant interactively in a notebook is faster and safer. Agents pay off on the tenth repetition, not the first.
exec() model-generated code in your host process.Build the sandbox first. A minimal Docker approach: image with pandas, matplotlib, duckdb; run with --network none, a read-only volume mount for /data, a writable /output, a 60-second timeout, and a memory cap. The executor function takes a code string, runs it, returns stdout/stderr and any files written to /output.
Define the agent with one code tool. Using the Anthropic Python SDK's tool runner, the loop (call model → execute tool → feed result back → repeat) is handled for you:
import anthropic
from anthropic import beta_tool
client = anthropic.Anthropic()
MAX_STEPS = 12
steps = {"n": 0}
@beta_tool
def run_python(code: str) -> str:
"""Execute Python in the analysis sandbox and return stdout/stderr.
Args:
code: Python code. Data is at /data (read-only); write charts to /output.
"""
steps["n"] += 1
if steps["n"] > MAX_STEPS:
return "STEP BUDGET EXHAUSTED. Summarize findings from results so far."
return sandbox.run(code, timeout=60) # your Docker/E2B executor
runner = client.beta.messages.tool_runner(
model="claude-opus-4-8",
max_tokens=16000,
tools=[run_python],
messages=[{"role": "user", "content": TASK_PROMPT}],
)
for message in runner:
pass # last message is the final answer
Write the system/task prompt as an analyst's contract. Require: state a plan before the first tool call; profile the data before analyzing it (dtypes, nulls, date ranges); every claimed number must come from executed code, not memory; end with a chart file path and a 5-sentence narrative.
Add the independent verification step. After the agent finishes, send a fresh request (new conversation, same sandbox): "Re-compute only the headline number X using a different method than this transcript used, and report agreement or disagreement." Disagreement blocks delivery.
Run it on three real questions you have previously answered manually. Compare the agent's numbers against your known-good answers before showing anyone else.
The sandbox is not optional hardening — model-generated code will eventually try to install packages, hit the network, or read paths you did not intend, and the sandbox is what turns that from an incident into a log line.
{"type": "code_execution_20260120", "name": "code_execution"} in tools and upload the file via the Files API; you trade infra control for zero sandbox maintenance.pip install a package and to read /etc/passwd; both must fail.DROP or a runaway full-table scan and you own the incident. Extract to files first.For client or regulated data: run the executor fully on-prem (local Docker), and swap the hosted model for a self-hosted one served by vLLM or Ollama with an OpenAI-compatible endpoint — the tool-calling loop is identical. Redact or pseudonymize column names and sample values in the prompt if even schema metadata is sensitive; the agent only needs the schema shape, not real customer identifiers.
Tool-call transcripts contain data samples the model printed while exploring — treat stored transcripts with the same access controls as the underlying dataset.