创建 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 包括:
- 命名参数的字典。
-
Context:用于将其他环境数据传递到函数中的任意
IDictionary<object, object>值。 -
Services
IServiceProvider:允许
AIFunction解析依赖项注入(DI)容器中的任意状态。
在您的AIFunctionFactory.Create委托内部如果想访问AIFunctionArguments或IServiceProvider,请创建IServiceProvider或AIFunctionArguments类型的参数。 该参数将绑定到从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) 重载,也可以在创建每次调用实例方法的目标对象时运行自己的任意逻辑。 你可以随意配置该实例。