2025-10-11 18:25:59 +08:00

861 lines
33 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using XiaoZhiSharp;
using XiaoZhiSharp.Protocols;
using System.Collections.ObjectModel;
using System.IO.Pipelines;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using XiaoZhiSharp_MauiBlazorApp.McpTools;
using ModelContextProtocol.Protocol;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using XiaoZhiSharp_MauiBlazorApp.McpTools;
using System.Net.WebSockets;
using XiaoZhiSharp.Models;
namespace XiaoZhiSharp_MauiBlazorApp.Services
{
// 聊天消息类
public class ChatMessage
{
public string Content { get; set; } = "";
public bool IsUser { get; set; } = false;
public DateTime Timestamp { get; set; } = DateTime.Now;
public ChatMessage(string content, bool isUser)
{
Content = content;
IsUser = isUser;
Timestamp = DateTime.Now;
}
}
public class XiaoZhi_AgentService : IDisposable
{
private readonly XiaoZhiAgent _agent;
private bool _disposed = false;
// MCP相关字段
private IMcpClient _mcpClient = null!;
private Pipe _clientToServerPipe = new Pipe();
private Pipe _serverToClientPipe = new Pipe();
private IHost? _host;
public string QuestionMessae = "";
public string AnswerMessae = "";
public string Emotion = "normal";
public float AudioLevel = 0.0f; // 音频强度 0-1
public bool IsConnected = false; // 连接状态
// 设置相关属性
public string ServerUrl { get; set; } = "wss://api.tenclass.net/xiaozhi/v1/";
public string OtaUrl { get; set; } = "https://api.tenclass.net/xiaozhi/ota/";
public string DeviceId { get; set; } = Global.DeviceId;
public int VadThreshold { get; set; } = 40;
public bool IsDebugMode { get; set; } = false;
// VAD配置从Unity版本移植
public bool UseVAD { get; set; } = true;
public float VadEnergyThreshold { get; set; } = 0.015f;
public int VadSilenceFrames { get; set; } = 20;
public float TtsCooldownTime { get; set; } = 0.5f;
// OTA 相关信息
public OtaResponse? LatestOtaResponse { get; private set; }
public string OtaStatus { get; private set; } = "未检查";
public DateTime? LastOtaCheckTime { get; private set; }
public string? ActivationCode { get; private set; }
public string? ActivationMessage { get; private set; }
public string? FirmwareVersion { get; private set; }
public string? FirmwareUrl { get; private set; }
public DateTime? ServerTime { get; private set; }
public string? MqttEndpoint { get; private set; }
public ObservableCollection<string> DebugLogs { get; private set; } = new();
// 版本信息
public string CurrentVersion { get; private set; } = "1.0.0"; // 当前版本
public string? LatestVersion { get; private set; } // 最新版本
public bool NeedUpdate { get; private set; } = false; // 是否需要更新
public string UpdateMessage { get; private set; } = ""; // 更新提示信息
// 聊天历史记录
public ObservableCollection<ChatMessage> ChatHistory { get; private set; } = new();
public XiaoZhiAgent Agent
{
get { return _agent; }
}
public int VadCounter
{
get { return _agent.AudioService?.VadCounter ?? 0; }
}
public bool IsRecording
{
get { return _agent.AudioService?.IsRecording ?? false; }
}
private System.Timers.Timer? _connectionMonitorTimer;
private DateTime _lastReconnectAttempt = DateTime.MinValue;
private int _reconnectAttempts = 0;
private const int MaxReconnectAttempts = 5; // 最大重连尝试次数
private const int ReconnectInterval = 5000; // 重连间隔(毫秒)
public XiaoZhi_AgentService()
{
// 从配置初始化设置
LoadSettings();
// 获取当前版本(可以从应用程序信息中获取)
CurrentVersion = AppInfo.VersionString ?? "1.0.0";
XiaoZhiSharp.Global.VadThreshold = VadThreshold;
XiaoZhiSharp.Global.IsDebug = IsDebugMode;
XiaoZhiSharp.Global.IsMcp = true; // 启用MCP协议支持
//XiaoZhiSharp.Global.IsAudio = false;
_agent = new XiaoZhiAgent();
_agent.DeviceId = DeviceId;
_agent.WsUrl = ServerUrl;
_agent.OtaUrl = OtaUrl;
_agent.OnMessageEvent += Agent_OnMessageEvent;
_agent.OnAudioPcmEvent += Agent_OnAudioPcmEvent;
_agent.OnOtaEvent += Agent_OnOtaEvent;
// 根据平台注册相应的音频服务
if (DeviceInfo.Platform == DevicePlatform.Android)
{
try
{
var audioService = new Services.AudioService();
// 配置VAD参数
//audioService.ConfigureVAD(UseVAD, VadEnergyThreshold, VadSilenceFrames, TtsCooldownTime);
_agent.AudioService = audioService;
AddDebugLog($"已初始化Android音频服务并配置VAD: 启用={UseVAD}, 阈值={VadEnergyThreshold}, 静音帧数={VadSilenceFrames}, 冷却时间={TtsCooldownTime}秒");
}
catch (Exception ex)
{
AddDebugLog($"音频服务初始化失败: {ex.Message}");
}
}
else if (DeviceInfo.Platform == DevicePlatform.WinUI)
{
//_agent.AudioService = new Services.AudioService();
AddDebugLog("Windows平台暂未实现音频服务");
}
// 初始化MCP服务
InitializeMcpService();
_ = Task.Run(async () => await _agent.Start());
IsConnected = true; // 假设启动后就连接成功
// 启动连接状态监测定时器
StartConnectionMonitor();
}
// 初始化MCP服务
private void InitializeMcpService()
{
try
{
var builder = Host.CreateApplicationBuilder();
builder.Services
.AddMcpServer()
.WithStreamServerTransport(_clientToServerPipe.Reader.AsStream(), _serverToClientPipe.Writer.AsStream())
.WithTools<IotThings_Tool>()
.WithTools<Chrome_Tool>()
.WithTools<WindowsApp_Tool>();
_host = builder.Build();
_host.StartAsync();
AddDebugLog("MCP服务初始化成功");
}
catch (Exception ex)
{
AddDebugLog($"MCP服务初始化失败: {ex.Message}");
}
}
private void LoadSettings()
{
// 从Preferences加载设置
ServerUrl = Preferences.Get("ServerUrl", "wss://api.tenclass.net/xiaozhi/v1/");
OtaUrl = Preferences.Get("OtaUrl", "https://api.tenclass.net/xiaozhi/ota/");
DeviceId = Preferences.Get("DeviceId", Global.DeviceId);
VadThreshold = Preferences.Get("VadThreshold", 40);
IsDebugMode = Preferences.Get("IsDebugMode", false);
// 加载VAD配置
UseVAD = Preferences.Get("UseVAD", true);
VadEnergyThreshold = Preferences.Get("VadEnergyThreshold", 0.015f);
VadSilenceFrames = Preferences.Get("VadSilenceFrames", 20);
TtsCooldownTime = Preferences.Get("TtsCooldownTime", 0.5f);
}
public void SaveSettings()
{
// 保存设置到Preferences
Preferences.Set("ServerUrl", ServerUrl);
Preferences.Set("OtaUrl", OtaUrl);
Preferences.Set("DeviceId", DeviceId);
Preferences.Set("VadThreshold", VadThreshold);
Preferences.Set("IsDebugMode", IsDebugMode);
// 保存VAD配置
Preferences.Set("UseVAD", UseVAD);
Preferences.Set("VadEnergyThreshold", VadEnergyThreshold);
Preferences.Set("VadSilenceFrames", VadSilenceFrames);
Preferences.Set("TtsCooldownTime", TtsCooldownTime);
}
public async Task ApplySettings()
{
// 应用设置并重启连接
SaveSettings();
// 更新全局配置
XiaoZhiSharp.Global.VadThreshold = VadThreshold;
XiaoZhiSharp.Global.IsDebug = IsDebugMode;
// 更新Agent配置
_agent.DeviceId = DeviceId;
_agent.WsUrl = ServerUrl;
_agent.OtaUrl = OtaUrl;
// 更新音频服务的VAD配置
if (_agent.AudioService != null && DeviceInfo.Platform == DevicePlatform.Android)
{
var audioService = _agent.AudioService as Services.AudioService;
//audioService?.ConfigureVAD(UseVAD, VadEnergyThreshold, VadSilenceFrames, TtsCooldownTime);
}
// 重启连接
await _agent.Restart();
// 重置VAD配置
if (_agent.AudioService != null && DeviceInfo.Platform == DevicePlatform.Android)
{
var audioService = _agent.AudioService as Services.AudioService;
//audioService?.ConfigureVAD(UseVAD, VadEnergyThreshold, VadSilenceFrames, TtsCooldownTime);
AddDebugLog("连接重启后已重置VAD参数");
}
}
public void ResetSettings()
{
// 重置为默认值
ServerUrl = "wss://api.tenclass.net/xiaozhi/v1/";
OtaUrl = "https://api.tenclass.net/xiaozhi/ota/";
DeviceId = Global.DeviceId;
VadThreshold = 40;
IsDebugMode = false;
// 重置VAD配置为默认值
UseVAD = true;
VadEnergyThreshold = 0.015f;
VadSilenceFrames = 20;
TtsCooldownTime = 0.5f;
}
private async Task Agent_OnAudioPcmEvent(byte[] pcm)
{
// 计算音频强度
AudioLevel = CalculateAudioLevel(pcm);
}
private float CalculateAudioLevel(byte[] pcmData)
{
if (pcmData == null || pcmData.Length == 0)
return 0.0f;
double rms = 0;
int sampleCount = pcmData.Length / 2; // 16位音频
for (int i = 0; i < sampleCount; i++)
{
short sample = BitConverter.ToInt16(pcmData, i * 2);
rms += sample * sample;
}
rms = Math.Sqrt(rms / sampleCount);
float level = (float)(rms / short.MaxValue);
// 限制在0-1范围
return Math.Max(0.0f, Math.Min(1.0f, level * 10)); // 放大10倍以便显示
}
private async Task Agent_OnMessageEvent(string type, string message)
{
if (type == "question")
{
QuestionMessae = message;
// 添加用户问题到聊天历史,但首先检查是否已经存在相同内容的用户消息
if (ChatHistory.Count == 0 || ChatHistory.Last().Content != message || !ChatHistory.Last().IsUser)
{
ChatHistory.Add(new ChatMessage(message, true));
}
}
else if (type == "answer")
{
AnswerMessae = message;
// 添加AI回答到聊天历史
if (ChatHistory.Count > 0 && !ChatHistory.Last().IsUser)
{
// 如果最后一条消息是AI的更新它
ChatHistory.Last().Content = message;
}
else
{
// 否则添加新的AI消息
ChatHistory.Add(new ChatMessage(message, false));
}
}
else if (type == "emotion")
{
Emotion = message;
}
else if (type == "answer_stop")
{
// TTS播放结束触发音频服务的冷却期
if (_agent.AudioService != null && DeviceInfo.Platform == DevicePlatform.Android)
{
var audioService = _agent.AudioService as Services.AudioService;
audioService?.StopPlaying(); // 这会触发冷却期
// 添加新逻辑:在冷却期结束后,自动启动新一轮录音
_ = Task.Run(async () =>
{
// 等待冷却期结束(ttsCooldownTime+0.1秒额外缓冲)
await Task.Delay(TimeSpan.FromSeconds(TtsCooldownTime + 0.1));
// 确保不在录音中
if (_agent != null && !_agent.IsRecording)
{
AddDebugLog("冷却期结束,自动开始新一轮录音");
await MainThread.InvokeOnMainThreadAsync(async () =>
{
try
{
await _agent.StartRecording("auto");
}
catch (Exception ex)
{
AddDebugLog($"自动开始录音失败: {ex.Message}");
}
});
}
});
}
}
// 处理MCP消息
else if (type == "mcp")
{
await HandleMcpMessage(message);
}
// 添加调试日志
if (IsDebugMode)
{
AddDebugLog($"[{type}] {message}");
}
}
private async Task Agent_OnOtaEvent(OtaResponse? otaResponse)
{
LastOtaCheckTime = DateTime.Now;
LatestOtaResponse = otaResponse;
if (otaResponse != null)
{
OtaStatus = "检查成功";
// 提取激活信息
if (otaResponse.Activation != null)
{
ActivationCode = otaResponse.Activation.Code;
ActivationMessage = otaResponse.Activation.Message;
}
// 提取固件信息
if (otaResponse.Firmware != null)
{
FirmwareVersion = otaResponse.Firmware.Version;
FirmwareUrl = otaResponse.Firmware.Url;
LatestVersion = FirmwareVersion;
// 比较版本
CompareVersions();
}
// 提取服务器时间
if (otaResponse.ServerTime != null)
{
ServerTime = DateTimeOffset.FromUnixTimeMilliseconds(otaResponse.ServerTime.Timestamp).DateTime;
}
// 提取MQTT信息
if (otaResponse.Mqtt != null)
{
MqttEndpoint = otaResponse.Mqtt.Endpoint;
}
// 如果WebSocket配置更新了更新当前URL
if (otaResponse.WebSocket != null && !string.IsNullOrEmpty(otaResponse.WebSocket.Url))
{
ServerUrl = otaResponse.WebSocket.Url;
}
AddDebugLog($"OTA检查成功激活码: {ActivationCode}");
if (!string.IsNullOrEmpty(FirmwareUrl))
{
AddDebugLog($"发现固件更新: {FirmwareVersion}");
}
}
else
{
OtaStatus = "检查失败";
AddDebugLog("OTA检查失败使用默认配置");
}
}
private void AddDebugLog(string message)
{
var logMessage = $"[{DateTime.Now:HH:mm:ss}] {message}";
if (DebugLogs.Count >= 100) // 保持最新100条日志
{
DebugLogs.RemoveAt(0);
}
DebugLogs.Add(logMessage);
}
public void ClearDebugLogs()
{
DebugLogs.Clear();
}
/// <summary>
/// 清除聊天记录
/// </summary>
public void ClearChatHistory()
{
ChatHistory.Clear();
QuestionMessae = "";
AnswerMessae = "";
}
/// <summary>
/// 获取OTA更新状态信息
/// </summary>
/// <returns>包含当前版本、最新版本和更新状态的字符串</returns>
public string GetUpdateStatusInfo()
{
if (string.IsNullOrEmpty(LatestVersion))
{
return $"当前版本: {CurrentVersion}\n尚未检查更新";
}
if (NeedUpdate)
{
return $"当前版本: {CurrentVersion}\n最新版本: {LatestVersion}\n状态: 需要更新";
}
else
{
return $"当前版本: {CurrentVersion}\n最新版本: {LatestVersion}\n状态: 已是最新";
}
}
// 版本比较方法
private void CompareVersions()
{
try
{
if (string.IsNullOrEmpty(LatestVersion))
{
NeedUpdate = false;
UpdateMessage = "无法获取最新版本信息";
return;
}
// 解析版本号
var currentParts = CurrentVersion.Split('.');
var latestParts = LatestVersion.Split('.');
// 比较主版本号、次版本号、修订号
for (int i = 0; i < Math.Min(currentParts.Length, latestParts.Length); i++)
{
if (int.TryParse(currentParts[i], out int current) &&
int.TryParse(latestParts[i], out int latest))
{
if (latest > current)
{
NeedUpdate = true;
UpdateMessage = $"发现新版本!当前版本: {CurrentVersion},最新版本: {LatestVersion}";
AddDebugLog(UpdateMessage);
return;
}
else if (latest < current)
{
NeedUpdate = false;
UpdateMessage = $"当前版本已是最新(当前: {CurrentVersion},服务器: {LatestVersion}";
return;
}
}
}
// 如果版本号位数不同
if (latestParts.Length > currentParts.Length)
{
NeedUpdate = true;
UpdateMessage = $"发现新版本!当前版本: {CurrentVersion},最新版本: {LatestVersion}";
}
else
{
NeedUpdate = false;
UpdateMessage = $"当前版本已是最新({CurrentVersion}";
}
AddDebugLog(UpdateMessage);
}
catch (Exception ex)
{
AddDebugLog($"版本比较失败: {ex.Message}");
NeedUpdate = false;
UpdateMessage = "版本比较失败";
}
}
public async Task ManualOtaCheck()
{
try
{
OtaStatus = "检查中...";
AddDebugLog("开始手动OTA检查");
// 确保设置当前版本
_agent.CurrentVersion = CurrentVersion;
var result = await _agent.CheckOtaUpdate();
if (result != null)
{
AddDebugLog("手动OTA检查成功");
// 显示版本比较结果
if (NeedUpdate)
{
AddDebugLog($"【版本信息】{UpdateMessage}");
if (!string.IsNullOrEmpty(FirmwareUrl))
{
AddDebugLog($"【更新地址】{FirmwareUrl}");
}
}
else
{
AddDebugLog($"【版本信息】{UpdateMessage}");
}
}
else
{
AddDebugLog("手动OTA检查失败");
}
}
catch (Exception ex)
{
OtaStatus = "检查异常";
AddDebugLog($"OTA检查异常: {ex.Message}");
}
}
// 处理MCP消息
private async Task HandleMcpMessage(string message)
{
try
{
AddDebugLog($"收到MCP消息: {message}");
if (_mcpClient == null)
{
var clientTransport = new StreamClientTransport(
serverInput: _clientToServerPipe.Writer.AsStream(),
serverOutput: _serverToClientPipe.Reader.AsStream());
_mcpClient = await McpClientFactory.CreateAsync(clientTransport);
AddDebugLog("MCP客户端初始化成功");
}
dynamic? mcp = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(message);
if (mcp == null)
{
AddDebugLog("MCP消息解析失败");
return;
}
if (mcp.method == "initialize")
{
// 处理初始化请求
var resultData = new
{
protocolVersion = "2024-11-05",
capabilities = _mcpClient.ServerCapabilities,
serverInfo = new
{
name = "XiaoZhiSharp MAUI",
version = CurrentVersion
}
};
JsonNode resultNode = JsonSerializer.SerializeToNode(resultData);
JsonRpcResponse? response = new JsonRpcResponse()
{
Id = new RequestId((long)mcp.id),
JsonRpc = "2.0",
Result = resultNode
};
await _agent.McpMessage(JsonSerializer.Serialize(response));
AddDebugLog("已响应MCP初始化请求");
}
else if (mcp.method == "tools/list")
{
// 处理工具列表请求
var tools = await _mcpClient.ListToolsAsync();
List<Tool> toolsList = new List<Tool>();
foreach (var item in tools)
{
toolsList.Add(item.ProtocolTool);
}
var resultData = new
{
tools = toolsList
};
JsonNode resultNode = JsonSerializer.SerializeToNode(resultData);
JsonRpcResponse? response = new JsonRpcResponse()
{
Id = new RequestId((long)mcp.id),
JsonRpc = "2.0",
Result = resultNode
};
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
await _agent.McpMessage(JsonSerializer.Serialize(response, options));
AddDebugLog("已响应MCP工具列表请求");
}
else if (mcp.method == "tools/call")
{
// 处理工具调用请求
JsonNode? root = JsonNode.Parse(message);
string? name = root?["params"]?["name"]?.GetValue<string>();
JsonNode? argumentsNode = root?["params"]?["arguments"];
Dictionary<string, object>? arguments = null;
if (argumentsNode != null)
{
arguments = argumentsNode.Deserialize<Dictionary<string, object>>();
}
AddDebugLog($"调用工具: {name}, 参数: {argumentsNode}");
CallToolResponse? callToolResponse = await _mcpClient.CallToolAsync(name, arguments);
JsonNode jsonNode = JsonSerializer.SerializeToNode(callToolResponse);
JsonRpcResponse? response = new JsonRpcResponse()
{
Id = new RequestId((long)mcp.id),
JsonRpc = "2.0",
Result = jsonNode
};
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
await _agent.McpMessage(JsonSerializer.Serialize(response, options));
AddDebugLog("已响应MCP工具调用请求");
}
}
catch (Exception ex)
{
AddDebugLog($"处理MCP消息出错: {ex.Message}");
}
}
/// <summary>
/// 启动连接状态监测定时器
/// </summary>
private void StartConnectionMonitor()
{
if (_connectionMonitorTimer != null)
{
_connectionMonitorTimer.Stop();
_connectionMonitorTimer.Dispose();
}
_connectionMonitorTimer = new System.Timers.Timer(2000); // 每2秒检查一次
_connectionMonitorTimer.Elapsed += async (sender, e) => await CheckConnectionStatus();
_connectionMonitorTimer.AutoReset = true;
_connectionMonitorTimer.Start();
AddDebugLog("启动连接状态监测");
}
/// <summary>
/// 检查连接状态并在必要时重连
/// </summary>
private async Task CheckConnectionStatus()
{
try
{
bool wasConnected = IsConnected;
IsConnected = _agent.ConnectState == WebSocketState.Open;
// 如果连接状态发生变化
if (IsConnected != wasConnected)
{
await MainThread.InvokeOnMainThreadAsync(() => {
if (IsConnected)
{
AddDebugLog("连接状态: 已连接");
_reconnectAttempts = 0; // 连接成功,重置重连尝试计数
// 重连成功后彻底重置音频服务和VAD状态
if (_agent.AudioService != null && DeviceInfo.Platform == DevicePlatform.Android)
{
var audioService = _agent.AudioService as Services.AudioService;
if (audioService != null)
{
// 停止任何可能在进行的录音
if (audioService.IsRecording)
{
audioService.StopRecording();
}
// 确保不在播放状态
if (audioService.IsPlaying)
{
audioService.StopPlaying();
}
// 重置VAD参数和状态
//audioService.ConfigureVAD(UseVAD, VadEnergyThreshold, VadSilenceFrames, TtsCooldownTime);
//audioService.ResetTtsState(); // 特别重置TTS状态
AddDebugLog("重连成功已重置音频服务和VAD参数");
// 延迟一秒后尝试开始新的录音会话
_ = Task.Run(async () =>
{
await Task.Delay(1000);
await MainThread.InvokeOnMainThreadAsync(async () =>
{
try
{
if (!_agent.IsRecording)
{
AddDebugLog("重连后开始新一轮录音");
await _agent.StartRecording("auto");
}
}
catch (Exception ex)
{
AddDebugLog($"重连后开始录音失败: {ex.Message}");
}
});
});
}
}
}
else
{
AddDebugLog("连接状态: 已断开");
// 断线时停止任何录音
if (_agent.IsRecording)
{
_ = _agent.StopRecording();
AddDebugLog("断线检测,已停止录音");
}
}
});
}
// 如果断开连接,尝试重连
if (!IsConnected &&
(DateTime.Now - _lastReconnectAttempt).TotalMilliseconds > ReconnectInterval &&
_reconnectAttempts < MaxReconnectAttempts)
{
_lastReconnectAttempt = DateTime.Now;
_reconnectAttempts++;
await MainThread.InvokeOnMainThreadAsync(async () => {
AddDebugLog($"尝试重新连接... (第{_reconnectAttempts}次)");
try
{
await _agent.Restart();
}
catch (Exception ex)
{
AddDebugLog($"重新连接失败: {ex.Message}");
}
});
}
// 如果多次尝试重连都失败,暂停一段时间后重试
if (!IsConnected && _reconnectAttempts >= MaxReconnectAttempts &&
(DateTime.Now - _lastReconnectAttempt).TotalSeconds > 30)
{
_reconnectAttempts = 0; // 重置尝试次数,进入新一轮尝试
}
}
catch (Exception ex)
{
AddDebugLog($"连接监测异常: {ex.Message}");
}
}
/// <summary>
/// 释放资源,确保应用程序关闭时能够正确释放所有资源
/// </summary>
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
// 释放连接监测定时器
if (_connectionMonitorTimer != null)
{
_connectionMonitorTimer.Stop();
_connectionMonitorTimer.Dispose();
_connectionMonitorTimer = null;
}
// 停止MCP服务
_host?.StopAsync().Wait();
_host?.Dispose();
// 释放其他资源
_agent?.Dispose();
// 标记为已释放
GC.SuppressFinalize(this);
}
}
}
}