Skip to Content
ObservabilityAgentGraph visualization

AgentGraph visualization

build_graph draws a picture of how an agent or a Workflow is put together: its architecture, every sub-agent, the tools each one can call, and a workflow’s steps and routes. The result renders as a Mermaid  diagram. Use it to:

  • Document a team layout in a README or design doc.
  • Debug wiring: see at a glance whether a worker is connected where you think it is, and which tools it has.
  • Onboard someone: a new engineer reads the diagram before the code.

This is a drawing only; nothing in it runs. To see the architecture of every version your app has run, with the path each request took highlighted, turn on Studio: it draws the same graph.

Quick render

build_graph is async (listing tools can go through an MCP server), so await it. It returns an AgentGraph; call to_mermaid() on it for the diagram text.

from loomflow import Agent, tool from loomflow.graph import build_graph from loomflow.team import Team @tool async def web_search(query: str) -> str: """Search the web.""" return "results" researcher = Agent("Find sources for the topic.", model="echo", tools=[web_search]) writer = Agent("Write the draft.", model="echo") team = Team.supervisor( workers={"researcher": researcher, "writer": writer}, instructions="Manage the research-and-write pipeline.", model="echo", ) graph = await build_graph(team) print(graph.to_mermaid())

Output (the classDef styling lines are trimmed here):

Each agent is labelled with its name= when it has one, else the first line of its instructions (cut at 60 characters). Node ids are stable (a0, a1 for agents, t0, t1 for tools), so the output diffs cleanly between runs.

Every Agent also has a shortcut that does the same thing:

mermaid_text = await team.generate_graph() # returns the text await team.generate_graph("docs/team.md") # also writes a file

Workflows

Pass a Workflow and the graph shows its steps: a start and end node, each step, and the edges between them. Routes are labelled with their keys and a default route is dashed. An agent in a node is drawn with its tools (and its own team, if it has one); a nested workflow is a node with a dashed runs edge into its own group; a Workflow.parallel step has an in parallel edge to each branch.

from loomflow import Agent, Workflow from loomflow.graph import build_graph async def classify(text: str) -> str: return "billing" if "invoice" in text else "tech" billing = Agent("You handle billing.", model="echo", name="billing") tech = Agent("You fix tech issues.", model="echo", name="tech-support") router = Workflow.route(classify, {"billing": billing, "tech": tech}, name="support-router") graph = await build_graph(router, title="support-router") print(graph.to_mermaid())

As data

graph.to_dict() returns the same graph as JSON-ready data, to draw it your own way or to diff two versions:

data = graph.to_dict() data["title"] # "support-router" data["nodes"][2] # {"id": "a0", "label": "billing", "kind": "subagent", # "keys": ["billing", "route_billing"], # "detail": "node route_billing"} data["edges"][-2] # {"source": "n0", "target": "a0", # "label": "billing", "style": "solid"} data["subgraphs"] # []

A node’s kind is one of agent, subagent, tool, step, workflow, start, end, store (a lesson store) or blackboard. Its keys are the names recorded runs use for it (an agent’s name, the workflow node it runs as, a tool’s name), which is how Studio highlights the parts a run used. detail is a short note: a team’s architecture, or the node an agent runs as. Pass key_for=lambda agent: ... to build_graph to choose how agents are keyed (the default is their name=).

Write to disk

write_graph(agent, path) renders the graph and writes it to path. The file extension picks the format:

ExtensionWhat gets written
.mmdRaw Mermaid source.
.mdA Markdown file with a # <title> heading and the diagram in a mermaid fence. Renders on GitHub, GitLab, and most IDE previews.
.png / .svgAn image rendered by the public mermaid.ink  service (needs network access).
anything elseRaw Mermaid source.
from loomflow.graph import write_graph await write_graph(team, "docs/team.md") # Markdown + diagram await write_graph(team, "docs/team.mmd") # Mermaid source await write_graph(team, "docs/team.svg", title="Team") # image via mermaid.ink

write_graph returns the Mermaid text in every case. title= sets the heading of the .md file and defaults to the file name without its extension.

If mermaid.ink can’t be reached for a .png / .svg, write_graph writes the Mermaid source next to the requested path (same name, .mmd extension) and raises RuntimeError saying where it put it. You can paste that file into mermaid.live  to render it.

What gets included

The walk starts at the agent you pass and follows its architecture down:

  • Every agent, labelled with its name or from its instructions. The same Agent instance reached twice is drawn once.
  • Every tool each agent can call, including tools that come from an MCP server. There is no switch for this; tools are always drawn, as parallelogram nodes with a uses edge.
  • Sub-agents, recursively. A worker that is itself a Supervisor, or a Reflexion loop around a Supervisor, appears as a nested group.
  • The relationships each architecture creates:
ArchitectureWhat the diagram shows
SupervisorCoordinator to each worker, with delegate and (dashed) forward edges. Workers sit in a workers group.
RouterClassifier to each route with a classify: <name> edge, in a group noting that only one route runs.
SwarmAn entry edge to the entry peer, then dashed handoff edges between every pair of peers.
ActorCriticActor to critic (critique) and a dashed refine if below threshold edge back.
MultiAgentDebateEach debater in a group, with responses edges to the judge.
BlackboardA shared blackboard node that every agent reads and writes, plus the coordinator and decider.
ReflexionA Reflexion retry loop group around the base architecture, plus a lesson_store node when one is set.
ReActJust the agent and its tools.

A custom architecture is drawn from its declared_workers(): one edge from the parent to each named worker.

Why not OTel traces?

The two answer different questions. OTel traces show what happened during one specific agent.run(). AgentGraph shows the shape of your agent, which is the same whatever the input.

QuestionTool
”What does this agent’s structure look like?”AgentGraph
”Why was this run slow?”OTel traces
”Which worker was called for this prompt?”OTel traces
”Could the supervisor delegate to the reviewer at all?”AgentGraph

Keep README diagrams in sync. Run a small script like this in your release process and the diagram always matches the code:

import asyncio from loomflow.graph import write_graph from myapp import team # your Agent or Team asyncio.run(write_graph(team, "docs/team.md"))
Last updated on