在现代前端开发中,AI智能对话功能越来越常见。实现这类功能时,关键是要让用户能够实时看到AI返回的内容,而不是等待全部生成完毕才显示。Server-Sent Events(SSE)技术为此提供了一种高效且易于实现的解决方案。
SSE是一种基于HTTP的服务器向客户端实时推送数据的技术。它与WebSocket不同,SSE是单向通信,只支持服务器向客户端发送数据。这种特性使得SSE特别适合AI对话场景,因为通常只需要服务器持续向客户端推送AI生成的文本内容。
SSE有几个重要特点:
最简单的SSE请求可以通过浏览器原生的EventSource实现:
const eventSource = new EventSource('/api/chat/stream');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
// 将内容显示在界面上
};
eventSource.onerror = (error) => {
console.error('连接出错:', error);
eventSource.close();
};
但这种方法有局限性:无法自定义请求头、只能使用GET方法、数据格式受限。因此在实际项目中,我们通常需要更灵活的解决方案。
使用fetch API配合ReadableStream可以更好地处理SSE流:
async function handleAIStream(prompt) {
const response = await fetch('/api/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your_token'
},
body: JSON.stringify({ message: prompt })
});
if (!response.ok) {
throw new Error('请求失败');
}
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 dataStr = line.slice(6).trim();
if (dataStr === '[DONE]') break;
try {
const data = JSON.parse(dataStr);
// 更新界面显示
updateUI(data.content);
} catch (e) {
console.warn('解析数据失败', e);
}
}
}
}
}
这种方法支持POST请求、自定义请求头,并且可以灵活处理各种数据格式。
为了提高代码复用性,我们可以将流式请求封装成独立函数:
export async function createAIStream(url, options) {
const {
prompt,
onMessage,
onError,
onComplete,
token
} = options;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : ''
},
body: JSON.stringify({ message: prompt })
});
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) {
onComplete?.();
break;
}
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.slice(6).trim();
if (dataStr === '[DONE]') {
onComplete?.();
return;
}
try {
const data = JSON.parse(dataStr);
onMessage?.(data);
} catch (e) {
console.warn('解析JSON失败', e);
}
}
}
}
} catch (error) {
onError?.(error);
}
}
使用这个函数时:
createAIStream('/api/chat/stream', {
prompt: '你好,请介绍SSE技术',
token: 'your_token',
onMessage: (data) => {
// 处理每条消息
appendToChat(data.content);
},
onError: (error) => {
showError(error.message);
},
onComplete: () => {
setLoading(false);
}
});
为了让AI对话体验更好,可以考虑以下优化措施:
实现打字机效果:逐字显示内容,而不是一次性显示整段文字
添加中断功能:允许用户停止正在进行的对话
处理网络异常:实现自动重连机制
管理对话历史:保持多轮对话的连贯性
使用AbortController可以实现请求中断:
const controller = new AbortController();
// 在fetch选项中添加
fetch(url, {
signal: controller.signal
});
// 需要中断时
controller.abort();
在React项目中,可以使用自定义Hook来管理AI流式对话:
import { useState, useCallback } from 'react';
export function useAIStream() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const startStream = useCallback(async (url, options) => {
setLoading(true);
setError(null);
const controller = new AbortController();
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': options.token ? `Bearer ${options.token}` : ''
},
body: JSON.stringify({ message: options.prompt }),
signal: controller.signal
});
// 处理响应流...
} catch (err) {
setError(err.message);
options.onError?.(err);
} finally {
setLoading(false);
}
return () => controller.abort();
}, []);
return { startStream, loading, error };
}
SSE技术为前端实现AI对话功能提供了简单有效的解决方案。通过fetch API和ReadableStream,我们可以灵活地处理流式数据,实现良好的用户体验。
关键要点:
SSE适合服务器向客户端推送数据的场景
使用fetch比原生EventSource更灵活
适当封装可以提高代码复用性
考虑用户体验优化,如中断功能、错误处理等
选择SSE而不是WebSocket的原因主要是:AI对话通常只需要单向数据流,SSE基于HTTP协议更简单,且不需要额外的协议升级处理。
通过合理的实现和优化,前端可以高效地处理AI流式对话,为用户提供流畅的交互体验。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!