feat: 完成agent接口功能

This commit is contained in:
chenchun
2025-12-24 12:18:33 +08:00
parent 62940ae25a
commit eb6ec06157
4 changed files with 123 additions and 38 deletions

View File

@@ -1,6 +1,4 @@
using System.ClientModel;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.Json;
@@ -13,49 +11,52 @@ using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using OpenAI;
using OpenAI.Chat;
using OpenAI.Responses;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
using Yi.Framework.AiHub.Domain.AiGateWay;
using Yi.Framework.AiHub.Domain.Entities.Chat;
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
using Yi.Framework.AiHub.Domain.Shared.Consts;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Shared.Enums;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.AiHub.Domain.Managers;
public class ChatManager : DomainService
{
private readonly AiGateWayManager _aiGateWayManager;
private readonly ILoggerFactory _loggerFactory;
private readonly ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
private readonly ISqlSugarRepository<AgentStoreAggregateRoot> _agentStoreRepository;
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
public ChatManager(AiGateWayManager aiGateWayManager, ILoggerFactory loggerFactory,
private readonly AiMessageManager _aiMessageManager;
private readonly UsageStatisticsManager _usageStatisticsManager;
private readonly PremiumPackageManager _premiumPackageManager;
private readonly AiGateWayManager _aiGateWayManager;
public ChatManager(ILoggerFactory loggerFactory,
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository,
ISqlSugarRepository<TokenAggregateRoot> tokenRepository)
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository, AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager, PremiumPackageManager premiumPackageManager, AiGateWayManager aiGateWayManager)
{
_aiGateWayManager = aiGateWayManager;
_loggerFactory = loggerFactory;
_messageRepository = messageRepository;
_agentStoreRepository = agentStoreRepository;
_tokenRepository = tokenRepository;
_aiMessageManager = aiMessageManager;
_usageStatisticsManager = usageStatisticsManager;
_premiumPackageManager = premiumPackageManager;
_aiGateWayManager = aiGateWayManager;
}
public async Task AgentCompleteChatStreamAsync(HttpContext httpContext,
Guid sessionId,
string content,
string token,
Guid tokenId,
string modelId,
Guid userId,
List<string> tools
, CancellationToken cancellationToken)
{
// HttpClient.DefaultProxy = new WebProxy("127.0.0.1:8888");
var response = httpContext.Response;
// 设置响应头,声明是 SSE 流
@@ -63,16 +64,37 @@ public class ChatManager : DomainService
response.Headers.TryAdd("Cache-Control", "no-cache");
response.Headers.TryAdd("Connection", "keep-alive");
var modelDescribe=await _aiGateWayManager.GetModelAsync(ModelApiTypeEnum.OpenAi,modelId);
//token状态检查在应用层统一处理
var token = await _tokenRepository.GetFirstAsync(x => x.Id == tokenId);
var client = new OpenAIClient(new ApiKeyCredential(token.Token),
var client = new OpenAIClient(new ApiKeyCredential(token),
new OpenAIClientOptions
{
Endpoint = new Uri("https://yxai.chat/v1"),
});
#pragma warning disable OPENAI001
var agent = client.GetChatClient(modelId)
.CreateAIAgent("你是一个专业的网页ai助手擅长解答用户问题");
#pragma warning restore OPENAI001
.CreateAIAgent(new ChatClientAgentOptions
{
ChatOptions = new()
{
Instructions = """
Ai
"""
},
Name = "橙子小弟",
ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore(
#pragma warning disable MEAI001
new MessageCountingChatReducer(10), // 保留最近10条非系统消息
#pragma warning restore MEAI001
ctx.SerializedState,
ctx.JsonSerializerOptions
)
});
//线程根据sessionId数据库中获取
var agentStore =
@@ -103,7 +125,8 @@ public class ChatManager : DomainService
ToolMode = ChatToolMode.Auto
};
await foreach (var update in agent.RunStreamingAsync(content, currentThread, new ChatClientAgentRunOptions(chatOptions), cancellationToken))
await foreach (var update in agent.RunStreamingAsync(content, currentThread,
new ChatClientAgentRunOptions(chatOptions), cancellationToken))
{
// 检查每个更新中的内容
foreach (var updateContent in update.Contents)
@@ -120,7 +143,7 @@ public class ChatManager : DomainService
},
isDone: false, cancellationToken);
break;
//工具调用完成
case FunctionResultContent functionResult:
await SendHttpStreamMessageAsync(httpContext,
@@ -131,7 +154,7 @@ public class ChatManager : DomainService
},
isDone: false, cancellationToken);
break;
//内容输出
case TextContent textContent:
//发送消息给前端
@@ -143,24 +166,50 @@ public class ChatManager : DomainService
},
isDone: false, cancellationToken);
break;
//用量统计
case UsageContent usageContent:
//存储message 为了token算费
var usage = new ThorUsageResponse
{
InputTokens = Convert.ToInt32(usageContent.Details.InputTokenCount ?? 0),
OutputTokens = Convert.ToInt32(usageContent.Details.OutputTokenCount ?? 0),
TotalTokens = usageContent.Details.TotalTokenCount ?? 0,
};
//设置倍率
usage.SetSupplementalMultiplier(modelDescribe.Multiplier);
//创建系统回答,用于计费统计
await _aiMessageManager.CreateSystemMessageAsync(userId, sessionId, new MessageInputDto
{
Content = "不与存储",
ModelId = modelId,
TokenUsage = usage
}, tokenId);
//创建用量统计,用于统计分析
await _usageStatisticsManager.SetUsageAsync(userId, modelId, usage, tokenId);
//扣减尊享token包用量
if (PremiumPackageConst.ModeIds.Contains(modelId))
{
var totalTokens = usage?.TotalTokens ?? 0;
if (totalTokens > 0)
{
await _premiumPackageManager.TryConsumeTokensAsync(userId, totalTokens);
}
}
await SendHttpStreamMessageAsync(httpContext,
new AgentResultOutput
{
TypeEnum = AgentResultTypeEnum.Usage,
Content = new ThorUsageResponse
{
InputTokens = Convert.ToInt32(usageContent.Details.InputTokenCount ?? 0),
OutputTokens = Convert.ToInt32(usageContent.Details.OutputTokenCount ?? 0),
TotalTokens = usageContent.Details.TotalTokenCount ?? 0,
}
TypeEnum = update.RawRepresentation is ChatResponseUpdate raw
? raw.FinishReason?.Value == "tool_calls"
? AgentResultTypeEnum.ToolCallUsage
: AgentResultTypeEnum.Usage
: AgentResultTypeEnum.Usage,
Content = usage!
},
isDone: false, cancellationToken);
Console.WriteLine();
Console.WriteLine($"✅ 用量统计: {usageContent.Details.TotalTokenCount}");
break;
}
}
@@ -220,7 +269,7 @@ public class ChatManager : DomainService
}
else
{
output = JsonSerializer.Serialize(content,ThorJsonSerializer.DefaultOptions);
output = JsonSerializer.Serialize(content, ThorJsonSerializer.DefaultOptions);
}
await response.WriteAsync($"data: {output}\n\n", Encoding.UTF8, cancellationToken).ConfigureAwait(false);