跳到主要内容

🏗️ 状态设计

状态是 LangGraph 应用的核心,良好的状态设计直接影响应用的可维护性、性能和可扩展性。本节将介绍状态设计的最佳实践。

📋 核心原则

1. 最小化状态原则

只保留必要的信息,避免冗余数据。

设计建议
  • 只存储无法通过计算得出的数据
  • 避免存储可以从其他状态推导的信息
  • 定期审查状态结构,移除不必要的字段

2. 类型安全原则

使用 TypeScript 类型系统确保状态的类型安全。

3. 不可变更新原则

通过不可变的方式更新状态,避免副作用。

4. 状态规范化原则

对复杂的嵌套数据进行规范化处理。

🎯 状态设计模式

最小化状态设计

最小化状态示例
import { Annotation } from '@langchain/langgraph';
import { BaseMessage } from '@langchain/core/messages';

// ❌ 错误示例:包含冗余信息
const BadStateAnnotation = Annotation.Root({
messageCount: Annotation<number>(), // 冗余:可从 messages 计算
lastMessage: Annotation<string>(), // 冗余:可从 messages 推导
hasMessages: Annotation<boolean>(), // 冗余:可计算得出
messages: Annotation<BaseMessage[]>({
reducer: (state, update) => state.concat(update),
default: () => [],
}),
timestamp: Annotation<number>(), // 冗余:可实时计算
});

// ✅ 正确示例:最小化状态
const MinimalStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (state, update) => state.concat(update),
default: () => [],
}),
userId: Annotation<string>(),
status: Annotation<'idle' | 'processing' | 'waiting'>(),
});

// 状态工具函数
export class StateUtils {
static getMessageCount(state: typeof MinimalStateAnnotation.State): number {
return state.messages.length;
}

static getLastMessage(
state: typeof MinimalStateAnnotation.State
): BaseMessage | null {
return state.messages.length > 0
? state.messages[state.messages.length - 1]
: null;
}

static hasMessages(state: typeof MinimalStateAnnotation.State): boolean {
return state.messages.length > 0;
}

static getUserMessageCount(
state: typeof MinimalStateAnnotation.State
): number {
return state.messages.filter((msg) => msg._getType() === 'human').length;
}
}

// 实际应用示例
export const ChatAppStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (state, update) => state.concat(update),
default: () => [],
}),
userId: Annotation<string>(),
conversationId: Annotation<string>(),
status: Annotation<'idle' | 'thinking' | 'tool_calling'>(),
error: Annotation<string | null>(),
});

export class ChatStateUtils {
static getConversationSummary(state: typeof ChatAppStateAnnotation.State) {
return {
messageCount: state.messages.length,
userMessageCount: state.messages.filter((m) => m._getType() === 'human')
.length,
hasError: state.error !== null,
isActive: state.status !== 'idle',
};
}

static canSendMessage(state: typeof ChatAppStateAnnotation.State): boolean {
return state.status === 'idle' && state.error === null;
}
}

类型安全状态设计

类型安全状态示例
/**
* 类型安全状态设计示例
*
* 演示如何设计类型安全的状态结构,确保编译时类型检查
*/

import { Annotation } from '@langchain/langgraph';
import { BaseMessage, HumanMessage, AIMessage } from '@langchain/core/messages';

// 定义严格的类型
export type UserRole = 'admin' | 'user' | 'guest';
export type ProcessingStatus = 'idle' | 'processing' | 'completed' | 'failed';
export type Priority = 'low' | 'medium' | 'high' | 'urgent';

// 定义复杂的业务对象类型
export interface UserProfile {
id: string;
name: string;
email: string;
role: UserRole;
preferences: {
theme: 'light' | 'dark';
language: 'zh-CN' | 'en-US';
notifications: boolean;
};
metadata?: Record<string, unknown>;
}

export interface TaskItem {
id: string;
title: string;
description?: string;
priority: Priority;
status: ProcessingStatus;
assignee?: string;
dueDate?: Date;
tags: string[];
createdAt: Date;
updatedAt: Date;
}

