class VoiceControlSystem { constructor(config) { this.config = config; this.isListening = false; this.recognition = null; this.apiKey = 'sk-020189889aac40f3b050f7c60ca597f8'; // 替换为您的DeepSeek API密钥 this.init(); } init() { console.log(123); this.setupSpeechRecognition(); this.setupEventListeners(); this.uploadConfigToModel(); this.getSupportedLanguages(); } // 检测浏览器支持的语音识别语言(部分 Chromium 浏览器支持) async getSupportedLanguages() { console.log(123); if (window.SpeechRecognition?.getSupportedLanguages) { try { const languages = await window.SpeechRecognition.getSupportedLanguages(); console.log('浏览器支持的语言列表:', languages); return languages; } catch (e) { console.warn('无法获取支持的语言列表:', e); } } // 若浏览器不支持 getSupportedLanguages,返回常见兼容语言 return ['zh-CN', 'en-US', 'zh-TW', 'en-GB']; } setupSpeechRecognition() { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { this.updateStatus('浏览器不支持语音识别功能', 'error'); return; } this.recognition = new SpeechRecognition(); this.recognition.lang = 'zh-CN'; this.recognition.extra = { 'alternatives': 5, 'language': ['zh-CN', 'zh-HK', 'zh-TW', 'en-US'] // 提供备选语言 }; this.recognition.continuous = false; this.recognition.interimResults = false; this.recognition.onstart = () => { this.isListening = true; this.updateStatus('正在聆听...', 'listening'); document.getElementById('voice-btn').classList.add('listening'); }; this.recognition.onend = () => { this.isListening = false; document.getElementById('voice-btn').classList.remove('listening'); this.updateStatus('准备就绪', 'ready'); }; this.recognition.onresult = async (event) => { const transcript = event.results[0][0].transcript; console.log(transcript,"-----transcript"); this.updateStatus(`识别结果: "${transcript}"`, 'success'); await this.processVoiceCommand(transcript); }; this.recognition.onerror = (event) => { this.updateStatus(`语音识别错误: ${event.error}`, 'error'); }; } setupEventListeners() { const voiceBtn = document.getElementById('voice-btn'); voiceBtn.addEventListener('click', () => { const input = document.getElementById('input-text'); let val = input.value; console.log(val,"---val"); this.toggleListening(val) }); // 添加API密钥配置界面 this.setupApiKeyConfig(); } setupApiKeyConfig() { const configBtn = document.createElement('button'); configBtn.textContent = '配置API密钥'; configBtn.style.marginLeft = '10px'; configBtn.addEventListener('click', () => this.showApiKeyConfig()); document.getElementById('voice-btn').after(configBtn); } showApiKeyConfig() { const apiKey = prompt('请输入DeepSeek API密钥:', this.apiKey); if (apiKey !== null) { this.apiKey = apiKey; this.updateStatus('API密钥已更新', 'success'); } } async toggleListening(value) { if (!this.apiKey) { this.updateStatus('请先配置DeepSeek API密钥', 'error'); this.showApiKeyConfig(); return; } let transcript = value; await this.processVoiceCommand(transcript); // if (this.isListening) { // this.recognition.stop(); // } else { // this.recognition.start(); // } } async uploadConfigToModel() { try { this.updateStatus('配置文件已加载', 'info'); console.log('DeepSeek配置已准备:', this.config); } catch (error) { this.updateStatus('配置加载失败', 'error'); } } async processVoiceCommand(voiceText) { try { this.updateStatus('正在调用DeepSeek分析指令...', 'info'); const sequence = await this.callDeepSeekAPI(voiceText); this.updateStatus('DeepSeek指令序列生成成功', 'success'); await this.executeSequence(sequence); } catch (error) { console.error('DeepSeek处理错误:', error); this.updateStatus(`处理失败: ${error.message}`, 'error'); // 尝试使用备用方案 try { this.updateStatus('尝试使用备用方案...', 'info'); const fallbackSequence = this.fallbackModelResponse(voiceText); await this.executeSequence(fallbackSequence); } catch (fallbackError) { this.updateStatus('备用方案也失败了', 'error'); } } } async callDeepSeekAPI(voiceText) { const API_URL = 'https://api.deepseek.com/v1/chat/completions'; if (!this.apiKey) { throw new Error('请先配置DeepSeek API密钥'); } const prompt = this.buildDeepSeekPrompt(voiceText); try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}` }, body: JSON.stringify({ model: "deepseek-chat", messages: [ { role: "system", content: `你是一个专业的网页语音控制助手。请严格根据提供的配置文件和用户指令,生成准确的操作序列。 重要规则: 1. 只返回纯JSON格式,不要包含任何其他文本 2. JSON结构必须包含sequence数组 3. 每个指令必须存在于配置文件中 4. 参数必须匹配指令定义 5. 按逻辑顺序排列指令` }, { role: "user", content: prompt } ], temperature: 0.1, max_tokens: 1000, response_format: { type: "json_object" } }) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(`DeepSeek API错误: ${response.status} ${errorData.message || ''}`); } const data = await response.json(); const content = data.choices[0].message.content; // 解析JSON响应 let result; try { result = JSON.parse(content); } catch (parseError) { // 尝试提取JSON内容 const jsonMatch = content.match(/\{[\s\S]*\}/); if (jsonMatch) { result = JSON.parse(jsonMatch[0]); } else { throw new Error('DeepSeek返回了非JSON格式的响应'); } } console.log(result,"---result"); // 验证响应结构 if (!result.sequence || !Array.isArray(result.sequence)) { throw new Error('DeepSeek返回了无效的指令序列格式'); } return result; } catch (error) { console.error('DeepSeek API调用错误:', error); throw new Error(`DeepSeek处理失败: ${error.message}`); } } buildDeepSeekPrompt(voiceText) { // 构建指令选择指南 const commandSelectionGuide = this.buildCommandSelectionGuide(); return `网页交互配置文件: ${JSON.stringify(this.config, null, 2)} 用户语音指令:"${voiceText}" ${commandSelectionGuide} 请严格遵循以下规则生成指令序列: 1. 每个用户指令只选择最匹配的一个命令 2. 不要重复执行相同功能的命令 3. 如果多个命令描述相似,选择命令名称最简洁的那个 4. 确保指令顺序合理 5. 只返回纯JSON格式 要求格式: { "sequence": [ {"command": "指令名", "params": {参数对象}} ] } 请为指令"${voiceText}"生成正确的序列:`; } buildCommandSelectionGuide() { // 分组相似命令 const commandGroups = {}; this.config.commands.forEach(cmd => { const key = cmd.description + cmd.selector; if (!commandGroups[key]) { commandGroups[key] = []; } commandGroups[key].push(cmd); }); let guide = "指令选择指南:\n"; Object.values(commandGroups).forEach(group => { if (group.length > 1) { guide += `\n相似命令组(选择其中一个):\n`; group.forEach(cmd => { guide += `- ${cmd.command}: ${cmd.description}\n`; }); // 推荐选择规则 const recommended = group.reduce((prev, current) => prev.command.length < current.command.length ? prev : current ); guide += `推荐选择: ${recommended.command} (名称最简洁)\n`; } }); return guide; } // 备用方案(当DeepSeek API不可用时) fallbackModelResponse(voiceText) { const lowerText = voiceText.toLowerCase(); // 简单的规则匹配 if (lowerText.includes('登录')) { const username = this.extractParam(voiceText, '用户') || this.extractParam(voiceText, '账号') || 'testuser'; const password = this.extractParam(voiceText, '密码') || '123456'; return { sequence: [ { command: "open_login" }, { command: "input_username", params: { username } }, { command: "input_password", params: { password } }, { command: "submit_login" } ] }; } if (lowerText.includes('搜索')) { const keyword = this.extractSearchKeyword(voiceText) || '商品'; return { sequence: [ { command: "search_product", params: { keyword } } ] }; } if (lowerText.includes('购物车') || lowerText.includes('加入')) { return { sequence: [ { command: "add_to_cart" } ] }; } throw new Error('无法识别的指令'); } extractParam(text, paramName) { const regex = new RegExp(`${paramName}[::]*\\s*([^\\s]+)`, 'i'); const match = text.match(regex); return match ? match[1] : null; } extractSearchKeyword(text) { const regex = /搜索[::]*\s*([^。,!?]+)/i; const match = text.match(regex); return match ? match[1].trim() : null; } async executeSequence(sequence) { for (const [index, instruction] of sequence.sequence.entries()) { try { await this.executeInstruction(instruction); this.logCommand(`✓ 完成: ${instruction.command}`); // 在指令之间添加延迟 if (index < sequence.sequence.length - 1) { await this.delay(800); } } catch (error) { this.logCommand(`✗ 错误: ${instruction.command} - ${error.message}`); throw error; } } } async executeInstruction(instruction) { const commandConfig = this.config.commands.find(c => c.command === instruction.command); if (!commandConfig) { throw new Error(`未知指令: ${instruction.command}`); } this.logCommand(`开始执行: ${instruction.command}`); const element = document.querySelector(commandConfig.selector); if (!element) { throw new Error(`找不到元素: ${commandConfig.selector}`); } // 滚动到元素可见 element.scrollIntoView({ behavior: 'smooth', block: 'center' }); switch (commandConfig.action) { case 'click': element.click(); this.logCommand(`点击: ${commandConfig.selector}`); break; case 'input': const inputParam = commandConfig.params[0]; if (instruction.params && instruction.params[inputParam.name]) { element.value = instruction.params[inputParam.name]; element.dispatchEvent(new Event('input', { bubbles: true })); this.logCommand(`输入${inputParam.name}: ${instruction.params[inputParam.name]}`); } break; case 'navigate': if (instruction.params && instruction.params.product_url) { window.location.href = instruction.params.product_url; } break; case 'input_and_submit': if (instruction.params && instruction.params.keyword) { element.value = instruction.params.keyword; element.dispatchEvent(new Event('input', { bubbles: true })); // 尝试提交表单或点击相关按钮 if (element.form) { element.form.submit(); } else { // 查找提交按钮 const submitBtn = document.querySelector('#search-submit, [type="submit"]'); if (submitBtn) submitBtn.click(); } this.logCommand(`搜索: ${instruction.params.keyword}`); } break; default: throw new Error(`未知动作类型: ${commandConfig.action}`); } // 添加视觉反馈 this.highlightElement(element); } highlightElement(element) { const originalStyle = element.style.boxShadow; element.style.boxShadow = '0 0 0 3px #4CAF50'; setTimeout(() => { element.style.boxShadow = originalStyle; }, 1000); } updateStatus(message, type = 'info') { const statusElement = document.getElementById('status'); statusElement.textContent = message; statusElement.className = `status ${type}`; } logCommand(message) { return const list = document.getElementById('command-list'); const item = document.createElement('li'); item.textContent = `${new Date().toLocaleTimeString()}: ${message}`; list.appendChild(item); list.scrollTop = list.scrollHeight; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // 初始化系统 document.addEventListener('DOMContentLoaded', () => { new VoiceControlSystem(config); });