做了一个AI聊天应用后,我决定试试这个状态管理库
AI应用前端最大的坑,不是LLM调用,而是状态管理
背景
最近做了个AI聊天应用,类似ChatGPT的那种。
本来想用Redux,毕竟老牌方案,结果被毒打了一遍。
Redux的痛
痛点1:状态类型爆炸
// AI聊天应用需要管理的状态
interface ChatState {
// 对话
messages: Message[];
// 流式响应
streamingText: string;
isStreaming: boolean;
// 工具调用
toolCalls: ToolCall[];
currentToolCall: ToolCall | null;
toolResults: Record<string, any>;
// 上下文
contextWindow: Message[];
longTermMemory: MemoryItem[];
// 用户意图
currentIntent: Intent | null;
intentHistory: Intent[];
// 执行状态
currentStep: number;
stepResults: Record<number, any>;
// 错误处理
errors: Error[];
retryQueue: RetryItem[];
// ...
}
这还只是一个聊天模块的状态。
痛点2:action type能写死你
// 光是状态更新action就能写30个
const ADD_MESSAGE = "chat/ADD_MESSAGE";
const UPDATE_STREAMING = "chat/UPDATE_STREAMING";
const APPEND_STREAMING = "chat/APPEND_STREAMING";
const START_TOOL_CALL = "chat/START_TOOL_CALL";
const COMPLETE_TOOL_CALL = "chat/COMPLETE_TOOL_CALL";
const UPDATE_CONTEXT = "chat/UPDATE_CONTEXT";
const ADD_INTENT = "chat/ADD_INTENT";
// ... 还有几十个
痛点3:跨组件同步难
// Chat组件
const messages = useSelector((s) => s.chat.messages);
// Status组件
const toolCalls = useSelector((s) => s.chat.toolCalls);
// 怎么保证两个组件状态一致?靠redux-thunk?middleware?
然后我用了easy-model
// 一个类搞定AI聊天全状态
class AIChatModel {
// 对话
messages: Message[] = [];
// 流式响应
streamingText = "";
isStreaming = false;
// 工具调用
toolCalls: ToolCall[] = [];
currentToolCall: ToolCall | null = null;
toolResults: Map<string, any> = new Map();
// 上下文
contextWindow: Message[] = [];
longTermMemory: MemoryItem[] = [];
// 用户意图
currentIntent: Intent | null = null;
intentHistory: Intent[] = [];
// 执行
currentStep = 0;
stepResults: Map<number, any> = new Map();
// 错误
errors: Error[] = [];
// 方法
@loader.load()
async sendMessage(content: string) {
this.messages.push({ role: "user", content });
this.isStreaming = true;
const response = await llm.streamChat(this.messages);
for await (const chunk of response) {
this.streamingText += chunk;
}
this.messages.push({ role: "assistant", content: this.streamingText });
this.streamingText = "";
this.isStreaming = false;
}
async executeToolCall(tool: Tool, params: any) {
this.currentToolCall = { tool, params, status: "running" };
this.toolCalls.push(this.currentToolCall);
const result = await tool.execute(params);
this.currentToolCall.status = "completed";
this.currentToolCall.result = result;
this.toolResults.set(tool.name, result);
this.currentToolCall = null;
}
}
一个类,200行代码搞定Redux 500行都搞不定的事。
还能解决什么问题?
1. 撤销重做,调试AI回复
const chat = useModel(AIChatModel, []);
const history = useModelHistory(chat);
// 用户想撤回AI的上一次回复?
history.back();
// 想重做?
history.forward();
2. 跨组件状态共享
// 聊天区域
function ChatArea() {
const chat = useModel(AIChatModel, ["main"]);
return <MessageList messages={chat.messages} />;
}
// 状态显示
function StatusPanel() {
const chat = useModel(AIChatModel, ["main"]);
return <StatusBadge isStreaming={chat.isStreaming} />;
}
// 工具调用面板
function ToolPanel() {
const chat = useModel(AIChatModel, ["main"]);
return <ToolList calls={chat.toolCalls} />;
}
// 三个组件,状态自动同步
3. 深度监听
// 监听任意状态变化
watch(chat, (keys, prev, next) => {
console.log(`状态变化: ${keys.join(".")}`, prev, "→", next);
// "messages.5.content" - 第6条消息内容变了
// "toolCalls.0.status" - 第一个工具调用状态变了
// "streamingText" - 流式文本更新了
});
对比其他方案
| 特性 | Redux | Zustand | MobX | easy-model |
|---|---|---|---|---|
| 类模型 | ❌ | ❌ | ✅ | ✅ |
| 无装饰器 | ✅ | ✅ | ❌ | ✅ |
| 依赖注入 | ❌ | ❌ | ❌ | ✅ |
| 撤销重做 | ❌ | ❌ | ❌ | ✅ |
| 深度监听 | ❌ | ⚠️ | ✅ | ✅ |
| TypeScript友好 | ⚠️ | ✅ | ⚠️ | ✅ |
结论
AI应用前端,状态管理选easy-model就对了。
GitHub: github.com/ZYF93/easy-…
npm: pnpm add @e7w/easy-model
做AI应用前端,状态管理别再踩坑了,点个⭐️!