尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
LangChain Agent 执行图可视化:用 Mermaid 自动生成 Agent 的运行时流程图
LangChain Agent 执行图可视化用 Mermaid 自动生成 Agent 的运行时流程图一、深度引言与场景痛点调试 LangChain Agent 最让人头疼的事不是它报错而是它不报错却给了错误答案。你看不到它在每一步调用了什么工具、传了什么参数、工具返回了什么、又是怎么选到最终答案的。日志打了一地但你得一行行翻效率极低。理想的情况是每次 Agent 跑完自动生成一张执行流程图把工具调用链路、决策分支、耗时标注都画出来。一眼就能看出哪一步重试了三回、哪一步超时了、哪一步的结果被忽略掉了。这件事用 Mermaid 来做出乎意料地顺手。LangChain 的on_tool_start、on_tool_end、on_agent_action这些回调已经记录了足够多的运行时信息我们只需要把它们转换成 Mermaid 语法就行了。二、底层机制与原理深度剖析flowchart LR A[Agent 启动] -- B[LLM 推理] B -- C{需要调用工具?} C --|是| D[记录工具名和参数] C --|否| G[生成最终答案] D -- E[执行工具] E -- F[记录耗时和返回] F -- B G -- H[回调收集器导出 Mermaid] H -- I[生成流程图]这个流程的关键是回调收集器。每次工具调用开始记录一个节点和入边工具返回时记录出边和耗时。最终把节点列表和边列表拼成 Mermaid 语法写入文件。整个过程不侵入 Agent 主逻辑纯粹靠回调完成。这里有个容易踩的坑LangChain 的AgentAction和AgentFinish是两个不同的事件类型。工具调用的开始和结束是靠on_tool_start/on_tool_end捕获的而 LLM 推理的开始和结束要靠on_llm_start/on_llm_end。两种事件都要监听否则图是不完整的。另一个要点是循环处理的标注。Agent 经常会有一个思考-调用-再思考的循环。如果同一个工具被多次调用每次都要生成新节点而不是覆盖旧节点。节点的命名规则建议用{工具名}_{序号}序号从 1 开始递增。这样图里能清楚看到某个工具被调用了 3 次。三、生产级代码实现from __future__ import annotations import time import uuid from dataclasses import dataclass, field from typing import Any, Optional from langchain.callbacks.base import BaseCallbackHandler dataclass class StepNode: id: str label: str step_type: str # llm, tool, output tool_name: str args: str result_preview: str duration_ms: float 0.0 error: str class MermaidTraceCallback(BaseCallbackHandler): 自动收集 Agent 执行轨迹并导出 Mermaid 流程图 def __init__(self, max_steps: int 50): self.nodes: list[StepNode] [] self.edges: list[tuple[str, str, str]] [] # (from_id, to_id, label) self._pending: dict[str, StepNode] {} self._step_counter: dict[str, int] {} self._last_node_id: Optional[str] None self._max_steps max_steps self._start_time time.monotonic() def on_llm_start(self, serialized: dict, prompts: list[str], **kwargs: Any) - None: if len(self.nodes) self._max_steps: return node_id fllm_{uuid.uuid4().hex[:6]} node StepNode(idnode_id, labelfLLM推理, step_typellm) node.args prompts[0][:200] if prompts else self.nodes.append(node) if self._last_node_id: self.edges.append((self._last_node_id, node_id, )) self._last_node_id node_id def on_llm_end(self, response, **kwargs: Any) - None: if self._last_node_id: last self.nodes[-1] last.duration_ms (time.monotonic() - self._start_time) * 1000 def on_tool_start( self, serialized: dict, input_str: str, *, run_id: str, **kwargs: Any ) - None: if len(self.nodes) self._max_steps: return tool_name serialized.get(name, unknown_tool) count self._step_counter.get(tool_name, 0) 1 self._step_counter[tool_name] count node_id ftool_{tool_name}_{count} node StepNode( idnode_id, labelf调用: {tool_name}, step_typetool, tool_nametool_name, argsinput_str[:200], ) self.nodes.append(node) self._pending[run_id] node if self._last_node_id: self.edges.append((self._last_node_id, node_id, 调用工具)) def on_tool_end(self, output: str, *, run_id: str, **kwargs: Any) - None: node self._pending.pop(run_id, None) if node is None: return node.result_preview output[:200] node.error self._last_node_id node.id def on_tool_error(self, error: BaseException, *, run_id: str, **kwargs: Any) - None: node self._pending.pop(run_id, None) if node: node.error str(error)[:200] self._last_node_id node.id def on_agent_finish(self, finish, **kwargs: Any) - None: if len(self.nodes) self._max_steps: return node_id foutput_{uuid.uuid4().hex[:6]} node StepNode( idnode_id, label最终回答, step_typeoutput, result_previewstr(finish.return_values.get(output, ))[:200], ) self.nodes.append(node) if self._last_node_id: self.edges.append((self._last_node_id, node_id, 汇总)) def to_mermaid(self) - str: lines [flowchart TD] safe_id lambda nid: nid.replace(-, _).replace( , ) for node in self.nodes: shape { llm: [\\\{}\\\], tool: [({})], output: [({})], }.get(node.step_type, [({})]) display node.label[:40] lines.append(f {safe_id(node.id)}{shape.format(display)}) for from_id, to_id, label in self.edges: edge_label f|{label[:15]}| if label else lines.append(f {safe_id(from_id)} --{edge_label} {safe_id(to_id)}) return \n.join(lines) def save_mermaid(self, filepath: str) - None: with open(filepath, w, encodingutf-8) as f: f.write(self.to_mermaid())这个实现有几个细节值得展开说。第一_max_steps防止 Agent 无限循环时图也无限增长超过 50 步直接停止收集。第二_step_counter用tool_name做 key 给同名工具递增编号解决循环调用时的节点重名问题。第三on_tool_error单独处理工具失败的情况——失败的调用在图里也应显示标注红色或加错误提示方便排查。to_mermaid方法里做了节点 ID 的清理。Mermaid 的节点 ID 不能有连字符和空格所以用replace统一处理。工具节点的形状用圆角矩形[()]LLM 节点用梯形[/\]输出节点用圆角框([\)])视觉上自然区分三类操作。四、边界分析与架构权衡这个方案最大的问题不是技术上的而是信息量的取舍。Agent 跑一次可能产生上百个事件全部画在图里图会变成蜘蛛网。所以max_steps不只是一个保护参数它其实是信息压缩的决策点哪些步骤值得画、哪些可以合并。一个实用的补充策略是在最终图中只画 LLM 推理和工具调用两层tool 内部的子步骤不再展开。如果某个工具内部耗时异常再用单独的诊断图深入。另一个边界是 Mermaid 的渲染能力。超过 30 个节点时默认的 flowchart 布局可能很乱。这时候建议切换到flowchart LR左到右或stateDiagram-v2后者对树形布局支持更好。也可以在图生成后手动调整subgraph分组把推理性节点和执行性节点分别框起来。性能方面回调本身是同步执行的如果to_mermaid和save_mermaid在 Agent 主流程里做会增加几十毫秒的响应延迟。建议用asyncio.to_thread或一个后台任务异步写文件不影响 Agent 的正常响应。五、总结用回调机制 Mermaid 自动生成 Agent 执行流程图本质上是把运行时观测从事后翻日志变成了即时可视化。LangChain 的回调接口提供了足够的信息源难点在于怎么压缩到一张能看的图里。落地建议设置 50 步上限防止无限循环搞崩图循环调用用递增序号区分节点工具失败在图里显式标注写文件放到后台线程不阻塞主流程。一张清晰的可视化执行图比一屏日志更能帮你理解 Agent 到底怎么想的。
RELATED