// ✅ 类型安全的状态定义
export const TypeSafeStateAnnotation = Annotation.Root({
// 强类型的用户信息
currentUser: Annotation<UserProfile>(),

// 强类型的消息列表
messages: Annotation<BaseMessage[]>({
reducer: (state: BaseMessage[], update: BaseMessage[]) =>
state.concat(update),
default: () => [],
}),

// 强类型的任务列表
tasks: Annotation<TaskItem[]>({
reducer: (state: TaskItem[], update: TaskItem[]) => {
const newTasks = [...state];
update.forEach((updatedTask) => {
const index = newTasks.findIndex((task) => task.id === updatedTask.id);
if (index >= 0) {
newTasks[index] = updatedTask;
} else {
newTasks.push(updatedTask);
}
});
return newTasks;
},
default: () => [],
}),

// 强类型的状态枚举
processingStatus: Annotation<ProcessingStatus>(),

// 可选的错误信息
error: Annotation<Error | null>(),

// 强类型的配置对象
config: Annotation<{
maxRetries: number;
timeout: number;
enableLogging: boolean;
apiEndpoint: string;
}>(),
});

// 类型安全的工具函数
export class TypeSafeStateUtils {
/**
* 类型安全的用户权限检查
*/
static hasPermission(
state: typeof TypeSafeStateAnnotation.State,
requiredRole: UserRole
): boolean {
const roleHierarchy: Record<UserRole, number> = {
guest: 0,
user: 1,
admin: 2,
};

return roleHierarchy[state.currentUser.role] >= roleHierarchy[requiredRole];
}

/**
* 类型安全的任务过滤
*/
static getTasksByStatus(
state: typeof TypeSafeStateAnnotation.State,
status: ProcessingStatus
): TaskItem[] {
return state.tasks.filter((task) => task.status === status);
}

/**
* 类型安全的任务优先级排序
*/
static sortTasksByPriority(
state: typeof TypeSafeStateAnnotation.State
): TaskItem[] {
const priorityOrder: Record<Priority, number> = {
low: 0,
medium: 1,
high: 2,
urgent: 3,
};

return [...state.tasks].sort(
(a, b) => priorityOrder[b.priority] - priorityOrder[a.priority]
);
}

/**
* 类型安全的状态验证
*/
static validateState(state: typeof TypeSafeStateAnnotation.State): {
isValid: boolean;
errors: string[];
} {
const errors: string[] = [];

// 验证用户信息
if (!state.currentUser.id) {
errors.push('用户ID不能为空');
}

if (!state.currentUser.email.includes('@')) {
errors.push('用户邮箱格式无效');
}

// 验证任务
state.tasks.forEach((task, index) => {
if (!task.id) {
errors.push(`任务 ${index} 缺少ID`);
}

if (!task.title.trim()) {
errors.push(`任务 ${task.id} 标题不能为空`);
}

if (task.dueDate && task.dueDate < new Date()) {
errors.push(`任务 ${task.id} 截止日期已过期`);
}
});

// 验证配置
if (state.config.maxRetries < 0) {
errors.push('最大重试次数不能为负数');
}

if (state.config.timeout <= 0) {
errors.push('超时时间必须大于0');
}

return {
isValid: errors.length === 0,
errors,
};
}
}

// 类型安全的状态更新函数
export class TypeSafeStateUpdaters {
/**
* 安全地更新用户偏好
*/
static updateUserPreferences(
state: typeof TypeSafeStateAnnotation.State,
preferences: Partial<UserProfile['preferences']>
): Partial<typeof TypeSafeStateAnnotation.State> {
return {
currentUser: {
...state.currentUser,
preferences: {
...state.currentUser.preferences,
...preferences,
},
},
};
}

/**
* 安全地添加任务
*/
static addTask(
state: typeof TypeSafeStateAnnotation.State,
taskData: Omit<TaskItem, 'id' | 'createdAt' | 'updatedAt'>
): Partial<typeof TypeSafeStateAnnotation.State> {
const newTask: TaskItem = {
...taskData,
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
createdAt: new Date(),
updatedAt: new Date(),
};

return {
tasks: [...state.tasks, newTask],
};
}

/**
* 安全地更新任务状态
*/
static updateTaskStatus(
state: typeof TypeSafeStateAnnotation.State,
taskId: string,
status: ProcessingStatus
): Partial<typeof TypeSafeStateAnnotation.State> {
const updatedTasks = state.tasks.map((task) =>
task.id === taskId ? { ...task, status, updatedAt: new Date() } : task
);

return { tasks: updatedTasks };
}

/**
* 安全地添加消息
*/
static addMessage(
state: typeof TypeSafeStateAnnotation.State,
content: string,
type: 'human' | 'ai' = 'human'
): Partial<typeof TypeSafeStateAnnotation.State> {
const message =
type === 'human' ? new HumanMessage(content) : new AIMessage(content);

return {
messages: [...state.messages, message],
};
}
}

