初始化2
This commit is contained in:
189
content/docs/api/streaming.md
Normal file
189
content/docs/api/streaming.md
Normal file
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: 流式输出(SSE)接入
|
||||
description: 使用 Server-Sent Events 实现 AI 流式响应,提升用户体验。
|
||||
category: api
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 流式输出(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应数据结构
|
||||
|
||||
```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:
|
||||
|
||||
```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)
|
||||
const lines = chunk.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = JSON.parse(line.slice(6))
|
||||
console.log('收到:', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 前端组件示例
|
||||
|
||||
### 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 }
|
||||
}
|
||||
```
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### 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 流式输出 | 实时聊天 |
|
||||
Reference in New Issue
Block a user