初始提交

This commit is contained in:
2026-08-26 18:06:02 +08:00
commit 5544788917
53 changed files with 4101 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using OpenAI;
using OpenAI.Chat;
namespace MAF1.Utils;
public sealed class AgentFactory
{
private readonly ChatClient _chatClient;
public AgentFactory(LlmOptions options)
{
if (string.IsNullOrWhiteSpace(options.ApiKey))
{
throw new InvalidOperationException(
"""
appsettings.json Llm
OPENAI_API_KEY / Llm:ApiKey
OPENAI_ENDPOINT / Llm:Endpoint
OPENAI_CHAT_MODEL / Llm:Model
""");
}
string model = string.IsNullOrWhiteSpace(options.Model)
? DefaultModelFor(options.Endpoint)
: options.Model;
if (LooksLikeAzureOpenAI(options.Endpoint))
{
Console.Error.WriteLine($"使用 Azure OpenAI: {options.Endpoint} 部署: {model}");
_chatClient = new AzureOpenAIClient(new Uri(options.Endpoint), new ApiKeyCredential(options.ApiKey))
.GetChatClient(model);
return;
}
OpenAIClientOptions clientOptions = new();
if (!string.IsNullOrWhiteSpace(options.Endpoint))
{
clientOptions.Endpoint = ToOpenAICompatibleEndpoint(options.Endpoint);
}
Console.Error.WriteLine($"使用 OpenAI 兼容接口: {clientOptions.Endpoint?.ToString() ?? "https://api.openai.com/v1"} 模型: {model}");
_chatClient = new OpenAIClient(new ApiKeyCredential(options.ApiKey), clientOptions)
.GetChatClient(model);
}
public AIAgent CreateAgent(string name, string instructions, IList<AITool>? tools = null)
{
return _chatClient.AsAIAgent(instructions: instructions, name: name, tools: tools);
}
public static LlmOptions Load(IConfiguration config)
{
LlmOptions options = config.GetSection("Llm").Get<LlmOptions>() ?? new LlmOptions();
options.ApiKey = FirstNonEmpty(options.ApiKey, Env("OPENAI_API_KEY"), Env("AZURE_OPENAI_API_KEY"));
options.Endpoint = FirstNonEmpty(options.Endpoint, Env("OPENAI_ENDPOINT"), Env("OPENAI_BASE_URL"), Env("AZURE_OPENAI_ENDPOINT"));
options.Model = FirstNonEmpty(options.Model, Env("OPENAI_CHAT_MODEL"), Env("AZURE_OPENAI_DEPLOYMENT_NAME"));
return options;
}
private static string DefaultModelFor(string? endpoint)
{
if (!string.IsNullOrWhiteSpace(endpoint) && endpoint.Contains("deepseek", StringComparison.OrdinalIgnoreCase))
{
return "deepseek-chat";
}
return "gpt-4o-mini";
}
private static bool LooksLikeAzureOpenAI(string? endpoint)
{
if (string.IsNullOrWhiteSpace(endpoint) || !Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri))
{
return false;
}
string host = uri.Host;
return host.Contains("openai.azure.com", StringComparison.OrdinalIgnoreCase)
|| host.Contains("cognitiveservices.azure.com", StringComparison.OrdinalIgnoreCase)
|| host.Contains("services.ai.azure.com", StringComparison.OrdinalIgnoreCase);
}
private static Uri ToOpenAICompatibleEndpoint(string endpoint)
{
string trimmed = endpoint.TrimEnd('/');
if (!trimmed.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
{
trimmed += "/v1";
}
return new Uri(trimmed);
}
private static string? Env(string name) => Environment.GetEnvironmentVariable(name);
private static string FirstNonEmpty(params string?[] values)
{
foreach (string? value in values)
{
if (!string.IsNullOrWhiteSpace(value))
{
return value;
}
}
return "";
}
}