// 使用示例
export function createTypeSafeExample(): typeof TypeSafeStateAnnotation.State {
const initialState: typeof TypeSafeStateAnnotation.State = {
currentUser: {
id: 'user_001',
name: '张三',
email: 'zhangsan@example.com',
role: 'user',
preferences: {
theme: 'light',
language: 'zh-CN',
notifications: true,
},
},
messages: [],
tasks: [],
processingStatus: 'idle',
error: null,
config: {
maxRetries: 3,
timeout: 5000,
enableLogging: true,
apiEndpoint: 'https://api.example.com',
},
};

return initialState;
}

状态验证

状态验证示例
import { Annotation } from '@langchain/langgraph';
import { BaseMessage } from '@langchain/core/messages';

// 状态验证接口
interface StateValidator<T> {
validate(state: T): ValidationResult;
}

interface ValidationResult {
isValid: boolean;
errors: string[];
}

// 用户状态验证器
class UserStateValidator
implements StateValidator<{ userId: string; name: string }>
{
validate(state: { userId: string; name: string }): ValidationResult {
const errors: string[] = [];

if (!state.userId || state.userId.trim() === '') {
errors.push('用户ID不能为空');
}

if (!state.name || state.name.trim() === '') {
errors.push('用户名不能为空');
}

if (state.name && state.name.length > 50) {
errors.push('用户名长度不能超过50个字符');
}

return {
isValid: errors.length === 0,
errors,
};
}
}

// 消息状态验证器
class MessagesStateValidator
implements StateValidator<{ messages: BaseMessage[] }>
{
validate(state: { messages: BaseMessage[] }): ValidationResult {
const errors: string[] = [];

if (!Array.isArray(state.messages)) {
errors.push('messages必须是数组');
return { isValid: false, errors };
}

if (state.messages.length > 1000) {
errors.push('消息数量不能超过1000条');
}

// 验证每条消息
state.messages.forEach((message, index) => {
const content =
typeof message.content === 'string'
? message.content
: JSON.stringify(message.content);

if (!content || content.trim() === '') {
errors.push(`${index + 1}条消息内容不能为空`);
}

if (content.length > 10000) {
errors.push(`${index + 1}条消息内容过长`);
}
});

return {
isValid: errors.length === 0,
errors,
};
}
}

// 带验证的状态定义
const ValidatedStateAnnotation = Annotation.Root({
userId: Annotation<string>(),
name: Annotation<string>(),
messages: Annotation<BaseMessage[]>({
reducer: (current, update) => [...current, ...update],
default: () => [],
}),
lastValidated: Annotation<Date>({
reducer: (_, update) => update,
default: () => new Date(),
}),
});

// 状态验证节点
function validateStateNode(state: typeof ValidatedStateAnnotation.State) {
const userValidator = new UserStateValidator();
const messagesValidator = new MessagesStateValidator();

// 验证用户信息
const userValidation = userValidator.validate({
userId: state.userId,
name: state.name,
});

// 验证消息
const messagesValidation = messagesValidator.validate({
messages: state.messages,
});

// 收集所有错误
const allErrors = [...userValidation.errors, ...messagesValidation.errors];

if (allErrors.length > 0) {
throw new Error(`状态验证失败: ${allErrors.join(', ')}`);
}

return {
lastValidated: new Date(),
};
}

// 运行时状态验证装饰器
function withStateValidation<T extends Record<string, any>>(
nodeFunction: (state: T) => Partial<T>,
validator: StateValidator<T>
) {
return (state: T): Partial<T> => {
// 执行前验证
const validation = validator.validate(state);
if (!validation.isValid) {
throw new Error(`输入状态验证失败: ${validation.errors.join(', ')}`);
}

// 执行节点逻辑
const result = nodeFunction(state);

// 验证输出状态
const mergedState = { ...state, ...result };
const outputValidation = validator.validate(mergedState);
if (!outputValidation.isValid) {
throw new Error(
`输出状态验证失败: ${outputValidation.errors.join(', ')}`
);
}

return result;
};
}

