🏗️ 图(Graph)结构
引言
在 LangGraphJS 中,图(Graph) 是整个框架的核心概念。如果你熟悉前端开发中的状态管理(如 Redux)或者组件树结构(如 React),那么理解 LangGraph 的图结构会相对容易。
图结构本质上是一个有向图,它定义了你的 AI 应用的执行流程。与传统的链式结构不同,图结构允许 LLM 动态决定下一步的执行路径,这为构建智能代理提供了强大的灵活性。
与前端开发的类比
- 图结构 ≈ React 应用的组件树 + 路由系统
- 状态管理 ≈ Redux Store 或 React Context
- 编译过程 ≈ Webpack/Vite 的构建过程
概念解释
StateGraph - 主要图类
StateGraph 是 LangGraphJS 中最重要的图类,它由用户定义的状态结构进行参数化。这就像是为你的应用定义了一个全局的状态管理器。
核心特征:
- 📊 状态驱动:所有节点共享同一个状态结构
- 🔄 动态流程:可以根据状态动态决定执行路径
- 🛠️ 类型安全:完全支持 TypeScript 类型推导
MessageGraph - 特殊图类(较少使用)
MessageGraph 是一个特殊的图类,其状态仅为消息数组。它主要用于简单的聊天机器人场景。
使用场景:
- 🤖 简单的聊天机器人
- 💬 纯消息处理流程
- 🚀 快速原型开发
选择建议
对于大多数应用场景,推荐使用 StateGraph,因为它提供了更大的灵活性和扩展性。
编译过程
图必须经过编译才能使用,这个过程类似于前端项目的构建过程:
- 结构检查:验证图的完整性(无孤立节点等)
- 类型验证:确保状态和节点的类型一致性
- 优化处理:为执行做准备
- 配置应用:应用运行时配置(如检查点、断点等)
代码示例
让我们通过一个完整的示例来理解图结构的创建和使用:
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);
})
可视化说明
下面的图表展示了 StateGraph 的基本结构和执行流程:
图表说明:
- 圆角矩形:特殊节点(开始/结束)
- 矩形:普通处理节点
- 菱形:决策节点
- 实线箭头:确定的执行路径
- 虚线箭头:条件执行路径
实践指导
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 {
// 处理其他错误
}
}
小结与延伸
在本节中,我们深入了解了 LangGraphJS 的图结构概念:
🔑 核心要点:
- StateGraph 是主要的图类,支持复杂状态管理
- 图必须经过编译才能使用
- 图结构提供了灵活的控制流程管理
🚀 实践建议:
- 优先使用 StateGraph 而非 MessageGraph
- 合理设计状态结构,保持简洁和类型安全
- 充分利用编译时配置来增强功能
在下一节状态管理中,我们将深入探讨如何设计和管理图的状态结构,包括 Annotation 对象的使用和 Reducer 函数的定义。