对于新应用,我们推荐使用事件流——这是 LangGraph v1.2 中引入的类型化投影 API。事件流会为每种投影(messages、values、subgraphs、output)提供单独的迭代器,因此你可以独立消费它们,而不必根据 stream_mode 块进行分支处理。
本页介绍 LangGraph 的流模式 API。它通过 updatesvaluesmessagescustomcheckpointstasksdebug 等流模式暴露图执行过程。当你需要直接访问图运行时事件或特定流模式输出时,可以使用它。

开始使用

基本用法

LangGraph 图暴露 stream 方法,用于以迭代器形式产出流式输出。
for await (const chunk of await graph.stream(inputs, {
  streamMode: "updates",
})) {
  console.log(chunk);
}
使用 LangSmith 调试流式事件、逐 token 检查 LLM 输出,并监控延迟。按照追踪快速入门完成设置。

流模式

将以下一种或多种流模式作为列表传给 stream 方法:
模式描述
values每一步之后的完整状态。
updates每一步之后的状态更新。同一步中的多个更新会分别以流式方式输出。
messages来自 LLM 调用的 (LLM token, metadata) 二元组。
custom由节点通过 writer 配置参数发出的自定义数据。
tools工具调用生命周期事件(on_tool_starton_tool_eventon_tool_endon_tool_error)。
debug图执行期间的所有可用信息。

图状态

使用 updatesvalues 流模式,在图执行时流式输出图的状态。
  • updates 会在图的每一步之后流式输出状态的更新
  • values 会在图的每一步之后流式输出状态的完整值
import { StateGraph, StateSchema, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";

const State = new StateSchema({
  topic: z.string(),
  joke: z.string(),
});

const graph = new StateGraph(State)
  .addNode("refineTopic", (state) => {
    return { topic: state.topic + " and cats" };
  })
  .addNode("generateJoke", (state) => {
    return { joke: `This is a joke about ${state.topic}` };
  })
  .addEdge(START, "refineTopic")
  .addEdge("refineTopic", "generateJoke")
  .addEdge("generateJoke", END)
  .compile();
使用它仅流式输出每一步之后节点返回的状态更新。流式输出中包含节点名称以及对应更新。
for await (const chunk of await graph.stream(
  { topic: "ice cream" },
  { streamMode: "updates" }
)) {
  for (const [nodeName, state] of Object.entries(chunk)) {
    console.log(`Node ${nodeName} updated:`, state);
  }
}

LLM token

使用 messages 流模式,从图中的任意部分(包括节点、工具、子图或任务)逐 token 流式输出大语言模型(LLM)的输出。 messages 模式的流式输出是一个元组 [message_chunk, metadata],其中:
  • message_chunk:来自 LLM 的 token 或消息片段。
  • metadata:包含图节点和 LLM 调用详细信息的字典。
如果你的 LLM 不能作为 LangChain 集成使用,也可以改用 custom 模式流式输出它的结果。详情请参见与任意 LLM 一起使用
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, StateSchema, GraphNode, START } from "@langchain/langgraph";
import * as z from "zod";

const MyState = new StateSchema({
  topic: z.string(),
  joke: z.string().default(""),
});

const model = new ChatOpenAI({ model: "gpt-5.4-mini" });

const callModel: GraphNode<typeof MyState> = async (state) => {
  // 调用 LLM 生成一个关于某个主题的笑话
  // 注意,即使 LLM 使用 .invoke 而不是 .stream 运行,也会发出消息事件
  const modelResponse = await model.invoke([
    { role: "user", content: `Generate a joke about ${state.topic}` },
  ]);
  return { joke: modelResponse.content };
};

const graph = new StateGraph(MyState)
  .addNode("callModel", callModel)
  .addEdge(START, "callModel")
  .compile();

