如何将结构化输出流式传输到客户端
本指南将引导您了解我们如何使用此目录中的React 服务器组件将代理数据流式传输到客户端。 此文档中的代码取自此目录中的page.tsx
和action.ts
文件。要查看完整、无间断的代码,请点击此处查看 actions 文件和此处查看客户端文件.
先决条件
本指南假设您熟悉以下概念
设置
首先,安装必要的 LangChain 和 AI SDK 包
- npm
- Yarn
- pnpm
npm install @langchain/openai @langchain/core ai zod zod-to-json-schema
yarn add @langchain/openai @langchain/core ai zod zod-to-json-schema
pnpm add @langchain/openai @langchain/core ai zod zod-to-json-schema
接下来,我们将创建服务器文件。它将包含用于进行工具调用并将数据发送回客户端的所有逻辑。
从添加必要的导入和"use server"
指令开始
"use server";
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { createStreamableValue } from "ai/rsc";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { JsonOutputKeyToolsParser } from "@langchain/core/output_parsers/openai_tools";
之后,我们将定义工具模式。对于此示例,我们将使用简单的演示天气模式
const Weather = z
.object({
city: z.string().describe("City to search for weather"),
state: z.string().describe("State abbreviation to search for weather"),
})
.describe("Weather search parameters");
定义好模式后,我们可以实现executeTool
函数。此函数接受一个string
类型的输入,并包含工具的所有逻辑以及将数据流式传输回客户端的逻辑
export async function executeTool(
input: string,
) {
"use server";
const stream = createStreamableValue();
createStreamableValue
函数很重要,因为我们将使用它来真正将所有数据流式传输回客户端。
对于主逻辑,我们将使用异步函数将其包装起来。从定义提示和聊天模型开始
(async () => {
const prompt = ChatPromptTemplate.fromMessages([
[
"system",
`You are a helpful assistant. Use the tools provided to best assist the user.`,
],
["human", "{input}"],
]);
const llm = new ChatOpenAI({
model: "gpt-4o-2024-05-13",
temperature: 0,
});
定义好聊天模型后,我们将使用 LCEL 定义可运行链。
我们开始将之前定义的weather
工具绑定到模型
const modelWithTools = llm.bind({
tools: [
{
type: "function" as const,
function: {
name: "get_weather",
description: Weather.description,
parameters: zodToJsonSchema(Weather),
},
},
],
});
接下来,我们将使用 LCEL 将每个组件连接起来,从提示开始,然后是带工具的模型,最后是输出解析器
const chain = prompt.pipe(modelWithTools).pipe(
new JsonOutputKeyToolsParser<z.infer<typeof Weather>>({
keyName: "get_weather",
zodSchema: Weather,
})
);
最后,我们将在链上调用.stream
,与流式代理示例类似,我们将遍历流并对数据进行字符串化和解析,然后再更新流值
const streamResult = await chain.stream({
input,
});
for await (const item of streamResult) {
stream.update(JSON.parse(JSON.stringify(item, null, 2)));
}
stream.done();
})();
return { streamData: stream.value };
}