图结构详解
深入了解 StateGraph 和 MessageGraph,掌握图的创建、编译和可视化流程
📚 学习目标
学完这篇文章后,你将能够:
- 深刻理解
StateGraph的核心作用 - 掌握图的编译过程及其重要性
- 学会使用 Mermaid 可视化你的图结构
- 了解不同类型图结构的适用场景
前置知识
在开始学习之前,建议先阅读:
你需要了解:
- JavaScript/TypeScript 基础
- 基本的数据结构(图、节点、边)
引言
在 LangGraphJS 中,图(Graph) 是整个框架的核心概念。如果你熟悉前端开发中的状态管理(如 Redux)或者组件树结构(如 React),那么理解 LangGraph 的图结构会相对容易。
图结构本质上是一个有向图,它定义了你的 AI 应用的执行流程。与传统的链式结构不同,图结构允许 LLM 动态决定下一步的执行路径,这为构建智能代理提供了强大的灵活性。
💡 与前端开发的类比
- 图结构 ≈ React 应用的组件树 + 路由系统
- 状态管理 ≈ Redux Store 或 React Context
- 编译过程 ≈ Webpack/Vite 的构建过程
1 图(Graph)的核心地位
在 LangGraph 应用中,图是核心容器,它将状态、节点和边组织在一起,形成一个可执行的单元。
类比理解
| 前端概念 | LangGraph 概念 | 说明 |
|---|---|---|
| Component Tree | Graph | 应用的整体结构 |
| Redux Store | State | 全局数据存储 |
| Component | Node | 逻辑单元 |
| Router | Edge | 导航控制 |
2 主要图类
StateGraph(推荐)
StateGraph 是最通用的图类,允许你定义强类型的状态结构。
import { StateGraph, Annotation } from '@langchain/langgraph';
// 1. 定义状态
const StateAnnotation = Annotation.Root({
count: Annotation<number>(),
history: Annotation<string[]>(),
});
// 2. 创建图
const graph = new StateGraph(StateAnnotation);特点:
- ✅ 类型安全
- ✅ 高度灵活
- ✅ 支持复杂状态逻辑
MessageGraph
MessageGraph 是 StateGraph 的特化版本,状态固定为消息数组。
import { MessageGraph } from '@langchain/langgraph';
const graph = new MessageGraph();适用场景:
- 🤖 简单的聊天机器人
- 💬 仅需传递消息的链式调用
- 🚀 快速原型开发
ℹ️ 选择建议
对于大多数应用场景,推荐使用 StateGraph,因为它提供了更大的灵活性和扩展性。
3 编译过程(Compile)
图必须经过编译才能变为可执行的 Runnable。
const app = graph.compile();编译做了什么?
- 结构验证:检查是否有孤立节点、死循环(非预期)等。
- 状态绑定:将 StateAnnotation 绑定到每个节点。
- 运行时配置:注入 CheckpointSaver(用于持久化)等。
- 类型检查:确保状态和节点的类型一致性。
- 优化处理:为执行做准备。
编译选项
const app = graph.compile({
checkpointer: new MemorySaver(), // 启用记忆功能
interruptBefore: ['human_review'], // 在特定节点前暂停
interruptAfter: ['critical_decision'], // 在特定节点后暂停
});📝 代码示例
让我们通过一个完整的示例来理解图结构的创建和使用:
import { StateGraph, Annotation, START, END } from '@langchain/langgraph';
const StateAnnotation = Annotation.Root({
input: Annotation<string>(),
output: Annotation<string>(),
step: Annotation<number>(),
isProcessed: Annotation<boolean>(),
});
const inputNode = (state: typeof StateAnnotation.State) => {
console.log('%c Line:12 🍭 state', 'color:#6ec1c2', state.input);
return {
step: 1,
output: `处理后的数据:${state.input}`,
isProcessed: true,
};
};
const validateOutputNode = (state: typeof StateAnnotation.State) => {
console.log('%c Line:22 🧀 state', 'color:#f5ce50', state);
return {
step: state.step + 1,
output: `${state.output} [已验证]`,
};
};
const graph = new StateGraph(StateAnnotation)
.addNode('inputNode', inputNode)
.addNode('validateOutputNode', validateOutputNode)
.addEdge(START, 'inputNode')
.addEdge('inputNode', 'validateOutputNode')
.addEdge('validateOutputNode', END)
.compile();
graph.invoke({ input: '你好' }).then((res) => {
console.log('%c Line:39 🥤 res', 'color:#42b983', res);
});4 可视化与调试
LangGraph 提供了生成 Mermaid 图表的能力,帮助你直观地理解图结构。
代码生成图表
import { StateGraph } from '@langchain/langgraph';
// ... 构建图的代码 ...
const app = graph.compile();
// 获取 Mermaid 源码
const mermaidSource = app.getGraph().drawMermaid();
console.log(mermaidSource);典型结构可视化
线性结构
分支结构
循环结构(Agent)
🎨 实践指导
1. 创建你的第一个图
import { StateGraph, Annotation, START, END } from '@langchain/langgraph';
// 1. 定义状态结构
const StateAnnotation = Annotation.Root({
input: Annotation<string>(),
output: Annotation<string>(),
step: Annotation<number>({
default: () => 0,
}),
});
// 2. 创建图构建器
const graphBuilder = new StateGraph(StateAnnotation);
// 3. 添加节点和边
graphBuilder
.addNode('process', processNode)
.addEdge(START, 'process')
.addEdge('process', END);
// 4. 编译图
const graph = graphBuilder.compile();2. 状态设计最佳实践
// ✅ 好的状态设计
const StateAnnotation = Annotation.Root({
// 基础数据
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
// 业务状态
userInfo: Annotation<UserInfo>(),
currentStep: Annotation<string>(),
// 控制标志
isComplete: Annotation<boolean>({
default: () => false,
}),
});
// ❌ 避免的状态设计
const BadStateAnnotation = Annotation.Root({
// 避免嵌套过深的对象
complexNestedData: Annotation<{
level1: { level2: { level3: any } };
}>(),
// 避免存储函数或不可序列化的对象
callback: Annotation<Function>(),
});3. 图结构模式
线性流程
// 适用于:数据处理管道、简单工作流
graph
.addEdge(START, 'step1')
.addEdge('step1', 'step2')
.addEdge('step2', 'step3')
.addEdge('step3', END);条件分支
// 适用于:智能路由、决策系统
graph.addConditionalEdges('decision', routingFunction, {
path_a: 'nodeA',
path_b: 'nodeB',
end: END,
});循环结构
// 适用于:迭代优化、多轮对话
graph
.addConditionalEdges('check', shouldContinue, {
continue: 'process',
finish: END,
})
.addEdge('process', 'check');4. 编译时配置
// 基础编译
const graph = graphBuilder.compile();
// 带检查点的编译(支持持久化)
import { MemorySaver } from '@langchain/langgraph';
const graph = graphBuilder.compile({
checkpointer: new MemorySaver(),
});
// 带断点的编译(调试用)
const graph = graphBuilder.compile({
breakpoints: ['nodeA', 'nodeB'],
});🚀 高级特性
1. 多图组合
// 子图作为节点
const subGraph = subGraphBuilder.compile();
const mainGraph = new StateGraph(StateAnnotation)
.addNode('subProcess', subGraph)
.compile();2. 动态图构建
// 根据配置动态添加节点
const buildGraph = (config: GraphConfig) => {
const builder = new StateGraph(StateAnnotation);
config.nodes.forEach((nodeConfig) => {
builder.addNode(nodeConfig.name, nodeConfig.handler);
});
config.edges.forEach((edgeConfig) => {
builder.addEdge(edgeConfig.from, edgeConfig.to);
});
return builder.compile();
};❓ 常见问题解答
Q: StateGraph 和 MessageGraph 的主要区别是什么?
A: 主要区别在于状态结构的复杂度:
StateGraph:支持复杂的自定义状态结构,适用于大多数应用场景MessageGraph:状态仅为消息数组,适用于简单的聊天场景
Q: 为什么必须编译图才能使用?
A: 编译过程执行以下重要任务:
- 验证图结构的完整性和正确性
- 进行类型检查和优化
- 应用运行时配置(检查点、断点等)
- 为执行做准备和优化
Q: 如何处理图执行中的错误?
A: 可以通过以下方式处理错误:
try {
const result = await graph.invoke(input);
} catch (error) {
if (error instanceof GraphRecursionError) {
// 处理递归限制错误
} else {
// 处理其他错误
}
}💡 练习题
-
思考题:为什么
StateGraph比MessageGraph更适合复杂的企业级应用?点击查看答案
StateGraph可定义多字段结构化状态和自定义 reducer,适合复杂业务数据流。MessageGraph更偏消息序列场景,扩展复杂业务字段时约束较多。 -
操作题:使用
getGraph().drawMermaid()方法,尝试打印出上一章“环境搭建”中示例代码的图结构。点击查看答案
在 compile 后执行
console.log(app.getGraph().drawMermaid())即可得到图源码。 你应至少看到START -> llmNode -> END的主干结构。
✅ 总结
本章要点:
- 图是将业务逻辑组织成可执行流的容器
- StateGraph 是主要的图类,支持复杂状态管理
compile()是图从定义到运行的必经之路- 可视化是调试和沟通 LangGraph 架构的最佳工具
实践建议:
- 优先使用 StateGraph 而非 MessageGraph
- 合理设计状态结构,保持简洁和类型安全
- 充分利用编译时配置来增强功能(checkpointer、interruptBefore/After)
- 使用
drawMermaid()方法可视化图结构,便于调试和团队沟通
下一步:了解了图的骨架,我们需要填充血肉——状态管理。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。