// "messages" 流模式返回一个由元组 [messageChunk, metadata] 组成的迭代器
// 其中 messageChunk 是 LLM 流式输出的 token,metadata 是一个字典
// 包含调用 LLM 的图节点以及其他信息
for await (const [messageChunk, metadata] of await graph.stream(
  { topic: "ice cream" },
  { streamMode: "messages" }
)) {
  if (messageChunk.content) {
    console.log(messageChunk.content + "|");
  }
}

按 LLM 调用过滤

你可以为 LLM 调用关联 tags,以便按 LLM 调用过滤流式 token。
import { ChatOpenAI } from "@langchain/openai";

// model1 标记为 "joke"
const model1 = new ChatOpenAI({
  model: "gpt-5.4-mini",
  tags: ['joke']
});
// model2 标记为 "poem"
const model2 = new ChatOpenAI({
  model: "gpt-5.4-mini",
  tags: ['poem']
});

const graph = // ... 定义一个使用这些 LLM 的图

// streamMode 设置为 "messages",用于流式输出 LLM token
// metadata 包含有关 LLM 调用的信息,包括标签
for await (const [msg, metadata] of await graph.stream(
  { topic: "cats" },
  { streamMode: "messages" }
)) {
  // 按 metadata 中的 tags 字段过滤流式 token,只包含
  // 带有 "joke" 标签的 LLM 调用所产生的 token
  if (metadata.tags?.includes("joke")) {
    console.log(msg.content + "|");
  }
}
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, StateSchema, GraphNode, START } from "@langchain/langgraph";
import * as z from "zod";

// jokeModel 标记为 "joke"
const jokeModel = new ChatOpenAI({
  model: "gpt-5.4-mini",
  tags: ["joke"]
});
// poemModel 标记为 "poem"
const poemModel = new ChatOpenAI({
  model: "gpt-5.4-mini",
  tags: ["poem"]
});

const State = new StateSchema({
  topic: z.string(),
  joke: z.string(),
  poem: z.string(),
});

const callModel: GraphNode<typeof State> = async (state) => {
  const topic = state.topic;
  console.log("Writing joke...");

  const jokeResponse = await jokeModel.invoke([
    { role: "user", content: `Write a joke about ${topic}` }
  ]);

  console.log("\n\nWriting poem...");
  const poemResponse = await poemModel.invoke([
    { role: "user", content: `Write a short poem about ${topic}` }
  ]);

  return {
    joke: jokeResponse.content,
    poem: poemResponse.content
  };
};

const graph = new StateGraph(State)
  .addNode("callModel", callModel)
  .addEdge(START, "callModel")
  .compile();

// streamMode 设置为 "messages",用于流式输出 LLM token
// metadata 包含有关 LLM 调用的信息,包括标签
for await (const [msg, metadata] of await graph.stream(
  { topic: "cats" },
  { streamMode: "messages" }
)) {
  // 按 metadata 中的 tags 字段过滤流式 token,只包含
  // 带有 "joke" 标签的 LLM 调用所产生的 token
  if (metadata.tags?.includes("joke")) {
    console.log(msg.content + "|");
  }
}

从流中省略消息

使用 nostream 标签可以将 LLM 输出完全排除在流之外。带有 nostream 标签的调用仍会运行并产生输出;只是它们的 token 不会在 messages 模式中发出。 这在以下场景中很有用:
  • 你需要 LLM 输出用于内部处理(例如结构化输出),但不想将其流式传给客户端
  • 你通过其他通道流式传输相同内容(例如自定义 UI 消息),并希望避免在 messages 流中出现重复输出
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, START } from "@langchain/langgraph";
import * as z from "zod";

const streamModel = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
const internalModel = new ChatAnthropic({
  model: "claude-haiku-4-5-20251001",
}).withConfig({
  tags: ["nostream"],
});

const State = new StateSchema({
  topic: z.string(),
  answer: z.string().optional(),
  notes: z.string().optional(),
});

const writeAnswer = async (state: typeof State.State) => {
  const r = await streamModel.invoke([
    { role: "user", content: `Reply briefly about ${state.topic}` },
  ]);
  return { answer: r.content };
};