// 使用示例
const validatedProcessNode = withStateValidation(
(state: typeof ValidatedStateAnnotation.State) => {
// 处理逻辑
return {
name: state.name.toUpperCase(),
};
},
new UserStateValidator()
);

状态规范化

状态规范化示例
/**
* ============================================================================
* 状态规范化 - State Normalization
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本文件展示如何规范化状态结构,将嵌套数据扁平化,使用ID引用代替嵌套对象,提高数据更新效率和一致性。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 状态定义:清晰定义状态结构和字段类型
* 2️⃣ Reducer函数:实现正确的状态合并和更新逻辑
* 3️⃣ 默认值:为状态字段提供合理的默认值
* 4️⃣ 示例演示:展示状态在实际图中的使用
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 使用Annotation.Root定义类型安全的状态
* • 为每个字段指定合适的reducer函数
* • 使用TypeScript类型确保类型安全
* • 通过示例展示状态的完整生命周期
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 最佳实践/状态设计/state-normalization.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 状态应该保持扁平化,避免深度嵌套
* • Reducer函数必须是纯函数,不应有副作用
* • 使用TypeScript类型确保状态操作的类型安全
* • 避免在状态中存储可计算的派生数据
* • 合理设置默认值,避免undefined导致的问题
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/

import '../../utils/loadEnv';
import { Annotation } from '@langchain/langgraph';
import { BaseMessage } from '@langchain/core/messages';

// 原始嵌套状态(不推荐)
interface NestedUserState {
user: {
id: string;
profile: {
name: string;
email: string;
preferences: {
theme: 'light' | 'dark';
language: string;
notifications: {
email: boolean;
push: boolean;
};
};
};
conversations: Array<{
id: string;
title: string;
messages: BaseMessage[];
metadata: {
createdAt: Date;
updatedAt: Date;
tags: string[];
};
}>;
};
}

// 规范化后的状态结构(推荐)
interface NormalizedState {
// 用户基本信息
userId: string;
userName: string;
userEmail: string;

// 用户偏好设置
userPreferences: {
theme: 'light' | 'dark';
language: string;
emailNotifications: boolean;
pushNotifications: boolean;
};

// 当前会话信息
currentConversationId: string;

// 消息列表(当前会话)
messages: BaseMessage[];

// 会话元数据
conversationMetadata: {
title: string;
createdAt: Date;
updatedAt: Date;
tags: string[];
};
}

// 规范化状态定义
const NormalizedStateAnnotation = Annotation.Root({
userId: Annotation<string>(),
userName: Annotation<string>(),
userEmail: Annotation<string>(),

userPreferences: Annotation<{
theme: 'light' | 'dark';
language: string;
emailNotifications: boolean;
pushNotifications: boolean;
}>({
reducer: (_, update) => update,
default: () => ({
theme: 'light' as const,
language: 'zh-CN',
emailNotifications: true,
pushNotifications: false,
}),
}),

currentConversationId: Annotation<string>(),

messages: Annotation<BaseMessage[]>({
reducer: (current, update) => [...current, ...update],
default: () => [],
}),

conversationMetadata: Annotation<{
title: string;
createdAt: Date;
updatedAt: Date;
tags: string[];
}>({
reducer: (_, update) => update,
default: () => ({
title: '新对话',
createdAt: new Date(),
updatedAt: new Date(),
tags: [],
}),
}),
});

// 状态规范化工具函数
class StateNormalizer {
// 从嵌套状态转换为规范化状态
static fromNested(
nestedState: NestedUserState
): typeof NormalizedStateAnnotation.State {
const currentConversation = nestedState.user.conversations[0] || {
id: 'default',
title: '新对话',
messages: [],
metadata: {
createdAt: new Date(),
updatedAt: new Date(),
tags: [],
},
};

return {
userId: nestedState.user.id,
userName: nestedState.user.profile.name,
userEmail: nestedState.user.profile.email,

userPreferences: {
theme: nestedState.user.profile.preferences.theme,
language: nestedState.user.profile.preferences.language,
emailNotifications:
nestedState.user.profile.preferences.notifications.email,
pushNotifications:
nestedState.user.profile.preferences.notifications.push,
},

currentConversationId: currentConversation.id,
messages: currentConversation.messages,

conversationMetadata: {
title: currentConversation.title,
createdAt: currentConversation.metadata.createdAt,
updatedAt: currentConversation.metadata.updatedAt,
tags: currentConversation.metadata.tags,
},
};
}

// 转换回嵌套状态(如果需要)
static toNested(
normalizedState: typeof NormalizedStateAnnotation.State
): NestedUserState {
return {
user: {
id: normalizedState.userId,
profile: {
name: normalizedState.userName,
email: normalizedState.userEmail,
preferences: {
theme: normalizedState.userPreferences.theme,
language: normalizedState.userPreferences.language,
notifications: {
email: normalizedState.userPreferences.emailNotifications,
push: normalizedState.userPreferences.pushNotifications,
},
},
},
conversations: [
{
id: normalizedState.currentConversationId,
title: normalizedState.conversationMetadata.title,
messages: normalizedState.messages,
metadata: {
createdAt: normalizedState.conversationMetadata.createdAt,
updatedAt: normalizedState.conversationMetadata.updatedAt,
tags: normalizedState.conversationMetadata.tags,
},
},
],
},
};
}

// 提取用户相关状态
static extractUserState(state: typeof NormalizedStateAnnotation.State) {
return {
userId: state.userId,
userName: state.userName,
userEmail: state.userEmail,
userPreferences: state.userPreferences,
};
}

// 提取会话相关状态
static extractConversationState(
state: typeof NormalizedStateAnnotation.State
) {
return {
currentConversationId: state.currentConversationId,
messages: state.messages,
conversationMetadata: state.conversationMetadata,
};
}
}

// 状态分片管理
class StateSliceManager {
// 用户状态切片
static userSlice = {
select: (state: typeof NormalizedStateAnnotation.State) => ({
userId: state.userId,
userName: state.userName,
userEmail: state.userEmail,
userPreferences: state.userPreferences,
}),

update: (
updates: Partial<{
userName: string;
userEmail: string;
userPreferences: (typeof NormalizedStateAnnotation.State)['userPreferences'];
}>
) => updates,
};

// 会话状态切片
static conversationSlice = {
select: (state: typeof NormalizedStateAnnotation.State) => ({
currentConversationId: state.currentConversationId,
messages: state.messages,
conversationMetadata: state.conversationMetadata,
}),

update: (
updates: Partial<{
currentConversationId: string;
messages: BaseMessage[];
conversationMetadata: (typeof NormalizedStateAnnotation.State)['conversationMetadata'];
}>
) => updates,
};
}

// 状态访问器模式
class StateAccessor {
constructor(private state: typeof NormalizedStateAnnotation.State) {}

// 用户信息访问器
get user() {
return {
id: this.state.userId,
name: this.state.userName,
email: this.state.userEmail,
preferences: this.state.userPreferences,
};
}

// 会话信息访问器
get conversation() {
return {
id: this.state.currentConversationId,
messages: this.state.messages,
metadata: this.state.conversationMetadata,
messageCount: this.state.messages.length,
lastMessage: this.state.messages[this.state.messages.length - 1],
};
}

// 偏好设置访问器
get preferences() {
return {
...this.state.userPreferences,
isDarkMode: this.state.userPreferences.theme === 'dark',
isNotificationsEnabled:
this.state.userPreferences.emailNotifications ||
this.state.userPreferences.pushNotifications,
};
}
}

// 使用示例节点
function updateUserPreferencesNode(
state: typeof NormalizedStateAnnotation.State
) {
const accessor = new StateAccessor(state);

// 使用访问器获取当前偏好
const currentPrefs = accessor.preferences;

// 更新偏好设置
return {
userPreferences: {
...currentPrefs,
theme: currentPrefs.isDarkMode ? ('light' as const) : ('dark' as const),
},
};
}

function addMessageNode(state: typeof NormalizedStateAnnotation.State) {
const accessor = new StateAccessor(state);

// 创建新消息
const newMessage = {
content: `消息 #${accessor.conversation.messageCount + 1}`,
role: 'user' as const,
};

return {
messages: [newMessage],
conversationMetadata: {
...state.conversationMetadata,
updatedAt: new Date(),
},
};
}

export {
NormalizedStateAnnotation,
StateNormalizer,
StateSliceManager,
StateAccessor,
updateUserPreferencesNode,
addMessageNode,
type NormalizedState,
type NestedUserState,
};

/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
*/

