From ee4cb20eef25fa077a39b32cf68d75da63a95dda Mon Sep 17 00:00:00 2001 From: chenchun Date: Wed, 24 Dec 2025 14:17:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90agent=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Chat/AgentToolOutput.cs | 8 ++ .../Services/Chat/AiChatService.cs | 131 ++++++++++-------- .../Yi.Framework.AiHub.Domain.Shared.csproj | 1 + .../Managers/ChatManager.cs | 26 +++- .../Mcp/DeepThinkTool.cs | 2 +- .../Mcp/OnlineSearchTool.cs | 2 +- 6 files changed, 100 insertions(+), 70 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/AgentToolOutput.cs diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/AgentToolOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/AgentToolOutput.cs new file mode 100644 index 00000000..3be3e16c --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/AgentToolOutput.cs @@ -0,0 +1,8 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; + +public class AgentToolOutput +{ + public string Code { get; set; } + + public string Name { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs index 6fab3406..341638ce 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs @@ -20,6 +20,7 @@ using Yi.Framework.AiHub.Application.Contracts.Dtos; using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; using Yi.Framework.AiHub.Domain; using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.AiHub.Domain.Entities.Chat; using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Extensions; using Yi.Framework.AiHub.Domain.Managers; @@ -47,13 +48,16 @@ public class AiChatService : ApplicationService private readonly ChatManager _chatManager; private readonly TokenManager _tokenManager; private readonly IAccountService _accountService; + private readonly ISqlSugarRepository _agentStoreRepository; + public AiChatService(IHttpContextAccessor httpContextAccessor, AiBlacklistManager aiBlacklistManager, ISqlSugarRepository aiModelRepository, ILogger logger, AiGateWayManager aiGateWayManager, PremiumPackageManager premiumPackageManager, - ChatManager chatManager, TokenManager tokenManager, IAccountService accountService) + ChatManager chatManager, TokenManager tokenManager, IAccountService accountService, + ISqlSugarRepository agentStoreRepository) { _httpContextAccessor = httpContextAccessor; _aiBlacklistManager = aiBlacklistManager; @@ -64,6 +68,7 @@ public class AiChatService : ApplicationService _chatManager = chatManager; _tokenManager = tokenManager; _accountService = accountService; + _agentStoreRepository = agentStoreRepository; } @@ -154,50 +159,6 @@ public class AiChatService : ApplicationService CurrentUser.Id, sessionId, null, cancellationToken); } - /// - /// Agent 发送消息 - /// - [HttpPost("ai-chat/agent/send")] - public async Task PostAgentSendAsync([FromBody] AgentSendInput input, CancellationToken cancellationToken) - { - var tokenValidation = await _tokenManager.ValidateTokenAsync(input.Token, input.ModelId); - - await _aiBlacklistManager.VerifiyAiBlacklist(tokenValidation.UserId); - // 验证用户是否为VIP - var userInfo = await _accountService.GetAsync(null, null, tokenValidation.UserId); - if (userInfo == null) - { - throw new UserFriendlyException("用户信息不存在"); - } - - // 检查是否为VIP(使用RoleCodes判断) - if (!userInfo.RoleCodes.Contains(AiHubConst.VipRole) && userInfo.User.UserName != "cc") - { - throw new UserFriendlyException("该接口为尊享服务专用,需要VIP权限才能使用"); - } - - //如果是尊享包服务,需要校验是是否尊享包足够 - if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) - { - // 检查尊享token包用量 - var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId); - if (availableTokens <= 0) - { - throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); - } - } - - await _chatManager.AgentCompleteChatStreamAsync(_httpContextAccessor.HttpContext, - input.SessionId, - input.Content, - input.Token, - tokenValidation.TokenId, - input.ModelId, - tokenValidation.UserId, - input.Tools, - cancellationToken); - } - /// /// 发送消息 /// @@ -234,27 +195,75 @@ public class AiChatService : ApplicationService CurrentUser.Id, null, null, cancellationToken); } - [HttpPost("ai-chat/tool")] - public string GetTool() - { - var toolClasses = typeof(YiFrameworkAiHubDomainModule).Assembly.GetTypes() - .Where(x => x.GetCustomAttribute() is not null) - .ToList(); - List mcpTools = new List(); - foreach (var toolClass in toolClasses) + /// + /// Agent 发送消息 + /// + [HttpPost("ai-chat/agent/send")] + public async Task PostAgentSendAsync([FromBody] AgentSendInput input, CancellationToken cancellationToken) + { + var tokenValidation = await _tokenManager.ValidateTokenAsync(input.Token, input.ModelId); + + await _aiBlacklistManager.VerifiyAiBlacklist(tokenValidation.UserId); + // 验证用户是否为VIP + var userInfo = await _accountService.GetAsync(null, null, tokenValidation.UserId); + if (userInfo == null) { - var instance = LazyServiceProvider.GetRequiredService(toolClass); - var toolMethods = toolClass.GetMethods() - .Where(y => y.GetCustomAttribute() is not null).ToList(); - foreach (var toolMethod in toolMethods) + throw new UserFriendlyException("用户信息不存在"); + } + + // 检查是否为VIP(使用RoleCodes判断) + if (!userInfo.RoleCodes.Contains(AiHubConst.VipRole) && userInfo.User.UserName != "cc") + { + throw new UserFriendlyException("该接口为尊享服务专用,需要VIP权限才能使用"); + } + + //如果是尊享包服务,需要校验是是否尊享包足够 + if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) + { + // 检查尊享token包用量 + var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId); + if (availableTokens <= 0) { - mcpTools.add(McpServerTool.Create(toolMethod, instance)); + throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } } - var json = System.Text.Json.JsonSerializer.Serialize(mcpTools.Select(x => x.ProtocolTool).ToList(), - McpJsonUtilities.DefaultOptions); - return json; + await _chatManager.AgentCompleteChatStreamAsync(_httpContextAccessor.HttpContext, + input.SessionId, + input.Content, + input.Token, + tokenValidation.TokenId, + input.ModelId, + tokenValidation.UserId, + input.Tools, + cancellationToken); + } + + /// + /// 获取 Agent 工具 + /// + /// + [HttpPost("ai-chat/agent/tool")] + public List GetAgentToolAsync() + { + var agentTools = _chatManager.GetTools().Select(x => new AgentToolOutput + { + Code = x.Code, + Name = x.Name + }).ToList(); + return agentTools; + } + + /// + /// 获取 Agent 上下文 + /// + /// + [HttpPost("ai-chat/agent/context/{sessionId}")] + [Authorize] + public async Task GetAgentContextAsync([FromRoute] Guid sessionId) + { + var data = await _agentStoreRepository.GetFirstAsync(x => x.SessionId == sessionId); + return data?.Store; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj index b9a8850e..c88e6115 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Yi.Framework.AiHub.Domain.Shared.csproj @@ -2,6 +2,7 @@ + diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs index 468e7e60..b0a95a01 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs @@ -49,7 +49,18 @@ public class ChatManager : DomainService _aiGateWayManager = aiGateWayManager; } - + /// + /// agent流式对话 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public async Task AgentCompleteChatStreamAsync(HttpContext httpContext, Guid sessionId, string content, @@ -120,11 +131,11 @@ public class ChatManager : DomainService currentThread = agent.GetNewThread(); } - + //给agent塞入工具 var toolContents = GetTools(); var chatOptions = new ChatOptions() { - Tools = toolContents.Select(x => (AITool)x).ToList(), + Tools = toolContents.Where(x=>tools.Contains(x.Code)).Select(x => (AITool)x.Tool).ToList(), ToolMode = ChatToolMode.Auto }; @@ -230,13 +241,13 @@ public class ChatManager : DomainService } - private List GetTools() + public List<(string Code, string Name, AIFunction Tool)> GetTools() { var toolClasses = typeof(YiFrameworkAiHubDomainModule).Assembly.GetTypes() .Where(x => x.GetCustomAttribute() is not null) .ToList(); - List mcpTools = new List(); + List<(string Code, string Name, AIFunction Tool)> mcpTools = new(); foreach (var toolClass in toolClasses) { var instance = LazyServiceProvider.GetRequiredService(toolClass); @@ -244,10 +255,11 @@ public class ChatManager : DomainService .Where(y => y.GetCustomAttribute() is not null).ToList(); foreach (var toolMethod in toolMethods) { - mcpTools.add(AIFunctionFactory.Create(toolMethod, instance)); + var display = toolMethod.GetCustomAttribute()?.DisplayName; + var tool = AIFunctionFactory.Create(toolMethod, instance); + mcpTools.add((tool.Name, display, tool)); } } - return mcpTools; } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/DeepThinkTool.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/DeepThinkTool.cs index dd51d44e..2ac5521d 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/DeepThinkTool.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/DeepThinkTool.cs @@ -7,7 +7,7 @@ namespace Yi.Framework.AiHub.Domain.Mcp; [McpServerToolType] public class DeepThinkTool:ISingletonDependency { - [McpServerTool, Description("进行深度思考")] + [McpServerTool, Description("进行深度思考"),DisplayName("深度思考")] public void DeepThink() { diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/OnlineSearchTool.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/OnlineSearchTool.cs index c11540d1..7efd2fb9 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/OnlineSearchTool.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Mcp/OnlineSearchTool.cs @@ -7,7 +7,7 @@ namespace Yi.Framework.AiHub.Domain.Mcp; [McpServerToolType] public class OnlineSearchTool:ISingletonDependency { - [McpServerTool, Description("进行在线搜索")] + [McpServerTool, Description("进行在线搜索"),DisplayName("在线搜索")] public string OnlineSearch(string keyword) { return "奥德赛第一中学学生会会长是:郭老板";