You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
11 KiB

import 'dart:async';
import 'package:get/get.dart';
import 'package:flutter/widgets.dart';
import '../../core/utils/logger.dart';
import '../providers/agent_provider.dart';
import '../models/agent.dart';
import '../../modules/chat/controllers/chat_controller.dart';
import '../../modules/chat/models/message_model.dart';
import 'volcano_asr_api_service.dart';
import 'volcano_ai_service.dart';
import 'azure_flutter_tts_service.dart';
import 'chat_history_service.dart';
/// 后台代理服务,用于在应用处于后台时处理AI交互
class BackgroundAgentService extends GetxService {
// 单例实例
static BackgroundAgentService? _instance;
// 单例工厂方法
factory BackgroundAgentService() {
_instance ??= BackgroundAgentService._internal();
return _instance!;
}
// 私有构造函数
BackgroundAgentService._internal() {
Logger.info('BackgroundAgentService 已创建');
}
// 服务实例
final VolcanoAsrApiService _asrService = Get.find<VolcanoAsrApiService>();
final VolcanoAIService _aiService = Get.find<VolcanoAIService>();
final AzureFlutterTtsService _ttsService = Get.find<AzureFlutterTtsService>();
final ChatHistoryService _historyService = Get.find<ChatHistoryService>();
// 状态控制
// isProcessing: 表示整个代理交互会话是否处于活跃状态(包括语音识别、AI处理和TTS播放)
final RxBool isProcessing = false.obs;
final RxString recognizedText = ''.obs;
// 流式响应控制
StreamSubscription? _recognitionSubscription;
StreamSubscription? _aiResponseSubscription;
// 定时器,用于检测无活动超时
Timer? _inactivityTimer;
// 当前处理的Agent
Agent? _currentAgent;
// 本地消息历史备份
final List<Map<String, String>> _localMessageHistory = [];
// 获取消息历史 - 优先从ChatController获取,否则使用本地存储
List<Map<String, String>> get messageHistory {
if (Get.isRegistered<ChatController>()) {
final chatController = Get.find<ChatController>();
if (chatController.agent?.id == 'personal_assistant') {
return chatController.messages.map((m) => {
'role': m.role,
'content': m.content,
}).toList();
}
}
if (_localMessageHistory.isEmpty) {
_loadChatHistory();
}
return _localMessageHistory;
}
// 语音识别是否活跃
bool get isListening => _recognitionSubscription != null;
// TTS是否正在播放
bool get isTtsSpeaking => _ttsService.isActuallyPlaying();
@override
void onClose() {
_cleanupResources();
super.onClose();
}
/// 清理所有资源
void _cleanupResources() {
_recognitionSubscription?.cancel();
_aiResponseSubscription?.cancel();
if (_ttsService.isActuallyPlaying()) {
_ttsService.stop();
}
if (isListening) {
_asrService.stopRecognition();
}
_inactivityTimer?.cancel();
}
/// 开始代理交互
Future<void> startAgentInteraction(String agentId) async {
// 防止重复处理
if (isProcessing.value) {
Logger.warning('已经在处理交互,忽略此次请求');
return;
}
// 先说"我在听"
_ttsService.speak("我在!");
isProcessing.value = true;
try {
// 获取Agent
_currentAgent = AgentProvider.getAgentById(agentId);
if (_currentAgent == null) {
Logger.error('无法获取Agent: $agentId');
isProcessing.value = false;
return;
}
// 加载该Agent的聊天历史
_loadChatHistory();
// 启动语音识别
await _startVoiceRecognition();
// 启动无活动定时器
_startInactivityTimer();
} catch (e) {
Logger.error('启动代理交互失败', e);
isProcessing.value = false;
}
}
/// 停止语音识别
Future<void> _stopVoiceRecognition() async {
if (!isListening) return;
await _recognitionSubscription?.cancel();
_recognitionSubscription = null;
_asrService.stopRecognition();
recognizedText.value = '';
}
/// 开始语音识别
Future<void> _startVoiceRecognition() async {
if (isListening) return;
try {
final success = await _asrService.startRecognition();
if (success) {
_recognitionSubscription = _asrService.recognitionStream.listen(
(event) {
switch (event.type) {
case RecognitionEventType.recognizing:
if (event.text.isEmpty) return;
recognizedText.value = event.text;
Logger.info('处理语音识别中间结果:${event.text}');
// 检测到用户说话时中断当前响应
if (_ttsService.isActuallyPlaying() || _aiResponseSubscription != null) {
_interruptCurrentResponse("检测到用户开始讲话,中断当前响应");
}
break;
case RecognitionEventType.finalResult:
if (event.text.isEmpty) return;
recognizedText.value = event.text;
Logger.info('处理语音识别最终结果:${event.text}');
_processRecognizedText(event.text);
recognizedText.value = '';
break;
case RecognitionEventType.error:
Logger.info('语音识别错误: ${event.error}');
stopInteraction();
break;
default:
break;
}
},
onError: (error) {
Logger.info('语音识别错误:$error');
stopInteraction();
},
);
} else {
Logger.error('启动语音识别失败');
}
} catch (e) {
Logger.error('启动语音识别失败', e);
isProcessing.value = false;
}
}
/// 中断当前响应
void _interruptCurrentResponse(String reason) {
Logger.info(reason);
if (_ttsService.isActuallyPlaying()) {
_ttsService.stop();
}
if (_aiResponseSubscription != null) {
_aiResponseSubscription!.cancel();
_aiResponseSubscription = null;
Logger.info('已中断AI响应流和TTS播放');
}
}
/// 处理识别到的文本
Future<void> _processRecognizedText(String text) async {
if (text.trim().isEmpty || _currentAgent == null) return;
try {
// 中断任何正在进行的响应
_interruptCurrentResponse("处理新的用户输入,中断当前响应");
// 添加用户消息到历史记录
_addMessageToHistory('user', text);
String fullResponse = '';
// 获取AI响应流
final responseStream = _aiService.sendMessageStream(
messages: messageHistory,
systemPrompt: _currentAgent!.systemPrompt,
);
// 订阅响应流
_aiResponseSubscription = responseStream.listen(
(chunk) {
fullResponse += chunk;
_ttsService.speak(chunk);
_resetInactivityTimer();
},
onDone: () {
_addMessageToHistory('assistant', fullResponse);
_aiResponseSubscription = null;
},
onError: (error) {
Logger.error('AI响应错误', error);
const errorMessage = '抱歉,我遇到了一些问题,无法回应您的请求。';
_ttsService.speak(errorMessage);
_addMessageToHistory('assistant', errorMessage);
_aiResponseSubscription = null;
},
);
} catch (e) {
Logger.error('处理识别文本失败', e);
const errorMessage = '抱歉,我遇到了一些问题,无法回应您的请求。';
_ttsService.speak(errorMessage);
_addMessageToHistory('assistant', errorMessage);
}
}
/// 重新启动语音识别
Future<void> _restartVoiceRecognition() async {
await _stopVoiceRecognition();
await Future.delayed(Duration(milliseconds: 500));
await _startVoiceRecognition();
}
/// 添加消息到历史记录
void _addMessageToHistory(String role, String content) {
// 添加到本地历史记录
_localMessageHistory.add({
'role': role,
'content': content,
});
// 保持本地历史记录不超过10条消息
if (_localMessageHistory.length > 10) {
_localMessageHistory.removeAt(0);
}
// 同步消息到ChatController或保存到本地存储
if (Get.isRegistered<ChatController>()) {
final chatController = Get.find<ChatController>();
if (chatController.agent?.id == 'personal_assistant') {
chatController.addMessage(Message(
role: role,
content: content,
timestamp: DateTime.now(),
));
}
} else {
_saveChatHistory();
}
}
// 加载聊天历史
void _loadChatHistory() {
if (_currentAgent == null) return;
try {
final messages = _historyService.loadHistory(_currentAgent!.id);
_localMessageHistory.clear();
for (final message in messages) {
_localMessageHistory.add({
'role': message.role,
'content': message.content,
});
}
} catch (e) {
Logger.error('加载聊天历史记录失败', e);
}
}
// 保存聊天历史
void _saveChatHistory() {
if (_currentAgent == null) return;
try {
final messages = _localMessageHistory.map((m) => Message(
role: m['role']!,
content: m['content']!,
timestamp: DateTime.now(),
)).toList();
_historyService.saveHistory(_currentAgent!.id, messages);
} catch (e) {
Logger.error('保存聊天历史记录失败', e);
}
}
/// 启动无活动定时器
void _startInactivityTimer() {
_inactivityTimer?.cancel();
_inactivityTimer = Timer(Duration(seconds: 30), () {
if (!_ttsService.isActuallyPlaying() && isProcessing.value) {
Logger.info('检测到30秒内无活动,停止语音识别');
stopInteraction();
}
});
}
/// 重置无活动定时器
void _resetInactivityTimer() {
if (_inactivityTimer != null) {
_inactivityTimer!.cancel();
_startInactivityTimer();
}
}
/// 响应新的媒体事件(双击媒体按钮)
Future<void> respondToNewMediaEvent() async {
_ttsService.speak("我在!");
if (!isListening) {
await _restartVoiceRecognition();
}
_resetInactivityTimer();
}
/// 停止当前交互
Future<void> stopInteraction() async {
try {
_saveChatHistory();
_cleanupResources();
isProcessing.value = false;
_currentAgent = null;
Logger.info('已停止后台代理交互');
} catch (e) {
Logger.error('停止交互失败', e);
}
}
}