📊 可视化
引言
可视化是理解和调试 LangGraph 应用的重要工具。通过图表和可视化界面,开发者可以直观地看到图的结构、执行流程和状态变化。本节将介绍 LangGraph 中的各种可视化方法和工具。
核心概念
可视化的重要性
在复杂的 LangGraph 应用中,可视化帮助我们:
- 理解图结构:清晰地看到节点和边的关系
- 调试执行流程:跟踪状态变化和执行路径
- 优化性能:识别瓶颈和优化机会
- 团队协作:便于团队成员理解应用架构
可视化类型
基础可视化
Mermaid 图表生成
LangGraph 可以自动生成 Mermaid 格式的图表:
基础图表生成
/**
* ============================================================================
* 基础可视化 - Basic Visualization for LangGraph
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何为 LangGraph 生成可视化图表,支持 Mermaid 和 DOT 格式。
* 提供基础和高级两种可视化生成器,支持主题配置、节点样式定制等功能。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ Mermaid 图表生成:生成 Mermaid 格式的流程图
* 2️⃣ DOT 图表生成:生成 Graphviz DOT 格式的图表
* 3️⃣ 主题定制:支持多种主题(default、dark、forest、neutral)
* 4️⃣ HTML 渲染:将图表渲染为可交互的 HTML 页面
* 5️⃣ 高级配置:支持方向、标签、紧凑模式等配置选项
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • BasicVisualizationGenerator:提取图的节点和边信息,生成基础图表代码
* • ChartRenderer:将图表代码渲染为 HTML 格式,支持浏览器展示
* • AdvancedVisualizationGenerator:扩展基础生成器,支持主题和高级配置
* • 支持节点形状定制(圆形、矩形、菱形等)和边样式(实线、虚线等)
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/basic-visualization.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • Mermaid 图表需要在支持 Mermaid 的环境中渲染
* • DOT 图表需要 Graphviz 工具支持
* • 实际应用中需要访问图的内部结构来提取真实的节点和边信息
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
});
// 基础图表生成器
class BasicVisualizationGenerator {
// 生成 Mermaid 格式的图表
generateMermaidChart(graph: any): string {
const nodes = this.extractNodes(graph);
const edges = this.extractEdges(graph);
let mermaidCode = 'graph TD;\n';
// 添加节点
nodes.forEach((node) => {
const nodeShape = this.getNodeShape(node.type);
mermaidCode += ` ${node.id}${nodeShape};\n`;
});
// 添加边
edges.forEach((edge) => {
const edgeStyle = this.getEdgeStyle(edge.type);
mermaidCode += ` ${edge.from} ${edgeStyle} ${edge.to};\n`;
});
// 添加样式
mermaidCode += this.getDefaultStyles();
return mermaidCode;
}
// 提取节点信息
protected extractNodes(graph: StateGraph<any>) {
// 模拟节点提取(实际实现需要访问图的内部结构)
return [
{ id: 'start', type: 'entry', label: '开始' },
{ id: 'process', type: 'normal', label: '处理' },
{ id: 'decision', type: 'decision', label: '决策' },
{ id: 'end', type: 'finish', label: '结束' },
];
}
// 提取边信息
protected extractEdges(graph: StateGraph<any>) {
// 模拟边提取
return [
{ from: 'start', to: 'process', type: 'normal' },
{ from: 'process', to: 'decision', type: 'normal' },
{ from: 'decision', to: 'end', type: 'conditional' },
];
}
// 获取节点形状
protected getNodeShape(type: string): string {
const shapes = {
entry: '([开始])',
normal: '[处理]',
decision: '{决策}',
finish: '([结束])',
};
return shapes[type as keyof typeof shapes] || '[节点]';
}
// 获取边样式
protected getEdgeStyle(type: string): string {
const styles = {
normal: '-->',
conditional: '-.->',
error: '==>',
};
return styles[type as keyof typeof styles] || '-->';
}
// 获取默认样式
private getDefaultStyles(): string {
return `
classDef default fill:#f2f0ff;
classDef entry fill:#4CAF50,color:#fff;
classDef finish fill:#F44336,color:#fff;
classDef decision fill:#FF9800,color:#fff;
`;
}
// 生成 DOT 格式图表
generateDotChart(graph: any): string {
const nodes = this.extractNodes(graph);
const edges = this.extractEdges(graph);
let dotCode = 'digraph G {\n';
dotCode += ' rankdir=TD;\n';
dotCode += ' node [shape=box, style=rounded];\n\n';
// 添加节点
nodes.forEach((node) => {
const nodeAttrs = this.getDotNodeAttributes(node.type);
dotCode += ` ${node.id} [label="${node.label}"${nodeAttrs}];\n`;
});
dotCode += '\n';
// 添加边
edges.forEach((edge) => {
const edgeAttrs = this.getDotEdgeAttributes(edge.type);
dotCode += ` ${edge.from} -> ${edge.to}${edgeAttrs};\n`;
});
dotCode += '}';
return dotCode;
}
// 获取 DOT 节点属性
private getDotNodeAttributes(type: string): string {
const attributes = {
entry: ', fillcolor=lightgreen, style="rounded,filled"',
normal: ', fillcolor=lightblue, style="rounded,filled"',
decision: ', shape=diamond, fillcolor=orange, style=filled',
finish: ', fillcolor=lightcoral, style="rounded,filled"',
};
return attributes[type as keyof typeof attributes] || '';
}
// 获取 DOT 边属性
private getDotEdgeAttributes(type: string): string {
const attributes = {
normal: '',
conditional: ' [style=dashed, color=orange]',
error: ' [color=red, penwidth=2]',
};
return attributes[type as keyof typeof attributes] || '';
}
}
// 图表渲染器
class ChartRenderer {
// 渲染到 HTML
renderToHtml(chartCode: string, type: 'mermaid' | 'dot'): string {
if (type === 'mermaid') {
return this.renderMermaidToHtml(chartCode);
} else {
return this.renderDotToHtml(chartCode);
}
}
private renderMermaidToHtml(mermaidCode: string): string {
return `
<!DOCTYPE html>
<html>
<head>
<title>LangGraph 可视化</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
<div class="mermaid">
${mermaidCode}
</div>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body>
</html>`;
}
private renderDotToHtml(dotCode: string): string {
return `
<!DOCTYPE html>
<html>
<head>
<title>LangGraph DOT 可视化</title>
<script src="https://unpkg.com/@hpcc-js/wasm/dist/index.min.js"></script>
<script src="https://unpkg.com/d3-graphviz/build/d3-graphviz.js"></script>
</head>
<body>
<div id="graph"></div>
<script>
d3.select("#graph").graphviz()
.renderDot(\`${dotCode}\`);
</script>
</body>
</html>`;
}
// 保存为文件
saveToFile(content: string, filename: string): void {
console.log(`保存图表到文件: ${filename}`);
console.log('内容预览:');
console.log(content.substring(0, 200) + '...');
}
}
// 创建示例图
function createSampleGraph() {
return new StateGraph(StateAnnotation)
.addNode('start', async (state) => {
return {
messages: ['开始处理'],
step: state.step + 1,
};
})
.addNode('process', async (state) => {
return {
messages: ['处理数据'],
step: state.step + 1,
};
})
.addNode('decision', async (state) => {
const shouldContinue = state.messages.length < 5;
return {
messages: shouldContinue ? ['继续处理'] : ['准备结束'],
step: state.step + 1,
};
})
.addNode('end', async (state) => {
return {
messages: ['处理完成'],
step: state.step + 1,
};
})
.addEdge('start', 'process')
.addEdge('process', 'decision')
.addConditionalEdges('decision', (state) => {
return state.messages.length < 5 ? 'process' : 'end';
})
.setEntryPoint('start')
.setFinishPoint('end');
}
// 使用示例
function demonstrateBasicVisualization() {
console.log('开始基础可视化演示...');
// 创建示例图
const graph = createSampleGraph();
// 创建可视化生成器
const generator = new BasicVisualizationGenerator();
const renderer = new ChartRenderer();
// 生成 Mermaid 图表
console.log('\n=== Mermaid 图表 ===');
const mermaidChart = generator.generateMermaidChart(graph);
console.log(mermaidChart);
// 生成 DOT 图表
console.log('\n=== DOT 图表 ===');
const dotChart = generator.generateDotChart(graph);
console.log(dotChart);
// 渲染为 HTML
console.log('\n=== HTML 渲染 ===');
const mermaidHtml = renderer.renderToHtml(mermaidChart, 'mermaid');
const dotHtml = renderer.renderToHtml(dotChart, 'dot');
// 保存文件
renderer.saveToFile(mermaidHtml, 'graph-mermaid.html');
renderer.saveToFile(dotHtml, 'graph-dot.html');
console.log('基础可视化演示完成');
}
// 图表配置选项
interface VisualizationOptions {
theme: 'default' | 'dark' | 'forest' | 'neutral';
direction: 'TD' | 'LR' | 'BT' | 'RL';
showLabels: boolean;
showTypes: boolean;
compactMode: boolean;
}
// 高级可视化生成器
class AdvancedVisualizationGenerator extends BasicVisualizationGenerator {
private options: VisualizationOptions;
constructor(options: Partial<VisualizationOptions> = {}) {
super();
this.options = {
theme: 'default',
direction: 'TD',
showLabels: true,
showTypes: false,
compactMode: false,
...options,
};
}
// 生成带配置的 Mermaid 图表
generateConfiguredMermaidChart(graph: any): string {
let mermaidCode = `graph ${this.options.direction};\n`;
const nodes = this.extractNodes(graph);
const edges = this.extractEdges(graph);
// 添加节点
nodes.forEach((node) => {
const nodeShape = this.getNodeShape(node.type);
const label = this.options.showLabels ? node.label : node.id;
const typeInfo = this.options.showTypes
? `<br/><small>${node.type}</small>`
: '';
if (this.options.compactMode) {
mermaidCode += ` ${node.id}[${label}];\n`;
} else {
mermaidCode += ` ${node.id}${nodeShape.replace(
node.label,
label + typeInfo
)};\n`;
}
});
// 添加边
edges.forEach((edge) => {
const edgeStyle = this.getEdgeStyle(edge.type);
mermaidCode += ` ${edge.from} ${edgeStyle} ${edge.to};\n`;
});
// 添加主题样式
mermaidCode += this.getThemeStyles();
return mermaidCode;
}
// 获取主题样式
private getThemeStyles(): string {
const themes = {
default: `
classDef default fill:#f2f0ff;
classDef entry fill:#4CAF50,color:#fff;
classDef finish fill:#F44336,color:#fff;
classDef decision fill:#FF9800,color:#fff;`,
dark: `
classDef default fill:#2d3748,color:#fff;
classDef entry fill:#38a169,color:#fff;
classDef finish fill:#e53e3e,color:#fff;
classDef decision fill:#ed8936,color:#fff;`,
forest: `
classDef default fill:#f0fff4;
classDef entry fill:#22543d,color:#fff;
classDef finish fill:#742a2a,color:#fff;
classDef decision fill:#744210,color:#fff;`,
neutral: `
classDef default fill:#f7fafc;
classDef entry fill:#4a5568,color:#fff;
classDef finish fill:#2d3748,color:#fff;
classDef decision fill:#718096,color:#fff;`,
};
return themes[this.options.theme];
}
// 更新配置
updateOptions(newOptions: Partial<VisualizationOptions>) {
this.options = { ...this.options, ...newOptions };
}
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateBasicVisualization();
}
export {
BasicVisualizationGenerator,
ChartRenderer,
AdvancedVisualizationGenerator,
createSampleGraph,
demonstrateBasicVisualization,
type VisualizationOptions,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始基础可视化演示...
=== Mermaid 图表 ===
graph TD;
start([开始]);
process[处理];
decision{决策};
end([结束]);
start --> process;
process --> decision;
decision -.-> end;
classDef default fill:#f2f0ff;
classDef entry fill:#4CAF50,color:#fff;
classDef finish fill:#F44336,color:#fff;
classDef decision fill:#FF9800,color:#fff;
=== DOT 图表 ===
digraph G {
rankdir=TD;
node [shape=box, style=rounded];
start [label="开始", fillcolor=lightgreen, style="rounded,filled"];
process [label="处理", fillcolor=lightblue, style="rounded,filled"];
decision [label="决策", shape=diamond, fillcolor=orange, style=filled];
end [label="结束", fillcolor=lightcoral, style="rounded,filled"];
start -> process;
process -> decision;
decision -> end [style=dashed, color=orange];
}
=== HTML 渲染 ===
保存图表到文件: graph-mermaid.html
内容预览:
<!DOCTYPE html>
<html>
<head>
<title>LangGraph 可视化</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
<div class="mermaid">
graph TD;
...
保存图表到文件: graph-dot.html
内容预览:
<!DOCTYPE html>
<html>
<head>
<title>LangGraph DOT 可视化</title>
<script src="https://unpkg.com/@hpcc-js/wasm/dist/index.min.js"></script>
<script src="https://unpkg.com/d3-graphviz/build/d...
基础可视化演示完成
*/
自定义图表样式
自定义图表样式
/**
* ============================================================================
* 自定义样式可视化 - Custom Styling Visualization for LangGraph
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何为 LangGraph 图表应用自定义样式,支持多种可视化格式(Mermaid、SVG、
* D3.js、Cytoscape.js)和主题(浅色、深色、彩色)。提供丰富的节点和边样式定制选项。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 节点样式定制:设置填充色、边框色、边框宽度、形状等
* 2️⃣ 边样式定制:设置线条颜色、宽度、虚线样式等
* 3️⃣ 主题切换:支持 light、dark、colorful 三种内置主题
* 4️⃣ 多格式导出:支持 Mermaid、SVG、D3.js、Cytoscape.js 格式
* 5️⃣ 交互式配置:动态生成可视化库的配置对象
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • CustomStyleVisualizer:管理节点和边的样式配置
* • 主题系统:预定义多套配色方案,支持一键切换
* • SVG 生成:直接生成 SVG 代码,无需依赖外部库
* • 配置导出:为 D3.js 和 Cytoscape.js 等可视化库生成配置
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/custom-styling.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 不同可视化库的样式配置格式可能不同
* • SVG 代码需要在支持 SVG 的环境中渲染
* • 建议根据实际需求选择合适的可视化格式
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation, START, END } from '@langchain/langgraph';
import { BaseMessage, HumanMessage, AIMessage } from '@langchain/core/messages';
// 定义状态
const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (state: BaseMessage[], update: BaseMessage[]) =>
state.concat(update),
default: () => [],
}),
nodeType: Annotation<string>({
reducer: (state: string, update: string) => update,
default: () => 'unknown',
}),
});
// 节点函数
async function inputNode(state: typeof StateAnnotation.State) {
return {
messages: [new AIMessage({ content: '输入处理完成' })],
nodeType: 'input',
};
}
async function processNode(state: typeof StateAnnotation.State) {
return {
messages: [new AIMessage({ content: '数据处理完成' })],
nodeType: 'process',
};
}
async function outputNode(state: typeof StateAnnotation.State) {
return {
messages: [new AIMessage({ content: '输出生成完成' })],
nodeType: 'output',
};
}
// 构建图
const workflow = new StateGraph(StateAnnotation)
.addNode('input', inputNode)
.addNode('process', processNode)
.addNode('output', outputNode)
.addEdge(START, 'input')
.addEdge('input', 'process')
.addEdge('process', 'output')
.addEdge('output', END);
const app = workflow.compile();
// 自定义样式生成器
class CustomStyleVisualizer {
private nodeStyles: Record<string, any> = {
input: {
fill: '#e1f5fe',
stroke: '#0277bd',
strokeWidth: 2,
shape: 'circle',
},
process: {
fill: '#f3e5f5',
stroke: '#7b1fa2',
strokeWidth: 2,
shape: 'rectangle',
},
output: {
fill: '#e8f5e8',
stroke: '#388e3c',
strokeWidth: 2,
shape: 'diamond',
},
default: {
fill: '#f5f5f5',
stroke: '#757575',
strokeWidth: 1,
shape: 'rectangle',
},
};
private edgeStyles: Record<string, any> = {
normal: {
stroke: '#424242',
strokeWidth: 2,
strokeDasharray: 'none',
},
conditional: {
stroke: '#ff9800',
strokeWidth: 2,
strokeDasharray: '5,5',
},
error: {
stroke: '#f44336',
strokeWidth: 3,
strokeDasharray: '10,5',
},
};
// 生成带自定义样式的 Mermaid 图表
generateStyledMermaid(): string {
const mermaidCode = `
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::inputStyle
process[处理节点]:::processStyle
output{输出节点}:::outputStyle
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:#4caf50,stroke:#2e7d32,stroke-width:3px,color:#fff
classDef end fill:#f44336,stroke:#c62828,stroke-width:3px,color:#fff
classDef inputStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
classDef processStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef outputStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
`;
return mermaidCode.trim();
}
// 生成主题化的图表
generateThemedMermaid(theme: 'light' | 'dark' | 'colorful'): string {
const themes = {
light: {
background: '#ffffff',
primaryColor: '#333333',
secondaryColor: '#666666',
tertiaryColor: '#999999',
nodeColors: ['#e3f2fd', '#f3e5f5', '#e8f5e8'],
},
dark: {
background: '#1a1a1a',
primaryColor: '#ffffff',
secondaryColor: '#cccccc',
tertiaryColor: '#999999',
nodeColors: ['#263238', '#4a148c', '#1b5e20'],
},
colorful: {
background: '#f8f9fa',
primaryColor: '#212529',
secondaryColor: '#495057',
tertiaryColor: '#6c757d',
nodeColors: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'],
},
};
const selectedTheme = themes[theme];
const mermaidCode = `
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::node1
process[处理节点]:::node2
output{输出节点}:::node3
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:${selectedTheme.nodeColors[0]},stroke:${
selectedTheme.primaryColor
},stroke-width:3px
classDef end fill:${selectedTheme.nodeColors[1]},stroke:${
selectedTheme.primaryColor
},stroke-width:3px
classDef node1 fill:${selectedTheme.nodeColors[2]},stroke:${
selectedTheme.secondaryColor
},stroke-width:2px
classDef node2 fill:${selectedTheme.nodeColors[3]},stroke:${
selectedTheme.secondaryColor
},stroke-width:2px
classDef node3 fill:${
selectedTheme.nodeColors[4] || selectedTheme.nodeColors[0]
},stroke:${selectedTheme.secondaryColor},stroke-width:2px
`;
return mermaidCode.trim();
}
// 生成 SVG 格式的图表
generateSVG(): string {
return `
<svg width="600" height="400" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7"
refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#333" />
</marker>
</defs>
<!-- 节点 -->
<circle cx="100" cy="200" r="40" fill="#e1f5fe" stroke="#0277bd" stroke-width="2"/>
<text x="100" y="205" text-anchor="middle" font-family="Arial" font-size="12">输入</text>
<rect x="220" y="170" width="80" height="60" rx="5" fill="#f3e5f5" stroke="#7b1fa2" stroke-width="2"/>
<text x="260" y="205" text-anchor="middle" font-family="Arial" font-size="12">处理</text>
<polygon points="420,170 480,200 420,230 360,200" fill="#e8f5e8" stroke="#388e3c" stroke-width="2"/>
<text x="420" y="205" text-anchor="middle" font-family="Arial" font-size="12">输出</text>
<!-- 连接线 -->
<line x1="140" y1="200" x2="220" y2="200" stroke="#333" stroke-width="2" marker-end="url(#arrowhead)"/>
<line x1="300" y1="200" x2="360" y2="200" stroke="#333" stroke-width="2" marker-end="url(#arrowhead)"/>
</svg>
`.trim();
}
// 生成 D3.js 配置
generateD3Config(): any {
return {
nodes: [
{ id: 'input', label: '输入节点', type: 'input', x: 100, y: 200 },
{ id: 'process', label: '处理节点', type: 'process', x: 260, y: 200 },
{ id: 'output', label: '输出节点', type: 'output', x: 420, y: 200 },
],
links: [
{ source: 'input', target: 'process', type: 'normal' },
{ source: 'process', target: 'output', type: 'normal' },
],
styles: {
nodes: this.nodeStyles,
edges: this.edgeStyles,
},
layout: {
width: 600,
height: 400,
nodeRadius: 40,
linkDistance: 150,
},
};
}
// 生成 Cytoscape.js 配置
generateCytoscapeConfig(): any {
return {
elements: [
// 节点
{ data: { id: 'input', label: '输入节点', type: 'input' } },
{ data: { id: 'process', label: '处理节点', type: 'process' } },
{ data: { id: 'output', label: '输出节点', type: 'output' } },
// 边
{ data: { id: 'e1', source: 'input', target: 'process' } },
{ data: { id: 'e2', source: 'process', target: 'output' } },
],
style: [
{
selector: 'node',
style: {
'background-color': '#666',
label: 'data(label)',
'text-valign': 'center',
'text-halign': 'center',
'font-size': '12px',
width: 80,
height: 40,
},
},
{
selector: 'node[type="input"]',
style: {
'background-color': '#e1f5fe',
'border-color': '#0277bd',
'border-width': 2,
shape: 'ellipse',
},
},
{
selector: 'node[type="process"]',
style: {
'background-color': '#f3e5f5',
'border-color': '#7b1fa2',
'border-width': 2,
shape: 'rectangle',
},
},
{
selector: 'node[type="output"]',
style: {
'background-color': '#e8f5e8',
'border-color': '#388e3c',
'border-width': 2,
shape: 'diamond',
},
},
{
selector: 'edge',
style: {
width: 2,
'line-color': '#333',
'target-arrow-color': '#333',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
},
},
],
layout: {
name: 'breadthfirst',
directed: true,
spacingFactor: 1.5,
},
};
}
}
// 使用示例
async function demonstrateCustomStyling() {
console.log('=== 自定义样式可视化示例 ===\n');
const visualizer = new CustomStyleVisualizer();
// 1. 生成基础样式的 Mermaid 图表
console.log('=== 基础样式 Mermaid 图表 ===');
const styledMermaid = visualizer.generateStyledMermaid();
console.log(styledMermaid);
// 2. 生成不同主题的图表
console.log('\n=== 浅色主题 ===');
const lightTheme = visualizer.generateThemedMermaid('light');
console.log(lightTheme);
console.log('\n=== 深色主题 ===');
const darkTheme = visualizer.generateThemedMermaid('dark');
console.log(darkTheme);
console.log('\n=== 彩色主题 ===');
const colorfulTheme = visualizer.generateThemedMermaid('colorful');
console.log(colorfulTheme);
// 3. 生成 SVG 格式
console.log('\n=== SVG 格式 ===');
const svgCode = visualizer.generateSVG();
console.log(svgCode);
// 4. 生成 D3.js 配置
console.log('\n=== D3.js 配置 ===');
const d3Config = visualizer.generateD3Config();
console.log(JSON.stringify(d3Config, null, 2));
// 5. 生成 Cytoscape.js 配置
console.log('\n=== Cytoscape.js 配置 ===');
const cytoscapeConfig = visualizer.generateCytoscapeConfig();
console.log(JSON.stringify(cytoscapeConfig, null, 2));
// 6. 执行图并展示结果
console.log('\n=== 执行图 ===');
try {
const result = await app.invoke({
messages: [new HumanMessage({ content: '开始处理数据' })],
});
console.log('执行结果:');
console.log('最终节点类型:', result.nodeType);
console.log('消息历史:');
result.messages.forEach((msg: BaseMessage, index: number) => {
console.log(` ${index + 1}. ${msg.content}`);
});
} catch (error) {
console.error('执行失败:', error.message);
}
}
// 运行示例
if (require.main === module) {
demonstrateCustomStyling().catch(console.error);
}
export { app, CustomStyleVisualizer, demonstrateCustomStyling };
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
=== 自定义样式可视化示例 ===
=== 基础样式 Mermaid 图表 ===
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::inputStyle
process[处理节点]:::processStyle
output{输出节点}:::outputStyle
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:#4caf50,stroke:#2e7d32,stroke-width:3px,color:#fff
classDef end fill:#f44336,stroke:#c62828,stroke-width:3px,color:#fff
classDef inputStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
classDef processStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef outputStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
=== 浅色主题 ===
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::node1
process[处理节点]:::node2
output{输出节点}:::node3
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:#e3f2fd,stroke:#333333,stroke-width:3px
classDef end fill:#f3e5f5,stroke:#333333,stroke-width:3px
classDef node1 fill:#e8f5e8,stroke:#666666,stroke-width:2px
classDef node2 fill:undefined,stroke:#666666,stroke-width:2px
classDef node3 fill:#e3f2fd,stroke:#666666,stroke-width:2px
=== 深色主题 ===
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::node1
process[处理节点]:::node2
output{输出节点}:::node3
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:#263238,stroke:#ffffff,stroke-width:3px
classDef end fill:#4a148c,stroke:#ffffff,stroke-width:3px
classDef node1 fill:#1b5e20,stroke:#cccccc,stroke-width:2px
classDef node2 fill:undefined,stroke:#cccccc,stroke-width:2px
classDef node3 fill:#263238,stroke:#cccccc,stroke-width:2px
=== 彩色主题 ===
graph TD
__start__([<p>__start__</p>]):::start
input([输入节点]):::node1
process[处理节点]:::node2
output{输出节点}:::node3
__end__([<p>__end__</p>]):::end
__start__ --> input
input --> process
process --> output
output --> __end__
classDef start fill:#ff6b6b,stroke:#212529,stroke-width:3px
classDef end fill:#4ecdc4,stroke:#212529,stroke-width:3px
classDef node1 fill:#45b7d1,stroke:#495057,stroke-width:2px
classDef node2 fill:#96ceb4,stroke:#495057,stroke-width:2px
classDef node3 fill:#feca57,stroke:#495057,stroke-width:2px
=== SVG 格式 ===
<svg width="600" height="400" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7"
refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#333" />
</marker>
</defs>
<!-- 节点 -->
<circle cx="100" cy="200" r="40" fill="#e1f5fe" stroke="#0277bd" stroke-width="2"/>
<text x="100" y="205" text-anchor="middle" font-family="Arial" font-size="12">输入</text>
<rect x="220" y="170" width="80" height="60" rx="5" fill="#f3e5f5" stroke="#7b1fa2" stroke-width="2"/>
<text x="260" y="205" text-anchor="middle" font-family="Arial" font-size="12">处理</text>
<polygon points="420,170 480,200 420,230 360,200" fill="#e8f5e8" stroke="#388e3c" stroke-width="2"/>
<text x="420" y="205" text-anchor="middle" font-family="Arial" font-size="12">输出</text>
<!-- 连接线 -->
<line x1="140" y1="200" x2="220" y2="200" stroke="#333" stroke-width="2" marker-end="url(#arrowhead)"/>
<line x1="300" y1="200" x2="360" y2="200" stroke="#333" stroke-width="2" marker-end="url(#arrowhead)"/>
</svg>
=== D3.js 配置 ===
{
"nodes": [
{
"id": "input",
"label": "输入节点",
"type": "input",
"x": 100,
"y": 200
},
{
"id": "process",
"label": "处理节点",
"type": "process",
"x": 260,
"y": 200
},
{
"id": "output",
"label": "输出节点",
"type": "output",
"x": 420,
"y": 200
}
],
"links": [
{
"source": "input",
"target": "process",
"type": "normal"
},
{
"source": "process",
"target": "output",
"type": "normal"
}
],
"styles": {
"nodes": {
"input": {
"fill": "#e1f5fe",
"stroke": "#0277bd",
"strokeWidth": 2,
"shape": "circle"
},
"process": {
"fill": "#f3e5f5",
"stroke": "#7b1fa2",
"strokeWidth": 2,
"shape": "rectangle"
},
"output": {
"fill": "#e8f5e8",
"stroke": "#388e3c",
"strokeWidth": 2,
"shape": "diamond"
},
"default": {
"fill": "#f5f5f5",
"stroke": "#757575",
"strokeWidth": 1,
"shape": "rectangle"
}
},
"edges": {
"normal": {
"stroke": "#424242",
"strokeWidth": 2,
"strokeDasharray": "none"
},
"conditional": {
"stroke": "#ff9800",
"strokeWidth": 2,
"strokeDasharray": "5,5"
},
"error": {
"stroke": "#f44336",
"strokeWidth": 3,
"strokeDasharray": "10,5"
}
}
},
"layout": {
"width": 600,
"height": 400,
"nodeRadius": 40,
"linkDistance": 150
}
}
=== Cytoscape.js 配置 ===
{
"elements": [
{
"data": {
"id": "input",
"label": "输入节点",
"type": "input"
}
},
{
"data": {
"id": "process",
"label": "处理节点",
"type": "process"
}
},
{
"data": {
"id": "output",
"label": "输出节点",
"type": "output"
}
},
{
"data": {
"id": "e1",
"source": "input",
"target": "process"
}
},
{
"data": {
"id": "e2",
"source": "process",
"target": "output"
}
}
],
"style": [
{
"selector": "node",
"style": {
"background-color": "#666",
"label": "data(label)",
"text-valign": "center",
"text-halign": "center",
"font-size": "12px",
"width": 80,
"height": 40
}
},
{
"selector": "node[type=\"input\"]",
"style": {
"background-color": "#e1f5fe",
"border-color": "#0277bd",
"border-width": 2,
"shape": "ellipse"
}
},
{
"selector": "node[type=\"process\"]",
"style": {
"background-color": "#f3e5f5",
"border-color": "#7b1fa2",
"border-width": 2,
"shape": "rectangle"
}
},
{
"selector": "node[type=\"output\"]",
"style": {
"background-color": "#e8f5e8",
"border-color": "#388e3c",
"border-width": 2,
"shape": "diamond"
}
},
{
"selector": "edge",
"style": {
"width": 2,
"line-color": "#333",
"target-arrow-color": "#333",
"target-arrow-shape": "triangle",
"curve-style": "bezier"
}
}
],
"layout": {
"name": "breadthfirst",
"directed": true,
"spacingFactor": 1.5
}
}
=== 执行图 ===
执行结果:
最终节点类型: output
消息历史:
1. 开始处理数据
2. 输入处理完成
3. 数据处理完成
4. 输出生成完成
*/
高级可视化
执行流程可视化
执行流程可视化
/**
* ============================================================================
* 执行流程可视化 - Execution Flow Visualization
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何可视化 LangGraph 的执行流程,通过记录节点执行路径、性能指标和
* 决策分支,生成流程图、性能报告和 Mermaid 图表代码。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 执行路径追踪:记录图执行过程中经过的所有节点
* 2️⃣ 节点性能监控:测量每个节点的执行时间和处理指标
* 3️⃣ 流程图生成:自动生成流程图可视化数据
* 4️⃣ Mermaid 导出:生成 Mermaid 图表代码用于文档
* 5️⃣ 性能分析报告:统计总耗时和各节点占比
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 在状态中维护 executionPath 数组记录执行路径
* • 每个节点执行前后记录时间戳,计算处理时间
* • 将节点指标和可视化数据存储在状态中传递
* • ExecutionFlowVisualizer 类封装可视化功能
* • 根据执行路径动态生成 Mermaid 图表代码
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/execution-flow.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 性能测量会增加轻微开销,不建议在生产环境详细记录
* • 执行历史会占用内存,需要定期清理
* • Mermaid 代码需要配合 Mermaid 渲染器使用
* • 复杂图的可视化可能需要额外的布局优化
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation, END } from '@langchain/langgraph';
import { MemorySaver } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
input: Annotation<string>,
currentStep: Annotation<string>,
result: Annotation<string>,
executionPath: Annotation<string[]>,
nodeMetrics: Annotation<Record<string, any>>,
visualData: Annotation<Record<string, any>>,
});
// 数据预处理节点
function preprocessData(state: typeof StateAnnotation.State) {
console.log('🔄 数据预处理');
const startTime = Date.now();
const input = state.input || '';
// 模拟数据预处理
const processedData = input.toLowerCase().trim();
const wordCount = processedData.split(' ').length;
const endTime = Date.now();
const processingTime = endTime - startTime;
return {
currentStep: 'preprocess',
result: `预处理完成: ${wordCount} 个词`,
executionPath: [...(state.executionPath || []), 'preprocess'],
nodeMetrics: {
...state.nodeMetrics,
preprocess: {
processingTime,
wordCount,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
preprocessed: processedData,
metrics: { wordCount, processingTime },
},
};
}
// 数据分析节点
function analyzeData(state: typeof StateAnnotation.State) {
console.log('📊 数据分析');
const startTime = Date.now();
const data = state.visualData?.preprocessed || '';
// 模拟数据分析
const analysis = {
length: data.length,
hasNumbers: /\d/.test(data),
hasSpecialChars: /[!@#$%^&*(),.?":{}|<>]/.test(data),
sentiment: Math.random() > 0.5 ? 'positive' : 'negative',
complexity: data.length > 50 ? 'high' : data.length > 20 ? 'medium' : 'low',
};
const endTime = Date.now();
const processingTime = endTime - startTime;
return {
currentStep: 'analyze',
result: `分析完成: ${analysis.complexity} 复杂度`,
executionPath: [...(state.executionPath || []), 'analyze'],
nodeMetrics: {
...state.nodeMetrics,
analyze: {
processingTime,
analysis,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
analysis,
},
};
}
// 决策节点
function makeDecision(state: typeof StateAnnotation.State) {
console.log('🤔 决策制定');
const startTime = Date.now();
const analysis = state.visualData?.analysis || {};
// 基于分析结果做决策
let decision: string;
let confidence: number;
if (analysis.complexity === 'high') {
decision = 'complex_processing';
confidence = 0.9;
} else if (analysis.sentiment === 'negative') {
decision = 'special_handling';
confidence = 0.7;
} else {
decision = 'standard_processing';
confidence = 0.8;
}
const endTime = Date.now();
const processingTime = endTime - startTime;
return {
currentStep: 'decision',
result: `决策: ${decision} (置信度: ${confidence})`,
executionPath: [...(state.executionPath || []), 'decision'],
nodeMetrics: {
...state.nodeMetrics,
decision: {
processingTime,
decision,
confidence,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
decision,
confidence,
},
};
}
// 复杂处理节点
function complexProcessing(state: typeof StateAnnotation.State) {
console.log('⚙️ 复杂处理');
const startTime = Date.now();
// 模拟复杂处理
const steps = [
'tokenization',
'feature_extraction',
'model_inference',
'post_processing',
];
const results = steps.map((step) => `${step}_completed`);
const endTime = Date.now();
const processingTime = endTime - startTime + 100; // 模拟更长时间
return {
currentStep: 'complex_processing',
result: `复杂处理完成: ${results.length} 个步骤`,
executionPath: [...(state.executionPath || []), 'complex_processing'],
nodeMetrics: {
...state.nodeMetrics,
complex_processing: {
processingTime,
steps: results,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
processingSteps: results,
processingType: 'complex',
},
};
}
// 特殊处理节点
function specialHandling(state: typeof StateAnnotation.State) {
console.log('🔧 特殊处理');
const startTime = Date.now();
// 模拟特殊处理逻辑
const handlingType = 'sentiment_correction';
const corrections = ['tone_adjustment', 'context_enhancement'];
const endTime = Date.now();
const processingTime = endTime - startTime + 50;
return {
currentStep: 'special_handling',
result: `特殊处理完成: ${handlingType}`,
executionPath: [...(state.executionPath || []), 'special_handling'],
nodeMetrics: {
...state.nodeMetrics,
special_handling: {
processingTime,
handlingType,
corrections,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
handlingType,
corrections,
processingType: 'special',
},
};
}
// 标准处理节点
function standardProcessing(state: typeof StateAnnotation.State) {
console.log('📝 标准处理');
const startTime = Date.now();
// 模拟标准处理
const operations = ['validation', 'formatting', 'output_generation'];
const endTime = Date.now();
const processingTime = endTime - startTime + 30;
return {
currentStep: 'standard_processing',
result: `标准处理完成: ${operations.length} 个操作`,
executionPath: [...(state.executionPath || []), 'standard_processing'],
nodeMetrics: {
...state.nodeMetrics,
standard_processing: {
processingTime,
operations,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
operations,
processingType: 'standard',
},
};
}
// 结果汇总节点
function summarizeResults(state: typeof StateAnnotation.State) {
console.log('📋 结果汇总');
const startTime = Date.now();
// 汇总所有处理结果
const totalSteps = state.executionPath?.length || 0;
const totalTime = Object.values(state.nodeMetrics || {}).reduce(
(sum: number, metric: any) => sum + (metric.processingTime || 0),
0
);
const summary = {
totalSteps,
totalTime,
processingType: state.visualData?.processingType || 'unknown',
success: true,
};
const endTime = Date.now();
const processingTime = endTime - startTime;
return {
currentStep: 'summary',
result: `处理完成: ${totalSteps} 步骤, ${totalTime}ms`,
executionPath: [...(state.executionPath || []), 'summary'],
nodeMetrics: {
...state.nodeMetrics,
summary: {
processingTime,
summary,
timestamp: new Date().toISOString(),
},
},
visualData: {
...state.visualData,
summary,
},
};
}
// 决策路由函数
function routeDecision(state: typeof StateAnnotation.State) {
const decision = state.visualData?.decision;
switch (decision) {
case 'complex_processing':
return 'complex_processing';
case 'special_handling':
return 'special_handling';
case 'standard_processing':
default:
return 'standard_processing';
}
}
// 构建执行流程图
const executionFlowGraph = new StateGraph(StateAnnotation)
.addNode('preprocess', preprocessData)
.addNode('analyze', analyzeData)
.addNode('decision', makeDecision)
.addNode('complex_processing', complexProcessing)
.addNode('special_handling', specialHandling)
.addNode('standard_processing', standardProcessing)
.addNode('summary', summarizeResults)
.setEntryPoint('preprocess')
.addEdge('preprocess', 'analyze')
.addEdge('analyze', 'decision')
.addConditionalEdges('decision', routeDecision, {
complex_processing: 'complex_processing',
special_handling: 'special_handling',
standard_processing: 'standard_processing',
})
.addEdge('complex_processing', 'summary')
.addEdge('special_handling', 'summary')
.addEdge('standard_processing', 'summary')
.addEdge('summary', END);
// 内存存储器
const memorySaver = new MemorySaver();
// 编译图
const app = executionFlowGraph.compile({
checkpointer: memorySaver,
});
// 执行流程可视化类
class ExecutionFlowVisualizer {
private executionHistory: any[] = [];
// 执行并记录流程
async executeAndVisualize(input: string, threadId?: string) {
console.log(`\n🚀 开始执行流程: "${input}"`);
console.log('='.repeat(50));
const actualThreadId = threadId || `flow-${Date.now()}`;
const startTime = Date.now();
try {
const result = await app.invoke(
{
input,
executionPath: [],
nodeMetrics: {},
visualData: {},
},
{ configurable: { thread_id: actualThreadId } }
);
const endTime = Date.now();
const totalTime = endTime - startTime;
// 记录执行历史
const execution = {
threadId: actualThreadId,
input,
result,
totalTime,
timestamp: new Date().toISOString(),
};
this.executionHistory.push(execution);
console.log('\n✅ 执行完成');
console.log(`总耗时: ${totalTime}ms`);
console.log(`最终结果: ${result.result}`);
return execution;
} catch (error) {
console.error('❌ 执行失败:', error);
return null;
}
}
// 生成流程图可视化
generateFlowChart(execution: any) {
console.log('\n📊 流程图可视化');
console.log('='.repeat(50));
const path = execution.result.executionPath || [];
const metrics = execution.result.nodeMetrics || {};
console.log('\n🛤️ 执行路径:');
path.forEach((step: string, index: number) => {
const metric = metrics[step];
const time = metric?.processingTime || 0;
const arrow = index < path.length - 1 ? ' → ' : '';
console.log(` ${index + 1}. ${step} (${time}ms)${arrow}`);
});
console.log('\n⏱️ 节点性能:');
Object.entries(metrics).forEach(([node, metric]: [string, any]) => {
console.log(` ${node}: ${metric.processingTime}ms`);
});
return {
path,
metrics,
totalNodes: path.length,
totalTime: Object.values(metrics).reduce(
(sum: number, m: any) => sum + m.processingTime,
0
),
};
}
// 生成 Mermaid 图表代码
generateMermaidChart(execution: any) {
console.log('\n🎨 Mermaid 图表代码');
console.log('='.repeat(50));
const path = execution.result.executionPath || [];
const decision = execution.result.visualData?.decision;
let mermaidCode = 'graph TD;\n';
mermaidCode += ' Start([开始]) --> preprocess[数据预处理];\n';
mermaidCode += ' preprocess --> analyze[数据分析];\n';
mermaidCode += ' analyze --> decision{决策制定};\n';
// 根据实际执行路径添加决策分支
if (path.includes('complex_processing')) {
mermaidCode += ' decision -->|复杂处理| complex[复杂处理];\n';
mermaidCode += ' complex --> summary[结果汇总];\n';
}
if (path.includes('special_handling')) {
mermaidCode += ' decision -->|特殊处理| special[特殊处理];\n';
mermaidCode += ' special --> summary[结果汇总];\n';
}
if (path.includes('standard_processing')) {
mermaidCode += ' decision -->|标准处理| standard[标准处理];\n';
mermaidCode += ' standard --> summary[结果汇总];\n';
}
mermaidCode += ' summary --> End([结束]);\n';
// 添加样式
mermaidCode += '\n classDef default fill:#f2f0ff;\n';
mermaidCode += ' classDef decision fill:#fff2cc;\n';
mermaidCode += ' classDef processing fill:#d5e8d4;\n';
mermaidCode += ' classDef summary fill:#ffe6cc;\n';
mermaidCode += '\n class decision decision;\n';
mermaidCode += ' class complex,special,standard processing;\n';
mermaidCode += ' class summary summary;\n';
console.log(mermaidCode);
return mermaidCode;
}
// 生成性能报告
generatePerformanceReport(execution: any) {
console.log('\n📈 性能报告');
console.log('='.repeat(50));
const metrics = execution.result.nodeMetrics || {};
const totalTime = execution.totalTime;
console.log(`总执行时间: ${totalTime}ms`);
console.log('\n各节点耗时:');
Object.entries(metrics)
.sort(
([, a]: [string, any], [, b]: [string, any]) =>
b.processingTime - a.processingTime
)
.forEach(([node, metric]: [string, any]) => {
const percentage = ((metric.processingTime / totalTime) * 100).toFixed(
1
);
console.log(` ${node}: ${metric.processingTime}ms (${percentage}%)`);
});
return {
totalTime,
nodeMetrics: metrics,
slowestNode: Object.entries(metrics).reduce(
(max, [node, metric]: [string, any]) =>
metric.processingTime > max.time
? { node, time: metric.processingTime }
: max,
{ node: '', time: 0 }
),
};
}
// 获取执行历史
getExecutionHistory() {
return this.executionHistory;
}
// 清除执行历史
clearHistory() {
this.executionHistory = [];
}
}
// 演示函数
async function demonstrateExecutionFlow() {
console.log('🎯 执行流程可视化演示');
console.log('='.repeat(60));
const visualizer = new ExecutionFlowVisualizer();
const testCases = [
'This is a simple test message',
'This is a very long and complex message that should trigger complex processing due to its length and complexity',
'I am feeling quite negative about this terrible service',
'Great job! Excellent work!',
];
for (let i = 0; i < testCases.length; i++) {
const input = testCases[i];
console.log(`\n🧪 测试案例 ${i + 1}: "${input}"`);
const execution = await visualizer.executeAndVisualize(input);
if (execution) {
visualizer.generateFlowChart(execution);
visualizer.generateMermaidChart(execution);
visualizer.generatePerformanceReport(execution);
}
console.log('\n' + '='.repeat(60));
}
console.log('\n📊 总体统计');
const history = visualizer.getExecutionHistory();
console.log(`总执行次数: ${history.length}`);
const avgTime =
history.reduce((sum, exec) => sum + exec.totalTime, 0) / history.length;
console.log(`平均执行时间: ${avgTime.toFixed(2)}ms`);
}
// 运行演示
if (require.main === module) {
demonstrateExecutionFlow().catch(console.error);
}
export {
executionFlowGraph,
app,
ExecutionFlowVisualizer,
demonstrateExecutionFlow,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
🎯 执行流程可视化演示
============================================================
🧪 测试案例 1: "This is a simple test message"
🚀 开始执行流程: "This is a simple test message"
==================================================
🔄 数据预处理
📊 数据分析
🤔 决策制定
🔧 特殊处理
📋 结果汇总
✅ 执行完成
总耗时: 22ms
最终结果: 处理完成: 4 步骤, 51ms
📊 流程图可视化
==================================================
🛤️ 执行路径:
1. preprocess (0ms) →
2. analyze (1ms) →
3. decision (0ms) →
4. special_handling (50ms) →
5. summary (0ms)
⏱️ 节点性能:
preprocess: 0ms
analyze: 1ms
decision: 0ms
special_handling: 50ms
summary: 0ms
🎨 Mermaid 图表代码
==================================================
graph TD;
Start([开始]) --> preprocess[数据预处理];
preprocess --> analyze[数据分析];
analyze --> decision{决策制定};
decision -->|特殊处理| special[特殊处理];
special --> summary[结果汇总];
summary --> End([结束]);
classDef default fill:#f2f0ff;
classDef decision fill:#fff2cc;
classDef processing fill:#d5e8d4;
classDef summary fill:#ffe6cc;
class decision decision;
class complex,special,standard processing;
class summary summary;
📈 性能报告
==================================================
总执行时间: 22ms
各节点耗时:
special_handling: 50ms (227.3%)
analyze: 1ms (4.5%)
preprocess: 0ms (0.0%)
decision: 0ms (0.0%)
summary: 0ms (0.0%)
============================================================
🧪 测试案例 2: "This is a very long and complex message that should trigger complex processing due to its length and complexity"
🚀 开始执行流程: "This is a very long and complex message that should trigger complex processing due to its length and complexity"
==================================================
🔄 数据预处理
📊 数据分析
🤔 决策制定
⚙️ 复杂处理
📋 结果汇总
✅ 执行完成
总耗时: 8ms
最终结果: 处理完成: 4 步骤, 100ms
📊 流程图可视化
==================================================
🛤️ 执行路径:
1. preprocess (0ms) →
2. analyze (0ms) →
3. decision (0ms) →
4. complex_processing (100ms) →
5. summary (0ms)
⏱️ 节点性能:
preprocess: 0ms
analyze: 0ms
decision: 0ms
complex_processing: 100ms
summary: 0ms
🎨 Mermaid 图表代码
==================================================
graph TD;
Start([开始]) --> preprocess[数据预处理];
preprocess --> analyze[数据分析];
analyze --> decision{决策制定};
decision -->|复杂处理| complex[复杂处理];
complex --> summary[结果汇总];
summary --> End([结束]);
classDef default fill:#f2f0ff;
classDef decision fill:#fff2cc;
classDef processing fill:#d5e8d4;
classDef summary fill:#ffe6cc;
class decision decision;
class complex,special,standard processing;
class summary summary;
📈 性能报告
==================================================
总执行时间: 8ms
各节点耗时:
complex_processing: 100ms (1250.0%)
preprocess: 0ms (0.0%)
analyze: 0ms (0.0%)
decision: 0ms (0.0%)
summary: 0ms (0.0%)
============================================================
🧪 测试案例 3: "I am feeling quite negative about this terrible service"
🚀 开始执行流程: "I am feeling quite negative about this terrible service"
==================================================
🔄 数据预处理
📊 数据分析
🤔 决策制定
⚙️ 复杂处理
📋 结果汇总
✅ 执行完成
总耗时: 5ms
最终结果: 处理完成: 4 步骤, 100ms
📊 流程图可视化
==================================================
🛤️ 执行路径:
1. preprocess (0ms) →
2. analyze (0ms) →
3. decision (0ms) →
4. complex_processing (100ms) →
5. summary (0ms)
⏱️ 节点性能:
preprocess: 0ms
analyze: 0ms
decision: 0ms
complex_processing: 100ms
summary: 0ms
🎨 Mermaid 图表代码
==================================================
graph TD;
Start([开始]) --> preprocess[数据预处理];
preprocess --> analyze[数据分析];
analyze --> decision{决策制定};
decision -->|复杂处理| complex[复杂处理];
complex --> summary[结果汇总];
summary --> End([结束]);
classDef default fill:#f2f0ff;
classDef decision fill:#fff2cc;
classDef processing fill:#d5e8d4;
classDef summary fill:#ffe6cc;
class decision decision;
class complex,special,standard processing;
class summary summary;
📈 性能报告
==================================================
总执行时间: 5ms
各节点耗时:
complex_processing: 100ms (2000.0%)
preprocess: 0ms (0.0%)
analyze: 0ms (0.0%)
decision: 0ms (0.0%)
summary: 0ms (0.0%)
============================================================
🧪 测试案例 4: "Great job! Excellent work!"
🚀 开始执行流程: "Great job! Excellent work!"
==================================================
🔄 数据预处理
📊 数据分析
🤔 决策制定
📝 标准处理
📋 结果汇总
✅ 执行完成
总耗时: 4ms
最终结果: 处理完成: 4 步骤, 30ms
📊 流程图可视化
==================================================
🛤️ 执行路径:
1. preprocess (0ms) →
2. analyze (0ms) →
3. decision (0ms) →
4. standard_processing (30ms) →
5. summary (0ms)
⏱️ 节点性能:
preprocess: 0ms
analyze: 0ms
decision: 0ms
standard_processing: 30ms
summary: 0ms
🎨 Mermaid 图表代码
==================================================
graph TD;
Start([开始]) --> preprocess[数据预处理];
preprocess --> analyze[数据分析];
analyze --> decision{决策制定};
decision -->|标准处理| standard[标准处理];
standard --> summary[结果汇总];
summary --> End([结束]);
classDef default fill:#f2f0ff;
classDef decision fill:#fff2cc;
classDef processing fill:#d5e8d4;
classDef summary fill:#ffe6cc;
class decision decision;
class complex,special,standard processing;
class summary summary;
📈 性能报告
==================================================
总执行时间: 4ms
各节点耗时:
standard_processing: 30ms (750.0%)
preprocess: 0ms (0.0%)
analyze: 0ms (0.0%)
decision: 0ms (0.0%)
summary: 0ms (0.0%)
============================================================
📊 总体统计
总执行次数: 4
平均执行时间: 9.75ms
*/
状态变化追踪
状态变化追踪
/**
* ============================================================================
* 状态变化追踪 - State Tracking
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何追踪 LangGraph 执行过程中的状态变化,通过记录状态快照、
* 计算差异和生成时间线,帮助理解和调试图的执行过程。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 状态快照记录:在每个节点执行前后保存状态快照
* 2️⃣ 状态差异计算:对比两个时间点的状态变化
* 3️⃣ 变化监听器:支持注册回调函数监听状态变化
* 4️⃣ 时间线生成:生成带时间戳的状态变化时间线
* 5️⃣ 图表数据导出:导出可用于图表渲染的数据
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • StateTracker 类维护状态历史记录数组
* • 使用 JSON 序列化实现状态深拷贝
* • 通过 JSON.stringify 比较状态判断是否有变化
* • 监听器模式支持多个观察者订阅状态变化
* • createTrackingNode 包装原始节点,自动记录状态
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/state-tracking.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 状态深拷贝会增加内存开销,大状态对象需谨慎
* • JSON 序列化无法处理函数、Symbol、循环引用等
* • 状态历史会持续增长,需要考虑限制大小
* • 监听器函数应该避免耗时操作,防止阻塞执行
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
history: Annotation<any[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
});
// 状态变化追踪器
class StateTracker {
private stateHistory: any[] = [];
private listeners: ((state: any) => void)[] = [];
// 记录状态变化
trackState(state: any) {
const timestamp = new Date().toISOString();
const stateSnapshot = {
timestamp,
state: JSON.parse(JSON.stringify(state)),
step: state.step || 0,
};
this.stateHistory.push(stateSnapshot);
this.notifyListeners(stateSnapshot);
}
// 添加状态变化监听器
addListener(listener: (state: any) => void) {
this.listeners.push(listener);
}
// 通知所有监听器
private notifyListeners(state: any) {
this.listeners.forEach((listener) => listener(state));
}
// 获取状态历史
getHistory() {
return this.stateHistory;
}
// 获取状态差异
getStateDiff(fromIndex: number, toIndex: number) {
if (
fromIndex >= this.stateHistory.length ||
toIndex >= this.stateHistory.length
) {
return null;
}
const fromState = this.stateHistory[fromIndex].state;
const toState = this.stateHistory[toIndex].state;
return this.calculateDiff(fromState, toState);
}
// 计算状态差异
private calculateDiff(oldState: any, newState: any) {
const diff: any = {};
for (const key in newState) {
if (JSON.stringify(oldState[key]) !== JSON.stringify(newState[key])) {
diff[key] = {
old: oldState[key],
new: newState[key],
};
}
}
return diff;
}
// 生成状态变化可视化数据
generateVisualizationData() {
return this.stateHistory.map((snapshot, index) => ({
step: index,
timestamp: snapshot.timestamp,
state: snapshot.state,
changes: index > 0 ? this.getStateDiff(index - 1, index) : {},
}));
}
}
// 创建带状态追踪的节点
function createTrackingNode(
name: string,
handler: Function,
tracker: StateTracker
) {
return async (state: typeof StateAnnotation.State) => {
console.log(`执行节点: ${name}`);
// 执行前追踪状态
tracker.trackState({
...state,
currentNode: name,
phase: 'before',
});
// 执行节点逻辑
const result = await handler(state);
// 执行后追踪状态
tracker.trackState({
...result,
currentNode: name,
phase: 'after',
});
return result;
};
}
// 示例:创建带状态追踪的图
function createTrackingGraph() {
const tracker = new StateTracker();
// 添加状态变化监听器
tracker.addListener((state) => {
console.log('状态变化:', {
step: state.step,
node: state.currentNode,
phase: state.phase,
});
});
const graph = new StateGraph(StateAnnotation)
.addNode(
'start',
createTrackingNode(
'start',
async (state) => {
return {
messages: ['开始处理'],
step: state.step + 1,
};
},
tracker
)
)
.addNode(
'process',
createTrackingNode(
'process',
async (state) => {
return {
messages: ['处理中...'],
step: state.step + 1,
};
},
tracker
)
)
.addNode(
'end',
createTrackingNode(
'end',
async (state) => {
return {
messages: ['处理完成'],
step: state.step + 1,
};
},
tracker
)
)
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end');
return { graph: graph.compile(), tracker };
}
// 状态可视化工具
class StateVisualizer {
private tracker: StateTracker;
constructor(tracker: StateTracker) {
this.tracker = tracker;
}
// 生成状态时间线
generateTimeline() {
const history = this.tracker.getHistory();
return history.map((snapshot, index) => ({
step: index,
time: snapshot.timestamp,
state: this.formatState(snapshot.state),
}));
}
// 格式化状态显示
private formatState(state: any) {
return {
messages: state.messages?.length || 0,
step: state.step || 0,
currentNode: state.currentNode || 'unknown',
phase: state.phase || 'unknown',
};
}
// 生成状态变化图表数据
generateChartData() {
const timeline = this.generateTimeline();
return {
labels: timeline.map((t) => `Step ${t.step}`),
datasets: [
{
label: 'Messages Count',
data: timeline.map((t) => t.state.messages),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1,
},
],
};
}
// 导出状态历史
exportHistory() {
return {
timestamp: new Date().toISOString(),
history: this.tracker.getHistory(),
summary: {
totalSteps: this.tracker.getHistory().length,
duration: this.calculateDuration(),
},
};
}
private calculateDuration() {
const history = this.tracker.getHistory();
if (history.length < 2) return 0;
const start = new Date(history[0].timestamp);
const end = new Date(history[history.length - 1].timestamp);
return end.getTime() - start.getTime();
}
}
// 使用示例
async function demonstrateStateTracking() {
const { graph, tracker } = createTrackingGraph();
const visualizer = new StateVisualizer(tracker);
console.log('开始状态追踪演示...');
// 执行图
const result = await graph.invoke({
messages: [],
step: 0,
});
console.log('执行结果:', result);
// 显示状态时间线
console.log('状态时间线:', visualizer.generateTimeline());
// 显示图表数据
console.log('图表数据:', visualizer.generateChartData());
// 导出历史
console.log('状态历史:', visualizer.exportHistory());
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateStateTracking().catch(console.error);
}
export {
StateTracker,
StateVisualizer,
createTrackingNode,
createTrackingGraph,
demonstrateStateTracking,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始状态追踪演示...
执行节点: start
状态变化: { step: 0, node: undefined, phase: undefined }
状态变化: { step: 1, node: undefined, phase: undefined }
执行节点: process
状态变化: { step: 1, node: undefined, phase: undefined }
状态变化: { step: 2, node: undefined, phase: undefined }
执行节点: end
状态变化: { step: 2, node: undefined, phase: undefined }
状态变化: { step: 3, node: undefined, phase: undefined }
执行结果: { messages: [ '开始处理', '处理中...', '处理完成' ], step: 3, history: [] }
状态时间线: [
{
step: 0,
time: '2025-11-16T12:28:33.164Z',
state: { messages: 0, step: 0, currentNode: 'start', phase: 'before' }
},
{
step: 1,
time: '2025-11-16T12:28:33.165Z',
state: { messages: 1, step: 1, currentNode: 'start', phase: 'after' }
},
{
step: 2,
time: '2025-11-16T12:28:33.167Z',
state: { messages: 1, step: 1, currentNode: 'process', phase: 'before' }
},
{
step: 3,
time: '2025-11-16T12:28:33.167Z',
state: { messages: 1, step: 2, currentNode: 'process', phase: 'after' }
},
{
step: 4,
time: '2025-11-16T12:28:33.168Z',
state: { messages: 2, step: 2, currentNode: 'end', phase: 'before' }
},
{
step: 5,
time: '2025-11-16T12:28:33.168Z',
state: { messages: 1, step: 3, currentNode: 'end', phase: 'after' }
}
]
图表数据: {
labels: [ 'Step 0', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5' ],
datasets: [
{
label: 'Messages Count',
data: [Array],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}
]
}
状态历史: {
timestamp: '2025-11-16T12:28:33.173Z',
history: [
{ timestamp: '2025-11-16T12:28:33.164Z', state: [Object], step: 0 },
{ timestamp: '2025-11-16T12:28:33.165Z', state: [Object], step: 1 },
{ timestamp: '2025-11-16T12:28:33.167Z', state: [Object], step: 1 },
{ timestamp: '2025-11-16T12:28:33.167Z', state: [Object], step: 2 },
{ timestamp: '2025-11-16T12:28:33.168Z', state: [Object], step: 2 },
{ timestamp: '2025-11-16T12:28:33.168Z', state: [Object], step: 3 }
],
summary: { totalSteps: 6, duration: 4 }
}
*/
LangGraph Studio
功能特性
LangGraph Studio 是专门为 LangGraph 应用设计的可视化和调试工具:
使用 LangGraph Studio
Studio 集成
/**
* ============================================================================
* LangGraph Studio 集成 - LangGraph Studio Integration
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何将 LangGraph 应用与 LangGraph Studio 集成,实现可视化调试、
* 断点设置、状态检查等功能。LangGraph Studio 是官方提供的图形化开发工具。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 调试支持:为图添加调试元数据,支持 Studio 调试功能
* 2️⃣ 断点管理:在指定节点设置断点,暂停执行并检查状态
* 3️⃣ 状态检查:实时捕获和查看图执行过程中的状态快照
* 4️⃣ 可视化模式:支持简单、详细、交互式三种可视化模式
* 5️⃣ 数据导出:导出调试数据供 Studio 分析和展示
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • StudioIntegration:为图配置 Studio 支持功能
* • StudioVisualizer:生成 Studio 需要的图结构和执行流程数据
* • StudioDebugger:提供调试会话管理和调试命令执行
* • 状态快照:自动捕获执行过程中的状态变化
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/studio-integration.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 需要安装 LangGraph Studio 才能使用完整功能
* • 断点功能会暂停图的执行,影响性能
* • 示例中的 Studio 通信为模拟实现
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
user_input: Annotation<string>({
reducer: (x, y) => y,
default: () => '',
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
});
// LangGraph Studio 集成配置
interface StudioConfig {
enableDebugging: boolean;
enableBreakpoints: boolean;
enableStateInspection: boolean;
visualizationMode: 'simple' | 'detailed' | 'interactive';
}
// Studio 集成管理器
class StudioIntegration {
private config: StudioConfig;
private breakpoints: Set<string> = new Set();
private stateSnapshots: any[] = [];
constructor(config: StudioConfig) {
this.config = config;
}
// 配置图以支持 Studio
configureGraphForStudio<T>(graph: T): T {
if (this.config.enableDebugging) {
this.addDebuggingSupport(graph as any);
}
if (this.config.enableBreakpoints) {
this.addBreakpointSupport(graph as any);
}
if (this.config.enableStateInspection) {
this.addStateInspection(graph as any);
}
return graph;
}
// 添加调试支持
private addDebuggingSupport(graph: StateGraph<any>) {
console.log('启用 LangGraph Studio 调试支持');
// 添加调试元数据(模拟)
console.log('调试元数据已添加');
}
// 添加断点支持
private addBreakpointSupport(graph: StateGraph<any>) {
console.log('启用断点支持');
// 在每个节点添加断点检查
this.breakpoints.forEach((nodeId) => {
console.log(`设置断点: ${nodeId}`);
});
}
// 添加状态检查
private addStateInspection(graph: StateGraph<any>) {
console.log('启用状态检查');
// 模拟状态检查功能
console.log('状态检查已启用');
}
// 捕获状态快照
private captureStateSnapshot(phase: string, state: any) {
const snapshot = {
timestamp: new Date().toISOString(),
phase,
state: JSON.parse(JSON.stringify(state)),
};
this.stateSnapshots.push(snapshot);
if (this.config.visualizationMode === 'interactive') {
this.notifyStudio(snapshot);
}
}
// 通知 Studio
private notifyStudio(snapshot: any) {
// 模拟向 Studio 发送状态更新
console.log('Studio 状态更新:', {
phase: snapshot.phase,
timestamp: snapshot.timestamp,
stateKeys: Object.keys(snapshot.state),
});
}
// 设置断点
setBreakpoint(nodeId: string) {
this.breakpoints.add(nodeId);
console.log(`断点已设置: ${nodeId}`);
}
// 移除断点
removeBreakpoint(nodeId: string) {
this.breakpoints.delete(nodeId);
console.log(`断点已移除: ${nodeId}`);
}
// 获取状态快照
getStateSnapshots() {
return this.stateSnapshots;
}
// 生成 Studio 可视化数据
generateStudioData() {
return {
config: this.config,
breakpoints: Array.from(this.breakpoints),
snapshots: this.stateSnapshots,
metadata: {
totalSnapshots: this.stateSnapshots.length,
activeBreakpoints: this.breakpoints.size,
lastUpdate: new Date().toISOString(),
},
};
}
}
// Studio 可视化组件
class StudioVisualizer {
private integration: StudioIntegration;
constructor(integration: StudioIntegration) {
this.integration = integration;
}
// 生成图结构数据
generateGraphStructure(graph: StateGraph<any>) {
return {
nodes: this.extractNodes(graph),
edges: this.extractEdges(graph),
metadata: this.extractMetadata(graph),
};
}
private extractNodes(graph: StateGraph<any>) {
// 模拟提取节点信息
return [
{ id: 'start', type: 'entry', label: '开始' },
{ id: 'process', type: 'normal', label: '处理' },
{ id: 'end', type: 'finish', label: '结束' },
];
}
private extractEdges(graph: StateGraph<any>) {
// 模拟提取边信息
return [
{ from: 'start', to: 'process', condition: null },
{ from: 'process', to: 'end', condition: null },
];
}
private extractMetadata(graph: StateGraph<any>) {
return {
name: 'Studio Graph',
description: 'LangGraph Studio 集成示例',
version: '1.0.0',
created: new Date().toISOString(),
};
}
// 生成执行流程可视化
generateExecutionFlow() {
const snapshots = this.integration.getStateSnapshots();
return snapshots.map((snapshot, index) => ({
step: index,
phase: snapshot.phase,
timestamp: snapshot.timestamp,
state: this.formatStateForVisualization(snapshot.state),
}));
}
private formatStateForVisualization(state: any) {
return {
messages: state.messages?.length || 0,
step: state.step || 0,
hasUserInput: !!state.user_input,
};
}
// 导出 Studio 数据
exportForStudio() {
return {
version: '1.0.0',
timestamp: new Date().toISOString(),
data: this.integration.generateStudioData(),
visualization: {
executionFlow: this.generateExecutionFlow(),
},
};
}
}
// 创建 Studio 集成示例
function createStudioIntegratedGraph() {
const config: StudioConfig = {
enableDebugging: true,
enableBreakpoints: true,
enableStateInspection: true,
visualizationMode: 'interactive',
};
const integration = new StudioIntegration(config);
const visualizer = new StudioVisualizer(integration);
// 创建基础图
const graph = new StateGraph(StateAnnotation)
.addNode('start', async (state) => {
console.log('执行开始节点');
return {
messages: ['开始处理'],
step: state.step + 1,
};
})
.addNode('process', async (state) => {
console.log('执行处理节点');
return {
messages: ['处理用户输入'],
step: state.step + 1,
};
})
.addNode('end', async (state) => {
console.log('执行结束节点');
return {
messages: ['处理完成'],
step: state.step + 1,
};
})
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end');
// 配置 Studio 集成
const studioGraph = integration.configureGraphForStudio(graph);
return {
graph: studioGraph.compile(),
integration,
visualizer,
};
}
// Studio 调试工具
class StudioDebugger {
private integration: StudioIntegration;
private isDebugging = false;
constructor(integration: StudioIntegration) {
this.integration = integration;
}
// 开始调试会话
startDebugging() {
this.isDebugging = true;
console.log('开始 Studio 调试会话');
// 设置调试环境
this.setupDebugEnvironment();
}
// 停止调试会话
stopDebugging() {
this.isDebugging = false;
console.log('停止 Studio 调试会话');
}
// 设置调试环境
private setupDebugEnvironment() {
// 添加全局错误处理
process.on('uncaughtException', (error) => {
console.error('Studio 调试 - 未捕获异常:', error);
});
// 添加调试快捷键
this.setupDebugShortcuts();
}
private setupDebugShortcuts() {
console.log('调试快捷键已设置:');
console.log('- Ctrl+B: 设置/移除断点');
console.log('- Ctrl+S: 查看状态快照');
console.log('- Ctrl+E: 导出调试数据');
}
// 执行调试命令
executeDebugCommand(command: string, args?: any) {
if (!this.isDebugging) {
console.log('请先启动调试会话');
return;
}
switch (command) {
case 'breakpoint':
this.handleBreakpointCommand(args);
break;
case 'snapshot':
this.handleSnapshotCommand();
break;
case 'export':
this.handleExportCommand();
break;
default:
console.log(`未知调试命令: ${command}`);
}
}
private handleBreakpointCommand(nodeId: string) {
if (nodeId) {
this.integration.setBreakpoint(nodeId);
} else {
console.log('请指定节点 ID');
}
}
private handleSnapshotCommand() {
const snapshots = this.integration.getStateSnapshots();
console.log('状态快照:', snapshots);
}
private handleExportCommand() {
const data = this.integration.generateStudioData();
console.log('导出调试数据:', data);
}
}
// 使用示例
async function demonstrateStudioIntegration() {
console.log('开始 LangGraph Studio 集成演示...');
const { graph, integration, visualizer } = createStudioIntegratedGraph();
const studioDebugger = new StudioDebugger(integration);
// 启动调试
studioDebugger.startDebugging();
// 设置断点
integration.setBreakpoint('process');
// 执行图
const result = await graph.invoke({
messages: [],
user_input: '测试输入',
step: 0,
});
console.log('执行结果:', result);
// 显示可视化数据
console.log('Studio 数据:', visualizer.exportForStudio());
// 停止调试
studioDebugger.stopDebugging();
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateStudioIntegration().catch(console.error);
}
export {
StudioIntegration,
StudioVisualizer,
StudioDebugger,
createStudioIntegratedGraph,
demonstrateStudioIntegration,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始 LangGraph Studio 集成演示...
启用 LangGraph Studio 调试支持
调试元数据已添加
启用断点支持
启用状态检查
状态检查已启用
开始 Studio 调试会话
调试快捷键已设置:
- Ctrl+B: 设置/移除断点
- Ctrl+S: 查看状态快照
- Ctrl+E: 导出调试数据
断点已设置: process
执行开始节点
执行处理节点
执行结束节点
执行结果: { messages: [ '开始处理', '处理用户输入', '处理完成' ], user_input: '测试输入', step: 3 }
Studio 数据: {
version: '1.0.0',
timestamp: '2025-11-16T16:23:46.076Z',
data: {
config: {
enableDebugging: true,
enableBreakpoints: true,
enableStateInspection: true,
visualizationMode: 'interactive'
},
breakpoints: [ 'process' ],
snapshots: [],
metadata: {
totalSnapshots: 0,
activeBreakpoints: 1,
lastUpdate: '2025-11-16T16:23:46.076Z'
}
},
visualization: { executionFlow: [] }
}
停止 Studio 调试会话
*/
自定义可视化工具
构建可视化组件
自定义可视化组件
/**
* ============================================================================
* 自定义可视化组件 - Custom Visualization Components
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何构建自定义的可视化组件系统,通过组件化设计实现灵活的
* LangGraph 可视化方案,包括节点、边、状态和图表等多种组件类型。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 组件基类设计:定义统一的渲染、更新、销毁接口
* 2️⃣ 节点可视化组件:渲染图节点及其样式
* 3️⃣ 边可视化组件:渲染节点间的连接关系
* 4️⃣ 状态可视化组件:展示状态历史时间线
* 5️⃣ 可视化管理器:统一管理所有组件的生命周期
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • VisualizationComponent 抽象基类定义组件接口
* • 每种组件负责特定类型的可视化内容
* • VisualizationManager 使用 Map 管理组件实例
* • VisualizationDataExtractor 提供数据提取工具
* • 支持组件的动态注册、更新和销毁
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/custom-components.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 组件销毁时需要清理资源,防止内存泄漏
* • 示例中使用 console.log 模拟渲染,实际应用需集成真实渲染库
* • 大量组件同时更新可能影响性能
* • 组件接口设计应该保持一致性,便于扩展
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
});
// 可视化组件基类
abstract class VisualizationComponent {
protected containerId: string;
protected data: any;
constructor(containerId: string) {
this.containerId = containerId;
}
abstract render(data: any): void;
abstract update(data: any): void;
abstract destroy(): void;
}
// 节点可视化组件
class NodeVisualizationComponent extends VisualizationComponent {
private nodes: Map<string, any> = new Map();
render(data: any) {
console.log(`渲染节点可视化到容器: ${this.containerId}`);
data.nodes?.forEach((node: any) => {
this.renderNode(node);
});
}
update(data: any) {
console.log('更新节点可视化');
data.nodes?.forEach((node: any) => {
this.updateNode(node);
});
}
private renderNode(node: any) {
const nodeElement = {
id: node.id,
type: node.type,
position: node.position || { x: 0, y: 0 },
style: this.getNodeStyle(node.type),
label: node.label || node.id,
};
this.nodes.set(node.id, nodeElement);
console.log(`渲染节点: ${node.id}`, nodeElement);
}
private updateNode(node: any) {
const existingNode = this.nodes.get(node.id);
if (existingNode) {
existingNode.label = node.label || node.id;
existingNode.style = this.getNodeStyle(node.type);
console.log(`更新节点: ${node.id}`, existingNode);
}
}
private getNodeStyle(type: string) {
const styles = {
start: { backgroundColor: '#4CAF50', color: 'white' },
process: { backgroundColor: '#2196F3', color: 'white' },
decision: { backgroundColor: '#FF9800', color: 'white' },
end: { backgroundColor: '#F44336', color: 'white' },
default: { backgroundColor: '#9E9E9E', color: 'white' },
};
return styles[type as keyof typeof styles] || styles.default;
}
destroy() {
this.nodes.clear();
console.log(`销毁节点可视化组件: ${this.containerId}`);
}
}
// 边可视化组件
class EdgeVisualizationComponent extends VisualizationComponent {
private edges: Map<string, any> = new Map();
render(data: any) {
console.log(`渲染边可视化到容器: ${this.containerId}`);
data.edges?.forEach((edge: any) => {
this.renderEdge(edge);
});
}
update(data: any) {
console.log('更新边可视化');
data.edges?.forEach((edge: any) => {
this.updateEdge(edge);
});
}
private renderEdge(edge: any) {
const edgeElement = {
id: `${edge.from}-${edge.to}`,
from: edge.from,
to: edge.to,
label: edge.condition || '',
style: this.getEdgeStyle(edge.type),
};
this.edges.set(edgeElement.id, edgeElement);
console.log(`渲染边: ${edgeElement.id}`, edgeElement);
}
private updateEdge(edge: any) {
const edgeId = `${edge.from}-${edge.to}`;
const existingEdge = this.edges.get(edgeId);
if (existingEdge) {
existingEdge.label = edge.condition || '';
existingEdge.style = this.getEdgeStyle(edge.type);
console.log(`更新边: ${edgeId}`, existingEdge);
}
}
private getEdgeStyle(type?: string) {
const styles = {
conditional: { strokeDasharray: '5,5', stroke: '#FF9800' },
normal: { stroke: '#2196F3' },
error: { stroke: '#F44336' },
default: { stroke: '#9E9E9E' },
};
return styles[type as keyof typeof styles] || styles.default;
}
destroy() {
this.edges.clear();
console.log(`销毁边可视化组件: ${this.containerId}`);
}
}
// 状态可视化组件
class StateVisualizationComponent extends VisualizationComponent {
private stateHistory: any[] = [];
render(data: any) {
console.log(`渲染状态可视化到容器: ${this.containerId}`);
if (data.states) {
this.stateHistory = data.states;
this.renderStateTimeline();
}
}
update(data: any) {
console.log('更新状态可视化');
if (data.newState) {
this.stateHistory.push(data.newState);
this.renderStateTimeline();
}
}
private renderStateTimeline() {
console.log('渲染状态时间线:');
this.stateHistory.forEach((state, index) => {
console.log(`步骤 ${index}:`, {
timestamp: state.timestamp,
messages: state.messages?.length || 0,
step: state.step || 0,
});
});
}
destroy() {
this.stateHistory = [];
console.log(`销毁状态可视化组件: ${this.containerId}`);
}
}
// 图表可视化组件
class ChartVisualizationComponent extends VisualizationComponent {
private chartData: any = null;
render(data: any) {
console.log(`渲染图表到容器: ${this.containerId}`);
this.chartData = this.processChartData(data);
this.renderChart();
}
update(data: any) {
console.log('更新图表');
this.chartData = this.processChartData(data);
this.renderChart();
}
private processChartData(data: any) {
return {
labels: data.labels || [],
datasets: data.datasets || [],
options: {
responsive: true,
plugins: {
title: {
display: true,
text: data.title || '图表',
},
},
},
};
}
private renderChart() {
console.log('渲染图表数据:', this.chartData);
// 模拟图表渲染
if (this.chartData.datasets.length > 0) {
console.log('图表类型: 线图');
console.log('数据点数量:', this.chartData.datasets[0].data?.length || 0);
}
}
destroy() {
this.chartData = null;
console.log(`销毁图表组件: ${this.containerId}`);
}
}
// 可视化管理器
class VisualizationManager {
private components: Map<string, VisualizationComponent> = new Map();
// 注册组件
registerComponent(id: string, component: VisualizationComponent) {
this.components.set(id, component);
console.log(`注册可视化组件: ${id}`);
}
// 移除组件
removeComponent(id: string) {
const component = this.components.get(id);
if (component) {
component.destroy();
this.components.delete(id);
console.log(`移除可视化组件: ${id}`);
}
}
// 更新所有组件
updateAll(data: any) {
console.log('更新所有可视化组件');
this.components.forEach((component, id) => {
try {
component.update(data);
} catch (error) {
console.error(`更新组件 ${id} 时出错:`, error);
}
});
}
// 渲染所有组件
renderAll(data: any) {
console.log('渲染所有可视化组件');
this.components.forEach((component, id) => {
try {
component.render(data);
} catch (error) {
console.error(`渲染组件 ${id} 时出错:`, error);
}
});
}
// 销毁所有组件
destroyAll() {
console.log('销毁所有可视化组件');
this.components.forEach((component, id) => {
component.destroy();
});
this.components.clear();
}
// 获取组件
getComponent(id: string): VisualizationComponent | undefined {
return this.components.get(id);
}
// 获取所有组件ID
getComponentIds(): string[] {
return Array.from(this.components.keys());
}
}
// 可视化数据提取器
class VisualizationDataExtractor {
// 从图中提取可视化数据
static extractFromGraph(graph: StateGraph<any>) {
return {
nodes: this.extractNodes(graph),
edges: this.extractEdges(graph),
metadata: this.extractMetadata(graph),
};
}
private static extractNodes(graph: StateGraph<any>) {
// 模拟节点提取
return [
{
id: 'start',
type: 'start',
label: '开始',
position: { x: 100, y: 100 },
},
{
id: 'process',
type: 'process',
label: '处理',
position: { x: 300, y: 100 },
},
{ id: 'end', type: 'end', label: '结束', position: { x: 500, y: 100 } },
];
}
private static extractEdges(graph: StateGraph<any>) {
// 模拟边提取
return [
{ from: 'start', to: 'process', type: 'normal' },
{ from: 'process', to: 'end', type: 'normal' },
];
}
private static extractMetadata(graph: StateGraph<any>) {
return {
name: 'Custom Graph',
nodeCount: 3,
edgeCount: 2,
created: new Date().toISOString(),
};
}
// 从执行结果中提取状态数据
static extractStateData(executionResults: any[]) {
return executionResults.map((result, index) => ({
step: index,
timestamp: new Date().toISOString(),
messages: result.messages || [],
step_number: result.step || 0,
}));
}
// 生成图表数据
static generateChartData(stateData: any[]) {
return {
title: '执行步骤统计',
labels: stateData.map((_, index) => `步骤 ${index + 1}`),
datasets: [
{
label: '消息数量',
data: stateData.map((state) => state.messages?.length || 0),
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
},
],
};
}
}
// 创建自定义可视化示例
function createCustomVisualization() {
const manager = new VisualizationManager();
// 创建并注册组件
const nodeComponent = new NodeVisualizationComponent('nodes-container');
const edgeComponent = new EdgeVisualizationComponent('edges-container');
const stateComponent = new StateVisualizationComponent('state-container');
const chartComponent = new ChartVisualizationComponent('chart-container');
manager.registerComponent('nodes', nodeComponent);
manager.registerComponent('edges', edgeComponent);
manager.registerComponent('state', stateComponent);
manager.registerComponent('chart', chartComponent);
return manager;
}
// 使用示例
async function demonstrateCustomVisualization() {
console.log('开始自定义可视化演示...');
// 创建图
const graph = new StateGraph(StateAnnotation)
.addNode('start', async (state) => {
return {
messages: ['开始处理'],
step: state.step + 1,
};
})
.addNode('process', async (state) => {
return {
messages: ['处理中...'],
step: state.step + 1,
};
})
.addNode('end', async (state) => {
return {
messages: ['处理完成'],
step: state.step + 1,
};
})
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end');
const compiledGraph = graph.compile();
// 创建可视化管理器
const manager = createCustomVisualization();
// 提取图数据
const graphData = VisualizationDataExtractor.extractFromGraph(graph);
// 渲染初始可视化
manager.renderAll(graphData);
// 执行图并收集状态数据
const executionResults: any[] = [];
const result = await compiledGraph.invoke({
messages: [],
step: 0,
});
executionResults.push(result);
// 提取状态数据
const stateData =
VisualizationDataExtractor.extractStateData(executionResults);
const chartData = VisualizationDataExtractor.generateChartData(stateData);
// 更新可视化
manager.updateAll({
...graphData,
states: stateData,
...chartData,
});
console.log('自定义可视化演示完成');
console.log('执行结果:', result);
// 清理资源
setTimeout(() => {
manager.destroyAll();
console.log('可视化资源已清理');
}, 1000);
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateCustomVisualization().catch(console.error);
}
export {
VisualizationComponent,
NodeVisualizationComponent,
EdgeVisualizationComponent,
StateVisualizationComponent,
ChartVisualizationComponent,
VisualizationManager,
VisualizationDataExtractor,
createCustomVisualization,
demonstrateCustomVisualization,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始自定义可视化演示...
注册可视化组件: nodes
注册可视化组件: edges
注册可视化组件: state
注册可视化组件: chart
渲染所有可视化组件
渲染节点可视化到容器: nodes-container
渲染节点: start {
id: 'start',
type: 'start',
position: { x: 100, y: 100 },
style: { backgroundColor: '#4CAF50', color: 'white' },
label: '开始'
}
渲染节点: process {
id: 'process',
type: 'process',
position: { x: 300, y: 100 },
style: { backgroundColor: '#2196F3', color: 'white' },
label: '处理'
}
渲染节点: end {
id: 'end',
type: 'end',
position: { x: 500, y: 100 },
style: { backgroundColor: '#F44336', color: 'white' },
label: '结束'
}
渲染边可视化到容器: edges-container
渲染边: start-process {
id: 'start-process',
from: 'start',
to: 'process',
label: '',
style: { stroke: '#2196F3' }
}
渲染边: process-end {
id: 'process-end',
from: 'process',
to: 'end',
label: '',
style: { stroke: '#2196F3' }
}
渲染状态可视化到容器: state-container
渲染图表到容器: chart-container
渲染图表数据: {
labels: [],
datasets: [],
options: { responsive: true, plugins: { title: [Object] } }
}
更新所有可视化组件
更新节点可视化
更新节点: start {
id: 'start',
type: 'start',
position: { x: 100, y: 100 },
style: { backgroundColor: '#4CAF50', color: 'white' },
label: '开始'
}
更新节点: process {
id: 'process',
type: 'process',
position: { x: 300, y: 100 },
style: { backgroundColor: '#2196F3', color: 'white' },
label: '处理'
}
更新节点: end {
id: 'end',
type: 'end',
position: { x: 500, y: 100 },
style: { backgroundColor: '#F44336', color: 'white' },
label: '结束'
}
更新边可视化
更新边: start-process {
id: 'start-process',
from: 'start',
to: 'process',
label: '',
style: { stroke: '#2196F3' }
}
更新边: process-end {
id: 'process-end',
from: 'process',
to: 'end',
label: '',
style: { stroke: '#2196F3' }
}
更新状态可视化
更新图表
渲染图表数据: {
labels: [ '步骤 1' ],
datasets: [
{
label: '消息数量',
data: [Array],
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)'
}
],
options: { responsive: true, plugins: { title: [Object] } }
}
图表类型: 线图
数据点数量: 1
自定义可视化演示完成
执行结果: { messages: [ '开始处理', '处理中...', '处理完成' ], step: 3 }
销毁所有可视化组件
销毁节点可视化组件: nodes-container
销毁边可视化组件: edges-container
销毁状态可视化组件: state-container
销毁图表组件: chart-container
可视化资源已清理
*/
React 集成
React 可视化组件
import React, { useState, useEffect, useCallback } from 'react';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
});
// 节点数据接口
interface NodeData {
id: string;
type: string;
label: string;
position: { x: number; y: number };
status?: 'idle' | 'running' | 'completed' | 'error';
}
// 边数据接口
interface EdgeData {
id: string;
from: string;
to: string;
label?: string;
type?: string;
}
// 图数据接口
interface GraphData {
nodes: NodeData[];
edges: EdgeData[];
}
// 执行状态接口
interface ExecutionState {
currentNode?: string;
step: number;
messages: string[];
isRunning: boolean;
}
// 节点组件
const NodeComponent: React.FC<{
node: NodeData;
onClick?: (nodeId: string) => void;
}> = ({ node, onClick }) => {
const getNodeStyle = () => {
const baseStyle = {
position: 'absolute' as const,
left: node.position.x,
top: node.position.y,
width: 120,
height: 60,
borderRadius: 8,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
border: '2px solid',
fontSize: '14px',
fontWeight: 'bold',
transition: 'all 0.3s ease',
};
const statusStyles = {
idle: { backgroundColor: '#f5f5f5', borderColor: '#ddd', color: '#666' },
running: {
backgroundColor: '#fff3cd',
borderColor: '#ffc107',
color: '#856404',
},
completed: {
backgroundColor: '#d4edda',
borderColor: '#28a745',
color: '#155724',
},
error: {
backgroundColor: '#f8d7da',
borderColor: '#dc3545',
color: '#721c24',
},
};
return {
...baseStyle,
...statusStyles[node.status || 'idle'],
};
};
return (
<div
style={getNodeStyle()}
onClick={() => onClick?.(node.id)}
title={`节点: ${node.label} (${node.status || 'idle'})`}
>
{node.label}
</div>
);
};
// 边组件
const EdgeComponent: React.FC<{ edge: EdgeData; nodes: NodeData[] }> = ({
edge,
nodes,
}) => {
const fromNode = nodes.find((n) => n.id === edge.from);
const toNode = nodes.find((n) => n.id === edge.to);
if (!fromNode || !toNode) return null;
const startX = fromNode.position.x + 120;
const startY = fromNode.position.y + 30;
const endX = toNode.position.x;
const endY = toNode.position.y + 30;
return (
<svg
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'none',
zIndex: 1,
}}
>
<defs>
<marker
id='arrowhead'
markerWidth='10'
markerHeight='7'
refX='9'
refY='3.5'
orient='auto'
>
<polygon points='0 0, 10 3.5, 0 7' fill='#666' />
</marker>
</defs>
<line
x1={startX}
y1={startY}
x2={endX}
y2={endY}
stroke='#666'
strokeWidth='2'
markerEnd='url(#arrowhead)'
/>
{edge.label && (
<text
x={(startX + endX) / 2}
y={(startY + endY) / 2 - 5}
textAnchor='middle'
fontSize='12'
fill='#666'
>
{edge.label}
</text>
)}
</svg>
);
};
// 图可视化组件
const GraphVisualization: React.FC<{
graphData: GraphData;
executionState: ExecutionState;
onNodeClick?: (nodeId: string) => void;
}> = ({ graphData, executionState, onNodeClick }) => {
const [nodes, setNodes] = useState<NodeData[]>(graphData.nodes);
useEffect(() => {
// 更新节点状态
const updatedNodes = graphData.nodes.map((node) => ({
...node,
status:
node.id === executionState.currentNode
? executionState.isRunning
? 'running'
: 'completed'
: ('idle' as const),
}));
setNodes(updatedNodes);
}, [graphData.nodes, executionState]);
return (
<div
style={{
position: 'relative',
width: '100%',
height: '400px',
border: '1px solid #ddd',
borderRadius: '8px',
backgroundColor: '#fafafa',
overflow: 'hidden',
}}
>
{/* 渲染边 */}
{graphData.edges.map((edge) => (
<EdgeComponent key={edge.id} edge={edge} nodes={nodes} />
))}
{/* 渲染节点 */}
{nodes.map((node) => (
<NodeComponent key={node.id} node={node} onClick={onNodeClick} />
))}
</div>
);
};
// 状态面板组件
const StatePanel: React.FC<{ executionState: ExecutionState }> = ({
executionState,
}) => {
return (
<div
style={{
padding: '16px',
border: '1px solid #ddd',
borderRadius: '8px',
backgroundColor: '#f9f9f9',
marginTop: '16px',
}}
>
<h3 style={{ margin: '0 0 12px 0', fontSize: '16px' }}>执行状态</h3>
<div style={{ fontSize: '14px', lineHeight: '1.5' }}>
<div>
<strong>当前节点:</strong> {executionState.currentNode || '无'}
</div>
<div>
<strong>步骤:</strong> {executionState.step}
</div>
<div>
<strong>状态:</strong>{' '}
{executionState.isRunning ? '运行中' : '已停止'}
</div>
<div>
<strong>消息数量:</strong> {executionState.messages.length}
</div>
</div>
{executionState.messages.length > 0 && (
<div style={{ marginTop: '12px' }}>
<strong>最新消息:</strong>
<div
style={{
maxHeight: '100px',
overflowY: 'auto',
padding: '8px',
backgroundColor: 'white',
border: '1px solid #ddd',
borderRadius: '4px',
marginTop: '4px',
}}
>
{executionState.messages.map((message, index) => (
<div key={index} style={{ padding: '2px 0' }}>
{message}
</div>
))}
</div>
</div>
)}
</div>
);
};
// 控制面板组件
const ControlPanel: React.FC<{
onExecute: () => void;
onReset: () => void;
isRunning: boolean;
}> = ({ onExecute, onReset, isRunning }) => {
return (
<div
style={{
padding: '16px',
border: '1px solid #ddd',
borderRadius: '8px',
backgroundColor: '#f9f9f9',
marginTop: '16px',
display: 'flex',
gap: '12px',
}}
>
<button
onClick={onExecute}
disabled={isRunning}
style={{
padding: '8px 16px',
backgroundColor: isRunning ? '#ccc' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isRunning ? 'not-allowed' : 'pointer',
}}
>
{isRunning ? '执行中...' : '执行图'}
</button>
<button
onClick={onReset}
disabled={isRunning}
style={{
padding: '8px 16px',
backgroundColor: isRunning ? '#ccc' : '#6c757d',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isRunning ? 'not-allowed' : 'pointer',
}}
>
重置
</button>
</div>
);
};
// 主可视化组件
const LangGraphVisualization: React.FC = () => {
const [graphData] = useState<GraphData>({
nodes: [
{
id: 'start',
type: 'start',
label: '开始',
position: { x: 50, y: 150 },
},
{
id: 'process',
type: 'process',
label: '处理',
position: { x: 250, y: 150 },
},
{ id: 'end', type: 'end', label: '结束', position: { x: 450, y: 150 } },
],
edges: [
{ id: 'start-process', from: 'start', to: 'process' },
{ id: 'process-end', from: 'process', to: 'end' },
],
});
const [executionState, setExecutionState] = useState<ExecutionState>({
step: 0,
messages: [],
isRunning: false,
});
const [graph] = useState(() => {
return new StateGraph(StateAnnotation)
.addNode('start', async (state) => {
await new Promise((resolve) => setTimeout(resolve, 1000)); // 模拟延迟
return {
messages: ['开始处理'],
step: state.step + 1,
};
})
.addNode('process', async (state) => {
await new Promise((resolve) => setTimeout(resolve, 1500)); // 模拟延迟
return {
messages: ['处理中...', '处理完成'],
step: state.step + 1,
};
})
.addNode('end', async (state) => {
await new Promise((resolve) => setTimeout(resolve, 800)); // 模拟延迟
return {
messages: ['结束处理'],
step: state.step + 1,
};
})
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end')
.compile();
});
const executeGraph = useCallback(async () => {
setExecutionState((prev) => ({ ...prev, isRunning: true }));
try {
// 模拟逐步执行
const steps = ['start', 'process', 'end'];
for (const step of steps) {
setExecutionState((prev) => ({
...prev,
currentNode: step,
}));
// 模拟节点执行时间
await new Promise((resolve) => setTimeout(resolve, 1000));
}
// 执行实际图
const result = await graph.invoke({
messages: [],
step: 0,
});
setExecutionState((prev) => ({
...prev,
messages: result.messages,
step: result.step,
currentNode: undefined,
isRunning: false,
}));
} catch (error) {
console.error('执行图时出错:', error);
setExecutionState((prev) => ({
...prev,
isRunning: false,
currentNode: undefined,
}));
}
}, [graph]);
const resetExecution = useCallback(() => {
setExecutionState({
step: 0,
messages: [],
isRunning: false,
});
}, []);
const handleNodeClick = useCallback((nodeId: string) => {
console.log('点击节点:', nodeId);
// 可以在这里添加节点详情显示逻辑
}, []);
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h2 style={{ marginBottom: '20px' }}>LangGraph React 可视化</h2>
<GraphVisualization
graphData={graphData}
executionState={executionState}
onNodeClick={handleNodeClick}
/>
<ControlPanel
onExecute={executeGraph}
onReset={resetExecution}
isRunning={executionState.isRunning}
/>
<StatePanel executionState={executionState} />
</div>
);
};
// 简化的图表组件
const SimpleChart: React.FC<{
data: number[];
labels: string[];
title: string;
}> = ({ data, labels, title }) => {
const maxValue = Math.max(...data);
const chartHeight = 200;
const chartWidth = 400;
const barWidth = chartWidth / data.length - 10;
return (
<div
style={{ padding: '16px', border: '1px solid #ddd', borderRadius: '8px' }}
>
<h3 style={{ textAlign: 'center', marginBottom: '16px' }}>{title}</h3>
<svg width={chartWidth} height={chartHeight + 40}>
{data.map((value, index) => {
const barHeight = (value / maxValue) * chartHeight;
const x = index * (barWidth + 10) + 5;
const y = chartHeight - barHeight;
return (
<g key={index}>
<rect
x={x}
y={y}
width={barWidth}
height={barHeight}
fill='#007bff'
opacity={0.8}
/>
<text
x={x + barWidth / 2}
y={chartHeight + 15}
textAnchor='middle'
fontSize='12'
fill='#666'
>
{labels[index]}
</text>
<text
x={x + barWidth / 2}
y={y - 5}
textAnchor='middle'
fontSize='12'
fill='#333'
>
{value}
</text>
</g>
);
})}
</svg>
</div>
);
};
export {
LangGraphVisualization,
SimpleChart,
NodeComponent,
EdgeComponent,
GraphVisualization,
StatePanel,
ControlPanel,
};
性能监控可视化
执行时间分析
性能监控
/**
* ============================================================================
* 性能监控可视化 - Performance Monitoring Visualization
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何监控 LangGraph 的性能指标并进行可视化。收集节点执行时间、内存使用等
* 数据,生成性能报告和图表,帮助识别性能瓶颈和优化方向。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 执行时间监控:记录每个节点的执行时间(最小、最大、平均)
* 2️⃣ 内存使用监控:跟踪节点执行过程中的内存变化
* 3️⃣ 成功率统计:计算节点的成功率和错误次数
* 4️⃣ 性能可视化:生成执行时间、内存使用、成功率等图表
* 5️⃣ 性能报告:导出详细的性能分析报告
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • PerformanceMonitor:使用 performance.now() 和 process.memoryUsage() 收集指标
* • 监控节点包装器:使用高阶函数包装节点,自动记录性能数据
* • PerformanceVisualizer:生成 Chart.js 格式的图表数据
* • 统计分析:计算平均值、最值、成功率等统计指标
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/performance-monitoring.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 浏览器环境中无法获取精确的内存使用数据
* • 性能监控会带来少量额外开销
* • 建议在开发和测试环境中启用,生产环境需谨慎使用
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义状态结构
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
step: Annotation<number>({
reducer: (x, y) => y,
default: () => 0,
}),
});
// 性能指标接口
interface PerformanceMetrics {
nodeId: string;
executionTime: number;
memoryUsage: number;
timestamp: string;
status: 'success' | 'error' | 'timeout';
inputSize?: number;
outputSize?: number;
}
// 性能监控器
class PerformanceMonitor {
private metrics: PerformanceMetrics[] = [];
private startTimes: Map<string, number> = new Map();
private memoryBaseline: number = 0;
constructor() {
this.memoryBaseline = this.getMemoryUsage();
}
// 开始监控节点执行
startNodeExecution(nodeId: string, inputData?: any) {
this.startTimes.set(nodeId, performance.now());
console.log(`开始监控节点: ${nodeId}`);
}
// 结束监控节点执行
endNodeExecution(
nodeId: string,
status: 'success' | 'error' | 'timeout',
outputData?: any
) {
const startTime = this.startTimes.get(nodeId);
if (!startTime) {
console.warn(`未找到节点 ${nodeId} 的开始时间`);
return;
}
const executionTime = performance.now() - startTime;
const memoryUsage = this.getMemoryUsage() - this.memoryBaseline;
const metric: PerformanceMetrics = {
nodeId,
executionTime,
memoryUsage,
timestamp: new Date().toISOString(),
status,
inputSize: this.calculateDataSize(undefined),
outputSize: this.calculateDataSize(outputData),
};
this.metrics.push(metric);
this.startTimes.delete(nodeId);
console.log(`节点 ${nodeId} 执行完成:`, {
时间: `${executionTime.toFixed(2)}ms`,
内存: `${memoryUsage.toFixed(2)}MB`,
状态: status,
});
}
// 获取内存使用情况
private getMemoryUsage(): number {
if (typeof process !== 'undefined' && process.memoryUsage) {
return process.memoryUsage().heapUsed / 1024 / 1024; // MB
}
return 0; // 浏览器环境无法获取精确内存使用
}
// 计算数据大小
private calculateDataSize(data: any): number {
if (!data) return 0;
try {
return JSON.stringify(data).length;
} catch {
return 0;
}
}
// 获取所有指标
getMetrics(): PerformanceMetrics[] {
return [...this.metrics];
}
// 获取节点统计信息
getNodeStats(nodeId: string) {
const nodeMetrics = this.metrics.filter((m) => m.nodeId === nodeId);
if (nodeMetrics.length === 0) {
return null;
}
const executionTimes = nodeMetrics.map((m) => m.executionTime);
const memoryUsages = nodeMetrics.map((m) => m.memoryUsage);
return {
nodeId,
totalExecutions: nodeMetrics.length,
avgExecutionTime:
executionTimes.reduce((a, b) => a + b, 0) / executionTimes.length,
minExecutionTime: Math.min(...executionTimes),
maxExecutionTime: Math.max(...executionTimes),
avgMemoryUsage:
memoryUsages.reduce((a, b) => a + b, 0) / memoryUsages.length,
successRate:
nodeMetrics.filter((m) => m.status === 'success').length /
nodeMetrics.length,
errorCount: nodeMetrics.filter((m) => m.status === 'error').length,
};
}
// 获取整体统计信息
getOverallStats() {
if (this.metrics.length === 0) {
return null;
}
const totalExecutionTime = this.metrics.reduce(
(sum, m) => sum + m.executionTime,
0
);
const totalMemoryUsage = this.metrics.reduce(
(sum, m) => sum + m.memoryUsage,
0
);
const uniqueNodes = new Set(this.metrics.map((m) => m.nodeId));
return {
totalExecutions: this.metrics.length,
totalExecutionTime,
avgExecutionTime: totalExecutionTime / this.metrics.length,
totalMemoryUsage,
avgMemoryUsage: totalMemoryUsage / this.metrics.length,
uniqueNodesCount: uniqueNodes.size,
successRate:
this.metrics.filter((m) => m.status === 'success').length /
this.metrics.length,
errorRate:
this.metrics.filter((m) => m.status === 'error').length /
this.metrics.length,
};
}
// 清除指标
clearMetrics() {
this.metrics = [];
this.startTimes.clear();
console.log('性能指标已清除');
}
// 导出指标数据
exportMetrics() {
return {
timestamp: new Date().toISOString(),
metrics: this.metrics,
overallStats: this.getOverallStats(),
nodeStats: Array.from(new Set(this.metrics.map((m) => m.nodeId)))
.map((nodeId) => this.getNodeStats(nodeId))
.filter(Boolean),
};
}
}
// 性能可视化器
class PerformanceVisualizer {
private monitor: PerformanceMonitor;
constructor(monitor: PerformanceMonitor) {
this.monitor = monitor;
}
// 生成执行时间图表数据
generateExecutionTimeChart() {
const metrics = this.monitor.getMetrics();
const nodeGroups = this.groupMetricsByNode(metrics);
return {
title: '节点执行时间对比',
type: 'bar',
data: {
labels: Object.keys(nodeGroups),
datasets: [
{
label: '平均执行时间 (ms)',
data: Object.values(nodeGroups).map(
(group) =>
group.reduce((sum, m) => sum + m.executionTime, 0) /
group.length
),
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1,
},
],
},
};
}
// 生成内存使用图表数据
generateMemoryUsageChart() {
const metrics = this.monitor.getMetrics();
return {
title: '内存使用趋势',
type: 'line',
data: {
labels: metrics.map((_, index) => `执行 ${index + 1}`),
datasets: [
{
label: '内存使用 (MB)',
data: metrics.map((m) => m.memoryUsage),
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
tension: 0.1,
},
],
},
};
}
// 生成成功率图表数据
generateSuccessRateChart() {
const metrics = this.monitor.getMetrics();
const nodeGroups = this.groupMetricsByNode(metrics);
return {
title: '节点成功率',
type: 'doughnut',
data: {
labels: Object.keys(nodeGroups),
datasets: [
{
data: Object.values(nodeGroups).map(
(group) =>
(group.filter((m) => m.status === 'success').length /
group.length) *
100
),
backgroundColor: [
'rgba(75, 192, 192, 0.6)',
'rgba(54, 162, 235, 0.6)',
'rgba(255, 206, 86, 0.6)',
'rgba(255, 99, 132, 0.6)',
],
},
],
},
};
}
// 按节点分组指标
private groupMetricsByNode(metrics: PerformanceMetrics[]) {
return metrics.reduce((groups, metric) => {
if (!groups[metric.nodeId]) {
groups[metric.nodeId] = [];
}
groups[metric.nodeId].push(metric);
return groups;
}, {} as Record<string, PerformanceMetrics[]>);
}
// 生成性能报告
generatePerformanceReport() {
const overallStats = this.monitor.getOverallStats();
const metrics = this.monitor.getMetrics();
if (!overallStats) {
return '暂无性能数据';
}
const report = `
性能监控报告
====================
总体统计:
- 总执行次数: ${overallStats.totalExecutions}
- 总执行时间: ${overallStats.totalExecutionTime.toFixed(2)}ms
- 平均执行时间: ${overallStats.avgExecutionTime.toFixed(2)}ms
- 总内存使用: ${overallStats.totalMemoryUsage.toFixed(2)}MB
- 平均内存使用: ${overallStats.avgMemoryUsage.toFixed(2)}MB
- 成功率: ${(overallStats.successRate * 100).toFixed(1)}%
- 错误率: ${(overallStats.errorRate * 100).toFixed(1)}%
节点详情:
${Array.from(new Set(metrics.map((m) => m.nodeId)))
.map((nodeId) => {
const stats = this.monitor.getNodeStats(nodeId);
return `
${nodeId}:
- 执行次数: ${stats?.totalExecutions}
- 平均时间: ${stats?.avgExecutionTime.toFixed(2)}ms
- 最小时间: ${stats?.minExecutionTime.toFixed(2)}ms
- 最大时间: ${stats?.maxExecutionTime.toFixed(2)}ms
- 成功率: ${((stats?.successRate || 0) * 100).toFixed(1)}%
- 错误次数: ${stats?.errorCount}`;
})
.join('\n')}
生成时间: ${new Date().toLocaleString()}
`.trim();
return report;
}
}
// 创建带性能监控的节点
function createMonitoredNode(
nodeId: string,
handler: Function,
monitor: PerformanceMonitor
) {
return async (state: any) => {
monitor.startNodeExecution(nodeId, state);
try {
const result = await handler(state);
monitor.endNodeExecution(nodeId, 'success', result);
return result;
} catch (error) {
monitor.endNodeExecution(nodeId, 'error');
throw error;
}
};
}
// 创建带性能监控的图
function createMonitoredGraph() {
const monitor = new PerformanceMonitor();
const visualizer = new PerformanceVisualizer(monitor);
const graph = new StateGraph(StateAnnotation)
.addNode(
'start',
createMonitoredNode(
'start',
async (state) => {
// 模拟一些处理时间
await new Promise((resolve) =>
setTimeout(resolve, 100 + Math.random() * 200)
);
return {
messages: ['开始处理'],
step: state.step + 1,
};
},
monitor
)
)
.addNode(
'process',
createMonitoredNode(
'process',
async (state) => {
// 模拟更长的处理时间
await new Promise((resolve) =>
setTimeout(resolve, 300 + Math.random() * 500)
);
// 模拟偶尔的错误
if (Math.random() < 0.1) {
throw new Error('模拟处理错误');
}
return {
messages: ['处理中...', '处理完成'],
step: state.step + 1,
};
},
monitor
)
)
.addNode(
'end',
createMonitoredNode(
'end',
async (state) => {
// 模拟短处理时间
await new Promise((resolve) =>
setTimeout(resolve, 50 + Math.random() * 100)
);
return {
messages: ['结束处理'],
step: state.step + 1,
};
},
monitor
)
)
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end');
return {
graph: graph.compile(),
monitor,
visualizer,
};
}
// 使用示例
async function demonstratePerformanceMonitoring() {
console.log('开始性能监控演示...');
const { graph, monitor, visualizer } = createMonitoredGraph();
// 执行多次以收集性能数据
for (let i = 0; i < 5; i++) {
console.log(`\n执行第 ${i + 1} 次:`);
try {
const result = await graph.invoke({
messages: [],
step: 0,
});
console.log('执行成功:', result.messages.length, '条消息');
} catch (error) {
console.log('执行失败:', error.message);
}
}
// 显示性能统计
console.log('\n=== 性能统计 ===');
const overallStats = monitor.getOverallStats();
console.log('总体统计:', overallStats);
// 显示各节点统计
console.log('\n=== 节点统计 ===');
['start', 'process', 'end'].forEach((nodeId) => {
const stats = monitor.getNodeStats(nodeId);
if (stats) {
console.log(`${nodeId}:`, stats);
}
});
// 生成可视化数据
console.log('\n=== 可视化数据 ===');
console.log('执行时间图表:', visualizer.generateExecutionTimeChart());
console.log('内存使用图表:', visualizer.generateMemoryUsageChart());
console.log('成功率图表:', visualizer.generateSuccessRateChart());
// 生成性能报告
console.log('\n=== 性能报告 ===');
console.log(visualizer.generatePerformanceReport());
// 导出数据
console.log('\n=== 导出数据 ===');
const exportData = monitor.exportMetrics();
console.log('导出的性能数据:', exportData);
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstratePerformanceMonitoring().catch(console.error);
}
export {
PerformanceMonitor,
PerformanceVisualizer,
createMonitoredNode,
createMonitoredGraph,
demonstratePerformanceMonitoring,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始性能监控演示...
执行第 1 次:
开始监控节点: start
节点 start 执行完成: { '时间': '296.19ms', '内存': '1.20MB', '状态': 'success' }
开始监控节点: process
节点 process 执行完成: { '时间': '685.83ms', '内存': '1.73MB', '状态': 'success' }
开始监控节点: end
节点 end 执行完成: { '时间': '68.74ms', '内存': '2.03MB', '状态': 'success' }
执行成功: 4 条消息
执行第 2 次:
开始监控节点: start
节点 start 执行完成: { '时间': '300.94ms', '内存': '3.78MB', '状态': 'success' }
开始监控节点: process
节点 process 执行完成: { '时间': '361.78ms', '内存': '4.07MB', '状态': 'success' }
开始监控节点: end
节点 end 执行完成: { '时间': '86.57ms', '内存': '4.35MB', '状态': 'success' }
执行成功: 4 条消息
执行第 3 次:
开始监控节点: start
节点 start 执行完成: { '时间': '223.39ms', '内存': '5.12MB', '状态': 'success' }
开始监控节点: process
节点 process 执行完成: { '时间': '698.30ms', '内存': '5.40MB', '状态': 'success' }
开始监控节点: end
节点 end 执行完成: { '时间': '120.42ms', '内存': '5.69MB', '状态': 'success' }
执行成功: 4 条消息
执行第 4 次:
开始监控节点: start
节点 start 执行完成: { '时间': '202.60ms', '内存': '6.18MB', '状态': 'success' }
开始监控节点: process
节点 process 执行完成: { '时间': '520.70ms', '内存': '6.42MB', '状态': 'success' }
开始监控节点: end
节点 end 执行完成: { '时间': '108.52ms', '内存': '6.67MB', '状态': 'success' }
执行成功: 4 条消息
执行第 5 次:
开始监控节点: start
节点 start 执行完成: { '时间': '221.33ms', '内存': '7.15MB', '状态': 'success' }
开始监控节点: process
节点 process 执行完成: { '时间': '508.50ms', '内存': '7.39MB', '状态': 'error' }
执行失败: 模拟处理错误
=== 性能统计 ===
总体统计: {
totalExecutions: 14,
totalExecutionTime: 4403.816459999999,
avgExecutionTime: 314.5583185714285,
totalMemoryUsage: 67.1851806640625,
avgMemoryUsage: 4.798941476004464,
uniqueNodesCount: 3,
successRate: 0.9285714285714286,
errorRate: 0.07142857142857142
}
=== 节点统计 ===
start: {
nodeId: 'start',
totalExecutions: 5,
avgExecutionTime: 248.88860019999993,
minExecutionTime: 202.5972499999998,
maxExecutionTime: 300.9371249999999,
avgMemoryUsage: 4.685354614257813,
successRate: 1,
errorCount: 0
}
process: {
nodeId: 'process',
totalExecutions: 5,
avgExecutionTime: 555.0251999999999,
minExecutionTime: 361.7814999999998,
maxExecutionTime: 698.3028750000003,
avgMemoryUsage: 5.003312683105468,
successRate: 0.8,
errorCount: 1
}
end: {
nodeId: 'end',
totalExecutions: 4,
avgExecutionTime: 96.06186474999993,
minExecutionTime: 68.73520899999994,
maxExecutionTime: 120.41966700000012,
avgMemoryUsage: 4.685461044311523,
successRate: 1,
errorCount: 0
}
=== 可视化数据 ===
执行时间图表: {
title: '节点执行时间对比',
type: 'bar',
data: { labels: [ 'start', 'process', 'end' ], datasets: [ [Object] ] }
}
内存使用图表: {
title: '内存使用趋势',
type: 'line',
data: {
labels: [
'执行 1', '执行 2',
'执行 3', '执行 4',
'执行 5', '执行 6',
'执行 7', '执行 8',
'执行 9', '执行 10',
'执行 11', '执行 12',
'执行 13', '执行 14'
],
datasets: [ [Object] ]
}
}
成功率图表: {
title: '节点成功率',
type: 'doughnut',
data: { labels: [ 'start', 'process', 'end' ], datasets: [ [Object] ] }
}
=== 性能报告 ===
性能监控报告
====================
总体统计:
- 总执行次数: 14
- 总执行时间: 4403.82ms
- 平均执行时间: 314.56ms
- 总内存使用: 67.19MB
- 平均内存使用: 4.80MB
- 成功率: 92.9%
- 错误率: 7.1%
节点详情:
start:
- 执行次数: 5
- 平均时间: 248.89ms
- 最小时间: 202.60ms
- 最大时间: 300.94ms
- 成功率: 100.0%
- 错误次数: 0
process:
- 执行次数: 5
- 平均时间: 555.03ms
- 最小时间: 361.78ms
- 最大时间: 698.30ms
- 成功率: 80.0%
- 错误次数: 1
end:
- 执行次数: 4
- 平均时间: 96.06ms
- 最小时间: 68.74ms
- 最大时间: 120.42ms
- 成功率: 100.0%
- 错误次数: 0
生成时间: 11/16/2025, 8:28:27 PM
=== 导出数据 ===
导出的性能数据: {
timestamp: '2025-11-16T12:28:27.607Z',
metrics: [
{
nodeId: 'start',
executionTime: 296.19174999999996,
memoryUsage: 1.1966323852539062,
timestamp: '2025-11-16T12:28:23.408Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'process',
executionTime: 685.8339159999999,
memoryUsage: 1.7289657592773438,
timestamp: '2025-11-16T12:28:24.099Z',
status: 'success',
inputSize: 0,
outputSize: 39
},
{
nodeId: 'end',
executionTime: 68.73520899999994,
memoryUsage: 2.0334091186523438,
timestamp: '2025-11-16T12:28:24.174Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'start',
executionTime: 300.9371249999999,
memoryUsage: 3.7783355712890625,
timestamp: '2025-11-16T12:28:24.490Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'process',
executionTime: 361.7814999999998,
memoryUsage: 4.067108154296875,
timestamp: '2025-11-16T12:28:24.858Z',
status: 'success',
inputSize: 0,
outputSize: 39
},
{
nodeId: 'end',
executionTime: 86.57454199999984,
memoryUsage: 4.354461669921875,
timestamp: '2025-11-16T12:28:24.946Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'start',
executionTime: 223.3851669999999,
memoryUsage: 5.123176574707031,
timestamp: '2025-11-16T12:28:25.178Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'process',
executionTime: 698.3028750000003,
memoryUsage: 5.4042510986328125,
timestamp: '2025-11-16T12:28:25.878Z',
status: 'success',
inputSize: 0,
outputSize: 39
},
{
nodeId: 'end',
executionTime: 120.41966700000012,
memoryUsage: 5.685752868652344,
timestamp: '2025-11-16T12:28:25.999Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'start',
executionTime: 202.5972499999998,
memoryUsage: 6.1815948486328125,
timestamp: '2025-11-16T12:28:26.205Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'process',
executionTime: 520.7048749999999,
memoryUsage: 6.424919128417969,
timestamp: '2025-11-16T12:28:26.728Z',
status: 'success',
inputSize: 0,
outputSize: 39
},
{
nodeId: 'end',
executionTime: 108.51804099999981,
memoryUsage: 6.668220520019531,
timestamp: '2025-11-16T12:28:26.842Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'start',
executionTime: 221.33170900000005,
memoryUsage: 7.14703369140625,
timestamp: '2025-11-16T12:28:27.069Z',
status: 'success',
inputSize: 0,
outputSize: 30
},
{
nodeId: 'process',
executionTime: 508.5028339999999,
memoryUsage: 7.391319274902344,
timestamp: '2025-11-16T12:28:27.579Z',
status: 'error',
inputSize: 0,
outputSize: 0
}
],
overallStats: {
totalExecutions: 14,
totalExecutionTime: 4403.816459999999,
avgExecutionTime: 314.5583185714285,
totalMemoryUsage: 67.1851806640625,
avgMemoryUsage: 4.798941476004464,
uniqueNodesCount: 3,
successRate: 0.9285714285714286,
errorRate: 0.07142857142857142
},
nodeStats: [
{
nodeId: 'start',
totalExecutions: 5,
avgExecutionTime: 248.88860019999993,
minExecutionTime: 202.5972499999998,
maxExecutionTime: 300.9371249999999,
avgMemoryUsage: 4.685354614257813,
successRate: 1,
errorCount: 0
},
{
nodeId: 'process',
totalExecutions: 5,
avgExecutionTime: 555.0251999999999,
minExecutionTime: 361.7814999999998,
maxExecutionTime: 698.3028750000003,
avgMemoryUsage: 5.003312683105468,
successRate: 0.8,
errorCount: 1
},
{
nodeId: 'end',
totalExecutions: 4,
avgExecutionTime: 96.06186474999993,
minExecutionTime: 68.73520899999994,
maxExecutionTime: 120.41966700000012,
avgMemoryUsage: 4.685461044311523,
successRate: 1,
errorCount: 0
}
]
}
*/
资源使用追踪
资源使用监控
/**
* ============================================================================
* 资源监控可视化 - Resource Monitoring Visualization
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何监控 LangGraph 执行过程中的系统资源使用情况(CPU、内存、网络、存储),
* 生成资源使用趋势图表和警报,帮助及时发现资源瓶颈和异常情况。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 多维资源监控:同时监控 CPU、内存、网络、存储使用情况
* 2️⃣ 实时数据采集:定时收集资源使用指标(默认每秒)
* 3️⃣ 趋势可视化:生成文本图表和 Mermaid 图表展示资源趋势
* 4️⃣ 资源警报:设置阈值,自动检测并报告资源异常
* 5️⃣ 状态集成:将资源使用数据集成到图的状态中
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • ResourceMonitor:定时采集资源使用数据,维护指标历史
* • ResourceUsageVisualizer:生成文本图表和 Mermaid 可视化
* • ResourceAlertSystem:基于阈值检测异常并生成警报
* • 集成到图状态:将资源数据作为状态的一部分传递
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/resource-monitoring.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 示例中的资源数据为模拟数据,实际应用需使用系统 API
* • 频繁采集资源数据可能影响性能
* • 警报阈值应根据实际系统配置调整
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义资源监控状态
const ResourceMonitoringState = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
resourceUsage: Annotation<ResourceUsage>({
reducer: (x, y) => ({ ...x, ...y }),
default: () => ({
memory: 0,
cpu: 0,
network: 0,
storage: 0,
}),
}),
metrics: Annotation<PerformanceMetrics[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
});
// 资源使用情况接口
interface ResourceUsage {
memory: number; // MB
cpu: number; // 百分比
network: number; // KB/s
storage: number; // MB
}
// 性能指标接口
interface PerformanceMetrics {
timestamp: number;
nodeId: string;
executionTime: number;
memoryUsage: number;
cpuUsage: number;
}
// 资源监控器
class ResourceMonitor {
private startTime: number = 0;
private metrics: PerformanceMetrics[] = [];
private intervalId?: NodeJS.Timeout;
// 开始监控
startMonitoring() {
this.startTime = Date.now();
this.intervalId = setInterval(() => {
this.collectMetrics();
}, 1000); // 每秒收集一次指标
}
// 停止监控
stopMonitoring() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
}
// 收集指标
private collectMetrics() {
const currentUsage = this.getCurrentResourceUsage();
const metric: PerformanceMetrics = {
timestamp: Date.now(),
nodeId: 'system',
executionTime: Date.now() - this.startTime,
memoryUsage: currentUsage.memory,
cpuUsage: currentUsage.cpu,
};
this.metrics.push(metric);
}
// 获取当前资源使用情况
getCurrentResourceUsage(): ResourceUsage {
// 模拟资源使用情况(实际应用中应该使用真实的系统监控 API)
return {
memory: Math.random() * 100 + 50, // 50-150 MB
cpu: Math.random() * 50 + 10, // 10-60%
network: Math.random() * 1000 + 100, // 100-1100 KB/s
storage: Math.random() * 10 + 5, // 5-15 MB
};
}
// 获取指标历史
getMetrics(): PerformanceMetrics[] {
return [...this.metrics];
}
// 清除指标
clearMetrics() {
this.metrics = [];
}
}
// 资源使用可视化器
class ResourceUsageVisualizer {
private monitor: ResourceMonitor;
constructor() {
this.monitor = new ResourceMonitor();
}
// 生成资源使用图表
generateResourceChart(metrics: PerformanceMetrics[]): string {
if (metrics.length === 0) {
return 'No metrics available';
}
// 生成简单的文本图表
let chart = 'Resource Usage Over Time:\n';
chart += '========================\n\n';
// 内存使用图表
chart += 'Memory Usage (MB):\n';
chart += this.generateLineChart(
metrics.map((m) => m.memoryUsage),
'Memory'
);
// CPU 使用图表
chart += '\nCPU Usage (%):\n';
chart += this.generateLineChart(
metrics.map((m) => m.cpuUsage),
'CPU'
);
return chart;
}
// 生成简单的线性图表
private generateLineChart(data: number[], label: string): string {
const maxValue = Math.max(...data);
const minValue = Math.min(...data);
const range = maxValue - minValue;
let chart = '';
const chartHeight = 10;
for (let i = chartHeight; i >= 0; i--) {
const threshold = minValue + (range * i) / chartHeight;
let line = `${threshold.toFixed(1).padStart(6)} |`;
for (const value of data) {
if (value >= threshold) {
line += '█';
} else {
line += ' ';
}
}
chart += line + '\n';
}
// 添加时间轴
chart += ' +';
for (let i = 0; i < data.length; i++) {
chart += '-';
}
chart += '\n';
return chart;
}
// 生成 Mermaid 资源监控图表
generateMermaidResourceChart(metrics: PerformanceMetrics[]): string {
if (metrics.length === 0) {
return 'graph TD\n A[No Data Available]';
}
const avgMemory =
metrics.reduce((sum, m) => sum + m.memoryUsage, 0) / metrics.length;
const avgCpu =
metrics.reduce((sum, m) => sum + m.cpuUsage, 0) / metrics.length;
const totalTime = metrics[metrics.length - 1].executionTime;
return `
graph TD
A[资源监控] --> B[内存使用]
A --> C[CPU使用]
A --> D[执行时间]
B --> E[平均: ${avgMemory.toFixed(1)} MB]
C --> F[平均: ${avgCpu.toFixed(1)}%]
D --> G[总计: ${(totalTime / 1000).toFixed(1)}s]
E --> H{内存状态}
F --> I{CPU状态}
H -->|< 100MB| J[正常]
H -->|> 100MB| K[警告]
I -->|< 50%| L[正常]
I -->|> 50%| M[高负载]
classDef normal fill:#4CAF50,color:#fff;
classDef warning fill:#FF9800,color:#fff;
classDef high fill:#F44336,color:#fff;
class J,L normal;
class K warning;
class M high;
`;
}
// 开始监控并可视化
startVisualization() {
this.monitor.startMonitoring();
// 每5秒更新一次可视化
setInterval(() => {
const metrics = this.monitor.getMetrics();
console.log('\n=== 资源使用情况 ===');
console.log(this.generateResourceChart(metrics));
console.log('\n=== Mermaid 图表 ===');
console.log(this.generateMermaidResourceChart(metrics));
}, 5000);
}
// 停止监控
stopVisualization() {
this.monitor.stopMonitoring();
}
// 获取监控器实例
getMonitor(): ResourceMonitor {
return this.monitor;
}
}
// 创建带资源监控的图
function createMonitoredGraph() {
const monitor = new ResourceMonitor();
return new StateGraph(ResourceMonitoringState)
.addNode('start', async (state) => {
monitor.startMonitoring();
return {
messages: ['开始执行'],
resourceUsage: monitor.getCurrentResourceUsage(),
};
})
.addNode('process', async (state) => {
// 模拟一些处理工作
await new Promise((resolve) => setTimeout(resolve, 1000));
return {
messages: ['处理数据'],
resourceUsage: monitor.getCurrentResourceUsage(),
metrics: [
{
timestamp: Date.now(),
nodeId: 'process',
executionTime: 1000,
memoryUsage: 75,
cpuUsage: 30,
},
],
};
})
.addNode('end', async (state) => {
monitor.stopMonitoring();
return {
messages: ['执行完成'],
resourceUsage: monitor.getCurrentResourceUsage(),
};
})
.addEdge('start', 'process')
.addEdge('process', 'end')
.setEntryPoint('start')
.setFinishPoint('end');
}
// 资源警报系统
class ResourceAlertSystem {
private thresholds = {
memory: 100, // MB
cpu: 70, // %
executionTime: 30000, // ms
};
private alerts: string[] = [];
// 检查资源使用情况
checkResourceUsage(metrics: PerformanceMetrics[]): string[] {
this.alerts = [];
if (metrics.length === 0) {
return this.alerts;
}
const latest = metrics[metrics.length - 1];
// 检查内存使用
if (latest.memoryUsage > this.thresholds.memory) {
this.alerts.push(`内存使用过高: ${latest.memoryUsage.toFixed(1)} MB`);
}
// 检查 CPU 使用
if (latest.cpuUsage > this.thresholds.cpu) {
this.alerts.push(`CPU使用过高: ${latest.cpuUsage.toFixed(1)}%`);
}
// 检查执行时间
if (latest.executionTime > this.thresholds.executionTime) {
this.alerts.push(
`执行时间过长: ${(latest.executionTime / 1000).toFixed(1)}s`
);
}
return this.alerts;
}
// 生成警报可视化
generateAlertVisualization(): string {
if (this.alerts.length === 0) {
return `
graph TD
A[系统状态] --> B[✅ 正常]
classDef normal fill:#4CAF50,color:#fff;
class B normal;
`;
}
let mermaid = 'graph TD\n A[系统状态] --> B[⚠️ 警报]\n';
this.alerts.forEach((alert, index) => {
const nodeId = String.fromCharCode(67 + index); // C, D, E...
mermaid += ` B --> ${nodeId}[${alert}]\n`;
});
mermaid += `
classDef warning fill:#FF9800,color:#fff;
classDef alert fill:#F44336,color:#fff;
class B warning;
`;
return mermaid;
}
// 设置阈值
setThresholds(thresholds: Partial<typeof this.thresholds>) {
this.thresholds = { ...this.thresholds, ...thresholds };
}
}
// 使用示例
async function demonstrateResourceMonitoring() {
console.log('开始资源监控演示...');
const visualizer = new ResourceUsageVisualizer();
const alertSystem = new ResourceAlertSystem();
// 开始可视化
visualizer.startVisualization();
// 创建并执行监控图
const graph = createMonitoredGraph();
const compiled = graph.compile();
try {
const result = await compiled.invoke({
messages: [],
resourceUsage: { memory: 0, cpu: 0, network: 0, storage: 0 },
metrics: [],
});
console.log('\n=== 执行结果 ===');
console.log('消息:', result.messages);
console.log('最终资源使用:', result.resourceUsage);
// 检查警报
const alerts = alertSystem.checkResourceUsage(result.metrics);
if (alerts.length > 0) {
console.log('\n=== 资源警报 ===');
alerts.forEach((alert) => console.log(`⚠️ ${alert}`));
console.log('\n=== 警报可视化 ===');
console.log(alertSystem.generateAlertVisualization());
}
} finally {
// 停止监控
setTimeout(() => {
visualizer.stopVisualization();
console.log('资源监控演示完成');
}, 10000);
}
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateResourceMonitoring().catch(console.error);
}
export {
ResourceMonitor,
ResourceUsageVisualizer,
ResourceAlertSystem,
createMonitoredGraph,
demonstrateResourceMonitoring,
type ResourceUsage,
type PerformanceMetrics,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /home/user/langgraphjs-tutorial/websites/examples/utils/.env
开始资源监控演示...
=== 执行结果 ===
消息: [ '开始执行', '处理数据', '执行完成' ]
最终资源使用: { memory: 88.06MB, cpu: 42.41%, network: 400.35KB, storage: 9.25MB }
=== 资源使用情况 ===
展示了资源使用的 ASCII 图表:
- Memory Usage (MB) - 条形图显示内存使用趋势
- CPU Usage (%) - 条形图显示 CPU 使用趋势
=== Mermaid 图表 ===
生成了资源监控的 Mermaid 流程图,包含:
- 资源监控 → 内存使用 (平均: 84.4 MB)
- 资源监控 → CPU使用 (平均: 40.5%)
- 资源监控 → 执行时间 (总计: 4.0s)
- 内存状态判断 (< 100MB: 正常, > 100MB: 警告)
- CPU状态判断 (< 50%: 正常, > 50%: 高负载)
程序每秒更新一次资源监控可视化,持续10秒
资源监控演示完成
*/
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /home/user/langgraphjs-tutorial/websites/examples/utils/.env
*/
实际应用场景
调试复杂流程
当应用出现问题时,可视化帮助快速定位问题:
团队协作
团队协作可视化
/**
* ============================================================================
* 团队协作可视化 - Team Collaboration Visualization
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本示例展示如何在 LangGraph 项目中实现团队协作功能,支持团队成员管理、图表注释、
* 权限控制、活动跟踪等,帮助团队高效协作开发和维护 LangGraph 应用。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 团队成员管理:支持添加成员、分配角色(开发、设计、产品、QA)
* 2️⃣ 图表注释系统:支持评论、建议、问题、批准等类型的注释
* 3️⃣ 权限矩阵:基于角色的权限控制(读、写、审核、部署、管理)
* 4️⃣ 活动时间线:记录和展示团队成员的协作活动历史
* 5️⃣ 协作统计:生成团队规模、活跃度、参与度等统计数据
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • TeamCollaborationVisualizer:管理团队成员和注释数据
* • 注释系统:支持对图的节点添加多种类型的注释
* • 权限系统:基于角色的访问控制(RBAC)
* • 协作流程可视化:生成团队协作工作流的 Mermaid 图表
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 实用功能/可视化/team-collaboration.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 实际应用中需要持久化存储团队成员和注释数据
* • 权限控制需要与身份认证系统集成
* • 建议使用数据库存储协作数据而非内存
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/
import '../../utils/loadEnv';
import { StateGraph, Annotation } from '@langchain/langgraph';
// 定义团队协作状态
const TeamCollaborationState = Annotation.Root({
messages: Annotation<string[]>({
reducer: (x, y) => x.concat(y),
default: () => [],
}),
teamMembers: Annotation<TeamMember[]>({
reducer: (x, y) => [...x, ...y],
default: () => [],
}),
annotations: Annotation<GraphAnnotation[]>({
reducer: (x, y) => [...x, ...y],
default: () => [],
}),
version: Annotation<number>({
reducer: (x, y) => y,
default: () => 1,
}),
});
// 团队成员接口
interface TeamMember {
id: string;
name: string;
role: 'developer' | 'designer' | 'product' | 'qa';
permissions: string[];
}
// 图表注释接口
interface GraphAnnotation {
id: string;
nodeId: string;
author: string;
content: string;
type: 'comment' | 'suggestion' | 'issue' | 'approval';
timestamp: number;
resolved: boolean;
}
// 团队协作可视化器
class TeamCollaborationVisualizer {
private teamMembers: TeamMember[] = [];
private annotations: GraphAnnotation[] = [];
// 添加团队成员
addTeamMember(member: TeamMember) {
this.teamMembers.push(member);
}
// 添加注释
addAnnotation(annotation: Omit<GraphAnnotation, 'id' | 'timestamp'>) {
const newAnnotation: GraphAnnotation = {
...annotation,
id: this.generateId(),
timestamp: Date.now(),
};
this.annotations.push(newAnnotation);
return newAnnotation;
}
// 生成团队协作图表
generateTeamCollaborationChart(): string {
return `
graph TD
A[LangGraph 项目] --> B[团队成员]
A --> C[图表设计]
A --> D[代码实现]
A --> E[测试验证]
B --> F[开发者: ${this.getTeamMembersByRole('developer').length}人]
B --> G[设计师: ${this.getTeamMembersByRole('designer').length}人]
B --> H[产品: ${this.getTeamMembersByRole('product').length}人]
B --> I[QA: ${this.getTeamMembersByRole('qa').length}人]
C --> J[架构设计]
C --> K[流程设计]
D --> L[节点实现]
D --> M[状态管理]
E --> N[单元测试]
E --> O[集成测试]
J --> P{设计评审}
K --> P
L --> Q{代码评审}
M --> Q
N --> R{测试评审}
O --> R
P -->|通过| S[开始开发]
P -->|需修改| J
Q -->|通过| T[开始测试]
Q -->|需修改| L
R -->|通过| U[发布]
R -->|需修改| N
classDef team fill:#4CAF50,color:#fff;
classDef process fill:#2196F3,color:#fff;
classDef review fill:#FF9800,color:#fff;
classDef final fill:#9C27B0,color:#fff;
class F,G,H,I team;
class J,K,L,M,N,O process;
class P,Q,R review;
class S,T,U final;
`;
}
// 生成注释可视化
generateAnnotationVisualization(): string {
const commentCount = this.annotations.filter(
(a) => a.type === 'comment'
).length;
const suggestionCount = this.annotations.filter(
(a) => a.type === 'suggestion'
).length;
const issueCount = this.annotations.filter(
(a) => a.type === 'issue'
).length;
const approvalCount = this.annotations.filter(
(a) => a.type === 'approval'
).length;
const resolvedCount = this.annotations.filter((a) => a.resolved).length;
return `
graph TD
A[图表注释] --> B[评论: ${commentCount}]
A --> C[建议: ${suggestionCount}]
A --> D[问题: ${issueCount}]
A --> E[批准: ${approvalCount}]
B --> F[已解决: ${this.getResolvedByType('comment')}]
C --> G[已解决: ${this.getResolvedByType('suggestion')}]
D --> H[已解决: ${this.getResolvedByType('issue')}]
E --> I[已解决: ${this.getResolvedByType('approval')}]
F --> J[总解决率: ${(
(resolvedCount / this.annotations.length) *
100
).toFixed(1)}%]
G --> J
H --> J
I --> J
classDef comment fill:#4CAF50,color:#fff;
classDef suggestion fill:#2196F3,color:#fff;
classDef issue fill:#F44336,color:#fff;
classDef approval fill:#9C27B0,color:#fff;
classDef resolved fill:#FF9800,color:#fff;
class B,F comment;
class C,G suggestion;
class D,H issue;
class E,I approval;
class J resolved;
`;
}
// 生成权限矩阵
generatePermissionMatrix(): string {
const permissions = ['read', 'write', 'review', 'deploy', 'admin'];
let matrix = 'Permission Matrix:\n';
matrix += '=================\n\n';
// 表头
matrix += 'Role'.padEnd(12);
permissions.forEach((perm) => {
matrix += perm.padEnd(8);
});
matrix += '\n';
matrix += '-'.repeat(12 + permissions.length * 8) + '\n';
// 按角色统计权限
const roles = ['developer', 'designer', 'product', 'qa'];
roles.forEach((role) => {
matrix += role.padEnd(12);
permissions.forEach((perm) => {
const hasPermission = this.checkRolePermission(role, perm);
matrix += (hasPermission ? '✓' : '✗').padEnd(8);
});
matrix += '\n';
});
return matrix;
}
// 生成活动时间线
generateActivityTimeline(): string {
const sortedAnnotations = [...this.annotations].sort(
(a, b) => a.timestamp - b.timestamp
);
let timeline = 'Activity Timeline:\n';
timeline += '==================\n\n';
sortedAnnotations.forEach((annotation, index) => {
const date = new Date(annotation.timestamp).toLocaleString();
const icon = this.getAnnotationIcon(annotation.type);
timeline += `${index + 1}. ${icon} ${annotation.author} - ${
annotation.type
}\n`;
timeline += ` Time: ${date}\n`;
timeline += ` Node: ${annotation.nodeId}\n`;
timeline += ` Content: ${annotation.content.substring(0, 50)}...\n`;
timeline += ` Status: ${
annotation.resolved ? '✅ Resolved' : '⏳ Pending'
}\n\n`;
});
return timeline;
}
// 生成协作统计
generateCollaborationStats(): string {
const stats = {
totalMembers: this.teamMembers.length,
totalAnnotations: this.annotations.length,
activeMembers: this.getActiveMemberCount(),
avgResponseTime: this.calculateAvgResponseTime(),
collaborationScore: this.calculateCollaborationScore(),
};
return `
graph TD
A[协作统计] --> B[团队规模: ${stats.totalMembers}人]
A --> C[注释总数: ${stats.totalAnnotations}]
A --> D[活跃成员: ${stats.activeMembers}人]
A --> E[平均响应: ${stats.avgResponseTime}h]
A --> F[协作评分: ${stats.collaborationScore}/10]
B --> G{团队规模}
C --> H{活跃度}
D --> I{参与度}
E --> J{响应速度}
F --> K{整体评价}
G -->|< 5人| L[小团队]
G -->|5-15人| M[中团队]
G -->|> 15人| N[大团队]
H -->|< 10| O[低活跃]
H -->|10-50| P[中活跃]
H -->|> 50| Q[高活跃]
I -->|< 50%| R[需改进]
I -->|50-80%| S[良好]
I -->|> 80%| T[优秀]
J -->|< 4h| U[快速]
J -->|4-24h| V[正常]
J -->|> 24h| W[缓慢]
K -->|< 6| X[需改进]
K -->|6-8| Y[良好]
K -->|> 8| Z[优秀]
classDef good fill:#4CAF50,color:#fff;
classDef normal fill:#FF9800,color:#fff;
classDef poor fill:#F44336,color:#fff;
class M,P,S,U,Y good;
class L,O,V,X normal;
class N,Q,R,W,Z poor;
`;
}
// 导出协作报告
exportCollaborationReport(): string {
let report = 'LangGraph 团队协作报告\n';
report += '========================\n\n';
report += '1. 团队概览\n';
report += '-----------\n';
report += this.generateTeamOverview() + '\n\n';
report += '2. 注释分析\n';
report += '-----------\n';
report += this.generateAnnotationAnalysis() + '\n\n';
report += '3. 权限矩阵\n';
report += '-----------\n';
report += this.generatePermissionMatrix() + '\n\n';
report += '4. 活动时间线\n';
report += '-------------\n';
report += this.generateActivityTimeline() + '\n\n';
report += '5. 改进建议\n';
report += '-----------\n';
report += this.generateImprovementSuggestions() + '\n';
return report;
}
// 辅助方法
private getTeamMembersByRole(role: string): TeamMember[] {
return this.teamMembers.filter((member) => member.role === role);
}
private getResolvedByType(type: string): number {
return this.annotations.filter((a) => a.type === type && a.resolved).length;
}
private checkRolePermission(role: string, permission: string): boolean {
const rolePermissions = {
developer: ['read', 'write', 'review'],
designer: ['read', 'write'],
product: ['read', 'review', 'admin'],
qa: ['read', 'review'],
};
return (
rolePermissions[role as keyof typeof rolePermissions]?.includes(
permission
) || false
);
}
private getAnnotationIcon(type: string): string {
const icons = {
comment: '💬',
suggestion: '💡',
issue: '🐛',
approval: '✅',
};
return icons[type as keyof typeof icons] || '📝';
}
private getActiveMemberCount(): number {
const activeAuthors = new Set(this.annotations.map((a) => a.author));
return activeAuthors.size;
}
private calculateAvgResponseTime(): number {
// 模拟计算平均响应时间(小时)
return Math.round(Math.random() * 24 + 1);
}
private calculateCollaborationScore(): number {
// 基于多个因素计算协作评分
const participationRate =
this.getActiveMemberCount() / this.teamMembers.length;
const resolutionRate =
this.annotations.filter((a) => a.resolved).length /
this.annotations.length;
const activityLevel = Math.min(this.annotations.length / 50, 1);
return Math.round(
(participationRate * 0.4 + resolutionRate * 0.4 + activityLevel * 0.2) *
10
);
}
private generateId(): string {
return Math.random().toString(36).substr(2, 9);
}
private generateTeamOverview(): string {
let overview = '';
overview += `总成员数: ${this.teamMembers.length}\n`;
overview += `开发者: ${this.getTeamMembersByRole('developer').length}\n`;
overview += `设计师: ${this.getTeamMembersByRole('designer').length}\n`;
overview += `产品: ${this.getTeamMembersByRole('product').length}\n`;
overview += `QA: ${this.getTeamMembersByRole('qa').length}\n`;
return overview;
}
private generateAnnotationAnalysis(): string {
let analysis = '';
analysis += `总注释数: ${this.annotations.length}\n`;
analysis += `已解决: ${
this.annotations.filter((a) => a.resolved).length
}\n`;
analysis += `待处理: ${
this.annotations.filter((a) => !a.resolved).length
}\n`;
analysis += `解决率: ${(
(this.annotations.filter((a) => a.resolved).length /
this.annotations.length) *
100
).toFixed(1)}%\n`;
return analysis;
}
private generateImprovementSuggestions(): string {
const suggestions = [];
if (this.getActiveMemberCount() / this.teamMembers.length < 0.5) {
suggestions.push('- 提高团队参与度,鼓励更多成员参与协作');
}
if (this.annotations.filter((a) => !a.resolved).length > 10) {
suggestions.push('- 及时处理待解决的注释和问题');
}
if (this.annotations.filter((a) => a.type === 'issue').length > 5) {
suggestions.push('- 加强代码质量控制,减少问题数量');
}
if (suggestions.length === 0) {
suggestions.push('- 团队协作状况良好,继续保持');
}
return suggestions.join('\n');
}
}
// 创建团队协作图
function createTeamCollaborationGraph() {
return new StateGraph(TeamCollaborationState)
.addNode('init', async (state) => {
return {
messages: ['初始化团队协作'],
teamMembers: [
{
id: '1',
name: 'Alice',
role: 'developer',
permissions: ['read', 'write', 'review'],
},
{
id: '2',
name: 'Bob',
role: 'designer',
permissions: ['read', 'write'],
},
],
};
})
.addNode('collaborate', async (state) => {
return {
messages: ['团队协作进行中'],
annotations: [
{
id: 'ann1',
nodeId: 'process',
author: 'Alice',
content: '这个节点需要优化性能',
type: 'suggestion',
timestamp: Date.now(),
resolved: false,
},
],
};
})
.addNode('review', async (state) => {
return {
messages: ['协作评审完成'],
version: state.version + 1,
};
})
.addEdge('init', 'collaborate')
.addEdge('collaborate', 'review')
.setEntryPoint('init')
.setFinishPoint('review');
}
// 使用示例
async function demonstrateTeamCollaboration() {
console.log('开始团队协作演示...');
const visualizer = new TeamCollaborationVisualizer();
// 添加团队成员
visualizer.addTeamMember({
id: '1',
name: 'Alice',
role: 'developer',
permissions: ['read', 'write', 'review'],
});
visualizer.addTeamMember({
id: '2',
name: 'Bob',
role: 'designer',
permissions: ['read', 'write'],
});
visualizer.addTeamMember({
id: '3',
name: 'Carol',
role: 'product',
permissions: ['read', 'review', 'admin'],
});
// 添加注释
visualizer.addAnnotation({
nodeId: 'process',
author: 'Alice',
content: '这个节点的逻辑需要重构',
type: 'suggestion',
resolved: false,
});
visualizer.addAnnotation({
nodeId: 'decision',
author: 'Bob',
content: '界面设计需要调整',
type: 'comment',
resolved: true,
});
visualizer.addAnnotation({
nodeId: 'validation',
author: 'Carol',
content: '功能已通过产品验收',
type: 'approval',
resolved: true,
});
// 生成可视化
console.log('\n=== 团队协作图表 ===');
console.log(visualizer.generateTeamCollaborationChart());
console.log('\n=== 注释可视化 ===');
console.log(visualizer.generateAnnotationVisualization());
console.log('\n=== 协作统计 ===');
console.log(visualizer.generateCollaborationStats());
console.log('\n=== 协作报告 ===');
console.log(visualizer.exportCollaborationReport());
// 创建并执行协作图
const graph = createTeamCollaborationGraph();
const compiled = graph.compile();
const result = await compiled.invoke({
messages: [],
teamMembers: [],
annotations: [],
version: 1,
});
console.log('\n=== 执行结果 ===');
console.log('消息:', result.messages);
console.log('团队成员数:', result.teamMembers.length);
console.log('注释数:', result.annotations.length);
console.log('版本:', result.version);
console.log('团队协作演示完成');
}
// 如果直接运行此文件,执行示例
if (require.main === module) {
demonstrateTeamCollaboration().catch(console.error);
}
export {
TeamCollaborationVisualizer,
createTeamCollaborationGraph,
demonstrateTeamCollaboration,
type TeamMember,
type GraphAnnotation,
};
/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
开始团队协作演示...
=== 团队协作图表 ===
graph TD
A[LangGraph 项目] --> B[团队成员]
A --> C[图表设计]
A --> D[代码实现]
A --> E[测试验证]
B --> F[开发者: 1人]
B --> G[设计师: 1人]
B --> H[产品: 1人]
B --> I[QA: 0人]
C --> J[架构设计]
C --> K[流程设计]
D --> L[节点实现]
D --> M[状态管理]
E --> N[单元测试]
E --> O[集成测试]
J --> P{设计评审}
K --> P
L --> Q{代码评审}
M --> Q
N --> R{测试评审}
O --> R
P -->|通过| S[开始开发]
P -->|需修改| J
Q -->|通过| T[开始测试]
Q -->|需修改| L
R -->|通过| U[发布]
R -->|需修改| N
classDef team fill:#4CAF50,color:#fff;
classDef process fill:#2196F3,color:#fff;
classDef review fill:#FF9800,color:#fff;
classDef final fill:#9C27B0,color:#fff;
class F,G,H,I team;
class J,K,L,M,N,O process;
class P,Q,R review;
class S,T,U final;
=== 注释可视化 ===
graph TD
A[图表注释] --> B[评论: 1]
A --> C[建议: 1]
A --> D[问题: 0]
A --> E[批准: 1]
B --> F[已解决: 1]
C --> G[已解决: 0]
D --> H[已解决: 0]
E --> I[已解决: 1]
F --> J[总解决率: 66.7%]
G --> J
H --> J
I --> J
classDef comment fill:#4CAF50,color:#fff;
classDef suggestion fill:#2196F3,color:#fff;
classDef issue fill:#F44336,color:#fff;
classDef approval fill:#9C27B0,color:#fff;
classDef resolved fill:#FF9800,color:#fff;
class B,F comment;
class C,G suggestion;
class D,H issue;
class E,I approval;
class J resolved;
=== 协作统计 ===
graph TD
A[协作统计] --> B[团队规模: 3人]
A --> C[注释总数: 3]
A --> D[活跃成员: 3人]
A --> E[平均响应: 20h]
A --> F[协作评分: 7/10]
B --> G{团队规模}
C --> H{活跃度}
D --> I{参与度}
E --> J{响应速度}
F --> K{整体评价}
G -->|< 5人| L[小团队]
G -->|5-15人| M[中团队]
G -->|> 15人| N[大团队]
H -->|< 10| O[低活跃]
H -->|10-50| P[中活跃]
H -->|> 50| Q[高活跃]
I -->|< 50%| R[需改进]
I -->|50-80%| S[良好]
I -->|> 80%| T[优秀]
J -->|< 4h| U[快速]
J -->|4-24h| V[正常]
J -->|> 24h| W[缓慢]
K -->|< 6| X[需改进]
K -->|6-8| Y[良好]
K -->|> 8| Z[优秀]
classDef good fill:#4CAF50,color:#fff;
classDef normal fill:#FF9800,color:#fff;
classDef poor fill:#F44336,color:#fff;
class M,P,S,U,Y good;
class L,O,V,X normal;
class N,Q,R,W,Z poor;
=== 协作报告 ===
LangGraph 团队协作报告
========================
1. 团队概览
-----------
总成员数: 3
开发者: 1
设计师: 1
产品: 1
QA: 0
2. 注释分析
-----------
总注释数: 3
已解决: 2
待处理: 1
解决率: 66.7%
3. 权限矩阵
-----------
Permission Matrix:
=================
Role read write review deploy admin
----------------------------------------------------
developer ✓ ✓ ✓ ✗ ✗
designer ✓ ✓ ✗ ✗ ✗
product ✓ ✗ ✓ ✗ ✓
qa ✓ ✗ ✓ ✗ ✗
4. 活动时间线
-------------
Activity Timeline:
==================
1. 💡 Alice - suggestion
Time: 11/16/2025, 8:28:34 PM
Node: process
Content: 这个节点的逻辑需要重构...
Status: ⏳ Pending
2. 💬 Bob - comment
Time: 11/16/2025, 8:28:34 PM
Node: decision
Content: 界面设计需要调整...
Status: ✅ Resolved
3. ✅ Carol - approval
Time: 11/16/2025, 8:28:34 PM
Node: validation
Content: 功能已通过产品验收...
Status: ✅ Resolved
5. 改进建议
-----------
- 团队协作状况良好,继续保持
=== 执行结果 ===
消息: [ '初始化团队协作', '团队协作进行中', '协作评审完成' ]
团队成员数: 2
注释数: 1
版本: 2
团队协作演示完成
*/
最佳实践
1. 选择合适的可视化方式
// 根据不同场景选择可视化方式
class VisualizationManager {
static getVisualizationType(context: string) {
switch (context) {
case 'development':
return 'interactive'; // 交互式可视化
case 'documentation':
return 'static'; // 静态图表
case 'debugging':
return 'dynamic'; // 动态追踪
case 'presentation':
return 'simplified'; // 简化视图
default:
return 'basic';
}
}
}
2. 优化可视化性能
// 大型图的可视化优化
class LargeGraphVisualizer {
private maxNodes = 50;
private maxEdges = 100;
optimizeForVisualization(graph: any) {
// 简化复杂图结构
if (graph.nodes.length > this.maxNodes) {
return this.createSummaryView(graph);
}
// 分层显示
return this.createLayeredView(graph);
}
private createSummaryView(graph: any) {
// 创建摘要视图
return {
summary: true,
nodeCount: graph.nodes.length,
edgeCount: graph.edges.length,
keyNodes: this.identifyKeyNodes(graph),
};
}
private createLayeredView(graph: any) {
// 创建分层视图
return {
layers: this.groupNodesByLayer(graph),
navigation: this.createNavigationControls(),
};
}
}
3. 交互式功能
// 添加交互式功能
class InteractiveVisualizer {
private eventHandlers: Map<string, Function> = new Map();
addInteraction(elementId: string, handler: Function) {
this.eventHandlers.set(elementId, handler);
}
handleNodeClick(nodeId: string) {
// 显示节点详细信息
this.showNodeDetails(nodeId);
}
handleEdgeClick(edgeId: string) {
// 显示边的条件信息
this.showEdgeConditions(edgeId);
}
handleStateChange(newState: any) {
// 更新可视化状态
this.updateVisualization(newState);
}
}
4. 响应式设计
// 响应式可视化设计
class ResponsiveVisualizer {
private breakpoints = {
mobile: 768,
tablet: 1024,
desktop: 1440,
};
adaptToScreenSize(width: number) {
if (width < this.breakpoints.mobile) {
return this.getMobileLayout();
} else if (width < this.breakpoints.tablet) {
return this.getTabletLayout();
} else {
return this.getDesktopLayout();
}
}
private getMobileLayout() {
return {
layout: 'vertical',
nodeSize: 'small',
showLabels: false,
collapsible: true,
};
}
private getTabletLayout() {
return {
layout: 'horizontal',
nodeSize: 'medium',
showLabels: true,
collapsible: true,
};
}
private getDesktopLayout() {
return {
layout: 'free-form',
nodeSize: 'large',
showLabels: true,
collapsible: false,
};
}
}
工具和库推荐
1. 图表库
// 推荐的可视化库
const VISUALIZATION_LIBRARIES = {
// 基础图表
mermaid: {
use: '静态图表生成',
pros: ['简单易用', '支持多种图表类型'],
cons: ['交互性有限'],
},
// 交互式图表
d3: {
use: '复杂交互式可视化',
pros: ['功能强大', '高度可定制'],
cons: ['学习曲线陡峭'],
},
// React 组件
reactFlow: {
use: 'React 应用中的流程图',
pros: ['React 友好', '丰富的交互功能'],
cons: ['仅限 React 环境'],
},
// 网络图
cytoscape: {
use: '复杂网络图可视化',
pros: ['专业的图分析功能', '性能优秀'],
cons: ['API 复杂'],
},
};
2. 调试工具
// 调试工具集成
class DebugVisualization {
private debugMode = false;
private breakpoints: Set<string> = new Set();
enableDebugMode() {
this.debugMode = true;
this.addDebugControls();
}
addBreakpoint(nodeId: string) {
this.breakpoints.add(nodeId);
this.highlightBreakpoint(nodeId);
}
removeBreakpoint(nodeId: string) {
this.breakpoints.delete(nodeId);
this.removeHighlight(nodeId);
}
private addDebugControls() {
// 添加调试控制界面
const controls = {
step: () => this.stepExecution(),
continue: () => this.continueExecution(),
reset: () => this.resetExecution(),
};
this.renderDebugControls(controls);
}
}
常见问题和解决方案
问题 1:大型图渲染性能差
解决方案:
- 使用虚拟化技术,只渲染可见部分
- 实现图的分层显示
- 提供缩放和平移功能
- 使用 Web Workers 进行复杂计算
问题 2:图结构过于复杂难以理解
解决方案:
- 提供不同的视图模式(概览、详细、聚焦)
- 实现节点分组和折叠功能
- 添加搜索和过滤功能
- 使用颜色编码区分不同类型的节点
问题 3:实时更新导致界面闪烁
解决方案:
- 使用防抖技术减少更新频率
- 实现平滑的动画过渡
- 只更新变化的部分
- 提供暂停/恢复更新的选项
小结与延伸
可视化是 LangGraph 开发中不可或缺的工具,它不仅帮助我们理解应用结构,还能有效提升开发和调试效率。通过合理选择可视化方式和工具,你可以构建出既美观又实用的可视化界面。
掌握了可视化技术后,接下来我们将学习 Command 对象,了解如何在 LangGraph 中实现更灵活的控制流管理。
可视化提示
- 根据使用场景选择合适的可视化方式
- 注意大型图的性能优化
- 提供丰富的交互功能提升用户体验
- 结合调试工具提高开发效率