langchain.js中stream()的单模式 vs 多模式梳理

stream() 单模式 vs 多模式 完整梳理

核心分水岭:streamMode字符串 (单模式) vs 字符串数组 (多模式) 两种场景迭代返回的数据结构完全不一样

一、定义区分

1)单模式:streamMode: string

js 复制代码
// 单选一种
{ streamMode: "messages" }
// 或者
{ streamMode: "updates" }
// 或者
{ streamMode: "custom" }

每条迭代项结构:[messageChunk, metadata]

ts 复制代码
[AIMessageChunk, LangGraphMetadata]
  • 下标0:主体数据(分片对象)
  • 下标1:附加元数据(节点路径、分支信息等)

示例解构:

js 复制代码
for await (const [chunk, meta] of stream) {
  // chunk = AIMessageChunk
  // meta = 元数据对象
}

⚠️ 这里没有模式名字符串!数组第一位不是标记,是消息本体。


2)多模式:streamMode: string\[\]

js 复制代码
{ streamMode: ["updates", "messages", "custom"] }

每条迭代项结构:[modeName, payload]

ts 复制代码
["messages" | "updates" | "custom", payloadData]
  • 下标0:模式标识字符串 (重点!就是你打印出来的 streamMode:messages
  • 下标1:载荷 payload,不同mode载荷结构不同

解构标准写法:

js 复制代码
for await (const [mode, payload] of stream) {
  // mode:"messages" / "updates" / "custom"
  // payload:对应模式的数据
}

二、多模式下 payload 内部结构(重中之重)

  1. mode === "messages"
ini 复制代码
payload = [AIMessageChunk, metadata]

👉 和【单模式】迭代出来的数组一模一样!

js 复制代码
if(mode === "messages"){
  const [chunk, meta] = payload;
  console.log(chunk.content);
}
  1. mode === "updates"
css 复制代码
payload = State变更对象 { nodeName: {messages:[...]} }

用于观测图节点执行、状态更新。

  1. mode === "custom" payload = 自定义事件,writer 函数手动推送的数据。

三、一张对照表看懂差异

配置方式 streamMode 值 循环内每一项结构 标准解构
单模式 "messages" [chunk, meta] [chunk, meta]
多模式 ["messages","updates"] [mode字符串, payload] [mode, payload]

四、高频踩坑清单

坑1:两套结构混用

js 复制代码
// 多模式配置
{streamMode: ["messages","updates"]}

// ❌错误!沿用单模式解构
for await (const [chunk, meta] of stream){}

此时 chunk 拿到的是 "messages" 字符串,直接崩溃。

坑2:变量命名迷惑自己

js 复制代码
// 多模式
for await (const [streamMode, chunk] of stream) {
  // streamMode = "messages"(字符串)
  // chunk = payload
}

名字没问题,但一定要记住:只有数组形式streamMode才会拿到mode字符串

坑3:误以为 payload 直接等于 chunk

js 复制代码
if(mode === "messages"){
  const payload = xxx;
  console.log(payload.content) // ❌ undefined
  // payload 是 [chunk,meta] 数组,必须二次解构
  const [chunk] = payload;
  console.log(chunk.content) // ✅
}

坑4:单模式下试图判断 mode

js 复制代码
// 单模式
{streamMode:"messages"}
for await (const [chunk,meta] of stream){
  if(chunk === "messages"){} // ❌永远false,chunk是对象不是字符串
}

五、两套可直接复制的标准模板(打印详细信息)

模板1:单模式(仅messages流式输出)

js 复制代码
const stream = agent.stream(input, { streamMode: "messages" });

for await (const [chunk, meta] of stream) {
  console.log('chunk:',chunk)
  console.log('meta:',meta)
}

模板2:多模式(同时监听 updates + messages + custom,和你的日志格式匹配)

js 复制代码
const stream = await agent.stream(
  { messages: [{ role: "user", content: userInput }] },
  { streamMode: ["updates", "messages", "custom"] }
);
let index = 0;

for await (const [mode, payload] of stream) {
  index++;
  console.log(`\n===== streamMode #${index}  =====`);
  console.log(`streamMode:`, mode);

  if (mode === "messages") {
    console.log(`messages:`, payload);
    const [chunk] = payload;
    if (chunk?.content) process.stdout.write(chunk.content);
  } else if (mode === "updates") {
    console.log(`updates:`, payload);
  } else if (mode === "custom") {
    console.log(`custom:`, payload);
  }
}

六、终极一句话口诀

  • 字符串配置(单模式):第一项是chunk;
  • 数组配置(多模式):第一项是mode名字字符串,数据藏在第二个payload里;
  • messages模式下,payload内部又是 [chunk, meta],需要二次解构。
相关推荐
山间小僧4 小时前
「AI学习笔记」Agent Memory(二)跨会话记忆:从聊天记录到可演化的长期记忆
aigc·agent·vibecoding
dong_junshuai11 小时前
每天一个开源项目#99 OpenResearch:2.2K星的本地研究Agent工作台
开源·github·agent
花椒技术11 小时前
把 AI 视频接进直播:提前生成、按次生成、持续生成怎么选?
agent·音视频开发
用户80825986668711 小时前
用 LangGraph 重写自己手写的 Agent:框架替你解决了什么,什么它不管
agent
能不能静下心来看11 小时前
从 RAG 到 Agent:跑通两个 Demo 后,我终于分清了这两个词
agent
ZGi.ai11 小时前
知识库权限变了,怎样避免答错?
agent·权限管理·知识库·工作流·zgi·客服运营
大模型真好玩12 小时前
DeepSeek Harness 入门很简单(四)——DeepSeek Harness接入插件
人工智能·agent·deepseek
掰头战士12 小时前
MCP、Skill、Plugin,都是给agent拓展能力,到底有何区别?
typescript·llm·agent
嘟嘟嘟952713 小时前
AI Agent 的边缘困境
人工智能·架构·agent
HYDtomako13 小时前
Agent checkpoint设计
ai·agent·checkpoint·claude code