const internalNotes = async (state: typeof State.State) => {
  // Tokens from this model are omitted from streamMode: "messages" because of nostream
  const r = await internalModel.invoke([
    { role: "user", content: `Private notes on ${state.topic}` },
  ]);
  return { notes: r.content };
};

const graph = new StateGraph(State)
  .addNode("writeAnswer", writeAnswer)
  .addNode("internal_notes", internalNotes)
  .addEdge(START, "writeAnswer")
  .addEdge("writeAnswer", "internal_notes")
  .compile();

const stream = await graph.stream(
  { topic: "AI", answer: "", notes: "" },
  { streamMode: "messages" },
);

按节点过滤

如果只想流式输出特定节点的 token,请使用 stream_mode="messages",并按流式元数据中的 langgraph_node 字段过滤输出:
// "messages" 流模式返回一个 [messageChunk, metadata] 元组
// 其中 messageChunk 是 LLM 流式输出的 token,metadata 是一个字典
// 包含调用 LLM 的图节点以及其他信息
for await (const [msg, metadata] of await graph.stream(
  inputs,
  { streamMode: "messages" }
)) {
  // 按 metadata 中的 langgraph_node 字段过滤流式 token
  // 只包含来自指定节点的 token
  if (msg.content && metadata.langgraph_node === "some_node_name") {
    // ...
  }
}
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, StateSchema, GraphNode, START } from "@langchain/langgraph";
import * as z from "zod";

const model = new ChatOpenAI({ model: "gpt-5.4-mini" });

const State = new StateSchema({
  topic: z.string(),
  joke: z.string(),
  poem: z.string(),
});

const writeJoke: GraphNode<typeof State> = async (state) => {
  const topic = state.topic;
  const jokeResponse = await model.invoke([
    { role: "user", content: `Write a joke about ${topic}` }
  ]);
  return { joke: jokeResponse.content };
};

const writePoem: GraphNode<typeof State> = async (state) => {
  const topic = state.topic;
  const poemResponse = await model.invoke([
    { role: "user", content: `Write a short poem about ${topic}` }
  ]);
  return { poem: poemResponse.content };
};

const graph = new StateGraph(State)
  .addNode("writeJoke", writeJoke)
  .addNode("writePoem", writePoem)
  // 并发编写笑话和诗歌
  .addEdge(START, "writeJoke")
  .addEdge(START, "writePoem")
  .compile();

// "messages" 流模式返回一个 [messageChunk, metadata] 元组
// 其中 messageChunk 是 LLM 流式输出的 token,metadata 是一个字典
// 包含调用 LLM 的图节点以及其他信息
for await (const [msg, metadata] of await graph.stream(
  { topic: "cats" },
  { streamMode: "messages" }
)) {
  // 按 metadata 中的 langgraph_node 字段过滤流式 token
  // 只包含来自 writePoem 节点的 token
  if (msg.content && metadata.langgraph_node === "writePoem") {
    console.log(msg.content + "|");
  }
}

自定义数据

若要从 LangGraph 节点或工具内部发送用户自定义数据,请按以下步骤操作:
  1. 使用 LangGraphRunnableConfig 中的 writer 参数发出自定义数据。
  2. 调用 .stream() 时设置 streamMode: "custom",即可在流中获取自定义数据。你可以组合多个模式(例如 ["updates", "custom"]),但至少一个必须是 "custom"
import { StateGraph, StateSchema, GraphNode, START, LangGraphRunnableConfig } from "@langchain/langgraph";
import * as z from "zod";

const State = new StateSchema({
  query: z.string(),
  answer: z.string(),
});

const node: GraphNode<typeof State> = async (state, config) => {
  // 使用 writer 发出一个自定义键值对(例如进度更新)
  config.writer({ custom_key: "Generating custom data inside node" });
  return { answer: "some data" };
};

const graph = new StateGraph(State)
  .addNode("node", node)
  .addEdge(START, "node")
  .compile();

