# 流式输出(SSE)接入 > 使用 Server-Sent Events 实现 AI 流式响应,提升用户体验。 ## 🌊 什么是流式输出? 流式输出(Streaming)是一种数据传输方式,服务器将数据「分块」发送给客户端,而不是等待全部完成后再返回。 ### 对比 | 方式 | 等待时间 | 体验 | 适用场景 | |------|----------|------|----------| | 普通请求 | 等待完整响应 | 一般 | 简单、快速的请求 | | 流式输出 | 实时看到生成过程 | ⭐ 优秀 | AI 生成、长文本 | ## 🚀 开始使用 ### 前端:接收流式响应 ```typescript import { WebsopyClient } from '@websopy/sdk' const client = new WebsopyClient({ apiKey: process.env.WEBSOPY_API_KEY }) async function streamChat() { const stream = await client.ai.chatStream({ messages: [ { role: 'user', content: '写一篇关于 AI 的文章' } ] }) // 方法 1:遍历数据块 for await (const chunk of stream) { if (chunk.type === 'content') { process.stdout.write(chunk.content) } } // 或方法 2:事件监听 // stream.on('data', (chunk) => { ... }) // stream.on('end', () => { ... }) } ``` ### 响应数据结构 ```typescript interface StreamChunk { type: 'content' | 'tool_call' | 'done' | 'error' content?: string tool?: { name: string arguments: string } usage?: { promptTokens: number completionTokens: number } } // 示例数据块 { type: 'content', content: 'AI' } { type: 'content', content: ' 正在' } { type: 'content', content: '改变' } { type: 'content', content: '世界...' } { type: 'done', usage: { promptTokens: 20, completionTokens: 150 } } ``` ## 🌐 原生 SSE 实现 如果不用 SDK,直接使用 Fetch API: ### GET 请求 ```typescript async function nativeStream() { const response = await fetch( 'https://api.websopy.com/v1/ai/chat?message=Hello', { headers: { 'Authorization': `Bearer ${apiKey}`, 'Accept': 'text/event-stream' } } ) const reader = response.body.getReader() const decoder = new TextDecoder() while (true) { const { done, value } = await reader.read() if (done) break const chunk = decoder.decode(value) // 解析 SSE 数据 const lines = chunk.split('\n') for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)) console.log('收到:', data) } } } } ``` ### POST 请求 ```typescript async function streamPost() { const response = await fetch( 'https://api.websopy.com/v1/ai/chat/stream', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, body: JSON.stringify({ messages: [ { role: 'user', content: '解释量子计算' } ], model: 'gpt-4', stream: true }) } ) // 处理流... } ``` ## 🎨 前端组件示例 ### React Hook ```typescript import { useState } from 'react' function useStreamingChat() { const [messages, setMessages] = useState([]) const [streaming, setStreaming] = useState(false) const [currentContent, setCurrentContent] = useState('') const sendMessage = async (content) => { setStreaming(true) setCurrentContent('') // 添加用户消息 setMessages(prev => [...prev, { role: 'user', content }]) const stream = await client.ai.chatStream({ messages: [...messages, { role: 'user', content }] }) for await (const chunk of stream) { if (chunk.type === 'content') { setCurrentContent(prev => prev + chunk.content) } } // 保存完整消息 setMessages(prev => [...prev, { role: 'assistant', content: currentContent }]) setStreaming(false) } return { messages, streaming, currentContent, sendMessage } } ``` ### Vue Composition API ```typescript import { ref } from 'vue' export function useStreamingChat() { const messages = ref([]) const streaming = ref(false) const currentContent = ref('') const sendMessage = async (content) => { streaming.value = true currentContent.value = '' messages.value.push({ role: 'user', content }) const stream = await client.ai.chatStream({ messages: messages.value }) for await (const chunk of stream) { if (chunk.type === 'content') { currentContent.value += chunk.content } } messages.value.push({ role: 'assistant', content: currentContent.value }) streaming.value = false } return { messages, streaming, currentContent, sendMessage } } ``` ## ⚙️ 服务端 SSE 配置 ### Node.js Express ```typescript import express from 'express' app.post('/api/chat/stream', async (req, res) => { // 设置 SSE 响应头 res.setHeader('Content-Type', 'text/event-stream') res.setHeader('Cache-Control', 'no-cache') res.setHeader('Connection', 'keep-alive') // 保持连接 res.flushHeaders() try { const stream = await client.ai.chatStream(req.body) for await (const chunk of stream) { res.write(`data: ${JSON.stringify(chunk)}\n\n`) } res.write('data: [DONE]\n\n') } catch (error) { res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`) } res.end() }) ``` ### Next.js API Route ```typescript // app/api/chat/route.ts export async function POST(req: Request) { const encoder = new TextEncoder() const stream = new ReadableStream({ async start(controller) { try { const stream = await client.ai.chatStream(await req.json()) for await (const chunk of stream) { controller.enqueue( encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`) ) } controller.enqueue(encoder.encode('data: [DONE]\n\n')) } catch (error) { controller.enqueue( encoder.encode(`data: ${JSON.stringify({ error: error.message })}\n\n`) ) } controller.close() } }) return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' } }) } ``` ## ❓ 常见问题 ### Q: 流式中断怎么办? ```typescript async function streamWithReconnect() { const maxRetries = 3 let retries = 0 while (retries < maxRetries) { try { const stream = await client.ai.chatStream({...}) return stream } catch (error) { retries++ await sleep(1000 * retries) // 指数退避 } } } ``` ### Q: 如何在流式过程中取消请求? ```typescript const controller = new AbortController() // 发送请求 const stream = await client.ai.chatStream({ messages: [...], signal: controller.signal }) // 取消请求 controller.abort() ``` ### Q: SSE 和 WebSocket 区别? | 特性 | SSE | WebSocket | |------|-----|-----------| | 方向 | 单向(服务端→客户端) | 双向 | | 协议 | HTTP | ws:// | | 重连 | 自动 | 需手动处理 | | 复杂度 | 简单 | 复杂 | | 适用 | AI 流式输出 | 实时聊天 | --- **上一步:** [API Key 创建与管理](./apikey.md) **下一步:** [Webhook 事件接入](./webhook.md)