小程序实现对接百度AI大模型,通过websocket连接进行百度实时语音识别,将返回的文字调用AI大模型API获得返回的消息内容进行文字转语音朗诵并操作

小程序实现对接百度AI大模型,通过websocket连接百度实时语音识别进行处理后返回文字,调用大模型API返回的消息数据进行文字转语音朗诵并操作。

我们的需求是对接AI大模型来优化客户的操作,实现的方案是在小程序里面获取录音权限 后,调用百度的实时语音转文字将语音帧通过websocket进行处理 ,结束帧返回识别的文字后,调用AI大模型的api,将识别到的文字传入到百度AI大模型接口 中,返回识别的意图进行操作,并调用百度的文字转语音朗读返回的消息内容(ps:AI大模型中关键字需要自己在控制台创建)。

实现效果如下:

1. 点击ai助手时获取录音权限

复制代码
toRecordVoice() {
	var that = this
	wx.getSetting({
		success(res) {
			if (!res.authSetting['scope.record']) {
				wx.authorize({
					scope: 'scope.record',
					success(succ) {
						// 权限已授予,展示AI助手模型
						that.showAIModel = true
					},
					fail(err) {
						uni.showModal({
							title: '权限申请',
							content: '需要录音权限才能使用语音功能',
							success(res) {
								if (res.confirm) {
									wx.openSetting({
										success(settingdata) {
											if (settingdata.authSetting[
													'scope.record']) {
												console.log('授权成功');
												that.showAIModel = true
											} else {
												console.log('授权失败');
											}
										}
									})
								}
							}
						})
					}
				})
			} else {
				that.showAIModel = true
			}
		}
	})
},

2.ai模块组件进行封装

复制代码
<template>
	<view class="ai-model">
		<view class="ai-model-header">
			<image src="xxxx.gif" class="ai-model-header-img"></image>
			<view class="ai-model-header-text">{{text}}</view>
		</view>
		<!-- <view class="ai-model-result" v-if="recognitionResult">
			<text class="result-label">识别结果:</text>
			<text class="result-text">{{recognitionResult}}</text>
		</view> -->
	</view>