const inputs = { query: "example" };

// 设置 streamMode: "custom",以便在流中接收自定义数据
for await (const chunk of await graph.stream(inputs, { streamMode: "custom" })) {
  console.log(chunk);
}

工具进度

使用 tools 流模式可以接收工具执行的实时生命周期事件。这对于在工具运行时在 UI 中显示进度指示器、部分结果和错误状态非常有用。 tools 流模式会发出四种事件类型:
事件发生时机载荷
on_tool_start工具调用开始nameinputtoolCallId
on_tool_event工具产出中间数据namedatatoolCallId
on_tool_end工具返回最终结果nameoutputtoolCallId
on_tool_error工具抛出错误nameerrortoolCallId

定义可流式输出进度的工具

若要发出 on_tool_event 事件,请将工具函数定义为异步生成器async function*)。每次 yield 都会向流发送中间数据,而 return 值会作为工具的最终结果。
import { tool } from "@langchain/core/tools";
import { z } from "zod/v4";

const searchFlights = tool(
  async function* (input) {
    const airlines = ["United", "Delta", "American", "JetBlue"];
    const completed: string[] = [];

    for (let i = 0; i < airlines.length; i++) {
      await new Promise((r) => setTimeout(r, 500));
      completed.push(airlines[i]);

      // 每次 yield 都会向流发出一个 on_tool_event
      yield {
        message: `Searching ${airlines[i]}...`,
        progress: (i + 1) / airlines.length,
        completed,
      };
    }

    // return 值会成为工具结果(ToolMessage.content)
    return JSON.stringify({
      flights: [
        { airline: "United", price: 450, duration: "5h 30m" },
        { airline: "Delta", price: 520, duration: "5h 15m" },
      ],
    });
  },
  {
    name: "search_flights",
    description: "Search for available flights to a destination.",
    schema: z.object({
      destination: z.string(),
      date: z.string(),
    }),
  }
);
返回 Promise 的现有工具完全兼容。它们会发出 on_tool_starton_tool_end 事件,但不会发出 on_tool_event 事件。

在服务器端消费工具事件

streamMode: ["tools"](或与其他模式组合)传给 graph.stream()
for await (const [mode, chunk] of await graph.stream(
  { messages: [{ role: "user", content: "Find flights to Tokyo" }] },
  { streamMode: ["updates", "tools"] }
)) {
  if (mode === "tools") {
    switch (chunk.event) {
      case "on_tool_start":
        console.log(`Tool started: ${chunk.name}`, chunk.input);
        break;
      case "on_tool_event":
        console.log(`Tool progress: ${chunk.name}`, chunk.data);
        break;
      case "on_tool_end":
        console.log(`Tool finished: ${chunk.name}`, chunk.output);
        break;
      case "on_tool_error":
        console.error(`Tool failed: ${chunk.name}`, chunk.error);
        break;
    }
  }
}

在 React 中通过 useStream 使用工具进度

当你的流模式中包含 "tools" 时,来自 @langchain/langgraph-sdk/reactuseStream hook 会暴露一个 toolProgress 数组。每个条目都是一个 ToolProgress 对象,用于跟踪正在运行的工具的当前状态:
字段描述
name工具名称
state当前生命周期状态:"starting""running""completed""error"
toolCallId来自 LLM 的工具调用 ID
input工具的输入参数
data来自 on_tool_event 的最新 yield 数据
result最终结果,在 on_tool_end 时设置
error错误,在 on_tool_error 时设置
import { useStream } from "@langchain/langgraph-sdk/react";

