import 'package:get/get.dart'; import 'dart:async'; import 'volcano_ai_service.dart'; import 'volcano_tts_service.dart'; import 'voice_recognition_service.dart'; import '../../modules/chat/models/message_model.dart'; class BackgroundAgentService extends GetxService { static BackgroundAgentService get to => Get.find(); final VolcanoAIService _aiService; final VolcanoTtsService _ttsService; final VoiceRecognitionService _voiceRecognitionService; final List _messageHistory = []; String _pendingTtsText = ''; static const int _minTtsLength = 20; bool _isProcessing = false; bool _isListening = false; StreamSubscription? _recognitionSubscription; // 可观察的状态 final RxBool isListening = false.obs; final RxString recognizedText = ''.obs; // 添加一个标志,表示是否已经识别到语音 bool _hasRecognizedSpeech = false; // 添加一个标志,表示是否应该继续循环交互 bool _shouldContinueInteraction = false; // 添加一个 Completer 用于在识别到最终结果时完成 Completer? _recognitionCompleter; // 添加一个标志,表示是否已经收到最终结果 bool _hasFinalResult = false; // 添加一个计时器,用于在一段时间没有新的识别结果时提交当前结果 Timer? _silenceTimer; // 添加一个计时器,用于检测用户长时间没有说话 Timer? _noSpeechTimer; // 最后一次识别到语音的时间 DateTime? _lastSpeechTime; // 添加一个标志,表示系统是否正在播放 TTS bool _isSpeaking = false; // 添加一个变量来跟踪当前的AI响应流订阅 StreamSubscription? _aiResponseSubscription; // 添加一个标志,表示是否应该取消当前的AI响应 bool _shouldCancelAiResponse = false; // 添加一个变量来跟踪最后一次 TTS 状态变化的时间 DateTime? _lastTtsStateChangeTime; BackgroundAgentService() : _aiService = VolcanoAIService(), _ttsService = Get.find(), _voiceRecognitionService = Get.find() { // 不再监听 TTS 播放状态,而是使用定期检查 // 初始化 TTS 状态 _isSpeaking = _ttsService.isActuallyPlaying(); _lastTtsStateChangeTime = DateTime.now(); // 添加定期检查实际播放状态的计时器 Timer.periodic(const Duration(seconds: 1), (timer) { // 检查实际播放状态 final actuallyPlaying = _ttsService.isActuallyPlaying(); // 如果状态发生变化,更新状态并记录时间 if (_isSpeaking != actuallyPlaying) { print('TTS 播放状态变化: $_isSpeaking -> $actuallyPlaying'); _isSpeaking = actuallyPlaying; _lastTtsStateChangeTime = DateTime.now(); // 如果 TTS 服务的 isPlaying 与实际状态不一致,尝试修正 if (actuallyPlaying != _ttsService.isPlaying.value) { try { _ttsService.isPlaying.value = actuallyPlaying; print('已修正 TTS 服务的播放状态'); } catch (e) { print('修正 TTS 播放状态失败: $e'); } } // 如果 TTS 停止播放,且正在进行语音识别,重新启动无语音超时计时器 if (!actuallyPlaying && _isListening && _hasRecognizedSpeech && _noSpeechTimer == null) { _startNoSpeechTimer(); } } // 如果正在处理交互,进行更全面的检查 if (_isProcessing) { _checkAndUpdateTtsState(); } }); } // 添加一个方法来检查和更新 TTS 状态 void _checkAndUpdateTtsState() { try { // 获取实际播放状态 final actuallyPlaying = _ttsService.isActuallyPlaying(); // 如果状态显示正在播放,但已经很长时间没有状态变化,强制重置 if (actuallyPlaying && _lastTtsStateChangeTime != null) { final now = DateTime.now(); final difference = now.difference(_lastTtsStateChangeTime!); // 如果超过15秒没有状态变化,强制重置 if (difference.inSeconds > 15) { print('强制重置 TTS 状态:已经 ${difference.inSeconds} 秒没有状态变化'); try { // 停止 TTS 播放 _ttsService.stop(); // 强制更新状态 _isSpeaking = false; _ttsService.isPlaying.value = false; // 更新最后状态变化时间 _lastTtsStateChangeTime = DateTime.now(); print('TTS 状态已强制重置'); } catch (e) { print('强制重置 TTS 状态失败: $e'); } } } } catch (e) { print('检查 TTS 播放状态失败: $e'); } } // 处理蓝牙耳机按钮触发的交互 Future handleAgentInteraction(String systemPrompt) async { if (_isProcessing) { print('已经在处理交互,忽略此次请求'); return; } // 设置循环交互标志为 true _shouldContinueInteraction = true; try { // 确保之前的语音识别已经停止 if (_isListening) { print('交互开始前确保语音识别已停止'); await stopVoiceRecognition(); } // 循环进行交互,直到用户 10 秒没有说话或手动停止 while (_shouldContinueInteraction) { await _processSingleInteraction(systemPrompt); } } finally { // 确保在交互结束时停止语音识别 if (_isListening) { await stopVoiceRecognition(); } // 重置语音识别服务 try { print('交互结束,重置语音识别服务'); await _voiceRecognitionService.initialize(); } catch (e) { print('重置语音识别服务失败: $e'); } } } // 启动无语音超时计时器 void _startNoSpeechTimer() { // 检查 TTS 播放状态,使用 isActuallyPlaying 获取实际状态 _isSpeaking = _ttsService.isActuallyPlaying(); // 如果系统正在播放 TTS,不启动计时器 if (_isSpeaking) { print('系统正在播放 TTS,不启动无语音超时计时器 (isPlaying.value: ${_ttsService.isPlaying.value}, 实际播放状态: $_isSpeaking)'); // 即使TTS正在播放,也设置一个更长的安全超时,防止系统永远卡在循环中 _noSpeechTimer?.cancel(); _noSpeechTimer = Timer(const Duration(seconds: 30), () { // 再次检查 TTS 状态,使用 isActuallyPlaying _isSpeaking = _ttsService.isActuallyPlaying(); print('安全超时触发:即使TTS正在播放,30秒内没有新的语音输入,强制退出循环交互 (实际播放状态: $_isSpeaking)'); if (_recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { _recognitionCompleter!.complete(''); } _shouldContinueInteraction = false; }); return; } // 取消之前的计时器 _noSpeechTimer?.cancel(); // 设置新的计时器,如果 10 秒内没有新的识别结果,且系统没有在播放 TTS,则认为用户已经停止交互 _noSpeechTimer = Timer(const Duration(seconds: 10), () { // 再次检查是否正在播放 TTS,使用 isActuallyPlaying _isSpeaking = _ttsService.isActuallyPlaying(); print('无语音超时计时器触发,当前 TTS 播放状态: $_isSpeaking (isPlaying.value: ${_ttsService.isPlaying.value}, 实际播放状态: $_isSpeaking)'); if (!_isSpeaking && _isListening && _recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { print('10秒内没有新的语音输入,且系统没有在播放 TTS,退出循环交互'); _recognitionCompleter!.complete(''); // 直接设置标志,停止循环交互 _shouldContinueInteraction = false; } }); print('启动无语音超时计时器,10秒后检查'); } // 处理单次交互 Future _processSingleInteraction(String systemPrompt) async { _isProcessing = true; _hasRecognizedSpeech = false; _hasFinalResult = false; try { // 强制重置 TTS 状态,确保不会因为错误的状态导致系统卡住 _resetTtsStateIfNeeded(); // 先播放一个简短的提示音或提示语,表示开始监听 try { await _ttsService.speak("我在听"); } catch (e) { print('播放提示音失败: $e'); // 继续执行,不要因为提示音失败而中断整个流程 } // 开始语音识别 bool recognitionStarted = false; String userInput = ''; try { // 确保之前的语音识别已经停止 if (_isListening) { print('检测到上一次语音识别未正确停止,先停止它'); await stopVoiceRecognition(); } // 尝试启动语音识别 await startVoiceRecognition(); recognitionStarted = true; // 创建一个 Completer 来处理语音识别完成 _recognitionCompleter = Completer(); // 启动无语音超时计时器 _startNoSpeechTimer(); // 等待语音识别完成 userInput = await _recognitionCompleter!.future; // 取消无语音超时计时器 _noSpeechTimer?.cancel(); _noSpeechTimer = null; // 如果没有识别到语音,则停止循环交互 if (!_hasRecognizedSpeech || userInput.trim().isEmpty) { print('没有识别到用户语音或识别结果为空,退出循环交互'); // 停止语音识别 if (_isListening) { await stopVoiceRecognition(); } try { await _ttsService.speak("没有听到您说话,已退出语音交互"); } catch (e) { print('播放退出提示失败: $e'); } // 设置标志,停止循环交互 _shouldContinueInteraction = false; _isProcessing = false; return; } // 注意:不再停止语音识别,保持语音识别状态 // 只有在退出交互时才停止语音识别 } catch (e) { print('语音识别过程出错: $e'); } finally { // 清理资源,但保持语音识别状态 _recognitionCompleter = null; _silenceTimer?.cancel(); _silenceTimer = null; _noSpeechTimer?.cancel(); _noSpeechTimer = null; } if (userInput.isEmpty) { try { await _ttsService.speak("没有听到您说话"); } catch (_) {} // 如果没有识别到语音,停止循环交互 _shouldContinueInteraction = false; _isProcessing = false; // 停止语音识别 if (_isListening) { await stopVoiceRecognition(); } return; } // 保存用户消息到历史记录 _messageHistory.add(Message( role: 'user', content: userInput, timestamp: DateTime.now(), )); // 构建用于生成回应的消息列表 final messages = [ {'role': 'system', 'content': systemPrompt}, {'role': 'user', 'content': userInput}, ]; String fullResponse = ''; try { // 重置取消标志 _shouldCancelAiResponse = false; // 创建一个本地变量来跟踪是否已取消 bool isCancelled = false; // 获取AI响应流 final responseStream = _aiService.sendMessageStream( messages: messages, systemPrompt: systemPrompt, ); // 创建一个订阅来处理响应流 _aiResponseSubscription = responseStream.listen( (chunk) { // 如果已经设置了取消标志,则不处理这个块 if (_shouldCancelAiResponse) { isCancelled = true; return; } fullResponse += chunk; // 累积文本并处理TTS _pendingTtsText += chunk; if (_ttsService.isEnabled.value) { if (_pendingTtsText.length >= _minTtsLength) { int lastSentenceEnd = _findLastSentenceEnd(_pendingTtsText); if (lastSentenceEnd > 0) { String textToSpeak = _pendingTtsText.substring(0, lastSentenceEnd + 1); try { _ttsService.speak(textToSpeak); } catch (e) { print('播放TTS失败: $e'); } _pendingTtsText = _pendingTtsText.substring(lastSentenceEnd + 1); } } } }, onError: (e) { print('AI响应流错误: $e'); // 清理订阅 _aiResponseSubscription = null; }, onDone: () { // 如果已取消,不处理剩余的文本 if (!isCancelled && !_shouldCancelAiResponse) { // 处理剩余的文本 if (_ttsService.isEnabled.value && _pendingTtsText.isNotEmpty) { try { _ttsService.speak(_pendingTtsText); } catch (e) { print('播放剩余TTS失败: $e'); } } } _pendingTtsText = ''; _aiResponseSubscription = null; } ); // 等待响应流完成 await _aiResponseSubscription!.asFuture(); } catch (e) { print('AI响应生成失败: $e'); // 如果AI响应失败,使用默认回复 fullResponse = '抱歉,我现在无法回答您的问题。请稍后再试。'; } finally { // 清理资源 _aiResponseSubscription?.cancel(); _aiResponseSubscription = null; _shouldCancelAiResponse = false; } // 如果响应被取消,不保存到历史记录 if (!_shouldCancelAiResponse && fullResponse.isNotEmpty) { // 保存助手回复到历史记录 _messageHistory.add(Message( role: 'assistant', content: fullResponse, timestamp: DateTime.now(), )); } // 限制历史记录长度 if (_messageHistory.length > 20) { _messageHistory.removeRange(0, _messageHistory.length - 20); } // 短暂暂停,然后开始下一轮交互 await Future.delayed(const Duration(milliseconds: 500)); } catch (e) { print('Background agent interaction failed: $e'); try { await _ttsService.speak("抱歉,出现了一些问题"); } catch (_) {} // 发生错误时停止循环交互 _shouldContinueInteraction = false; } finally { _isProcessing = false; } } // 停止循环交互 void stopContinuousInteraction() { _shouldContinueInteraction = false; print('手动停止循环交互'); } // 开始语音识别 Future startVoiceRecognition() async { if (_isListening) { print('已经在监听中,先停止当前监听'); await stopVoiceRecognition(); } try { // 确保语音识别服务已初始化 if (!await _voiceRecognitionService.initialize()) { print('语音识别服务初始化失败,尝试重新初始化'); // 尝试重新初始化 await Future.delayed(const Duration(milliseconds: 500)); if (!await _voiceRecognitionService.initialize()) { throw Exception('无法初始化语音识别服务'); } } // 开始连续识别 final recognitionStream = await _voiceRecognitionService.startContinuousRecognition(); _isListening = true; isListening.value = true; recognizedText.value = ''; _hasRecognizedSpeech = false; _hasFinalResult = false; _lastSpeechTime = null; // 监听识别结果 _recognitionSubscription = recognitionStream.listen((event) { if (event.type == RecognitionEventType.finalResult) { recognizedText.value = event.text; if (event.text.isNotEmpty) { _hasRecognizedSpeech = true; _hasFinalResult = true; _lastSpeechTime = DateTime.now(); // 收到最终结果,立即完成识别过程 if (_recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { print('收到最终识别结果,立即处理: ${event.text}'); _recognitionCompleter!.complete(event.text); } } else { // 如果最终结果为空,但之前有中间结果,仍然标记为有语音 if (_hasRecognizedSpeech) { print('最终识别结果为空,但之前有中间结果,使用最后的中间结果'); if (_recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { _recognitionCompleter!.complete(recognizedText.value); } } else { // 如果最终结果为空,且没有中间结果,重新启动无语音超时计时器 print('最终识别结果为空,且没有中间结果,重新启动无语音超时计时器'); _startNoSpeechTimer(); } } print('最终识别结果: ${event.text}'); } else if (event.type == RecognitionEventType.intermediateResult) { recognizedText.value = event.text; if (event.text.isNotEmpty) { _hasRecognizedSpeech = true; _lastSpeechTime = DateTime.now(); // 如果系统正在播放TTS或接收AI响应,检测到用户开始说话时立即中断 if (_isSpeaking && event.text.trim().isNotEmpty) { print('检测到用户开始说话,中断TTS播放和AI响应'); _ttsService.stop(); // 停止当前TTS播放 // 设置标志,表示应该取消当前的AI响应 _shouldCancelAiResponse = true; // 取消当前的AI响应流订阅 _aiResponseSubscription?.cancel(); _aiResponseSubscription = null; // 清空待处理的TTS文本 _pendingTtsText = ''; } // 取消之前的静默计时器 _silenceTimer?.cancel(); // 取消之前的无语音超时计时器 _noSpeechTimer?.cancel(); _noSpeechTimer = null; // 设置新的静默计时器,如果 2 秒内没有新的识别结果,则认为用户已经停止说话 _silenceTimer = Timer(const Duration(seconds: 2), () { if (_hasRecognizedSpeech && !_hasFinalResult && _recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { print('用户停止说话 2 秒,使用当前识别结果: ${recognizedText.value}'); _recognitionCompleter!.complete(recognizedText.value); } }); } print('中间识别结果: ${event.text}'); } else if (event.type == RecognitionEventType.error) { print('识别错误: ${event.error}'); } }, onError: (error) { print('语音识别流错误: $error'); _isListening = false; isListening.value = false; // 发生错误时完成 completer if (_recognitionCompleter != null && !_recognitionCompleter!.isCompleted) { _recognitionCompleter!.completeError(error); } }); } catch (e) { print('启动语音识别失败: $e'); _isListening = false; isListening.value = false; rethrow; } } // 停止语音识别并返回识别的文本 Future stopVoiceRecognition() async { if (!_isListening) return ''; try { print('停止语音识别...'); // 取消静默计时器 _silenceTimer?.cancel(); _silenceTimer = null; // 取消无语音超时计时器 _noSpeechTimer?.cancel(); _noSpeechTimer = null; // 取消订阅 await _recognitionSubscription?.cancel(); _recognitionSubscription = null; // 停止语音识别 await _voiceRecognitionService.stopContinuousRecognition(); // 获取最终识别结果 final result = recognizedText.value; // 重置状态 _isListening = false; isListening.value = false; print('语音识别已停止'); return result; } catch (e) { print('停止语音识别失败: $e'); _isListening = false; isListening.value = false; // 尝试强制重置语音识别服务 try { await _voiceRecognitionService.initialize(); } catch (_) {} return recognizedText.value; // 返回当前已识别的文本 } } int _findLastSentenceEnd(String text) { final sentenceEnds = [ text.lastIndexOf('。'), text.lastIndexOf('!'), text.lastIndexOf('?'), text.lastIndexOf('.'), text.lastIndexOf('!'), text.lastIndexOf('?'), ]; return sentenceEnds.reduce((max, pos) => pos > max ? pos : max); } List get messageHistory => List.unmodifiable(_messageHistory); void clearHistory() { _messageHistory.clear(); } @override void onClose() { _recognitionSubscription?.cancel(); _silenceTimer?.cancel(); _noSpeechTimer?.cancel(); _aiResponseSubscription?.cancel(); _shouldContinueInteraction = false; super.onClose(); } // 添加一个方法来强制重置 TTS 状态 void _resetTtsStateIfNeeded() { // 检查 TTS 状态,使用 isActuallyPlaying 获取实际状态 bool isCurrentlyPlaying = false; try { isCurrentlyPlaying = _ttsService.isActuallyPlaying(); } catch (e) { print('获取实际播放状态失败: $e'); isCurrentlyPlaying = _ttsService.isPlaying.value; } // 如果实际状态与当前状态不一致,更新它 if (_isSpeaking != isCurrentlyPlaying) { print('重置前检测到 TTS 播放状态不一致,修正: _isSpeaking=$_isSpeaking, 实际=${isCurrentlyPlaying}'); _isSpeaking = isCurrentlyPlaying; // 如果实际状态与 TTS 服务的 isPlaying 不一致,尝试修正 TTS 服务的状态 if (isCurrentlyPlaying != _ttsService.isPlaying.value) { try { _ttsService.isPlaying.value = isCurrentlyPlaying; print('已修正 TTS 服务的播放状态'); } catch (e) { print('修正 TTS 播放状态失败: $e'); } } // 更新最后一次状态变化的时间 _lastTtsStateChangeTime = DateTime.now(); } // 检查是否需要强制重置 _checkAndUpdateTtsState(); } }