</template>
<script>
const md5 = require('@/libs/md5.js').md5;
const app = getApp()
const api = require('@/utils/api.js');
export default {
	name: 'aiModel',
	data() {
			return {
				text: '有什么需求尽管说',
				voicePath: '',
				// 百度语音识别配置
				baiduConfig: {
					appId: '',
					apiKey: '',
					secretKey: ''
				},
				// WebSocket连接
				socketTask: null,
				// 录音管理器
				recorderManager: null,
				// 识别状态
				isRecognizing: false,
				// 识别结果
				recognitionResult: '',
				// 临时结果
				tempResult: '',
				// WebSocket连接状态
				socketConnected: false,
				// 最后一次语音活动时间
				lastVoiceActivityTime: null,
				// 静默检测定时器
				silenceTimer: null,
				// 百度API访问令牌
				accessToken: null,
				// 令牌过期时间
				tokenExpiresTime: null,
				// 标识是否已开始过录音(避免重复启动)
				hasStartedRecognition: false,
				// 标识是否已经调用过AI接口(避免重复调用)
				hasCalledApi: false,
				// 连续静默帧数计数
				silenceFrameCount: 0,
				// 连续检测到有声音的帧数计数
				voiceFrameCount: 0,
				// 判断是否有声音的阈值
				voiceThreshold: 20,
				// 当前音频能量值
				currentEnergy: 0,
				// 音频播放器
				audioContext: null
			};
		},
	created() {
		console.log('组件挂载时初始化录音管理器');
		// 组件挂载时初始化录音管理器
		this.initRecorder();
		initAudioPlayer()
	},
	mounted() {
		// 组件更新时自动开始语音识别(仅在首次显示时)
		if (!this.isRecognizing && !this.hasStartedRecognition) {
			console.log('组件显示时自动开始语音识别');
			this.startVoiceRecognition();
			this.hasStartedRecognition = true;
		}
	},
	destroyed() {
		// 组件销毁时清理资源
		this.stopVoiceRecognition();
		this.closeSocket();
		
		// 重置所有标志位,避免下次显示时出现重复调用
		this.hasStartedRecognition = false;
		this.hasCalledApi = false;
		this.isRecognizing = false;
		this.recognitionResult = '';
		this.tempResult = '';
	},
	methods: {
		// 初始化音频播放器
			initAudioPlayer() {
				if (!this.audioContext) {
					this.audioContext = uni.createInnerAudioContext();
					this.audioContext.onError((res) => {
						console.error('音频播放失败:', res);
					});
				}
			},
        toJumap(info){
            var that = this;
            // 在跳转页面之前关闭录音和WebSocket连接
            if (that.isRecognizing && that.recorderManager) {
                try {
                    that.recorderManager.stop();
                    that.isRecognizing = false;
                } catch (e) {
                    console.error('停止录音失败:', e);
                }
            }
            
            // 清除静默检测定时器
            if (that.silenceTimer) {
                clearInterval(that.silenceTimer);
                that.silenceTimer = null;
            }
            
            // 关闭WebSocket连接
            that.closeSocket();
            //ai模型获取到的关键词进行直接跳转或文字转语音朗读
            if(info.intent == "chat"){
              this.textToSpeech(info.param.chat_reply);
           }else if(info.intent == "xx"){
      			uni.navigateTo({
					url: `xxxxx`
				})
           }
        },
		// 初始化录音管理器
		initRecorder() {
			if (!this.recorderManager) {
				this.recorderManager = uni.getRecorderManager();
			console.log('初始化录音管理器:', this.recorderManager);
			console.log('初始化时的百度配置:', this.baiduConfig);
				
				// 录音错误回调
				this.recorderManager.onError((res) => {
					console.error('录音失败:', res);
					this.text = '录音失败,请重试';
					this.isRecognizing = false;
				});
				
				// 录音停止回调
				this.recorderManager.onStop((res) => {
					console.log('录音停止:', res);
					this.voicePath = res.tempFilePath;
					this.isRecognizing = false;
				});
				
				// 录音数据回调
				this.recorderManager.onFrameRecorded((res) => {
					// 减少调试日志,仅保留关键信息
					// 在微信小程序中,frameBuffer直接打印为空对象,但实际上有内容
					if (res.frameBuffer) {
						// 计算音频能量,判断是否有语音活动
						let hasVoiceActivity = this.hasVoiceActivity(res.frameBuffer);
						
						if (hasVoiceActivity) {
							// 如果检测到语音活动,更新最后一次语音活动时间
							this.lastVoiceActivityTime = Date.now();
							this.silenceFrameCount = 0;
							this.voiceFrameCount++;
						} else {
							// 如果没有检测到语音活动,增加静默帧数
							this.silenceFrameCount++;
							this.voiceFrameCount = 0;
						}
						
						if (this.isRecognizing && this.socketConnected) {
							// 直接发送完整的音频帧,不进行分块
							this.sendAudioData(res.frameBuffer);
						}
					}
				});
			}
		},
		
		// 开始语音识别
		async startVoiceRecognition() {
			try {
				this.text = '正在连接语音识别服务...';
				
				// 确保录音管理器已初始化
				this.initRecorder();
				if (!this.recorderManager) {
					throw new Error('录音管理器初始化失败');
				}
				
				// 连接WebSocket
				await this.connectSocket();
				
				// 开始录音 - 使用成功案例中的配置参数
			this.recorderManager.start({
				duration: 600000,   // 10分钟超时
				format: 'pcm',     // 必须是pcm格式,与百度API兼容
				sampleRate: 16000, // 必须是16000Hz
				numberOfChannels: 1, // 必须是单声道
				sampleBits: 16,    // 必须是16位
				frameSize: 5,      // 当录音大小达到5KB时触发onFrameRecorded
				encodeBitRate: 96000 // 使用成功案例中的码率
			});
				
				this.isRecognizing = true;
				this.text = '我在哦,有什么需求尽管说~';
				this.recognitionResult = '';
				this.tempResult = '';
				// 初始化最后一次语音活动时间
				this.lastVoiceActivityTime = Date.now();
				console.log('开始录音,初始化lastVoiceActivityTime:', this.lastVoiceActivityTime);
				// 启动静默检测定时器
				this.startSilenceDetection();
				
			} catch (err) {
				console.error('开始语音识别失败:', err);
				this.text = '连接语音识别服务失败,请重试';
				this.isRecognizing = false;
			}
		},
		
		// 停止语音识别
		stopVoiceRecognition() {
			// 先将识别状态设为false,防止新的音频数据被处理
			this.isRecognizing = false;
			
			if (this.recorderManager) {
				try {
					this.recorderManager.stop();
				} catch (e) {
					console.error('停止录音失败:', e);
				}
			}
			
			// 清除静默检测定时器
			if (this.silenceTimer) {
				clearInterval(this.silenceTimer);
				this.silenceTimer = null;
			}
			
			// 如果已经调用过API接口,则不再重复调用
			if (this.hasCalledApi) {
				console.log('已经调用过API接口,不再重复调用');
				// 发送FINISH帧并关闭WebSocket
				this.sendFinishFrame();
				this.closeSocket();
				return;
			}
			
			// 立即发送FINISH帧
			this.sendFinishFrame();
			
			// 关闭WebSocket连接
			this.closeSocket();
			
			// 如果没有最终识别结果,尝试调用API(用于静默超时的情况)
			if (this.recognitionResult) {
				// 将标志位设为true,防止重复调用API接口
				this.hasCalledApi = true;
				console.log('停止录音时调用后端接口,发送识别结果:', this.recognitionResult);
				
			  var params = {
			        "content": this.recognitionResult
			    };
			    net.postRequest(api.common.aiAgent, params, false, true, (data) => {
			        console.log(data,'datatta11111111111')
			       this.toJumap(data);
			    },(err) => {
			        console.error('调用后端接口失败:', err);
			    })
				
            } else {
                // 没有识别结果,直接关闭组件
                this.closeComponent();
            }
		},
		
		// 生成WebSocket连接URL
		generateSocketUrl() {
			// 获取当前时间戳
			const timestamp = Math.floor(Date.now() / 1000);
			// 生成随机数作为sn参数
			const sn = this.generateSign(this.baiduConfig.apiKey, this.baiduConfig.secretKey, timestamp);
            
			// 构造完整的WebSocket URL - 使用百度API要求的参数
			return `wss://vop.baidu.com/realtime_asr?appid=${encodeURIComponent(this.baiduConfig.appId)}&appkey=${encodeURIComponent(this.baiduConfig.apiKey)}&timestamp=${encodeURIComponent(timestamp)}&sn=${encodeURIComponent(sn)}&cuid=${encodeURIComponent('mini_app_device')}`;
		},
		
		// 生成签名
		generateSign(apiKey, secretKey, timestamp) {
			const str = `${apiKey}${timestamp}${secretKey}`;
			return md5(str);
		},
		
		// 连接WebSocket
		async connectSocket() {
			return new Promise((resolve, reject) => {
				if (this.socketTask && this.socketConnected) {
					resolve();
					return;
				}
				
				try {
					// 直接使用签名认证方式连接,不依赖access token
					const url = this.generateSocketUrl();
					// console.log('连接WebSocket:', url);
					
					this.socketTask = uni.connectSocket({
						url: url,
                        protocols: ['websocket'],
						success: (res) => {
							// console.log('WebSocket连接成功',res);
						},
						fail: (err) => {
							// console.error('WebSocket连接失败:', err);
							reject(err);
						}
					});
					
					// 连接打开
				this.socketTask.onOpen(() => {
					// console.log('WebSocket连接已打开');
					this.socketConnected = true;
					// console.log('连接打开后准备发送START帧,socketConnected:', this.socketConnected);
					this.sendStartFrame();
					resolve();
				});
					
					// 连接关闭
					this.socketTask.onClose(() => {
						console.log('WebSocket连接已关闭');
						this.socketConnected = false;
						this.socketTask = null;
					});
					
					// 接收消息
					this.socketTask.onMessage((res) => {
                        console.log('收到消息:', res);
						this.handleSocketMessage(res);
					});
					
					// 连接错误
					this.socketTask.onError((err) => {
						console.error('WebSocket错误:', err);
						this.socketConnected = false;
						this.socketTask = null;
						reject(err);
					});
				} catch (error) {
					console.error('连接WebSocket异常:', error);
					reject(error);
				}
			});
		},
		
		// 关闭WebSocket
		closeSocket() {
			if (this.socketTask) {
				this.socketTask.close();
				this.socketTask = null;
				this.socketConnected = false;
			}
		},
		
		// 发送开始帧
		sendStartFrame() {
			if (!this.socketConnected) return;
			
			// 详细确认百度配置参数
			// console.log('百度配置参数 - 完整对象:', this.baiduConfig);
			// console.log('appId类型:', typeof this.baiduConfig.appId, '值:', this.baiduConfig.appId);
			// console.log('apiKey类型:', typeof this.baiduConfig.apiKey, '值:', this.baiduConfig.apiKey);
			// console.log('secretKey类型:', typeof this.baiduConfig.secretKey, '值:', this.baiduConfig.secretKey);
			
			// 单独创建data对象进行调试,确保format是小写
			const dataObj = {
				appid: this.baiduConfig.appId,
				appkey: String(this.baiduConfig.apiKey),
				dev_pid: 1537, // 使用普通话识别模型(1537为普通话,有标点)
				cuid: "mini_app_device",
				format: "pcm",
				sample: 16000
			};
			console.log('创建的data对象:', dataObj);
			
			// 根据百度API文档,START帧参数应在data对象内,appid使用int类型
			const startFrame = {
				type: "START",
				data: dataObj
			};
			
			const jsonData = JSON.stringify(startFrame);
			// console.log('发送START帧数据:', startFrame);
			// console.log('发送START帧JSON:', jsonData);
			// console.log('JSON字符串长度:', jsonData.length);
			
			this.socketTask.send({
				data: jsonData,
				success: (res) => {
					console.log('发送START帧成功',res);
				},
				fail: (err) => {
					console.error('发送START帧失败:', err);
				}
			});
		},
		
		// 发送音频数据
		sendAudioData(audioData) {
			if (!this.isRecognizing || !this.socketConnected) return;
			
			// 不再无条件更新lastVoiceActivityTime,只在检测到有声音时更新
			// 检测是否有声音(基于之前在onFrameRecorded中计算的hasVoiceActivity结果)
			// 这里不再重复检测,而是依赖onFrameRecorded中的检测结果
			
			// 在微信小程序中,frameBuffer可能是一个特殊的对象,需要直接发送
			try {
				// 直接使用uni-app的send方法发送二进制数据
				this.socketTask.send({
					data: audioData,
					success: (res) => {
						// 不再无条件更新lastVoiceActivityTime
					},
					fail: (err) => {
						// 打印详细的错误信息
						for (let key in err) {
							console.error(`错误属性${key}:`, err[key]);
						}
						
						// 如果直接发送失败,尝试将音频数据转换为ArrayBuffer后发送
						this.trySendAsArrayBuffer(audioData);
					}
				});
			} catch (e) {
				// 异常时尝试转换后发送
				this.trySendAsArrayBuffer(audioData);
			}
		},
		
		// 尝试将音频数据转换为ArrayBuffer后发送
		trySendAsArrayBuffer(audioData) {
			if (!this.isRecognizing || !this.socketConnected) return;
			
			try {
				let arrayBuffer;
				
				// 如果是TypedArray,转换为ArrayBuffer
				if (audioData.buffer && audioData.buffer instanceof ArrayBuffer) {
					arrayBuffer = audioData.buffer;
				} else if (audioData instanceof ArrayBuffer) {
					arrayBuffer = audioData;
				} else {
					// 其他情况,尝试转换
					console.warn('无法确定音频数据格式');
					return;
				}
				
				// 发送转换后的ArrayBuffer
				this.socketTask.send({
					data: arrayBuffer,
					success: (res) => {
						// 不再无条件更新lastVoiceActivityTime
					},
					fail: (err) => {
						console.error('转换后发送音频数据失败:', err);
						for (let key in err) {
							console.error(`错误属性${key}:`, err[key]);
						}
					}
				});
			} catch (e) {
				console.error('转换音频数据为ArrayBuffer异常:', e);
			}
		},
		
		// 音频能量检测,判断是否有声音
		hasVoiceActivity(frameBuffer) {
			if (!frameBuffer) return false;
			
			try {
				let arrayBuffer;
				// 获取ArrayBuffer
				if (frameBuffer.buffer && frameBuffer.buffer instanceof ArrayBuffer) {
					arrayBuffer = frameBuffer.buffer;
				} else if (frameBuffer instanceof ArrayBuffer) {
					arrayBuffer = frameBuffer;
				} else {
					console.warn('无法确定音频数据格式');
					return false;
				}
				
				// 将ArrayBuffer转换为Int16Array以获取音频样本
				const audioData = new Int16Array(arrayBuffer);
				
				// 计算音频能量(均方根值)
				let sumSquare = 0;
				for (let i = 0; i < audioData.length; i++) {
					sumSquare += audioData[i] * audioData[i];
				}
				const rms = Math.sqrt(sumSquare / audioData.length);
				
				// 计算分贝值(dB)
				const db = 20 * Math.log10(rms / 32768 + 1e-6);
				
				// 更新当前能量值
				this.currentEnergy = db;
				
				// 如果分贝值大于阈值,认为有声音
				// 调整阈值,从20dB改为-30dB,更适合实际使用场景
				return db > -30;
			} catch (e) {
				console.error('音频能量检测异常:', e);
				return false;
			}
		},
		
		// 重新开始语音识别
		restartRecognition() {
			console.log('重新开始语音识别');
			
			// 先检查是否正在识别,如果是,先停止
			if (this.isRecognizing) {
				// 停止当前的语音识别,但不发送FINISH帧和调用API
				if (this.recorderManager) {
					try {
						this.recorderManager.stop();
					} catch (e) {
						console.error('停止录音失败:', e);
					}
				}
				
				// 清除静默检测定时器
				if (this.silenceTimer) {
					clearInterval(this.silenceTimer);
					this.silenceTimer = null;
				}
				
				// 关闭WebSocket连接
				this.closeSocket();
			}
			
			// 重置所有状态
			this.isRecognizing = false;
			this.recognitionResult = '';
			this.tempResult = '';
			this.hasCalledApi = false;
			this.lastVoiceActivityTime = null;
			this.text = '有什么出行需求尽管说';
			
			// 延迟一段时间后重新开始识别,给系统足够的时间清理资源
			setTimeout(() => {
				this.startVoiceRecognition();
			}, 1000);
		},
		
		// 发送结束帧
		sendFinishFrame() {
			if (!this.socketConnected) return;
			
			const finishFrame = {
				type: 'FINISH'
			};
			
			this.socketTask.send({
				data: JSON.stringify(finishFrame),
				success: (res) => {
					console.log('发送FINISH帧成功',res);
				},
				fail: (err) => {
					console.error('发送FINISH帧失败:', err);
				}
			});
		},
		
		// 处理WebSocket消息
		handleSocketMessage(res) {
			try {
				// console.log('接收WebSocket原始消息:', res);
				// console.log('原始消息类型:', typeof res.data);
				let messageData = res.data;
				
				// 如果是ArrayBuffer类型,转换为字符串
				if (messageData instanceof ArrayBuffer) {
					messageData = new TextDecoder().decode(messageData);
					console.log('转换后的文本消息:', messageData);
				} else if (typeof messageData !== 'string') {
					// 如果是其他类型,尝试转换为字符串
					messageData = String(messageData);
					console.log('强制转换后的文本消息:', messageData);
				}
				
				// 检查消息是否为空
				if (!messageData) {
					console.warn('收到空的WebSocket消息');
					return;
				}
				
				// 尝试解析JSON数据
				let data;
				try {
					// 移除可能的换行符,与成功案例保持一致
					messageData = messageData.replace('\n','');
					data = JSON.parse(messageData);
					console.log('解析后的WebSocket消息:', data);
					console.log('消息类型:', data.type);
				} catch (parseErr) {
					console.error('解析WebSocket消息失败:', parseErr);
					console.error('原始消息内容:', messageData);
					return;
				}
				
				// 检查是否有错误码
				if (data.err_no) {
					console.error('识别错误,错误码:', data.err_no, '错误信息:', data.err_msg);
					
					// 不同错误码的处理策略
					if (data.err_no === -3101) {
						// 等待音频超时错误 - 这通常是因为音频帧没有及时发送到百度服务器
						// 不要立即重启识别,而是继续当前的识别过程
						console.log('处理等待音频超时错误,继续当前识别过程');
						// 更新提示文本,但不要停止识别
						this.text = '我在哦,有什么需求尽管说~';
						// 更新最后一次语音活动时间,防止静默检测结束录音
						this.lastVoiceActivityTime = Date.now();
					} else {
						// 其他错误,如网络错误、认证错误等
						this.text = `网络开小差了,请稍后重试`;
                        
						// 识别错误后2秒重新开始识别
						setTimeout(() => {
							this.restartRecognition();
						}, 2000);
					}
					return;
				}
				
				if (data.type === 'MID_TEXT') {
					// 临时识别结果
					this.tempResult = data.result;
					console.log('临时识别结果:', this.tempResult);
					// 立即更新显示文本为临时结果
					this.text = this.tempResult;
					// 更新最后一次语音活动时间,避免静默检测结束录音
					this.lastVoiceActivityTime = Date.now();
					console.log('收到临时结果,更新lastVoiceActivityTime:', this.lastVoiceActivityTime);
				} else if (data.type === 'FIN_TEXT') {
				// 最终识别结果
				this.recognitionResult = data.result;
				console.log('最终识别结果:', this.recognitionResult);
				// 更新显示文本为最终结果
				this.text = this.recognitionResult;
				this.tempResult = '';
				// 更新最后一次语音活动时间
				this.lastVoiceActivityTime = Date.now();
				
				// 向父组件发送识别结果
				this.$emit('recognition-result', this.recognitionResult);
				
				// 将标志位设为true,防止重复调用API接口
					this.hasCalledApi = true;
					// 调用后端处理的ai大模型接口,发送识别结果
					var params = {
						"content": this.recognitionResult
					};
					postRequest(api.aiAgent, params, (data) => {
						console.log('后端接口返回结果:', data);
						this.toJumap(data);
					}, (err) => {
						console.error('调用后端接口失败:', err);
						// 如果调用失败,仍然关闭组件
						this.closeComponent();
					});
				} else if (data.type === 'START') {
					// 收到START响应,说明连接成功
					console.log('START响应成功,开始接收音频');
					this.text = '我在哦,有什么需求尽管说~';
				} else if (data.type === 'ERROR') {
					// 错误信息
					console.error('识别错误:', data);
					this.text = `识别错误: ${data.err_msg}`;
					
					// 识别失败后2秒关闭组件
					setTimeout(() => {
						this.closeComponent();
					}, 2000);
				} else {
					// 其他类型消息
					console.log('其他类型WebSocket消息:', data);
					// 显示原始消息类型
					this.text = data.result;
				}
			} catch (e) {
				console.error('处理WebSocket消息异常:', e);
				// 尝试直接显示原始消息内容
				try {
					if (res.data instanceof ArrayBuffer) {
						this.text = `消息处理异常: ${new TextDecoder().decode(res.data)}`;
					} else {
						this.text = `消息处理异常: ${String(res.data)}`;
					}
				} catch (parseErr) {
					this.text = '消息处理异常';
				}
			}
		},
		
		// 关闭组件
		closeComponent() {
			// 先停止语音识别
			this.stopVoiceRecognition();
			// 通过事件通知父组件关闭弹窗
			this.$emit('closeModel');
		},
		
		// 启动静默检测
		startSilenceDetection() {
			// 清除现有的定时器
			if (this.silenceTimer) {
				clearInterval(this.silenceTimer);
				this.silenceTimer = null;
			}
			 
			console.log('启动静默检测,初始lastVoiceActivityTime:', this.lastVoiceActivityTime);
			// 每隔500ms检查一次是否静默
			this.silenceTimer = setInterval(() => {
				this.checkSilence();
			}, 500);
		},
		 
		// 检查是否静默
		checkSilence() {
			if (!this.isRecognizing) return;
			
			const currentTime = Date.now();
			console.log('检查静默 - 当前时间:', currentTime, 'lastVoiceActivityTime:', this.lastVoiceActivityTime, '间隔:', currentTime - this.lastVoiceActivityTime, 'ms');
			// 如果超过10秒没有语音活动,认为是静默(增加阈值,给百度API足够的处理时间)
			if (currentTime - this.lastVoiceActivityTime > 10000) {
				console.log('检测到静默,自动结束录音');
				this.stopVoiceRecognition();
			}
		},
		// 获取百度API访问令牌
			async getBaiduAccessToken() {
				// 检查令牌是否存在且未过期
				if (this.accessToken && this.tokenExpiresTime && Date.now() < this.tokenExpiresTime) {
					return this.accessToken;
				}

				const {
					apiKey,
					secretKey
				} = this.baiduConfig;
				const url =
					`https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${apiKey}&client_secret=${secretKey}`;

				return new Promise((resolve, reject) => {
					uni.request({
						url: url,
						method: 'GET',
						success: (res) => {
							console.log('获取访问令牌成功:', res);
							if (res.data.access_token) {
								this.accessToken = res.data.access_token;
								// 设置过期时间(提前10分钟)
								this.tokenExpiresTime = Date.now() + (res.data.expires_in - 600) *
									1000;
								resolve(this.accessToken);
							} else {
								reject(new Error('获取访问令牌失败'));
							}
						},
						fail: (err) => {
							console.error('获取访问令牌失败:', err);
							reject(err);
						}
					});
				});
			},

			// 语音合成函数
			async textToSpeech(text) {
				try {
					// 初始化音频播放器
					this.initAudioPlayer();

					// 获取访问令牌
					const token = await this.getBaiduAccessToken();

					// 调用百度语音合成API
					const url =
						`https://tsn.baidu.com/text2audio?tex=${encodeURIComponent(text)}&tok=${token}&cuid=mini_app_device&ctp=1&lan=zh&vol=8&per=4&spd=5&pit=5`;
					console.log('语音合成URL:', url);
					// 播放语音
					this.audioContext.src = url;
					this.audioContext.play();
				} catch (error) {
					console.error('语音合成失败:', error);
				}
			}
	},
};
</script>
<style lang="scss" scoped>
.ai-model {
	.ai-model-header {
        position: fixed;
        z-index: 999;
        width: 100%;
		top: 10%;
        min-height:700rpx;
        margin: auto;
        left: 0;
        right: 0;
		align-items:0 center;
        background:blue;
        text-align: center;
        overflow: hidden;
        padding: 24rpx;
        
        .ai-model-header-img{
            width: 178rpx;
            height: 218rpx;
            margin-top: 20%;
        }
        
        .ai-model-header-text{
            font-size: 28rpx;
            color: #FFFFFF;
            line-height: 1.6;
            margin-top: 24rpx;
        }
        
        .ai-btn {
            min-width: 200rpx;
        }
    }
    
    .ai-model-result {
        position: fixed;
        bottom: 100rpx;
        left: 20rpx;
        right: 20rpx;
        background: rgba(255, 255, 255, 0.9);
        border-radius: 10rpx;
        padding: 20rpx;
        box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
    }
    
    .result-label {
        font-size: 28rpx;
        font-weight: bold;
        color: #333;
    }
    
    .result-text {
        font-size: 28rpx;
        color: #666;
        margin-left: 10rpx;
    }
}
</style>
  1. 页面内进行调用

    <aiModel ref="aiModel" v-if="showAIModel" @closeModel="closeModel"></aiModel>
    import aiModel from '@/components/aiModel.vue';
    export default {
    components: {
    aiModel
    },
    data() {
    return {
    showAIModel: false, // 是否显示AI模型
    }
    },
    methods: {
    closeModel() {
    this.showAIModel = false;
    }
    }
    }

