访问 AI 函数中的数据

创建 AI 函数时,可能需要访问 AI 模型提供的参数之外的上下文数据。 该 Microsoft.Extensions.AI 库提供了多种机制,用于将数据传递给函数委托。

AIFunction

AIFunction 类型表示可描述为 AI 服务并调用的函数。 可以通过调用其中一个AIFunction重载来创建AIFunctionFactory.Create对象。 但 AIFunction 也是一个基类,你可以从该基类派生并实现自己的 AI 函数类型。 DelegatingAIFunction 提供了一种简单的方法来包装现有的 AIFunction,并添加额外的功能,包括捕获用于使用的其他数据。

传递数据

可以在函数创建时通过闭包或 AdditionalProperties 将数据与其关联。 如果要创建自己的函数,可以根据需要填充 AdditionalProperties 。 如果您使用 AIFunctionFactory 创建函数,则可以用 AIFunctionFactoryOptions.AdditionalProperties 来填充数据。

还可以捕获对数据的任何引用,作为提供给 AIFunctionFactory的委托的一部分。 也就是说,你可以将任何你想引用的信息作为 AIFunction 本身的一部分嵌入其中。

访问函数委托中的数据

你可以直接调用 AIFunction ,也可以通过使用 FunctionInvokingChatClient间接调用它。 以下各节介绍如何使用任一方法访问参数数据。

手动函数调用

如果你通过调用AIFunction.InvokeAsync(AIFunctionArguments, CancellationToken)来手动调用AIFunction,则你需要传入AIFunctionArguments。 类型 AIFunctionArguments 包括:

在您的AIFunctionFactory.Create委托内部如果想访问AIFunctionArgumentsIServiceProvider,请创建IServiceProviderAIFunctionArguments类型的参数。 该参数将绑定到从AIFunctionArguments传递到AIFunction.InvokeAsync()的相关数据。

以下代码演示了一个示例:

Delegate getWeatherDelegate = (AIFunctionArguments args) =>
{
    // Access named parameters from the arguments dictionary.
    string? location = args.TryGetValue("location", out object? loc) ? loc.ToString() : "Unknown";
    string? units = args.TryGetValue("units", out object? u) ? u.ToString() : "celsius";

    return $"Weather in {location}: 35°{units}";
};

// Create the AIFunction.
AIFunction getWeather = AIFunctionFactory.Create(getWeatherDelegate);

// Call the function manually.
var result = await getWeather.InvokeAsync(new AIFunctionArguments
{
    { "location", "Seattle" },
    { "units", "F" }
});
Console.WriteLine($"Function result: {result}");

CancellationToken 也是一个特殊情况:如果 AIFunctionFactory.Create 委托或 lambda 表达式具有参数 CancellationToken ,那么该参数将绑定到传递给 AIFunction.InvokeAsync()CancellationToken

调用 FunctionInvokingChatClient

FunctionInvokingChatClient 发布有关当前对 FunctionInvokingChatClient.CurrentContext 的调用状态的信息,其中不仅包括参数,还包括所有输入的 ChatMessage 对象,ChatOptions,以及调用了多少个函数中的哪一个的详细信息。 你可以在ChatOptions.AdditionalProperties中添加任何你想要的数据,并在AIFunction内从FunctionInvokingChatClient.CurrentContext.Options.AdditionalProperties提取这些数据。

以下代码演示了一个示例:

FunctionInvokingChatClient client = new FunctionInvokingChatClient(
    new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey))
    .GetChatClient(model).AsIChatClient());

AIFunction getWeather = AIFunctionFactory.Create(() =>
    {
        // Access named parameters from the arguments dictionary.
        AdditionalPropertiesDictionary props =
            FunctionInvokingChatClient.CurrentContext.Options.AdditionalProperties;

        string location = props["location"].ToString();
        string units = props["units"].ToString();

        return $"Weather in {location}: 35°{units}";
    });

var chatOptions = new ChatOptions
{
    Tools = [getWeather],
    AdditionalProperties = new AdditionalPropertiesDictionary {
        ["location"] = "Seattle",
        ["units"] = "F"
    },
};

List<ChatMessage> chatHistory = [
    new(ChatRole.System, "You're a helpful weather assistant.")
];
chatHistory.Add(new ChatMessage(ChatRole.User, "What's the weather like?"));

ChatResponse response = await client.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine($"Response: {response.Text}");

依赖项注入

如果使用 FunctionInvokingChatClient 自动调用函数,客户端会配置一个 AIFunctionArguments 对象,并将其传入 AIFunction。 因为AIFunctionArguments 包含 IServiceProvider(由 FunctionInvokingChatClient 自身提供),如果使用标准 DI 方法构建客户端,IServiceProvider 就会被一直传递到你的 AIFunction。 在这个阶段,你可以从 DI 中查询任何你所需要的内容。

高级技术

如果希望对参数的绑定方式进行更精细的控制,可以使用 AIFunctionFactoryOptions.ConfigureParameterBinding,这样您就可以掌握每个参数的配置方式。 例如, MCP C# SDK 使用此技术 从 DI 自动绑定参数。

如果使用 AIFunctionFactory.Create(MethodInfo, Func<AIFunctionArguments,Object>, AIFunctionFactoryOptions) 重载,也可以在创建每次调用实例方法的目标对象时运行自己的任意逻辑。 你可以随意配置该实例。