> 技术文档 > uniapp 小程序如何实现大模型流式交互?前端SSE技术完整实现解析_uniapp sse

uniapp 小程序如何实现大模型流式交互?前端SSE技术完整实现解析_uniapp sse


文章目录

  • 一、背景概述
  • 二、核心流程图解
  • 三、代码模块详解
    • 1. UTF-8解码器(处理二进制流)
    • 2. 请求控制器(核心通信模块)
    • 3. 流式请求处理器(分块接收)
    • 4. 数据解析器(处理SSE格式)
    • 5. 回调处理三剑客
  • 四、关键问题解决方案
    • 1. 乱码问题处理
    • 2. 数据截断问题
    • 3. 性能优化建议
  • 五、扩展思考
    • 1. 为什么要用SSE而不是WebSocket?
    • 2. 流量控制策略
  • 六、完整技术栈推荐

一、背景概述

在大模型应用中,流式响应技术(Server-Sent Events, SSE)能显著提升用户体验。本文将以代码为核心,讲解基于uni-app框架的流式交互完整实现方案,涵盖数据接收、解码、解析全流程。


二、核心流程图解

#mermaid-svg-Z41MDmagS7i7B0vB {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .error-icon{fill:#552222;}#mermaid-svg-Z41MDmagS7i7B0vB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z41MDmagS7i7B0vB .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Z41MDmagS7i7B0vB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z41MDmagS7i7B0vB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z41MDmagS7i7B0vB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z41MDmagS7i7B0vB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z41MDmagS7i7B0vB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z41MDmagS7i7B0vB .marker.cross{stroke:#333333;}#mermaid-svg-Z41MDmagS7i7B0vB svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z41MDmagS7i7B0vB .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .cluster-label text{fill:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .cluster-label span{color:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .label text,#mermaid-svg-Z41MDmagS7i7B0vB span{fill:#333;color:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .node rect,#mermaid-svg-Z41MDmagS7i7B0vB .node circle,#mermaid-svg-Z41MDmagS7i7B0vB .node ellipse,#mermaid-svg-Z41MDmagS7i7B0vB .node polygon,#mermaid-svg-Z41MDmagS7i7B0vB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z41MDmagS7i7B0vB .node .label{text-align:center;}#mermaid-svg-Z41MDmagS7i7B0vB .node.clickable{cursor:pointer;}#mermaid-svg-Z41MDmagS7i7B0vB .arrowheadPath{fill:#333333;}#mermaid-svg-Z41MDmagS7i7B0vB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Z41MDmagS7i7B0vB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Z41MDmagS7i7B0vB .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Z41MDmagS7i7B0vB .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Z41MDmagS7i7B0vB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Z41MDmagS7i7B0vB .cluster text{fill:#333;}#mermaid-svg-Z41MDmagS7i7B0vB .cluster span{color:#333;}#mermaid-svg-Z41MDmagS7i7B0vB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Z41MDmagS7i7B0vB :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 用户输入 构建请求体 发送SSE请求 接收数据块 二进制解码 数据清洗 解析JSON 提取内容 实时渲染


三、代码模块详解

1. UTF-8解码器(处理二进制流)