目前简化版的AI模型就这么实现了,如果要实现连续对话的话,在websocket发送消息时将上一个接收的消息的sessionid传入,就可以了,欢迎大家提出意见互相学习,AI组件模块后续会继续优化,欢期待你的关注。

相关推荐
Elastic 中国社区官方博客1 小时前
Elasticsearch:Apache Lucene 2025 年终总结
大数据·人工智能·elasticsearch·搜索引擎·apache·lucene
deephub1 小时前
让 Q 值估计更准确:从 DQN 到 Double DQN 的改进方案
人工智能·pytorch·深度学习·强化学习
Dyanic1 小时前
通用图像融合方法利用梯度迁移学习与融合规则展开
人工智能·机器学习·迁移学习
IvanCodes1 小时前
Clawdbot安装部署详细教程
人工智能·ai·agent
Yeats_Liao1 小时前
负载均衡设计:多节点集群下的请求分发与资源调度
运维·人工智能·深度学习·机器学习·华为·负载均衡
粉色挖掘机2 小时前
AI算子的分类及常见算子介绍
人工智能·分类·数据挖掘
陈思杰系统思考Jason2 小时前
系统思考:以客户为中心
百度·微信·微信公众平台·新浪微博·微信开放平台
2501_948120152 小时前
可再生能源并网预测模型
人工智能·区块链
人工智能AI技术2 小时前
【Agent从入门到实践】47 与前端系统集成:通过API对接,实现前端交互
人工智能·python