function Chat() {
  const stream = useStream({
    assistantId: "my-agent",
    streamMode: ["values", "tools"],
  });

  // 过滤出正在主动运行的工具
  const activeTools = stream.toolProgress.filter(
    (t) => t.state === "starting" || t.state === "running"
  );

  return (
    <div>
      {stream.messages.map((msg) => (
        <MessageBubble key={msg.id} message={msg} />
      ))}

      {/* 为正在运行的工具显示进度卡片 */}
      {activeTools.map((tool) => (
        <ToolProgressCard
          key={tool.toolCallId ?? tool.name}
          name={tool.name}
          state={tool.state}
          data={tool.data}
        />
      ))}
    </div>
  );
}
此示例展示了一个完整的智能体,它使用异步生成器工具将搜索进度流式输出到 React UI。智能体定义:
import { tool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { createAgent } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph-checkpoint-memory";
import { z } from "zod/v4";

const searchFlights = tool(
  async function* (input) {
    const airlines = ["United", "Delta", "American", "JetBlue"];
    const completed: string[] = [];

    for (let i = 0; i < airlines.length; i++) {
      await new Promise((r) => setTimeout(r, 600));
      completed.push(`${airlines[i]}: checked`);
      yield {
        message: `Searching ${airlines[i]}...`,
        progress: (i + 1) / airlines.length,
        completed,
      };
    }

    return JSON.stringify({
      flights: [
        { airline: "United", price: 450, duration: "5h 30m" },
        { airline: "Delta", price: 520, duration: "5h 15m" },
      ],
    });
  },
  {
    name: "search_flights",
    description: "Search for available flights.",
    schema: z.object({
      destination: z.string(),
      departure_date: z.string(),
    }),
  }
);

const checkHotels = tool(
  async function* (input) {
    const hotels = ["Grand Hyatt", "Marriott", "Hilton"];
    const completed: string[] = [];

    for (let i = 0; i < hotels.length; i++) {
      await new Promise((r) => setTimeout(r, 400));
      completed.push(`${hotels[i]}: available`);
      yield {
        message: `Checking ${hotels[i]}...`,
        progress: (i + 1) / hotels.length,
        completed,
      };
    }

    return JSON.stringify({
      hotels: [
        { name: "Grand Hyatt", price: 250, rating: 4.5 },
        { name: "Marriott", price: 180, rating: 4.2 },
      ],
    });
  },
  {
    name: "check_hotels",
    description: "Check hotel availability.",
    schema: z.object({
      city: z.string(),
      check_in: z.string(),
      nights: z.number(),
    }),
  }
);

export const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-4o-mini" }),
  tools: [searchFlights, checkHotels],
  checkpointer: new MemorySaver(),
});
带进度卡片的 React 组件:
import { useStream } from "@langchain/langgraph-sdk/react";

function TravelPlanner() {
  const stream = useStream<typeof agent>({
    assistantId: "travel-agent",
    streamMode: ["values", "tools"],
  });

  const activeTools = stream.toolProgress.filter(
    (t) => t.state === "starting" || t.state === "running"
  );

  return (
    <div>
      {stream.messages.map((msg) => (
        <div key={msg.id}>{msg.content}</div>
      ))}

      {activeTools.map((tool) => {
        const data = tool.data as {
          message?: string;
          progress?: number;
          completed?: string[];
        } | undefined;

        return (
          <div key={tool.toolCallId ?? tool.name}>
            <strong>{tool.name}</strong>
            {data?.message && <p>{data.message}</p>}
            {data?.progress != null && (
              <div style={{ width: "100%", background: "#eee" }}>
                <div
                  style={{
                    width: `${data.progress * 100}%`,
                    background: "#4CAF50",
                    height: 8,
                    transition: "width 0.3s ease",
                  }}
                />
              </div>
            )}
            {data?.completed?.map((step, i) => (
              <div key={i}>&#10003; {step}</div>
            ))}
          </div>
        );
      })}
    </div>
  );
}

toolscustom 流模式对比

这两种流模式都可以展示工具进度,但用途不同:
  • tools——自动发出结构化生命周期事件(on_tool_starton_tool_eventon_tool_endon_tool_error),除了使用 async function* 之外,不需要修改工具代码。useStream hook 开箱即用地提供响应式 toolProgress 数组。
  • custom——让你可以通过 config.writer() 完全控制何时发出什么数据。当你需要不映射到工具生命周期的自由格式数据,或者想从节点(不只是工具)流式输出数据时,请使用它。