不可变更新

不可变更新示例
/**
* ============================================================================
* 不可变状态更新 - Immutable State Updates
* ============================================================================
*
* 📖 概述
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 本文件展示如何以不可变的方式更新状态,避免直接修改状态对象,使用扩展运算符、Object.assign等技术确保数据不可变性。
*
* 🎯 核心功能
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* 1️⃣ 状态定义:清晰定义状态结构和字段类型
* 2️⃣ Reducer函数:实现正确的状态合并和更新逻辑
* 3️⃣ 默认值:为状态字段提供合理的默认值
* 4️⃣ 示例演示:展示状态在实际图中的使用
*
* 💡 实现思路
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 使用Annotation.Root定义类型安全的状态
* • 为每个字段指定合适的reducer函数
* • 使用TypeScript类型确保类型安全
* • 通过示例展示状态的完整生命周期
*
* 🚀 使用方式
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* // 运行示例
* $ npx esno 最佳实践/状态设计/immutable-updates.ts
*
* ⚠️ 注意事项
* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
* • 状态应该保持扁平化,避免深度嵌套
* • Reducer函数必须是纯函数,不应有副作用
* • 使用TypeScript类型确保状态操作的类型安全
* • 避免在状态中存储可计算的派生数据
* • 合理设置默认值,避免undefined导致的问题
*
* @author 程哥
* @version 1.0.0
* @updated 2025-11
*/

