import 'package:get/get.dart'; import 'dart:async'; import 'volcano_ai_service.dart'; import 'azure_asr_service.dart'; import 'volcano_tts_api_service.dart'; import '../../modules/chat/models/message_model.dart'; import '../../core/utils/logger.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../providers/agent_provider.dart'; class BackgroundAgentService extends GetxService { static BackgroundAgentService get to => Get.find(); final VolcanoAIService _aiService; final VolcanoTtsApiService _ttsService; final AzureAsrService _voiceRecognitionService; final List _messageHistory = []; bool _isProcessing = false; bool _isListening = false; StreamSubscription? _recognitionSubscription; // 当前使用的agent ID String? _currentAgentId; // 当前使用的agent voice String _agentVoice = 'zh_female_meilinvyou_moon_bigtts'; // 可观察的状态 final RxBool isListening = false.obs; final RxString recognizedText = ''.obs; // 添加一个标志,表示是否已经识别到语音 bool _hasRecognizedSpeech = false; // 添加一个标志,表示是否已经收到最终结果 bool _hasFinalResult = false; // 单一的交互超时计时器 (30秒) Timer? _interactionTimer; // 添加一个变量来跟踪当前的AI响应流订阅 StreamSubscription? _aiResponseSubscription; // 添加一个标志,表示是否应该取消当前的AI响应 bool _shouldCancelAiResponse = false; // 当前系统提示词 String? _currentSystemPrompt; BackgroundAgentService() : _aiService = VolcanoAIService(), _ttsService = Get.find(), _voiceRecognitionService = Get.find(); // 启动或重置交互计时器 void _resetInteractionTimer() { // 取消之前的计时器 _interactionTimer?.cancel(); // 设置30秒的超时计时器 _interactionTimer = Timer(const Duration(seconds: 30), () { // 检查TTS是否正在播放 if (_ttsService.isPlaying) { // 如果TTS正在播放,重置定时器 _resetInteractionTimer(); } else { // 如果TTS不在播放,且30秒内没有检测到用户输入,退出交互 Logger.info('30秒内没有检测到用户输入,退出交互'); _endInteraction(); } }); } // 结束交互 Future _endInteraction() async { // 完成当前的recognitionCompleter(如果有) if (_recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { _recognitionCompleter!.complete(''); } _isProcessing = false; // 播放退出提示 await _ttsService.speakSingle("没有听到您说话,已退出语音交互", speaker: _agentVoice); // 停止语音识别 if (_isListening) { await stopVoiceRecognition(); Logger.info('退出交互,已停止语音识别'); } // 清除当前系统提示词 _currentSystemPrompt = null; // 清除当前agent ID _currentAgentId = null; // 重置agent voice为默认值 _agentVoice = 'zh_female_meilinvyou_moon_bigtts'; } // 开始语音交互 Future startAgentInteraction(String agentId) async { // 设置当前agent ID _currentAgentId = agentId; // 获取agent信息,只查询一次 final agent = AgentProvider.getAgentById(agentId); if (agent == null) { Logger.error('无法获取agent: $agentId'); return; } // 存储需要的值 _agentVoice = agent.voice; _currentSystemPrompt = agent.systemPrompt; // 播放提示音 await _ttsService.speakSingle("我在听", speaker: _agentVoice); if (_isProcessing) { print('已经在处理交互,忽略此次请求'); return; } _isProcessing = true; try { // 确保之前的语音识别已经停止 if (_isListening) { await stopVoiceRecognition(); } // 启动语音识别 await startVoiceRecognition(); // 启动交互计时器 _resetInteractionTimer(); } catch (e) { Logger.error('启动语音交互失败: $e'); _isProcessing = false; // 播放错误提示 await _ttsService.speakSingle("抱歉,启动语音交互失败", speaker: _agentVoice); } } // 停止语音交互 Future stopAgentInteraction() async { // 取消交互计时器 _interactionTimer?.cancel(); _interactionTimer = null; // 停止TTS播放 if (_ttsService.isPlaying) { await _ttsService.stop(); } // 停止语音识别 if (_isListening) { await stopVoiceRecognition(); Logger.info('手动停止交互,已停止语音识别'); } // 清除处理状态 _isProcessing = false; _currentSystemPrompt = null; // 重置agent voice为默认值 _agentVoice = 'zh_female_meilinvyou_moon_bigtts'; Logger.info('手动停止交互,已结束TTS会话'); } // 添加一个 Completer 用于在识别到最终结果时完成 Completer? _recognitionCompleter; // 开始语音识别 Future startVoiceRecognition() async { if (_isListening) { await stopVoiceRecognition(); } try { Logger.info('开始初始化语音识别服务...'); // 确保语音识别服务已初始化 if (!await _voiceRecognitionService.initialize( subscriptionKey: dotenv.env['AZURE_ASR_SUBSCRIPTION_KEY'] ?? '', serviceRegion: dotenv.env['AZURE_ASR_SERVICE_REGION'] ?? 'eastasia', language: dotenv.env['AZURE_ASR_LANGUAGE'] ?? 'zh-CN', )) { // 初始化失败,直接抛出异常 Logger.error('语音识别服务初始化失败'); throw Exception('无法初始化语音识别服务'); } // 开始连续识别 final success = await _voiceRecognitionService.startContinuousRecognition(); if (!success) { Logger.error('启动连续语音识别失败'); throw Exception('无法启动语音识别'); } _isListening = true; isListening.value = true; recognizedText.value = ''; _hasRecognizedSpeech = false; _hasFinalResult = false; // 添加一个变量来存储完整的用户输入 String fullUserInput = ''; // 使用语音识别服务的recognitionStream而不是方法的返回值 _recognitionSubscription = _voiceRecognitionService.recognitionStream?.listen((event) { if (event.type == RecognitionEventType.finalResult) { // 添加日志输出最终识别结果 Logger.info('语音识别最终结果: ${event.text}'); if (event.text.isNotEmpty) { // 将最终结果添加到完整输入中,并添加适当的标点符号 if (fullUserInput.isNotEmpty && !fullUserInput.endsWith('。') && !fullUserInput.endsWith('?') && !fullUserInput.endsWith('!') && !fullUserInput.endsWith('.') && !fullUserInput.endsWith('?') && !fullUserInput.endsWith('!')) { fullUserInput += ','; } fullUserInput += event.text; // 更新可观察的识别文本 recognizedText.value = fullUserInput; _hasRecognizedSpeech = true; _hasFinalResult = true; // 重置交互计时器,因为用户刚刚说了话 _resetInteractionTimer(); _processUserInput(fullUserInput); // 重置用户输入 fullUserInput = ''; recognizedText.value = ''; _hasRecognizedSpeech = false; _hasFinalResult = false; } } else if (event.type == RecognitionEventType.recognizing) { // 添加日志输出中间识别结果 Logger.info('语音识别中间结果: ${event.text}'); if (event.text.isNotEmpty) { // 更新当前的中间结果,但不添加到完整输入中 recognizedText.value = fullUserInput + (fullUserInput.isEmpty ? "" : ",") + event.text; _hasRecognizedSpeech = true; // 检测到用户说话,重置交互计时器 _resetInteractionTimer(); // 如果系统正在播放TTS或接收AI响应,检测到用户开始说话时立即中断 if (_ttsService.isPlaying && event.text.trim().isNotEmpty) { // 停止当前TTS播放 _ttsService.stop(); // 设置标志,表示应该取消当前的AI响应 _shouldCancelAiResponse = true; // 取消当前的AI响应流订阅 _aiResponseSubscription?.cancel(); _aiResponseSubscription = null; // 添加日志输出中断TTS播放 Logger.info('检测到用户说话,中断TTS播放'); } } } else if (event.type == RecognitionEventType.error) { // 添加日志输出识别错误 Logger.error('语音识别错误: ${event.error}'); print('识别错误: ${event.error}'); } }, onError: (error) { // 添加日志输出语音识别流错误 Logger.error('语音识别流错误: $error'); print('语音识别流错误: $error'); _isListening = false; isListening.value = false; }); // 启动交互计时器 _resetInteractionTimer(); } catch (e) { Logger.error('启动语音识别失败: $e'); print('启动语音识别失败: $e'); _isListening = false; isListening.value = false; rethrow; } } // 处理用户输入 Future _processUserInput(String userInput) async { if (userInput.isEmpty || _currentSystemPrompt == null) { return; } try { // 保存用户消息到历史记录 _messageHistory.add(Message( role: 'user', content: userInput, timestamp: DateTime.now(), )); // 构建用于生成回应的消息列表 final messages = [ {'role': 'system', 'content': _currentSystemPrompt!}, {'role': 'user', 'content': userInput}, ]; String fullResponse = ''; Logger.info('开始生成AI响应...'); // 重置取消标志 _shouldCancelAiResponse = false; // 创建一个本地变量来跟踪是否已取消 bool isCancelled = false; // 使用成员变量中存储的agent voice _ttsService.startSession(_agentVoice); // 获取AI响应流 final responseStream = _aiService.sendMessageStream( messages: messages, systemPrompt: _currentSystemPrompt!, ); // 创建一个订阅来处理响应流 _aiResponseSubscription = responseStream.listen( (chunk) { // 如果已经设置了取消标志,则不处理这个块 if (_shouldCancelAiResponse) { isCancelled = true; return; } fullResponse += chunk; // 直接将每个文本块传递给 TTS 接口,不进行切句或攒句处理 try { // 使用成员变量中存储的agent voice _ttsService.speak(chunk, speaker: _agentVoice); // Logger.info('直接播放 AI 响应块: $chunk'); } catch (e) { Logger.error('TTS 播放出错: $e'); } }, onError: (e) { Logger.error('AI响应流错误: $e'); print('AI响应流错误: $e'); // 清理订阅 _aiResponseSubscription = null; }, onDone: () { _ttsService.endSession(); // 如果已取消,不处理剩余的文本 if (!isCancelled && !_shouldCancelAiResponse) { Logger.info('AI 响应生成完成'); } _aiResponseSubscription = null; // AI响应完成后,重新启动超时计时器 _resetInteractionTimer(); // 如果响应被取消,不保存到历史记录 if (!_shouldCancelAiResponse && fullResponse.isNotEmpty) { // 保存助手回复到历史记录 _messageHistory.add(Message( role: 'assistant', content: fullResponse, timestamp: DateTime.now(), )); // Logger.info('保存AI响应到历史记录,长度: ${fullResponse.length}'); } // 限制历史记录长度 if (_messageHistory.length > 20) { _messageHistory.removeRange(0, _messageHistory.length - 20); Logger.info('历史记录超过20条,已裁剪'); } } ); } catch (e) { Logger.error('处理用户输入失败: $e'); print('处理用户输入失败: $e'); // 播放错误提示,使用成员变量中存储的agent voice await _ttsService.speak("抱歉,出现了一些问题", speaker: _agentVoice); } } // 停止语音识别并返回识别的文本 Future stopVoiceRecognition() async { if (!_isListening) { return ''; } try { // 取消订阅 await _recognitionSubscription?.cancel(); _recognitionSubscription = null; // 停止语音识别 await _voiceRecognitionService.stopContinuousRecognition(); // 获取最终识别结果 final result = recognizedText.value; // 添加日志输出停止语音识别的最终结果 Logger.info('停止语音识别,最终结果: $result'); // 重置状态 _isListening = false; isListening.value = false; return result; } catch (e) { // 添加日志输出停止语音识别失败 Logger.error('停止语音识别失败: $e'); print('停止语音识别失败: $e'); _isListening = false; isListening.value = false; // 直接返回当前已识别的文本,不尝试重置 final result = recognizedText.value; Logger.info('停止语音识别失败,使用当前识别结果: $result'); return result; } } List get messageHistory => List.unmodifiable(_messageHistory); void clearHistory() { _messageHistory.clear(); } @override void onClose() { _recognitionSubscription?.cancel(); _interactionTimer?.cancel(); _interactionTimer = null; _aiResponseSubscription?.cancel(); // 停止TTS播放 if (_ttsService.isPlaying) { _ttsService.stop(); } super.onClose(); } }