子图输出

若要将子图的输出包含在流式输出中,可以在父图的 .stream() 方法中设置 subgraphs: true。这会同时流式输出父图和所有子图的输出。 输出会以元组 [namespace, data] 的形式流式输出,其中 namespace 是一个元组,表示调用子图的节点路径,例如 ["parent_node:<task_id>", "child_node:<task_id>"]
for await (const chunk of await graph.stream(
  { foo: "foo" },
  {
    // 设置 subgraphs: true 以流式输出子图的输出
    subgraphs: true,
    streamMode: "updates",
  }
)) {
  console.log(chunk);
}
import { StateGraph, StateSchema, START } from "@langchain/langgraph";
import { z } from "zod/v4";

// 定义子图
const SubgraphState = new StateSchema({
  foo: z.string(), // 注意,此键与父图状态共享
  bar: z.string(),
});

const subgraphBuilder = new StateGraph(SubgraphState)
  .addNode("subgraphNode1", (state) => {
    return { bar: "bar" };
  })
  .addNode("subgraphNode2", (state) => {
    return { foo: state.foo + state.bar };
  })
  .addEdge(START, "subgraphNode1")
  .addEdge("subgraphNode1", "subgraphNode2");
const subgraph = subgraphBuilder.compile();

// 定义父图
const ParentState = new StateSchema({
  foo: z.string(),
});

const builder = new StateGraph(ParentState)
  .addNode("node1", (state) => {
    return { foo: "hi! " + state.foo };
  })
  .addNode("node2", subgraph)
  .addEdge(START, "node1")
  .addEdge("node1", "node2");
const graph = builder.compile();

for await (const chunk of await graph.stream(
  { foo: "foo" },
  {
    streamMode: "updates",
    // 设置 subgraphs: true 以流式输出子图的输出
    subgraphs: true,
  }
)) {
  console.log(chunk);
}
[[], {'node1': {'foo': 'hi! foo'}}]
[['node2:dfddc4ba-c3c5-6887-5012-a243b5b377c2'], {'subgraphNode1': {'bar': 'bar'}}]
[['node2:dfddc4ba-c3c5-6887-5012-a243b5b377c2'], {'subgraphNode2': {'foo': 'hi! foobar'}}]
[[], {'node2': {'foo': 'hi! foobar'}}]
注意,我们收到的不只是节点更新,还包括命名空间,它们会告诉我们当前正在从哪个图(或子图)进行流式输出。

调试

使用 debug 流模式,可以在图执行期间流式输出尽可能多的信息。流式输出中包含节点名称以及完整状态。
for await (const chunk of await graph.stream(
  { topic: "ice cream" },
  { streamMode: "debug" }
)) {
  console.log(chunk);
}

同时使用多个模式

你可以将一个数组传给 streamMode 参数,以同时流式输出多个模式。 流式输出将是 [mode, chunk] 形式的元组,其中 mode 是流模式名称,chunk 是该模式流式输出的数据。
for await (const [mode, chunk] of await graph.stream(inputs, {
  streamMode: ["updates", "custom"],
})) {
  console.log(chunk);
}

高级

与任意 LLM 一起使用

你可以使用 streamMode: "custom"任意 LLM API 流式传输数据——即使该 API 没有实现 LangChain 聊天模型接口。 这让你可以集成原始 LLM 客户端,或集成提供自身流式接口的外部服务,使 LangGraph 对自定义设置非常灵活。
import { StateGraph, GraphNode, StateSchema } from "@langchain/langgraph";
import * as z from "zod";

const State = new StateSchema({ result: z.string() });

const callArbitraryModel: GraphNode<typeof State> = async (state, config) => {
  // 调用任意模型并流式传输输出的示例节点
  // 假设你有一个会产生 chunk 的流式客户端
  // 使用你的自定义流式客户端生成 LLM token
  for await (const chunk of yourCustomStreamingClient(state.topic)) {
    // 使用写入器将自定义数据发送到流中
    config.writer({ custom_llm_chunk: chunk });
  }
  return { result: "completed" };
};