import '../../utils/loadEnv';
import { Annotation } from '@langchain/langgraph';
import { BaseMessage, HumanMessage, AIMessage } from '@langchain/core/messages';

// 状态定义
const StateAnnotation = Annotation.Root({
user: Annotation<{
id: string;
name: string;
preferences: {
theme: 'light' | 'dark';
language: string;
};
}>({
reducer: (_, update) => update,
default: () => ({
id: '',
name: '',
preferences: {
theme: 'light' as const,
language: 'zh-CN',
},
}),
}),

messages: Annotation<BaseMessage[]>({
reducer: (current, update) => [...current, ...update],
default: () => [],
}),

metadata: Annotation<Record<string, any>>({
reducer: (current, update) => ({ ...current, ...update }),
default: () => ({}),
}),
});

// ❌ 错误的可变更新方式
function badMutableUpdate(state: typeof StateAnnotation.State) {
// 直接修改状态对象
state.user.name = 'New Name';
state.user.preferences.theme = 'dark';
state.messages.push(new HumanMessage('New message'));
state.metadata.lastUpdated = new Date();

return state; // 返回修改后的原始状态
}

// ✅ 正确的不可变更新方式
function goodImmutableUpdate(state: typeof StateAnnotation.State) {
return {
user: {
...state.user,
name: 'New Name',
preferences: {
...state.user.preferences,
theme: 'dark' as const,
},
},
messages: [...state.messages, new HumanMessage('New message')],
metadata: {
...state.metadata,
lastUpdated: new Date(),
},
};
}

// 不可变更新工具类
class ImmutableUpdater {
// 深度克隆对象
static deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}

if (obj instanceof Date) {
return new Date(obj.getTime()) as T;
}

if (Array.isArray(obj)) {
return obj.map((item) => this.deepClone(item)) as T;
}

const cloned = {} as T;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = this.deepClone(obj[key]);
}
}

return cloned;
}

// 安全的对象更新
static updateObject<T extends Record<string, any>>(
original: T,
updates: Partial<T>
): T {
return { ...original, ...updates };
}

// 安全的嵌套对象更新
static updateNestedObject<T extends Record<string, any>>(
original: T,
path: string[],
value: any
): T {
if (path.length === 0) {
return value;
}

const [head, ...tail] = path;
return {
...original,
[head]: this.updateNestedObject(original[head] || {}, tail, value),
};
}

// 数组不可变操作
static arrayOperations = {
// 添加元素
append<T>(array: T[], item: T): T[] {
return [...array, item];
},

// 前置添加元素
prepend<T>(array: T[], item: T): T[] {
return [item, ...array];
},

// 在指定位置插入
insert<T>(array: T[], index: number, item: T): T[] {
return [...array.slice(0, index), item, ...array.slice(index)];
},

// 移除指定索引的元素
removeAt<T>(array: T[], index: number): T[] {
return [...array.slice(0, index), ...array.slice(index + 1)];
},

// 更新指定索引的元素
updateAt<T>(array: T[], index: number, item: T): T[] {
return array.map((existing, i) => (i === index ? item : existing));
},

// 过滤元素
filter<T>(array: T[], predicate: (item: T) => boolean): T[] {
return array.filter(predicate);
},

// 映射转换
map<T, U>(array: T[], mapper: (item: T) => U): U[] {
return array.map(mapper);
},
};
}

