Agentomatic Studio ¶
Agentomatic Studio is a built-in visual development environment for debugging, inspecting, and tracing the execution of your agents in real-time. It works with any agent framework — LangGraph, LangChain, class-based (BaseGraphAgent), or any Python framework via GenericAdapter — through a universal adapter system.
Studio vs Chat Interface
Agentomatic provides two debug interfaces optimized for different workflows:
| Agentomatic Studio (this page) | Chat Interface | |
|---|---|---|
| Purpose | Visual debugging & state inspection | Conversational testing & feedback |
| Launch flag | --studio |
--with-ui |
| URL | /studio/ui/ |
/chat |
| Best for | Graph visualization, time-travel, breakpoints, state editing | Response quality testing, prompt A/B, feedback collection |
| Interface | Node graph + debug panels (React) | Chat bubbles (Chainlit) |
Studio is the primary debug tool. Use it when developing and debugging agents. Use the Chat Interface for conversational testing and evaluation.
Quick Start¶
The Studio is bundled directly into the agentomatic pip package. No separate setup required.
The unified platform starts serving your API endpoints at http://localhost:8000 and the Studio UI at http://localhost:8000/studio/ui/.
Quick Demo
Want to try Studio without setting up any agents? Use the built-in demo command:
This scaffolds a temporary demo agent and launches the platform with Studio enabled, giving you an instant hands-on experience with graph visualization, streaming, and state inspection.
Schema-driven input forms¶
Studio fetches GET /studio/agents/{name}/schemas and renders a dynamic
SchemaForm from the agent's JSON Schema (same idea as LangGraph Studio).
- Required fields, types, and descriptions come from
input_schema - Form ↔ Raw JSON toggle for power users
output_schemais shown as a read-only summary so you know what to expect while debugging
Declare schemas in agents/<name>/schemas.py (CustomInvokeRequest /
CustomInvokeResponse or <Agent>Request / <Agent>Response Pydantic models).
Framework Support¶
Agentomatic Studio uses a universal adapter system to provide the best possible debugging experience for every agent framework:
| Capability | LangGraph | Deep Agent | LangChain | Custom / Raw Python | With Decorators |
|---|---|---|---|---|---|
| Graph Topology | ✅ Real graph | ✅ Real graph + planning nodes | ✅ LCEL extraction or synthetic chain | ✅ Synthetic linear | ✅ Custom graph |
| SSE Node Streaming | ✅ astream_events |
✅ astream_events + subagent events |
✅ astream_events (v2) |
✅ Trace-based | ✅ Custom stream |
| Time-Travel History | ✅ Checkpointer | ✅ Checkpointer | ✅ In-memory traces | ✅ In-memory traces | ✅ In-memory traces |
| State Inspection | ✅ Checkpointer | ✅ Checkpointer | ✅ Message + I/O capture | ✅ Last I/O capture | ✅ Custom provider |
| State Mutation | ✅ aupdate_state |
✅ aupdate_state |
⚠️ In-memory only | ⚠️ In-memory only | ⚠️ In-memory only |
| Breakpoints | ✅ interrupt_before |
✅ Interrupt + middleware | ❌ | ❌ | ❌ |
| HITL Support | ✅ Native | ✅ Native + resume | ❌ | ❌ | ❌ |
| Subagent Tracking | ❌ | ✅ subagent_start/end |
❌ | ❌ | ❌ |
| Task Planning | ❌ | ✅ task_update events |
❌ | ❌ | ❌ |
Using LangChain Deep Agents?
See the dedicated Deep Agent Integration Guide for setup, subagent tracking, HITL interrupts, and the deepagent scaffold template.
Adapter Selection is Automatic
The Studio automatically selects the best adapter based on the framework field in your agent's manifest. You don't need to configure anything — just set framework="langgraph", "graph_agent", "langchain", or "custom" in your AgentManifest.
LangGraph Integration¶
LangGraph agents get the richest Studio experience with full graph extraction, checkpointer-based state management, breakpoints, and human-in-the-loop support.
Example: LangGraph Agent¶
# agents/researcher/__init__.py
from agentomatic import AgentManifest
from .graph import get_graph
manifest = AgentManifest(
name="researcher",
slug="researcher-agent",
description="Multi-step research agent with web search",
framework="langgraph", # ← Triggers the LangGraphAdapter
)
graph_fn = get_graph # Export the compiled graph factory
# agents/researcher/graph.py
from langgraph.graph import StateGraph, START, END
from .nodes import search_web, analyze_results, write_report
def get_graph():
builder = StateGraph(dict)
builder.add_node("search", search_web)
builder.add_node("analyze", analyze_results)
builder.add_node("report", write_report)
builder.add_edge(START, "search")
builder.add_edge("search", "analyze")
builder.add_conditional_edges(
"analyze",
lambda state: "report" if state.get("ready") else "search",
)
builder.add_edge("report", END)
return builder.compile()
When launched with --studio, the Studio automatically:
- Extracts the real graph topology from the
CompiledGraph - Streams node transitions via
astream_events— nodes pulse and light up as execution progresses - Captures checkpoints for time-travel debugging and state replay
- Supports breakpoints — pause execution before any node
- Enables state editing — modify graph state mid-execution via
aupdate_state
LangChain Integration¶
Agentomatic Studio provides first-class support for LangChain-based agents, chatbots, and LCEL chains. When your agent's manifest declares framework='langchain', the Studio automatically uses the dedicated LangChainAdapter.
Automatic Features¶
The LangChain adapter automatically provides:
- LCEL graph extraction — If your chain/runnable exposes
.get_graph(), Studio extracts the real topology. - Synthetic chain graph — If no
.get_graph()is found, Studio renders a typical chain layout:Input → Prompt → LLM → Output Parser → Output. - Rich SSE streaming — If
astream_eventsis available on the runnable, the Studio streamson_chain_start,on_chain_end,on_chat_model_stream,on_tool_start,on_tool_end, andon_llm_start/endevents in real-time. - Automatic message tracking — Captures conversation messages per thread for the State tab.
Example: LangChain Chatbot¶
# agents/chatbot/__init__.py
from agentomatic.core.manifest import AgentManifest
manifest = AgentManifest(
name="chatbot",
slug="my-langchain-chatbot",
description="A conversational chatbot using LangChain",
framework="langchain", # ← This triggers the LangChain adapter
)
async def node_fn(state: dict) -> dict:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("human", "{query}"),
])
llm = ChatOpenAI(model="gpt-4o-mini")
chain = prompt | llm
result = await chain.ainvoke({"query": state["current_query"]})
return {"response": result.content}
That's it! Drop this agent into your agents/ folder and launch with agentomatic run --studio. The Studio will automatically:
- Show a chain-style graph in the Graph View
- Stream LLM tokens in real-time via SSE
- Track conversation state per thread
- Record execution history for the History tab
Advanced: Exposing LCEL Graphs¶
For richer graph visualization, export your runnable as a module-level variable named chain, runnable, or agent. The LangChainAdapter will discover it and extract the real LCEL graph:
# agents/rag_bot/__init__.py
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "Answer based on the context: {context}"),
("human", "{query}"),
])
llm = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()
# Export as module-level — Studio will discover this automatically
chain = prompt | llm | parser
Example: LangChain Agent with Tools¶
# agents/tool_agent/__init__.py
from agentomatic.core.manifest import AgentManifest
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
manifest = AgentManifest(
name="tool_agent",
slug="langchain-tool-agent",
description="Agent with tool calling via LangChain",
framework="langchain",
)
@tool
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
return f"The weather in {city} is 22°C and sunny."
@tool
def search_database(query: str) -> str:
"""Search the internal knowledge database."""
return f"Found 3 results for: {query}"
llm = ChatOpenAI(model="gpt-4o-mini")
tools = [get_weather, search_database]
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with access to tools."),
("placeholder", "{chat_history}"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
runnable = AgentExecutor(agent=agent, tools=tools) # (1)!
async def node_fn(state: dict) -> dict:
result = await runnable.ainvoke({"input": state["current_query"]})
return {"response": result["output"]}
- Exported as
runnable— Studio auto-discovers this for graph extraction
Generic / Raw Python Integration¶
For agents that don't use LangGraph or LangChain, the Studio provides a GenericAdapter that wraps your node_fn() with timing, I/O capture, and trace events.
Example: Raw Python Agent¶
# agents/classifier/__init__.py
from agentomatic import AgentManifest
manifest = AgentManifest(
name="classifier",
slug="text-classifier",
description="Classifies text into categories",
framework="custom", # ← Triggers the GenericAdapter
)
async def node_fn(state: dict) -> dict:
query = state.get("current_query", "")
# Your custom logic — any Python code
if "urgent" in query.lower():
category = "high-priority"
elif "question" in query.lower():
category = "inquiry"
else:
category = "general"
return {
"response": f"Classified as: {category}",
"metadata": {"category": category, "confidence": 0.95},
}
The Studio will show a synthetic linear graph (__start__ → classifier → __end__) and capture execution timing, inputs, and outputs.
Key Features¶
1. Live Node Streaming¶
When you execute an agent query, the Graph View maps directly to your agent's topology. As the execution progresses, nodes pulse and light up in real-time.
- LangGraph agents: Server-Sent Events stream node transitions directly from
astream_events. - LangChain agents: SSE events stream
on_chain_start,on_chat_model_stream,on_tool_start/endevents. - Other agents: The generic adapter wraps execution with trace events that capture timing, input/output payloads, and exceptions.
2. Time-Travel Debugging¶
Agentomatic records every execution step for historical replay.
- History View: The Time Travel tab lists all past checkpoints (LangGraph) or execution traces (other frameworks).
- Replay: Click "Replay from here" on any snapshot to branch your thread and resume from that state.
Framework limitations
Full checkpoint-based time-travel is available for LangGraph agents only. Other frameworks use in-memory trace stores which provide history viewing but limited replay capabilities.
3. Conditional Breakpoints¶
Freeze execution before a critical node (LangGraph only).
- Setting Breakpoints: Right-click any node in the Graph View → "Add Breakpoint".
- Execution: The graph pauses before the target node. The node pulses, and the thread is suspended.
- Resuming: Resume execution or edit the state before continuing.
4. Live State Editing¶
During a breakpoint pause or HITL interrupt, you can mutate the graph state.
- State View: Navigate to the State tab in the Debug Console.
- Editing: Click "Edit State", modify the JSON, and click "Save".
- LangGraph: Changes are persisted via
graph.aupdate_state(). - Other frameworks: Changes are stored in the in-memory trace store.
Studio Decorators¶
For non-LangGraph agents, you can incrementally opt-in to richer Studio capabilities using decorators. These let you provide custom graph topologies, state providers, and stream functions.
@studio_graph¶
Register a custom graph topology for your agent:
from agentomatic.studio import studio_graph
@studio_graph
def my_topology():
return {
"nodes": [
{"id": "__start__", "name": "Start", "type": "start"},
{"id": "fetch_data", "name": "Fetch Data", "type": "tool"},
{"id": "process", "name": "Process", "type": "agent"},
{"id": "validate", "name": "Validate", "type": "condition"},
{"id": "__end__", "name": "End", "type": "end"},
],
"edges": [
{"source": "__start__", "target": "fetch_data"},
{"source": "fetch_data", "target": "process"},
{"source": "process", "target": "validate"},
{"source": "validate", "target": "__end__", "condition": "valid"},
{"source": "validate", "target": "process", "condition": "retry"},
]
}
@studio_state¶
Register a custom state provider:
from agentomatic.studio import studio_state
@studio_state
async def get_my_state(thread_id: str) -> dict:
"""Return the current state for a thread."""
return await my_database.get_thread_state(thread_id)
@studio_stream¶
Register a custom SSE event stream:
from agentomatic.studio import studio_stream
from agentomatic.studio.models import StudioRunEvent
@studio_stream
async def my_streamer(state, config, breakpoints):
yield StudioRunEvent(event="node_start", run_id="", timestamp="...", node="my_node")
result = await my_agent.process(state)
yield StudioRunEvent(event="node_end", run_id="", timestamp="...", node="my_node", data={"output": result})
Combining decorators
You can use any combination of decorators. For example, provide a custom graph topology while using the default trace-based streaming:
The agentomatic demo Command¶
For a quick hands-on experience with Studio, use the built-in demo command:
This command:
- Scaffolds a temporary demo agent with a pre-built LangGraph workflow
- Launches the platform with Studio enabled
- Opens the Studio UI in your default browser
It's the fastest way to see Studio's graph visualization, node streaming, and state inspection in action.
Demo agents are temporary
The demo agent is created in a temporary directory and cleaned up when the server stops. To create a permanent agent, use agentomatic init instead.
Architecture¶
The Studio uses a layered adapter architecture:
graph TB
subgraph Frontend["Studio Frontend (React)"]
UI["Studio UI at /studio/ui/"]
end
subgraph Router["Studio Router (FastAPI)"]
SR["Studio API Endpoints at /studio/*"]
AF["Adapter Factory"]
end
subgraph Adapters["Framework Adapters"]
LGA["LangGraphAdapter<br/>(full features)"]
GAA["GraphAgentAdapter<br/>(class-based agents)"]
LCA["LangChainAdapter<br/>(LCEL + streaming)"]
GA["GenericAdapter<br/>(trace-based)"]
CA["CustomAdapter<br/>(user decorators)"]
end
UI --> SR
SR --> AF
AF --> LGA
AF --> GAA
AF --> LCA
AF --> GA
AF --> CA
- Studio Router: Framework-agnostic FastAPI endpoints that delegate to adapters.
- Adapter Factory: Automatically selects the best adapter based on the agent's
frameworkfield and available decorators. - LangGraphAdapter: Full-featured — uses
CompiledGraphAPIs natively for graph extraction, checkpoint state, breakpoints, and HITL. Triggered byframework="langgraph"/"deepagent"(or a non-class agent withgraph_fn). - GraphAgentAdapter: For class-based agents (
BaseGraphAgent). Provides graph topology extraction, streaming, and trace support. Triggered byframework="graph_agent"or a registeredclass_instance— resolved before the genericgraph_fn→ LangGraph branch, because class agents also exposegraph_fn. - LangChainAdapter: Extracts LCEL graphs via
.get_graph(), streams viaastream_events, and tracks messages per thread. Triggered byframework="langchain". - GenericAdapter: Trace-based — wraps
node_fn()with timing and I/O capture for any Python agent. Fallback forframework="custom"or unknown frameworks. - Custom Adapter: User-registered via
@studio_graph,@studio_state, and@studio_streamdecorators.
API Endpoints¶
| Endpoint | Method | Description |
|---|---|---|
/studio/info |
GET | Platform metadata and capabilities |
/studio/agents |
GET | List agents with Studio capabilities |
/studio/agents/{name}/graph |
GET | Graph topology (real or synthetic) |
/studio/agents/{name}/schemas |
GET | Input/output JSON schemas |
/studio/agents/{name}/runs/stream |
POST | SSE-streamed execution |
/studio/agents/{name}/threads/{tid}/state |
GET | Thread state snapshot |
/studio/agents/{name}/threads/{tid}/state |
POST | Update thread state (LangGraph: persistent) |
/studio/agents/{name}/threads/{tid}/history |
GET | Checkpoint/trace history |
Troubleshooting¶
Studio page shows a blank screen
Ensure you have the studio extra installed:
agentomatic doctor.
Graph shows a generic linear layout instead of my real graph
- LangGraph: Ensure your
graph_fnreturns aCompiledGraph(not aStateGraph). Call.compile(). - LangChain: Export your chain as a module-level variable named
chain,runnable, oragent. - Custom: Use the
@studio_graphdecorator to register a custom topology.
SSE streaming doesn't show real-time node updates
- Check that your browser supports Server-Sent Events (all modern browsers do).
- For LangChain agents, ensure you're using a runnable that supports
astream_events(v2). - Check the browser's Network tab for SSE connection issues.