const graph = new StateGraph(State)
  .addNode("callArbitraryModel", callArbitraryModel)
  // 按需添加其他节点和边
  .compile();

// 设置 streamMode: "custom" 以在流中接收自定义数据
for await (const chunk of await graph.stream(
  { topic: "cats" },
  { streamMode: "custom" }
)) {
  // chunk 将包含从 llm 流式传输出来的自定义数据
  console.log(chunk);
}
import { StateGraph, StateSchema, MessagesValue, GraphNode, START, LangGraphRunnableConfig } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
import * as z from "zod";
import OpenAI from "openai";

const openaiClient = new OpenAI();
const modelName = "gpt-5.4-mini";

async function* streamTokens(modelName: string, messages: any[]) {
  const response = await openaiClient.chat.completions.create({
    messages,
    model: modelName,
    stream: true,
  });

  let role: string | null = null;
  for await (const chunk of response) {
    const delta = chunk.choices[0]?.delta;

    if (delta?.role) {
      role = delta.role;
    }

    if (delta?.content) {
      yield { role, content: delta.content };
    }
  }
}

// 这是我们的工具
const getItems = tool(
  async (input, config: LangGraphRunnableConfig) => {
    let response = "";
    for await (const msgChunk of streamTokens(
      modelName,
      [
        {
          role: "user",
          content: `你能告诉我在下面这个地方可能会找到哪些物品吗:'${input.place}'。请列出至少 3 个这样的物品,并用逗号分隔。同时包含每个物品的简短描述。`,
        },
      ]
    )) {
      response += msgChunk.content;
      config.writer?.(msgChunk);
    }
    return response;
  },
  {
    name: "get_items",
    description: "使用这个工具列出你被问到的某个地点中可能会找到的物品。",
    schema: z.object({
      place: z.string().describe("要查询物品的地点。"),
    }),
  }
);

const State = new StateSchema({
  messages: MessagesValue,
});

const callTool: GraphNode<typeof State> = async (state) => {
  const aiMessage = state.messages.at(-1);
  const toolCall = aiMessage.tool_calls?.at(-1);

  const functionName = toolCall?.function?.name;
  if (functionName !== "get_items") {
    throw new Error(`不支持工具 ${functionName}`);
  }

  const functionArguments = toolCall?.function?.arguments;
  const args = JSON.parse(functionArguments);

  const functionResponse = await getItems.invoke(args);
  const toolMessage = {
    tool_call_id: toolCall.id,
    role: "tool",
    name: functionName,
    content: functionResponse,
  };
  return { messages: [toolMessage] };
};

const graph = new StateGraph(State)
  // 这是调用工具的图节点
  .addNode("callTool", callTool)
  .addEdge(START, "callTool")
  .compile();
让我们使用一个包含工具调用的 AIMessage 来调用图:
const inputs = {
  messages: [
    {
      content: null,
      role: "assistant",
      tool_calls: [
        {
          id: "1",
          function: {
            arguments: '{"place":"bedroom"}',
            name: "get_items",
          },
          type: "function",
        }
      ],
    }
  ]
};

for await (const chunk of await graph.stream(
  inputs,
  { streamMode: "custom" }
)) {
  console.log(chunk.content + "|");
}

为特定聊天模型禁用流式传输

如果你的应用同时使用支持流式传输的模型和不支持流式传输的模型,你可能需要为不支持流式传输的模型显式禁用流式传输。 初始化模型时设置 streaming: false
import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({
  model: "o1-preview",
  // 设置 streaming: false 以为聊天模型禁用流式传输
  streaming: false,
});
并非所有聊天模型集成都支持 streaming 参数。如果你的模型不支持它,请改用 disableStreaming: true。该参数通过基类在所有聊天模型上都可用。