// 使用 Immer 风格的更新函数
function createImmerStyleUpdater<T>() {
return function update(
state: T,
updater: (draft: T) => void | Partial<T>
): T {
const draft = ImmutableUpdater.deepClone(state);
const result = updater(draft);

// 如果返回了新对象,使用返回值;否则使用修改后的 draft
return result !== undefined ? { ...draft, ...result } : draft;
};
}

// 状态更新示例
const updateState = createImmerStyleUpdater<typeof StateAnnotation.State>();

// 使用示例节点
function updateUserNameNode(state: typeof StateAnnotation.State) {
return updateState(state, (draft) => {
draft.user.name = 'Updated Name';
draft.metadata.lastUpdated = new Date();
});
}

function addMessageNode(state: typeof StateAnnotation.State) {
const newMessage = new AIMessage('Hello from immutable update!');

return {
messages: ImmutableUpdater.arrayOperations.append(
state.messages,
newMessage
),
metadata: ImmutableUpdater.updateObject(state.metadata, {
messageCount: state.messages.length + 1,
lastMessageAt: new Date(),
}),
};
}

function updateUserPreferencesNode(state: typeof StateAnnotation.State) {
return {
user: ImmutableUpdater.updateNestedObject(
state.user,
['preferences', 'theme'],
state.user.preferences.theme === 'light' ? 'dark' : 'light'
),
metadata: ImmutableUpdater.updateObject(state.metadata, {
preferencesUpdatedAt: new Date(),
}),
};
}

// 批量更新工具
class BatchUpdater {
private updates: Array<
(
state: typeof StateAnnotation.State
) => Partial<typeof StateAnnotation.State>
> = [];

addUpdate(
updater: (
state: typeof StateAnnotation.State
) => Partial<typeof StateAnnotation.State>
) {
this.updates.push(updater);
return this;
}

apply(state: typeof StateAnnotation.State): typeof StateAnnotation.State {
return this.updates.reduce((currentState, updater) => {
const update = updater(currentState);
return { ...currentState, ...update };
}, state);
}

clear() {
this.updates = [];
return this;
}
}

// 批量更新示例
function batchUpdateNode(state: typeof StateAnnotation.State) {
const batchUpdater = new BatchUpdater();

return batchUpdater
.addUpdate((s) => ({
user: {
...s.user,
name: 'Batch Updated Name',
},
}))
.addUpdate((s) => ({
messages: ImmutableUpdater.arrayOperations.append(
s.messages,
new AIMessage('Batch update message')
),
}))
.addUpdate((s) => ({
metadata: {
...s.metadata,
batchUpdatedAt: new Date(),
updateCount: (s.metadata.updateCount || 0) + 1,
},
}))
.apply(state);
}

// 条件更新工具
class ConditionalUpdater {
static when<T>(condition: boolean, updater: (state: T) => Partial<T>) {
return (state: T): Partial<T> => {
return condition ? updater(state) : {};
};
}

static unless<T>(condition: boolean, updater: (state: T) => Partial<T>) {
return this.when(!condition, updater);
}
}

// 条件更新示例
function conditionalUpdateNode(state: typeof StateAnnotation.State) {
const hasMessages = state.messages.length > 0;
const isLightTheme = state.user.preferences.theme === 'light';

const updates = [
ConditionalUpdater.when(hasMessages, (s: typeof StateAnnotation.State) => ({
metadata: {
...s.metadata,
hasConversation: true,
},
})),

ConditionalUpdater.unless(
isLightTheme,
(s: typeof StateAnnotation.State) => ({
user: {
...s.user,
preferences: {
...s.user.preferences,
theme: 'light' as const,
},
},
})
),
];

return updates.reduce((currentState, updater) => {
const update = updater(currentState);
return { ...currentState, ...update };
}, state);
}

