PlantUML 在 LLM Agent 中的角色:让 AI 推理过程可视化与人机协同

puml.online

LLM Agent 输出的 token 流对人不友好。PlantUML 是 Agent 把「思考」转成结构化可视图的优秀工具——本文整理 LangChain / AutoGPT / CrewAI 框架下 PlantUML 的角色。

Agent 的可解释性困境

当前 LLM Agent 的痛点:

  • 过程不透明:reasoning trace 是隐性 Prompt 链,开发者看不到决策依据
  • 结果不好调试:Agent 失败时,你不知道是哪一步出问题
  • 多 Agent 复杂:20 步 Agent 步骤出错后只能「重跑」
  • 人机协同弱:人想中途插入指令,但 Agent 没暴露「我现在在想 X」

PlantUML 是 Agent 自我表达 + 人介入的桥梁。

主要用例

1. 决策树可视化(ReAct Agent)

ReAct Agent 是「Reasoning + Acting」交替框架。每一步:

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: ...

把 trace 转 PlantUML activity:

1
2
3
4
5
6
7
8
ReAct Agent Flow
├── Thought: search
├── Action: search("plantuml")
├── Observation: 100 results
├── Thought: pick top 5
├── Action: rank()
├── Observation: top 5
└── Final: 5 results

PlantUML 输出:

1
2
3
4
5
6
7
8
9
10
11
@startuml
start
:Thought: search;
:Action: search("plantuml");
:Observation: 100 results;
:Thought: pick top 5;
:Action: rank();
:Observation: top 5;
:Final: 5 results;
stop
@enduml

价值:开发者一眼能看出「哪一步 observation 没推动 reasoning → 检查 prompt / 工具」。

2. 多 Agent 协作(CrewAI / AutoGen)

多 Agent 框架的「对话图」:

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

这个图是状态机而不是调用栈——表达的是「Agent 之间的信息流转」。

3. 工具调用决策(Function Calling)

function_call 决策也可以画出来:

1
2
3
4
5
6
7
8
9
10
11
12
@startuml
(*) --> "收到 user query"
if "query 涉及数据库?" then
-->[yes] "调用 db_query tool"
--> "得到 records"
--> "格式化"
--> "返回"
else
-->[no] "直接 LLM 回答"
--> "返回"
endif
@enduml

工程实现

在 LangChain 里追踪 trace

LangChain 有 langchain.debug = True 输出详细 trace,但人类读 nested 不友好

1
2
3
4
5
6
7
8
import langchain
from langchain.agents import AgentExecutor
from langchain.tools import tool

langchain.debug = True

agent = AgentExecutor(...)
result = agent.invoke({"input": "What is PlantUML?"})

输出:

1
2
3
4
5
6
[chain/start] [1:chain:AgentExecutor]
[llm/start] ...
[tool/start] search
[tool/end] ...
[llm/end] ...
[chain/end]

自定义 callback 输出 PlantUML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from langchain.callbacks.base import BaseCallbackHandler

class PlantUMLCallback(BaseCallbackHandler):
def __init__(self):
self.steps = []

def on_agent_action(self, action, **kwargs):
self.steps.append(f"Action: {action.tool}, Input: {action.tool_input}")

def on_agent_finish(self, finish, **kwargs):
self.steps.append(f"Finish: {finish.return_values}")

def to_puml(self):
puml = "@startuml\nstart\n"
for s in self.steps:
puml += f":{s};\n"
puml += "stop\n@enduml\n"
return puml

# 用法
cb = PlantUMLCallback()
agent.invoke({"input": "..."}, config={"callbacks": [cb]})
print(cb.to_puml())
# 渲染 → 在浏览器看决策过程

Trace 输出到 console

1
2
3
4
5
6
7
8
9
10
11
import plantuml
puml_server = plantuml.PlantUML(url='http://www.plantuml.com/plantuml/svg/')

def render_trace(steps):
puml = "@startuml\nstart\n"
for s in steps:
puml += f":{s};\n"
puml += "stop\n@enduml\n"
return puml_server.processes(puml)

# 调 /var/log/agent-trace.svg,方便 devops 看

在 web UI 里动态显示

1
2
3
4
5
6
7
8
9
// 实时把 trace 渲染到控制台
const pumlEncoder = require('plantuml-encoder');

async function fetchAndRender(traceId) {
const trace = await fetch(`/api/agent/trace/${traceId}`).then(r => r.json());
const puml = traceToPuml(trace);
const encoded = pumlEncoder.encode(puml);
document.getElementById('trace-img').src = `https://www.plantuml.com/plantuml/svg/~1${encoded}`;
}

LangSmith / LangGraph 的 PlantUML

LangSmith 把 trace 可视化但不是 PlantUML手动导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# LangGraph
from langgraph.graph import StateGraph