相关推荐

Patran中可直接运行的圆柱体参数化建模全套PCL脚本(含菜单、材料、网格、载荷与结果提取)

Patran中可直接运行的圆柱体参数化建模全套PCL脚本(含菜单、材料、网格、载荷与结果提取)

本文还有配套的精品资源,点击获取 简介:这套资源提供开箱即用的Patran圆柱体参数化建模能力,所有PCL脚本均经过实际环境验证,支持一键加载运行。包含几何建模(z轴对齐圆柱、尺寸驱动)、自动网格划分&…

📅 2026/9/9 0:34:07
SpringBoot轻量投票系统:MySQL一键建表+开箱即用部署包

SpringBoot轻量投票系统:MySQL一键建表+开箱即用部署包

本文还有配套的精品资源,点击获取 简介:基于SpringBoot开发的极简投票系统,无需手动建表,启动时自动在MySQL中创建voter数据库所需全部表结构。项目预置Voter数据库连接配置,只需本地安装MySQL并运行服务&#xff0…

📅 2026/8/24 2:30:29
Java做大模型工程化落地的优势

Java做大模型工程化落地的优势

大模型时代,工程化落地才是真正的战场大模型的技术突破令人兴奋——但对企业来说,兴奋归兴奋,真正的战场不在"模型有多聪明",而在"AI能不能可靠地落地到业务中"。这就是大模型工程化落地的问题——把大模型的…

📅 2026/9/9 22:41:26
MORE NEWS

更多资讯

📰

Go语言Context取消机制深度解析与实践指南

1. Go Context 取消信号传播机制解析 在Go语言并发编程中,Context是一个极其重要的基础组件。它最初由Google内部开发,后来成为Go标准库的一部分。Context的核心功能之一就是提供跨API边界和进程边界的取消信号传播能力,这正是我们今天要深入…

📰

HHT在ECG信号分析中的应用与MATLAB实现

1. 项目概述:HHT在ECG信号分析中的独特价值心电信号(ECG)作为典型的非平稳生物电信号,其频率成分随时间动态变化的特点使得传统傅里叶变换难以准确刻画信号特征。我在处理临床ECG数据时发现,当患者出现心律失常时&…

📰

ffmpeg mp4与m3u8互转:HLS切片与ffpreset预设实践指南

简介:一份围绕FFmpeg视频转流处理的实用工具包,面向需要进行MP4与m3u8格式互转的开发者、运维人员及流媒体学习者。其中内置FFmpeg可执行程序、多套libvpx系列ffpreset预设文件以及说明文档,可直接调用命令行完成视频切片与HLS播放列表生成&a…

📰

基于AI的电子书自动生成技术解析与实践

1. 一句话生成电子书的实现原理"一句话生成电子书"听起来像是魔法,但背后其实是一系列技术的巧妙组合。核心原理可以拆解为三个关键环节:自然语言理解(NLU):系统需要准确理解用户输入的那句话的意图和主题内…

📰

Open-LLM-VTuber:3步在本地跑起一个会说会动的Live2D AI虚拟主播

Open-LLM-VTuber:3步在本地跑起一个会说会动的Live2D AI虚拟主播 【免费下载链接】Open-LLM-VTuber Talk to any LLM with hands-free voice interaction, voice interruption, and Live2D avatar running locally across platforms 项目地址: https://gitcode.co…

📰

如何在 Vercel Sandbox 中用 renderMediaOnVercel 渲染视频并上传到 Vercel Blob?

如何在 Vercel Sandbox 中用 renderMediaOnVercel 渲染视频并上传到 Vercel Blob? 【免费下载链接】remotion 🎥 Make videos programmatically with React 项目地址: https://gitcode.com/GitHub_Trending/re/remotion 当你想在 Vercel 上按需渲…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