AI流式回复实现指南
理解AI流式回复,即模型逐字、逐词地生成并发送内容,是实现“一点点”接受和展示的关键。
您目前无法实现流式传输,可能有以下几个原因和对应的解决方案:
1. AI服务提供商API未开启流式输出
原因: 大多数AI模型默认支持流式传输,但API调用时可能需要明确指定。如果您使用的API默认是“一次性”返回完整结果,那么您在后端接收到的就已经是完整内容了。
检查与解决:
- 查阅API文档: 仔细阅读您使用的AI服务提供商(如OpenAI、Gemini等)的API文档。查找是否有
stream=true或类似参数的说明。通常,流式接口会与非流式接口分开,或者通过一个参数控制。 - 示例 (伪代码):
{ "model": "gemini-pro", "messages": [...], "stream": true // 这个参数通常是关键 }
2. Go后端代码没有处理或转发流式数据
即使AI API支持流式,您的Go后端也可能在接收到所有数据后才进行处理和转发。
检查与解决:
-
作为AI API的客户端:
- 如果使用官方SDK,查找是否有支持流式返回结果的方法,这些方法通常会返回一个迭代器或回调函数。
- 如果直接使用
net/http发送请求,AI API的流式响应体(http.Response.Body)会是一个io.Reader。您需要在一个循环中持续读取这个Reader,并将读取到的数据“实时”地发送给前端。
-
作为HTTP服务器(向前端提供服务):
-
使用
http.Flusher: Go的http.ResponseWriter接口实现了http.Flusher。当您写入一部分数据到ResponseWriter后,调用Flush()方法可以强制将缓冲区内容发送到客户端。这对于HTTP长连接非常有用。package main import ( "fmt" "net/http" "time" ) func streamHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) return } for i := 0; i < 5; i++ { fmt.Fprintf(w, "这是第 %d 段数据...
-
", i+1)
flusher.Flush() // 每次写入后立即刷新到客户端
time.Sleep(time.Second)
}
fmt.Fprintln(w, "数据发送完毕。")
}
func main() {
http.HandleFunc("/stream", streamHandler)
http.ListenAndServe(":8080", nil)
}
```
* **使用 Server-Sent Events (SSE):** SSE 是专门为服务器向客户端单向流式推送数据设计的协议。它比简单的 `http.Flusher` 更健壮,并且有更好的事件处理机制。只需将 `Content-Type` 设置为 `text/event-stream`,并按照SSE规范发送数据即可。
3. 前端(浏览器)没有处理流式数据
即使后端正确发送了流式数据,如果前端代码只是等待完整的HTTP响应才开始渲染,那依然会感觉是“一次性”的。
检查与解决:
-
对于SSE: 使用
EventSourceAPI。const eventSource = new EventSource('/stream'); // 替换为您的后端流式接口 eventSource.onmessage = function(event) { console.log('Received:', event.data); // 将 event.data 追加到聊天窗口中 document.getElementById('chat-output').innerText += event.data + '
';
};
eventSource.onerror = function(err) {
console.error('EventSource failed:', err);
eventSource.close();
};
```
-
对于普通的HTTP流(非SSE): 使用
fetchAPI 结合ReadableStream。async function fetchStream() { const response = await fetch('/stream'); // 替换为您的后端流式接口 const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) { console.log('Stream complete'); break; } const chunk = decoder.decode(value, { stream: true }); console.log('Received chunk:', chunk); // 将 chunk 追加到聊天窗口中 document.getElementById('chat-output').innerText += chunk; } } fetchStream(); -
WebSocket: 虽然WebSocket也能实现双向流,但对于AI的单向响应流来说,SSE或基于
ReadableStream的fetch更加轻量和适合。
总结: 实现流式传输需要“端到端”的支持。首先确保您的AI服务提供商API启用了流式,然后Go后端要能实时接收并转发这些数据(使用 Flusher 或 SSE),最后前端也要能实时处理和显示这些分块数据(使用 EventSource 或 ReadableStream)。