graph = StateGraph(MyState)
graph.add_node("search", search_node)
graph.add_node("rank", rank_node)
graph.add_edge("search", "rank")

# 手动导出 graph.adjacency 为 PlantUML
def to_puml(graph):
puml = "@startuml\n"
for src, dst in graph.edges:
puml += f"{src} --> {dst}\n"
puml += "@enduml"
return puml

with open("/tmp/agent-graph.puml", "w") as f:
f.write(to_puml(graph))

plantuml -tsvg /tmp/agent-graph.puml → 看可视化。

CrewAI 的内置可视化

CrewAI 有 crew.plot() 输出任务图:

1
2
crew = Crew(agents=[...], tasks=[...])
crew.plot() # 输出 tasks_graph.png

底层用 NetworkX + matplotlib。如果你想用 PlantUML:重新生成:

1
2
3
4
5
6
7
8
9
def crew_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

ReWOO / Plan-and-Execute Agent 的 Plan

ReWOO 类 Agent(先写 plan,再逐步执行):

1
2
3
4
5
6
7
8
9
10
11
@startuml
(*) --> "Plan:"
--> "Step 1: search"
--> "Step 2: extract"
--> "Step 3: synthesize"
--> "Execute plan:"
--> "Run step 1"
--> "Run step 2"
--> "Run step 3"
--> "Final answer"
@enduml

这是 PlantUML 状态机 + activity 混合图——只展示决策流向。

多 Agent 状态机的实现

让 Multi-Agent 系统自我解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MultiAgentSystem:
def __init__(self):
self.transitions = []

def transition(self, from_agent, to_agent, reason):
self.transitions.append({
"from": from_agent,
"to": to_agent,
"reason": reason
})

def render(self):
puml = "@startuml\n"
# 加所有 agent
for agent in set(a["from"] for a in self.transitions) | set(a["to"] for a in self.transitions):
puml += f'rectangle "{agent}" as {agent}\n'
for t in self.transitions:
puml += f"{t['from']} --> {t['to']} : {t['reason']}\n"
puml += "@enduml"
return puml

调试时:

1
2
3
4
5
6
mas = MultiAgentSystem()
mas.transition("user", "router", "ask plan")
mas.transition("router", "researcher", "needs data")
mas.transition("researcher", "critic", "review")
# ...
print(mas.render())

输出 plantuml → 看 agent 协作流。

调试协作时的「人介入」

PlantUML 图暴露 → 人能清晰地看到「我应该在哪儿按暂停、加约束」:

1
2
3
4
5
6
7
8
9
10
11
12
class InterventionHook:
def __init__(self, mas):
self.mas = mas

def check(self, agent_output):
for rule in self.rules:
if rule.matches(agent_output):
return HumanInterventionRequest(
agent=agent_output.agent,
reason=rule.reason,
diagram=self.mas.render() # 当前状态
)

HumanInterventionRequest 把当前 PlantUML 状态附加,人看到一个当前协作的可视化快照,决定「approve / modify / abort」。

vs 其他可视化

工具 谁用 适合什么
LangSmith LangChain trace timeline、token 计数
LangGraph viz LangGraph graph statics
PlantUML 跨框架 易于嵌文档、版本控制、PDF 导出
Mermaid LangGraph 默认 浏览器渲染
matplotlib CrewAI 默认 静态 PNG

PlantUML 优势

  • 文本易 diff → 可放 git
  • 多输出格式(SVG/PNG/PDF)→ 嵌入 PPT
  • CJK 可读 → 中文 paper
  • !include → 复用子图

实战项目:2 个 ReAct Agent 协作

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

一个完整的例子:debug 一个出错的 Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# debug_agent.py
from langchain_community.tools import DuckDuckGoSearchRun
from my_callback import PlantUMLCallback

cb = PlantUMLCallback()
tools = [DuckDuckGoSearchRun()]

agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
callbacks=[cb]
)

try:
result = agent.run("Tell me about plantuml.com")
except Exception as e:
print("Error:", e)
finally:
print(cb.to_puml())
# → 写到 /var/log/agent-trace.svg

agent 失败时输出失败时的状态机截图,便于复盘。

小结

  • PlantUML 在 Agent 场景下 4 个角色:debug 可视化、流程讲解、人介入快照、文档嵌入
  • 实现成本最低:自定义 callback 输出 puml + 渲染 → SVG
  • 多 Agent 框架图是**「Agent 之间信息流」,不是「程序栈」**——一定要区分

下一步

  • 标题: PlantUML 在 LLM Agent 中的角色:让 AI 推理过程可视化与人机协同
  • 作者: puml.online
  • 创建于 : 2026-07-30 11:18:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-llm-agent/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。