AETIUS

agentstage

agentstage reconstructs a real agentic system, its tools, its orchestration structure, and its system prompts where captured, from that system's own traces (Langfuse or Braintrust). It lets a developer run their actual agent against that reconstruction locally, instead of against real production systems.

Think of it as VCR.py for agent tools, extended to the whole agent. VCR.py records real HTTP interactions once and replays them in tests forever, no live network calls. agentstage does the same thing at the agent level: point it at a Langfuse or Braintrust project, it reconstructs the real system's structure, and the developer's own agent, running on its own real LLM and its own real API key, exactly like production, executes against that reconstruction instead of hitting a real payment API, a real customer database, a real email system, or whatever else the real tools were.

Why a staging environment for agents

A traditional software staging environment is a safe copy of a real system: spin up a copy of the database and services, point traffic at it. Most engineering teams have one. Agents mostly do not, because an agent's environment is not generic infrastructure, it is whatever bespoke real tools it calls. There is no generic way to spin up a copy of a company's real payment API or customer database. Teams either test agent changes against production, which is risky, or do not really test tool-calling behavior at all, which is also risky.

A reconstruction-based staging environment concretely enables:

  • Testing a change without it doing anything real. Change a prompt, swap a model, add a tool, see what the agent actually does (calls issue_refund with what arguments, in what order) without a real refund happening.
  • Fast, free, repeatable iteration. No real API calls means no rate limits, no cost, no waiting on real infrastructure. Run the same test hundreds of times while iterating on a prompt.
  • Reproducible, realistic test cases. Built from real historical traces, not synthetic guesses. Replaying the ten hardest support tickets from last month is a genuinely realistic test suite.
  • Catching regressions before deploy, not after. A prompt change that causes the agent to call the wrong tool, skip a tool, or misread a request gets caught against a realistic mock, before a real user hits it.
  • Debugging without production access. Replaying the exact situation behind "the agent did something weird for this customer" against mocked tools, without needing live production access or real customer data.
  • Lowering the barrier to letting other people work on the agent. A new engineer, a QA person, someone experimenting, none of them need real production credentials or the ability to actually trigger a refund just to poke at how the agent behaves.

The honest limit

agentstage is only as good as what it observed. If a tool call falls well outside anything seen in the traces, it reports unavailable, with the closest candidate it found and how similar that candidate actually was, rather than inventing a plausible answer. A wrong-but-confident guess is worse than an honest gap. The same discipline applies to system prompts and orchestration structure: every piece of reconstructed structure is tagged observed or unavailable, never fabricated.

The LLM boundary

agentstage's own tool-mocking layer never makes an LLM call and never calls a paid API. When the agent calls a tool, matching that call against observed traces and replaying a real historical response, or reporting unavailable, is purely mechanical. No model is involved in that decision.

The staging environment as a whole is built around a real, LLM-driven agent. The developer's agent runs on its own real model and its own real API key, exactly like production, that is the entire point, it is a staging environment for agents. agentstage is one component inside that environment, the tool layer. It is not a claim that LLMs are absent from the picture.

A first look

A real Langfuse project connects with agentstage.from_langfuse(project_id=..., public_key=..., secret_key=..., base_url=...). No live project was available while writing this documentation, so the example below, and every example in docs/, builds an Environment the same way but backed by a small local trace fixture instead (see the note at the end of this README):

Python
from examples.fixtures import build_environment

env = build_environment()
system = env.reconstruct("invoice-agent")
print(system.orchestration.pattern)
print(list(system.tools.keys()))
Output
single_agent
['lookup_invoice', 'send_receipt']

system carries the reconstructed tools (schema plus replay-backed behavior), the orchestration structure (roles and control flow, tagged observed or unavailable), and each role's system prompt (also tagged), the same shape regardless of whether it came from Langfuse or Braintrust; each source's own raw shape is normalized away before reconstruction ever sees it. agentstage.langgraph.adapt(system, ...), agentstage.crewai.adapt(system, ...), and agentstage.agno.adapt(system, ...) wire it into LangGraph, CrewAI, or Agno's own native way of building a multi-agent system, each with a full worked example on its own doc page below.

Documentation

agentstage has two separate plug-in points: sources (where traces come from) and adapters (which framework you plug the reconstruction into).

Sources:

Adapters:

Everything else:

No live Langfuse or Braintrust project was available while writing this documentation. Every example in docs/ runs against a small, realistic local trace fixture standing in for a real project (examples/fixtures.py for Langfuse, examples/braintrust_fixtures.py for Braintrust), shaped like each source's own real trace data, and every example's output shown is the real output of actually running it. The Braintrust source carries an extra caveat the Langfuse one doesn't: its field names were confirmed against the real installed SDK's own source, not against a live account. See docs/sources/braintrust.md and agentstage/sources/braintrust.py's docstring for exactly what that does and doesn't cover. This is stated once here and not repeated on every page.