Langgraph框架
const processingSteamChunks = useCallback(
async (stream: AsyncGenerator<{ data: any; event: string }>) => {
// 循环读取每一块数据(SDK已经帮你切分好了)
for await (const { data, event } of stream) {
// === 分频道处理逻辑 ===
// 频道 A: metadata (获取 Run ID)
if (event === "metadata") {
setRunId(data?.run_id as string);
}
// 频道 B: values (最核心的对话数据)
if (event === "values") {
// 1. 处理中断逻辑 (__interrupt__)
if ("__interrupt__" in data) {
// ... 更新 setChatBlocks ...
}
// 2. 处理正常对话消息
const messages = (data as { messages?: Message[] })?.messages as Message[];
messages.forEach((message) => {
// ⚠️ 注意这里:getObject4Data 就是为了解决我之前说的“双重序列化”坑
const content = getObject4Data(message.content);
// ... 更新 setStreamContent, setStartTime ...
// 更新 UI 上的 Blocks (折叠框、Markdown 等)
sessionData.blocks.forEach((block) => {
setChatBlocks((prev) => { ... })
});
});
}
// 频道 C: custom (自定义事件)
if (event === "custom") {
// ... 更新 setChatBlocks ...
}
}
},
[...]
);
步骤总结(从点击按钮开始到UI展示出来)
数据的一生
| 步骤 | 负责角色 | 核心动作 | 你的代码位置 |
| 1 | User | 点击发送 | UI Button |
| 2 | ThreadContext | 组装 JSON,发起 POST | handleSubmit |
| 3 | Proxy | 转发请求到远程 | vite.config.js |
| 4 | Backend | 思考、生成 Event | (Python Code) |
| 5 | ThreadContext | 解析流,提取 Block | processingSteamChunks |
| 6 | ChatContext | 存储 Block 到数组 | setChatBlocks |
| 7 | ChatItem | 数组转树,组件映射 | childrenByParentId |
| 8 | Browser | 滚动到底部 | scrollToBottom |
Langgraph和LangChain
| 概念 | LangChain (旧) | LangGraph (新) | 前端对应的概念 |
| 核心结构 | Chain (链) | Graph (图) | 线性代码 vs 状态机 (XState) |
| 数据流 | 参数透传 (Props drilling) | State Schema (共享状态) | React Context / Redux |
| 记忆机制 | 简单的 Memory List | Thread / Checkpoint | Redux Persist / 数据库 |
| 运行模式 | 一口气跑完 | Step by Step (可中断) | Generator 函数 /断点调试 |
| 适用场景 | 简单问答、翻译 | 复杂 Agent、多轮交互 | 简单的展示页 vs 复杂的管理系统 |
Langgraph运行逻辑
Life's a struggle, I'll conquer it.