ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 Node.js HTTP Server 集成 AI SDK:实现 streamText 流式文本与自定义 UI 消息流

用 Node.js HTTP Server 集成 AI SDK:实现 streamText 流式文本与自定义 UI 消息流 用 Node.js HTTP Server 集成 AI SDK实现 streamText 流式文本与自定义 UI 消息流【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai导读本指南以 examples/node-http-server 示例为核心演示如何在不依赖 Next.js、Express 等上层框架的情况下仅用 Node.js 原生http模块配合 Vercel AI SDK 构建流式 AI 接口。你将掌握两条核心链路一是用streamText生成文本并通过toUIMessageStream输出 UI 消息流二是用createUIMessageStream手动编排自定义数据分片data part与模型输出混合推送最终通过pipeUIMessageStreamToResponse以 Server-Sent EventsSSE形式写回 HTTP 响应。读完本指南你可以直接把该模式移植到自己的纯 Node 服务、微服务或内部工具中用一条 HTTP 接口同时承担流式文本、工具调用与自定义 UI 数据推送。示例概览一个极简的纯 Node 流式服务该示例的核心诉求是展示 AI SDK 的流式能力可以脱离前端框架独立使用。服务端代码集中在 src/server.ts整个服务仅用 Node 内置的createServer实现无需 Express 或 Fastify方法路径说明POST/用streamText生成文本回复并作为 UI 消息流输出POST/stream-data用createUIMessageStream混入自定义data-custom分片再合并streamText输出在package.json中可以看到这个示例的依赖组合非常精简ai与ai-sdk/openai是工作区内的包workspace:*dotenv负责加载环境变量zod预留给工具参数 schema 校验开发时用tsx直接运行 TypeScriptdev: tsx src/server.ts。也就是说这个示例不依赖任何 Web 框架也不依赖浏览器端代码是理解 AI SDK 底层流式传输协议的理想入口。环境准备与启动安装依赖在 AI SDK 仓库根目录安装依赖示例通过 pnpm workspace 引用ai与ai-sdk/openai源码因此必须在仓库根目录安装pnpm install配置 OpenAI API Key在 examples/node-http-server 目录下创建.env文件可参考.env.example写入OPENAI_API_KEYYOUR_OPENAI_API_KEYsrc/server.ts 顶部通过import dotenv/config加载该文件ai-sdk/openai会自动读取OPENAI_API_KEY。启动服务从示例目录启动pnpm dev服务默认监听http://localhost:8080。两条测试命令分别是curl -X POST http://localhost:8080/ curl -X POST http://localhost:8080/stream-data注意示例用的是 OpenAI 的gpt-4o模型server.ts如果改用其他模型或供应商只需要替换openai(gpt-4o)这一处即可整个流式链路保持不变——这正是 AI SDK 模型供应商抽象的价值所在。端点一POST /—— 用 streamText 输出流式文本这是最简路径把streamText的流转换成 UI 消息流再写回 HTTP 响应。case /: { const result streamText({ model: openai(gpt-4o), prompt: Invent a new holiday and describe its traditions., }); pipeUIMessageStreamToResponse({ response: res, stream: toUIMessageStream({ stream: result.stream }), }); break; }为什么需要 toUIMessageStreamstreamText返回的result.stream是TextStreamPartTOOLS类型的块流包含text-delta、tool-call、finish等分片而pipeUIMessageStreamToResponse期望的是 UI 消息块UIMessageChunk流。toUIMessageStream的职责就是完成这个转换它通过toUIMessageChunk把每个TextStreamPart映射为对应的 UI 消息块同时负责注入响应消息 ID、处理start/finish/abort/error等生命周期事件见 to-ui-message-stream.ts 中toUIMessageStream的实现。默认情况下toUIMessageStream会发送start与finish块对应sendStart、sendFinish选项并默认开启推理文本sendReasoning、关闭来源引用sendSources。如果模型端返回错误默认的onError会返回An error occurred.这类脱敏文案避免把服务端错误细节泄露给客户端。SSE 传输细节pipeUIMessageStreamToResponse实现见 pipe-ui-message-stream-to-response.ts会把 UI 消息块流经JsonToSseTransformStream转成 Server-Sent Events 格式再配合UI_MESSAGE_STREAM_HEADERS设置响应头最后通过TextEncoderStream写入 Node 的ServerResponse。因此客户端看到的是一个持续推送的 SSE 流而不是一次性 JSON 响应。端点二POST /stream-data—— 自定义数据与模型输出混流第二个端点展示了createUIMessageStream的编排能力在同一个流里先由你主动写入自定义数据块再把streamText的输出合并进来case /stream-data: { const stream createUIMessageStream({ execute: ({ writer }) { // write some custom data writer.write({ type: start }); writer.write({ type: data-custom, data: { custom: Hello, world!, }, }); const result streamText({ model: openai(gpt-4o), prompt: Invent a new holiday and describe its traditions., }); writer.merge( toUIMessageStream({ stream: result.stream, sendStart: false, // 因为我们已经手动写了 start 块 onError: error { // 错误消息默认会被脱敏如需把真实错误暴露给客户端可在此返回 return error instanceof Error ? error.message : String(error); }, }), ); }, }); pipeUIMessageStreamToResponse({ stream, response: res }); break; }writer.write 与 writer.merge 的分工从 create-ui-message-stream.ts 的实现可以看到createUIMessageStream返回一个ReadableStreamUIMessageChunkexecute回调接收的writer提供两个核心方法write(part)把单个 UI 消息块同步入队safeEnqueue用于手动写入start、data-custom等自定义分片merge(streamArg)异步读取另一个ReadableStream的所有块并依次入队内部用getReader()循环读取用于把toUIMessageStream的输出拼接到当前流中且所有子流的 Promise 都会被收集到ongoingStreamPromises直到全部完成才关闭主流。这里有一个关键细节因为我们在前面手动写了{ type: start }合并toUIMessageStream时必须传sendStart: false否则客户端会收到两个start块。反过来如果不手动写start就保持sendStart默认的true。同理如果你接管了错误处理onError返回真实错误消息就可以让客户端看到具体的失败原因而示例注释也明确提醒出于安全考虑AI SDK 默认会掩盖错误消息。可用的 UI 消息块类型在混流场景下知道有哪些块类型才能写出正确的自定义数据。从 ui-message-chunks.ts 的类型定义可以看到除了start、finish、abort、error、message-metadata等生命周期块外还包括文本类text-start、text-delta、text-end推理类reasoning-start、reasoning-delta、reasoning-end工具类tool-input-available、tool-input-error、tool-approval-request、tool-approval-response、tool-output-available、tool-output-error、tool-output-denied、tool-input-start、tool-input-delta来源与文件类source-url、source-document、file、reasoning-file步骤类start-step、finish-step、reset-step自定义类custom示例中手写的{ type: data-custom, data: { custom: Hello, world! } }属于自定义类块客户端拿到后既可以按约定渲染也可以忽略。这套协议与 AI SDK 的前端包ai-sdk/react、ai-sdk/vue、ai-sdk/svelte等原生兼容因此这个纯 Node 服务产出的流可以直接被 AI SDK 的 UI 组件消费。错误处理与生命周期回调createUIMessageStream与toUIMessageStream都暴露了错误处理和生命周期钩子onError把错误转换为发送给客户端的字符串。默认值为() An error occurred.防止服务端错误细节泄露源码注释明确写了这一安全考量传自定义函数即可脱敏或透传真实消息。onEnd旧名onFinish流结束时触发适合做用量统计或结果持久化。onStepEnd旧名onStepFinish多步 Agent 每次步骤结束时触发适合持久化中间消息。originalMessages传入历史消息后进入持久化模式AI SDK 会为响应消息生成并附带消息 IDgenerateId可自定义 ID 生成器。在pipeUIMessageStreamToResponse一侧还可以传入status、statusText、headers覆盖 HTTP 状态与响应头以及consumeSseStream把 SSE 流 tee 出一份副本供独立消费比如转发给日志系统或另一个下游且不会阻塞主响应。底层实现串联从 streamText 到 HTTP 响应把两个端点的调用链放在一起可以看清整个流式架构streamText({ model, prompt })发起模型调用返回包含streamTextStreamPart流、text、textStream等属性的结果对象toUIMessageStream({ stream, ... })把TextStreamPart流转成 UI 消息块流注入响应消息 ID 并处理 start/finish/error 事件可选createUIMessageStream在最外层用writer.write混入手写的自定义块用writer.merge合并上一步的流pipeUIMessageStreamToResponse({ stream, response })把 UI 消息块流经 JSON→SSE 转换、设置UI_MESSAGE_STREAM_HEADERS响应头最终写入 NodeServerResponse。其中createUIMessageStream的实现细节值得注意它用waitForStreams循环消费ongoingStreamPromises确保即使execute已经返回只要还有打开的合并流主流就不会提前关闭——这为回调内动态追加新流例如多轮工具调用留出了空间。相关测试覆盖在 create-ui-message-stream.test.ts 与 pipe-ui-message-stream-to-response.test.ts 中遇到边界行为时可对照查阅。扩展如何把它改造成你自己的服务切换模型供应商只需替换openai(gpt-4o)为其他供应商的模型实例例如anthropic(claude-...)、google(gemini-...)或任意 OpenAI 兼容端点。toUIMessageStream、createUIMessageStream与pipeUIMessageStreamToResponse的签名与模型无关流式协议完全一致。增加工具调用streamText支持tools参数如需在 UI 消息流中透出工具调用信息可以给toUIMessageStream传入对应的tools让转换层把tool-input、tool-output等分片正确映射为 UI 消息块。从单一路径扩展为多路径路由当前示例用switch (req.url)做路由。生产环境可以在此基础上增加鉴权、请求体解析如读取req的 body 来透传用户 prompt、超时控制与错误兜底。注意在流未结束时一旦出错应调用res.destroy()或触发流的错误控制器避免客户端一直等待。限制说明本示例默认绑定localhost:8080如需对外暴露请显式监听0.0.0.0并做好安全防护SSE 协议要求响应头包含正确的Content-Type: text/event-stream等头pipeUIMessageStreamToResponse已通过UI_MESSAGE_STREAM_HEADERS处理无需手工设置示例依赖.env中的OPENAI_API_KEY请勿把密钥提交到版本库。小结这个示例的价值在于AI SDK 的流式能力并不绑定特定框架。通过streamText生成、toUIMessageStream转换、createUIMessageStream编排、pipeUIMessageStreamToResponse输出这四件套你可以在原生 Node HTTP 服务器上直接构建支持流式文本、自定义数据混流、工具调用与多步 Agent 的 AI 接口。想要进一步了解 UI 消息流的完整块协议与持久化选项可以继续阅读 packages/ai/src/ui-message-stream 下的源码与测试想对比不同框架的用法仓库中的 examples/express、examples/fastify、examples/hono 与 examples/node-http-server 提供了多种等价实现。【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表