-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Add article about accessing data in AI functions #49944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
23e7d2c
Initial plan
Copilot e372665
Add comprehensive AIFunction data passing documentation
Copilot ec638e6
human edits
gewarren 6fe5961
add new how-to on accessing data
gewarren 8e5859b
add metadata
gewarren 198dedb
add snippet tags
gewarren bba3fb4
use .net 10
gewarren 482dce7
undo file rename
gewarren 03afd79
undo include deletion
gewarren 77efecf
review edits
gewarren 716be8d
remove unused options
gewarren 646389e
respond to feedback
gewarren 757f3a2
respond to feedback
gewarren File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| --- | ||
| title: Access data in AI functions | ||
| description: Learn how to pass data to AIFunction objects and how to access the data within the function delegate. | ||
| ms.date: 11/17/2025 | ||
| --- | ||
|
|
||
| # Access data in AI functions | ||
|
|
||
| When you create AI functions, you might need to access contextual data beyond the parameters provided by the AI model. The `Microsoft.Extensions.AI` library provides several mechanisms to pass data to function delegates. | ||
|
|
||
| ## `AIFunction` class | ||
|
|
||
| The <xref:Microsoft.Extensions.AI.AIFunction> type represents a function that can be described to an AI service and invoked. You can create `AIFunction` objects by calling one of the <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create*?displayProperty=nameWithType> overloads. But <xref:Microsoft.Extensions.AI.AIFunction> is also a base class, and you can derive from it and implement your own AI function type. <xref:Microsoft.Extensions.AI.DelegatingAIFunction> provides an easy way to wrap an existing `AIFunction` and layer in additional functionality, including capturing additional data to be used. | ||
|
|
||
| ## Pass data | ||
|
|
||
| You can associate data with the function at the time it's created, either via closure or via <xref:Microsoft.Extensions.AI.ChatOptions.AdditionalProperties>. If you're creating your own function, you can populate `AdditionalProperties` however you want. If you use <xref:Microsoft.Extensions.AI.AIFunctionFactory> to create the function, you can populate data using <xref:Microsoft.Extensions.AI.AIFunctionFactoryOptions.AdditionalProperties?displayProperty=nameWithType>. | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| You can also capture any references to data as part of the delegate provided to `AIFunctionFactory`. That is, you can bake in whatever you want to reference as part of the `AIFunction` itself. | ||
|
|
||
| ## Access data in function delegates | ||
|
|
||
| You might call your `AIFunction` directly, or you might call it indirectly by using <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient>. The following sections describe how to access argument data using either approach. | ||
|
|
||
| ### Manual function invocation | ||
|
|
||
| If you manually invoke an <xref:Microsoft.Extensions.AI.AIFunction> by calling <xref:Microsoft.Extensions.AI.AIFunction.InvokeAsync(Microsoft.Extensions.AI.AIFunctionArguments,System.Threading.CancellationToken)?displayProperty=nameWithType>, you pass in <xref:Microsoft.Extensions.AI.AIFunctionArguments>. The <xref:Microsoft.Extensions.AI.AIFunctionArguments> type includes: | ||
|
|
||
| - A dictionary of named arguments. | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - <xref:Microsoft.Extensions.AI.AIFunctionArguments.Context>: An arbitrary `IDictionary<object, object>` for passing additional ambient data into the function. | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - <xref:Microsoft.Extensions.AI.AIFunctionArguments.Services>: An <xref:System.IServiceProvider> that lets the `AIFunction` resolve arbitrary state from a [dependency injection (DI)](../../core/extensions/dependency-injection.md) container. | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| If you want to access either the `AIFunctionArguments` or the `IServiceProvider` from within your <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create*?displayProperty=nameWithType> delegate, create a parameter typed as `IServiceProvider` or `AIFunctionArguments`. That parameter will be bound to the relevant data from the `AIFunctionArguments` passed to `AIFunction.InvokeAsync()`. | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| The following code shows an example: | ||
|
|
||
| :::code language="csharp" source="snippets/access-data/ArgumentsExample.cs" id="UseAIFunctionArguments"::: | ||
|
|
||
| <xref:System.Threading.CancellationToken> is also special-cased: if the `AIFunctionFactory.Create` delegate or lambda has a `CancellationToken` parameter, it will be bound to the `CancellationToken` that was passed to `AIFunction.InvokeAsync()`. | ||
|
|
||
| ### Invocation through `FunctionInvokingChatClient` | ||
|
|
||
| <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient> publishes state about the current invocation to <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient.CurrentContext?displayProperty=nameWithType>, including not only the arguments, but all of the input `ChatMessage` objects, the <xref:Microsoft.Extensions.AI.ChatOptions>, and details on which function is being invoked (out of how many). You can add any data you want into <xref:Microsoft.Extensions.AI.ChatOptions.AdditionalProperties?displayProperty=nameWithType> and extract that inside of your `AIFunction` from `FunctionInvokingChatClient.CurrentContext.Options.AdditionalProperties`. | ||
|
|
||
| The following code shows an example: | ||
|
|
||
| :::code language="csharp" source="snippets/access-data/ArgumentsExample.cs" id="UseAdditionalProperties"::: | ||
|
|
||
| #### Dependency injection | ||
|
|
||
| If you use <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient> to invoke functions automatically, that client configures an <xref:Microsoft.Extensions.AI.AIFunctionArguments> object that it passes into the `AIFunction`. Because `AIFunctionArguments` includes the `IServiceProvider` that the `FunctionInvokingChatClient` was itself provided with, if you construct your client using standard DI means, that `IServiceProvider` is passed all the way into your `AIFunction`. At that point, you can query it for anything you want from DI. | ||
|
|
||
| ## Advanced techniques | ||
|
|
||
| If you want more fine-grained control over how parameters are bound, you can use <xref:Microsoft.Extensions.AI.AIFunctionFactoryOptions.ConfigureParameterBinding?displayProperty=nameWithType>, which puts you in control over how each parameter is populated. For example, the [MCP C# SDK uses this technique](https://github.com/modelcontextprotocol/csharp-sdk/blob/d344c651203841ec1c9e828736d234a6e4aebd07/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs#L83-L107) to automatically bind parameters from DI. | ||
|
|
||
| If you use the <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create(System.Reflection.MethodInfo,System.Func{Microsoft.Extensions.AI.AIFunctionArguments,System.Object},Microsoft.Extensions.AI.AIFunctionFactoryOptions)?displayProperty=nameWithType> overload, you can also run your own arbitrary logic when you create the target object that the instance method will be called on, each time. And you can do whatever you want to configure that instance. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // <SnippetGetChatClient> | ||
| using Azure; | ||
| using Azure.AI.OpenAI; | ||
| using Microsoft.Extensions.AI; | ||
| using Microsoft.Extensions.Configuration; | ||
|
|
||
| class ArgumentsExample | ||
| { | ||
| public static async Task RunManual() | ||
| { | ||
| // <SnippetUseAIFunctionArguments> | ||
| 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}"); | ||
| // </SnippetUseAIFunctionArguments> | ||
| } | ||
| public static async Task UseFICC() | ||
| { | ||
| IConfigurationRoot config = new ConfigurationBuilder() | ||
| .AddUserSecrets<Program>() | ||
| .Build(); | ||
|
|
||
| string endpoint = config["AZURE_OPENAI_ENDPOINT"]; | ||
| string apiKey = config["AZURE_OPENAI_API_KEY"]; | ||
| string model = config["AZURE_OPENAI_GPT_NAME"]; | ||
|
|
||
| // <SnippetUseAdditionalProperties> | ||
| 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}"; | ||
| }); | ||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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}"); | ||
| // </SnippetUseAdditionalProperties> | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| //await ArgumentsExample.RunManual(); | ||
| await ArgumentsExample.UseFICC(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <UserSecretsId>4d162886-0da5-4b62-a4db-d09780d06911</UserSecretsId> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.Identity" Version="1.17.0" /> | ||
| <PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" /> | ||
| <PackageReference Include="Microsoft.Extensions.AI" Version="10.0.0" /> | ||
| <PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.0-preview.1.25207.5" /> | ||
| <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.10" /> | ||
| <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.10" /> | ||
|
|
||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 20 additions & 17 deletions
37
docs/ai/quickstarts/snippets/function-calling/azure-openai/Program.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.