decodeUTF8(data) { // 将二进制数据转为Uint8数组 const uint8Array = new Uint8Array(data); // 传统方式转换字符串(兼容旧环境) let string = \'\'; for (let i = 0; i < uint8Array.length; i++) { string += String.fromCharCode(uint8Array[i]); } // 双重解码处理特殊字符(如中文) return decodeURIComponent(escape(string));}

关键点说明:

  • Uint8Array:将原始二进制数据转为可操作数组
  • escape():将字符串转义为ASCII字符
  • decodeURIComponent:解析URI编码内容(等效于UTF-8解码)

2. 请求控制器(核心通信模块)

getContent() { const sendContent = { \"messages\": [{ \"role\": \"user\", \"content\": this.content, }], \"section_id\": this.id, \"token\": this.token } // 显示等待状态 this.waitingForResponse = true; // 发起流式请求 this.streamPost(\'/api/xxx/xxx\',  sendContent,  this.onDataReceived,  this.onError,  this.onComplete);}

参数解析:

参数名 类型 说明 role String 角色标识(user/assistant) content String 用户输入的提问内容 section_id Number 会话分区ID token String 用户身份验证令牌

3. 流式请求处理器(分块接收)

streamPost(url, data, onData, onError = null, onComplete = null) { const requestTask = uni.request({ url: this.$baseUrl + url, method: \'POST\', header: { \'Accept\': \'text/event-stream\', // 声明接受事件流 \'token\': uni.getStorageSync(\'token\') }, data, enableChunked: true, // 启用分块传输模式 responseType: \'arraybuffer\', // 接收二进制数据 // 分块数据到达时触发 success: (res) => { /*...*/ }, // 注册分块接收监听器 onChunkReceived: (res) => { const decodedData = this.decode(res.data); if (decodedData) { onData(decodedData); // 触发数据回调 } } });}

技术要点:

  • enableChunked: true:启用微信小程序分块接收能力
  • responseType: \'arraybuffer\':确保正确处理二进制流
  • onChunkReceived:微信小程序特有分块事件监听

4. 数据解析器(处理SSE格式)

decode(data) { const text = this.decodeUTF8(data); const lines = text.split(\'\\n\'); let result = \'\'; for (let line of lines) { if (line.startsWith(\'data: \')) { const jsonData = line.slice(6).trim(); // 结束标识处理 if (jsonData === \'[DONE]\') return result; // 清理控制字符(防止JSON解析失败) const cleanedData = jsonData.replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, \'\'); try { const parsedData = JSON.parse(cleanedData); // 提取AI生成内容 result += parsedData.choices[0].delta.content || \'\'; } catch (e) { console.error(\'解析失败:\', e); } } } return result;}

数据示例:

// 原始SSE数据格式data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"你好\"}}]}// 解析后结果\"你好\"

5. 回调处理三剑客

// 实时数据渲染onDataReceived(data) { if (data.trim()) { this.displayText += data; // 增量更新显示内容 this.resultCount = this.displayText.length; // 统计字数 }}// 异常处理onError(error) { console.error(\'请求异常:\', error); uni.showToast({ title: \'服务响应异常\', icon: \'none\' });}// 完成处理onComplete() { this.waitingForResponse = false; console.log(\'会话结束\'); // 可添加历史记录存储等逻辑}

四、关键问题解决方案

1. 乱码问题处理

  • 现象:接收到响啊类乱码
  • 解决方案
    1. 检查decodeUTF8是否被正确调用
    2. 验证服务端编码是否为UTF-8
    3. 替换解码方案为new TextDecoder().decode(uint8Array)

2. 数据截断问题

  • 现象:JSON解析报错Unexpected end of JSON input
  • 处理策略
    // 增加数据清洗步骤const cleanedData = jsonData .replace(/\\n/g, \'\') // 移除换行符 .replace(/\\u2028/g, \'\') // 处理行分隔符 .replace(/\\u2029/g, \'\'); // 处理段落分隔符

3. 性能优化建议

// 使用文档片段批量更新let fragment = \'\';onDataReceived(data) { fragment += data; if (fragment.length > 100) { // 每100字符更新一次 this.displayText += fragment; fragment = \'\'; }}

五、扩展思考

1. 为什么要用SSE而不是WebSocket?

  • SSE优势
    • 基于HTTP协议,无需额外握手
    • 自动重连机制
    • 更简单的服务端实现

2. 流量控制策略

// 节流处理(每500ms更新一次)let updateTimer = null;onDataReceived(data) { this.buffer += data; if (!updateTimer) { updateTimer = setTimeout(() => { this.displayText += this.buffer; this.buffer = \'\'; updateTimer = null; }, 500); }}

六、完整技术栈推荐

层级 技术选型 前端框架 Vue3 + uni-app 状态管理 Pinia HTTP库 uni.request 数据格式 JSON + SSE 部署环境 微信小程序 + Web

通过以上实现方案,开发者可以构建出高可用的大模型流式交互系统。建议在实际项目中加入加载状态提示错误重试机制历史会话管理等功能模块,以提升完整用户体验。