跳到主要内容

🏗️ 图(Graph)结构

引言

在 LangGraphJS 中,图(Graph) 是整个框架的核心概念。如果你熟悉前端开发中的状态管理(如 Redux)或者组件树结构(如 React),那么理解 LangGraph 的图结构会相对容易。

图结构本质上是一个有向图,它定义了你的 AI 应用的执行流程。与传统的链式结构不同,图结构允许 LLM 动态决定下一步的执行路径,这为构建智能代理提供了强大的灵活性。

与前端开发的类比
  • 图结构 ≈ React 应用的组件树 + 路由系统
  • 状态管理 ≈ Redux Store 或 React Context
  • 编译过程 ≈ Webpack/Vite 的构建过程

概念解释

StateGraph - 主要图类

StateGraph 是 LangGraphJS 中最重要的图类,它由用户定义的状态结构进行参数化。这就像是为你的应用定义了一个全局的状态管理器。

核心特征:

  • 📊 状态驱动:所有节点共享同一个状态结构
  • 🔄 动态流程:可以根据状态动态决定执行路径
  • 🛠️ 类型安全:完全支持 TypeScript 类型推导

MessageGraph - 特殊图类(较少使用)

MessageGraph 是一个特殊的图类,其状态仅为消息数组。它主要用于简单的聊天机器人场景。

使用场景:

  • 🤖 简单的聊天机器人
  • 💬 纯消息处理流程
  • 🚀 快速原型开发
选择建议

对于大多数应用场景,推荐使用 StateGraph,因为它提供了更大的灵活性和扩展性。

编译过程

图必须经过编译才能使用,这个过程类似于前端项目的构建过程:

  1. 结构检查:验证图的完整性(无孤立节点等)
  2. 类型验证:确保状态和节点的类型一致性
  3. 优化处理:为执行做准备
  4. 配置应用:应用运行时配置(如检查点、断点等)

代码示例

让我们通过一个完整的示例来理解图结构的创建和使用:

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 函数的定义。