LLM agent output is a token stream that’s hostile to humans. PlantUML is the bridge between agents turning their “thinking” into structured, readable diagrams — a practical guide for LangChain / AutoGPT / CrewAI.
The interpretability headache for agents
Today’s pain points:
Process opaque: reasoning trace is implicit prompt chain; developers can’t see why a decision was made.
Hard to debug: when an agent fails, you don’t know which step broke.
Multi-agent complexity: a 20-step agent task → rerun, hope it works.
Weak human-in-the-loop: humans want to intervene, but agents don’t expose “I’m currently thinking X”.
PlantUML is the bridge.
Main use cases
1. Decision-tree visualisation (ReAct agent)
ReAct is “Reasoning + Acting” alternating:
1 2 3 4 5
Thought: I need to find X Action: search(query) Observation: result of search Thought: based on result, I now do Y Action: ...
Value: at a glance, developers see which observation didn’t drive reasoning forward → check the prompt / tool.
2. Multi-agent collaboration (CrewAI / AutoGen)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@startuml left to right direction skinparam rectangle<<agent>> { BackgroundColor<<researcher>> #FFE0E0 BackgroundColor<<coder>> #E0FFE0 BackgroundColor<<critic>> #E0E0FF }
rectangle "Researcher" <<researcher>> as r rectangle "Coder" <<coder>> as c rectangle "Critic" <<critic>> as crit
r -> c : "research findings" c -> crit : "draft implementation" crit -> c : "feedback" crit -> r : "ask for more research" @enduml
This is a state machine, not a call stack — it expresses information flow between agents.
# Usage cb = PlantUMLCallback() agent.invoke({"input": "..."}, config={"callbacks": [cb]}) print(cb.to_puml()) # Render → see the decision process in the browser
Under the hood: NetworkX + matplotlib. To get PlantUML: regenerate.
1 2 3 4 5 6 7 8 9
defcrew_to_puml(crew): puml = "@startuml\n" for agent in crew.agents: puml += f'rectangle "{agent.role}" as {agent.id}\n' for task in crew.tasks: for agent in task.assigned_agents: puml += f"{agent.id} --> {task.id}\n" puml += "@enduml" return puml
PlantUML exposes → humans can clearly see where to pause, add constraints:
1 2 3 4 5 6 7 8 9 10 11 12
classInterventionHook: def__init__(self, mas): self.mas = mas defcheck(self, agent_output): for rule inself.rules: if rule.matches(agent_output): return HumanInterventionRequest( agent=agent_output.agent, reason=rule.reason, diagram=self.mas.render() # current state )
HumanInterventionRequest attaches the current PlantUML state; humans see a visual snapshot of the current collaboration and decide “approve / modify / abort”.
vs other visualisation tools
Tool
Used by
Best for
LangSmith
LangChain
trace timeline, token counts
LangGraph viz
LangGraph
graph statics
PlantUML
Cross-framework
Easy doc embed, version control, PDF export
Mermaid
LangGraph default
Browser rendering
matplotlib
CrewAI default
Static PNG
PlantUML advantages:
Text is diff-friendly → lives in git
Multiple output formats (SVG/PNG/PDF) → embed in PPTs
CJK readable → Chinese papers
!include for sub-diagram reuse
Project case: two ReAct agents collaborating
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@startuml rectangle "User Query" as UQ rectangle "Agent A (Researcher)" as A rectangle "Agent B (Coder)" as B database "Tool Cache" as TC
UQ --> A : task A --> TC : search TC --> A : result A --> B : handoff to coder B --> A : ask for more A --> B B --> UQ : final output @enduml
try: result = agent.run("Tell me about plantuml.com") except Exception as e: print("Error:", e) finally: print(cb.to_puml()) # → write to /var/log/agent-trace.svg
When the agent fails, output the state-machine snapshot for post-mortem.
Recap
PlantUML has 4 roles in agent scenarios: debug visualisation, flow narrative, human-intervention snapshot, doc embed.