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],需要二次解构。
相关推荐
阿里云云原生3 小时前
告别功能堆叠,迎接“对话即服务”:FinXScope AI 原生六层架构设计详解
agent
昵称好难啊5 小时前
ClaudeCode: 怎么规划任务
llm·agent
京东云开发者5 小时前
ICML 2026|NaviAgent:面向 Oxygen 智能体的可扩展工具编排
agent·ai编程
玉鸯5 小时前
让 Agent 面向用户:AG-UI 协议构建 Agent 前端
前端·python·agent
文心快码BaiduComate5 小时前
不限额度!「文心快码测试版」限免体验来了
agent·ai编程·文心快码
人生百态,人生如梦6 小时前
每日论文解读 (8.3) 1——DeepResearch Agent System:稀疏激活架构驱动的自主深度研究Agent
架构·llm·agent·deepresearch
牧艺6 小时前
别让 Agent 猜需求:前端用「一页 Spec」把返工砍掉一半
人工智能·agent·vibecoding
ClouGence7 小时前
无需 API 配置,DeepSeek-V4-Flash 正式版落地使用指南
agent·ai编程·deepseek
关于不上作者榜就原神启动那件事7 小时前
从 MDC 到 Agent:我手搓的文档路由协议,在 Spring AI Alibaba 里找到了正式实现
java·人工智能·spring·ai·agent