前端实现大模型流式响应方案
两种方案各有优势,可根据项目具体需求选择。对于新项目或简单需求,推荐使用 Fetch API 方案;对于已有 Axios 集成的复杂项目,Axios 方案更为合适。在大模型对话场景中,传统的一次性响应方式会导致用户需要等待较长时间才能看到完整结果。流式响应可以将响应内容分块传输,实现逐字或逐段渲染的效果,显著提升用户体验。解析markdown。
·
需求背景
在大模型对话场景中,传统的一次性响应方式会导致用户需要等待较长时间才能看到完整结果。流式响应可以将响应内容分块传输,实现逐字或逐段渲染的效果,显著提升用户体验。
方案实现
Fetch API 实现
const requestBody = {
session_id: '1', // 会话唯一标识符
messages: [val], // 对话消息数组
background_info: background_info.value // 上下文信息
};
const response = await fetch(`${import.meta.env.VITE_API_JYH_URL6}chat/stream`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
setTimeOutAddText('检测异常');
over.reqOver = true;
over.inputOver = true;
loading.value = false;
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let allText = '';
let first = true;
while (true) {
const {done, value} = await reader.read();
loading.value = false;
if (done) break;
const chunk = decoder.decode(value, {stream: true});
allText += chunk;
if (first) {
first = false;
list.value.push({text: marked.parse(allText), type: 6});
} else {
list.value[list.value.length - 1].text = marked.parse(allText);
}
setScrollToBottom();
}
特点:
- 使用原生
Fetch API
和ReadableStream
- 支持 Markdown 格式实时渲染
- 简单直接,无需额外依赖
Axios 实现
// 接口封装
export const decisionChat = (data, onMessage, onComplete, onError) => {
axios.request({
url: '/chat',
method: 'POST',
data: { ...data },
headers: {
'Accept': 'text/event-stream',
},
responseType: 'stream',
onDownloadProgress: (progressEvent) => {
const chunk = progressEvent.currentTarget.responseText;
if (chunk) {
try {
const lines = chunk.split('\n').filter(line => line.trim());
lines.forEach(line => {
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6).trim();
try {
const parsed = JSON.parse(jsonStr);
if (parsed.type === 'token' && parsed.content) {
onMessage(parsed.content);
}
} catch (err) {
console.error('解析 SSE 数据失败:', err, '原始行:', line);
}
}
});
} catch (err) {
console.error('流式数据解析错误:', err);
onError?.(err);
}
}
}
})
.then(() => onComplete?.())
.catch((err) => {
console.error('流式请求错误:', err);
onError?.(err);
});
};
// 接口调用
const submitForm = async () => {
if (!over.value) return ElMessage.warning("请等待上一次回答完成");
if (text.value == "\n") {
text.value = "";
return ElMessage.warning("请输入问题");
}
over.value = false;
list.value.push(text.value);
list.value.push('');
const lastIndex = list.value.length - 1;
await setScrollToBottom();
await decisionChat(
{
message: text.value,
conversation_id: null
},
(content) => {
list.value[lastIndex] += content;
setScrollToBottom();
},
() => {
if (!window.isSpecialHandled) over.value = true;
console.log("流式返回结束");
},
(err) => {
console.error("接口请求失败:", err);
list.value.push("请求失败,请重试");
over.value = true;
}
);
text.value = "";
};
解析markdown
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({
html: true, // 允许HTML标签
linkify: true, // 自动识别URL
typographer: true // 智能标点
})
// 使用 computed 定义可响应的解析函数
const parsedMarkdown = computed(() => {
return (content: string) => {
if (!content) return '';
return md.render(content);
};
});
特点:
- 基于 Axios 封装,适合已有 Axios 集成的项目
- 支持 Server-Sent Events (SSE) 协议
- 提供完整的回调机制(消息、完成、错误)
方案对比
特性 | Fetch API 方案 | Axios 方案 |
---|---|---|
兼容性 | 现代浏览器 | 现代浏览器 |
依赖 | 无 | 需要 Axios |
复杂度 | 简单 | 中等 |
错误处理 | 基础 | 完善 |
Markdown 支持 | 内置 | 需额外实现 |
适合场景 | 简单流式需求 | 复杂企业级应用 |
优化建议
-
错误处理增强
- 添加网络异常和超时处理
- 实现重试机制
-
性能优化
- 添加防抖机制避免频繁渲染
- 考虑使用 Web Worker 处理大数据量
-
用户体验
- 添加加载动画
- 实现中断请求功能
-
代码结构
- 将流式处理逻辑封装为独立 Hook/组件
- 添加 TypeScript 类型定义
-
兼容性处理
- 添加对旧浏览器的 polyfill
- 考虑降级方案(非流式)
两种方案各有优势,可根据项目具体需求选择。对于新项目或简单需求,推荐使用 Fetch API 方案;对于已有 Axios 集成的复杂项目,Axios 方案更为合适。
更多推荐
所有评论(0)