// 性能优化的更新器
class OptimizedUpdater {
// 浅比较检查是否需要更新
static shouldUpdate<T>(current: T, next: T): boolean {
if (current === next) return false;

if (typeof current !== 'object' || typeof next !== 'object') {
return current !== next;
}

if (current === null || next === null) {
return current !== next;
}

const currentKeys = Object.keys(current);
const nextKeys = Object.keys(next);

if (currentKeys.length !== nextKeys.length) {
return true;
}

return currentKeys.some(
(key) => (current as any)[key] !== (next as any)[key]
);
}

// 只在需要时进行更新
static updateIfChanged<T>(current: T, updater: () => T): T {
const next = updater();
return this.shouldUpdate(current, next) ? next : current;
}
}

export {
StateAnnotation,
ImmutableUpdater,
createImmerStyleUpdater,
BatchUpdater,
ConditionalUpdater,
OptimizedUpdater,
updateUserNameNode,
addMessageNode,
updateUserPreferencesNode,
batchUpdateNode,
conditionalUpdateNode,
goodImmutableUpdate,
badMutableUpdate,
};

/*
执行结果:
Line:8 🌭 path.resolve(__dirname, '.env') /Users/loock/myFile/langgraphjs-tutorial/websites/examples/utils/.env
*/

🔄 状态生命周期管理

状态初始化

状态更新流程

📊 状态设计检查清单

✅ 设计阶段检查

  • 状态结构是否最小化?
  • 是否使用了 TypeScript 类型定义?
  • 是否定义了合适的默认值?
  • 是否考虑了状态的扩展性?
  • 是否避免了循环引用?

✅ 实现阶段检查

  • 是否实现了状态验证?
  • 是否使用了不可变更新?
  • 是否处理了状态合并冲突?
  • 是否考虑了性能影响?
  • 是否添加了适当的错误处理?

✅ 测试阶段检查

  • 是否测试了状态初始化?
  • 是否测试了状态更新逻辑?
  • 是否测试了边界条件?
  • 是否测试了错误处理?
  • 是否进行了性能测试?

⚠️ 常见陷阱

1. 状态过度设计

// ❌ 错误:状态过于复杂
const BadStateAnnotation = Annotation.Root({
user: Annotation<{
id: string;
name: string;
email: string;
profile: {
avatar: string;
bio: string;
preferences: {
theme: string;
language: string;
notifications: {
email: boolean;
push: boolean;
sms: boolean;
};
};
};
}>(),
// ... 更多复杂嵌套
});

// ✅ 正确:简化状态结构
const GoodStateAnnotation = Annotation.Root({
userId: Annotation<string>(),
userPreferences: Annotation<UserPreferences>(),
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
});

2. 可变状态更新

// ❌ 错误:直接修改状态
function badNode(state: typeof StateAnnotation.State) {
state.messages.push(newMessage); // 直接修改
return state;
}

// ✅ 正确:不可变更新
function goodNode(state: typeof StateAnnotation.State) {
return {
messages: [...state.messages, newMessage],
};
}

3. 缺乏类型安全

// ❌ 错误:使用 any 类型
const BadStateAnnotation = Annotation.Root({
data: Annotation<any>(), // 失去类型安全
});

// ✅ 正确:明确的类型定义
interface AppData {
id: string;
status: 'pending' | 'processing' | 'completed';
result?: string;
}

const GoodStateAnnotation = Annotation.Root({
data: Annotation<AppData>(),
});

🚀 性能优化建议

1. 状态分片

对于大型应用,考虑将状态分片:

// 按功能域分片状态
const UserStateAnnotation = Annotation.Root({
currentUser: Annotation<User>(),
preferences: Annotation<UserPreferences>(),
});

const ChatStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
activeConversation: Annotation<string>(),
});

2. 延迟加载

对于非关键数据,使用延迟加载:

const StateAnnotation = Annotation.Root({
// 核心数据
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),

// 延迟加载的数据
metadata: Annotation<Record<string, any>>({
default: () => ({}),
}),
});

📚 小结

良好的状态设计是构建可维护 LangGraph 应用的基础。遵循最小化、类型安全、不可变更新和规范化的原则,可以帮助你构建出高质量的应用。

关键要点

  1. 最小化状态:只保留必要信息
  2. 类型安全:使用 TypeScript 类型系统
  3. 不可变更新:避免直接修改状态
  4. 状态验证:确保数据一致性
  5. 性能考虑:合理设计状态结构

接下来我们将学习节点设计的最佳实践。