feat: 新增方案 C 的 Handoff 路由宿主,并让抽城市支持自然语言。
把图 JSON 编译成 CreateHandoffBuilderWith + WithHandoff;Agent 固定 Id 以免交接工具名错位;DeepSeek 关闭 thinking,避免多轮丢掉 reasoning_content 导致 400。
This commit is contained in:
@@ -15,6 +15,7 @@ namespace MAF1.Utils;
|
||||
public sealed class AgentFactory
|
||||
{
|
||||
private readonly ChatClient _chatClient;
|
||||
private readonly bool _disableDeepSeekThinking;
|
||||
|
||||
public AgentFactory(LlmOptions options)
|
||||
{
|
||||
@@ -33,6 +34,8 @@ public sealed class AgentFactory
|
||||
? DefaultModelFor(options.Endpoint)
|
||||
: options.Model;
|
||||
|
||||
_disableDeepSeekThinking = LooksLikeDeepSeek(options.Endpoint);
|
||||
|
||||
if (LooksLikeAzureOpenAI(options.Endpoint))
|
||||
{
|
||||
Console.Error.WriteLine($"使用 Azure OpenAI: {options.Endpoint} 部署: {model}");
|
||||
@@ -48,14 +51,34 @@ public sealed class AgentFactory
|
||||
}
|
||||
|
||||
Console.Error.WriteLine($"使用 OpenAI 兼容接口: {clientOptions.Endpoint?.ToString() ?? "https://api.openai.com/v1"} 模型: {model}");
|
||||
if (_disableDeepSeekThinking)
|
||||
{
|
||||
Console.Error.WriteLine("已关闭 DeepSeek thinking,避免 Handoff 多轮丢掉 reasoning_content 导致 HTTP 400。");
|
||||
}
|
||||
|
||||
_chatClient = new OpenAIClient(new ApiKeyCredential(options.ApiKey), clientOptions)
|
||||
.GetChatClient(model);
|
||||
}
|
||||
|
||||
/// <summary>把 ChatClient 包成 Microsoft.Agents.AI 的 AIAgent,并挂上 tools。</summary>
|
||||
public AIAgent CreateAgent(string name, string instructions, IList<AITool>? tools = null)
|
||||
/// <summary>把 ChatClient 包成 Microsoft.Agents.AI 的 AIAgent,并挂上 tools。Id 固定为 name,Handoff 工具才是 handoff_to_n1。</summary>
|
||||
public AIAgent CreateAgent(string name, string instructions, IList<AITool>? tools = null, string? description = null)
|
||||
{
|
||||
return _chatClient.AsAIAgent(instructions: instructions, name: name, tools: tools);
|
||||
ChatOptions chatOptions = new()
|
||||
{
|
||||
Instructions = instructions,
|
||||
Tools = tools,
|
||||
};
|
||||
ChatClientAgentOptions options = new()
|
||||
{
|
||||
Id = name,
|
||||
Name = name,
|
||||
Description = description,
|
||||
ChatOptions = chatOptions,
|
||||
};
|
||||
return _chatClient.AsAIAgent(options, clientFactory: inner =>
|
||||
_disableDeepSeekThinking
|
||||
? new ConfigureOptionsChatClient(inner, DeepSeekThinking.Disable)
|
||||
: inner);
|
||||
}
|
||||
|
||||
/// <summary>合并配置文件和环境变量。插件进程里环境变量通常由宿主写入。</summary>
|
||||
@@ -78,6 +101,10 @@ public sealed class AgentFactory
|
||||
return "gpt-4o-mini";
|
||||
}
|
||||
|
||||
private static bool LooksLikeDeepSeek(string? endpoint)
|
||||
=> !string.IsNullOrWhiteSpace(endpoint)
|
||||
&& endpoint.Contains("deepseek", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static bool LooksLikeAzureOpenAI(string? endpoint)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(endpoint) || !Uri.TryCreate(endpoint, UriKind.Absolute, out Uri? uri))
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace MAF1.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// DeepSeek V4 默认开 thinking。Handoff 多轮带 tools 时必须回传 reasoning_content,
|
||||
/// 客户端会丢掉该字段导致 HTTP 400。请求里关闭 thinking 即可。
|
||||
/// </summary>
|
||||
internal static class DeepSeekThinking
|
||||
{
|
||||
public static void Disable(ChatOptions options)
|
||||
{
|
||||
options.RawRepresentationFactory = _ =>
|
||||
{
|
||||
ChatCompletionOptions raw = new();
|
||||
#pragma warning disable SCME0001
|
||||
raw.Patch.Set("$.thinking"u8, BinaryData.FromObjectAsJson(new Dictionary<string, string>
|
||||
{
|
||||
["type"] = "disabled",
|
||||
}));
|
||||
#pragma warning restore SCME0001
|
||||
return raw;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user