57 KiB
Instructions for migrating from Semantic Kernel Agents to Agent Framework in .NET projects.
Scope
When you are asked to migrate a project from Microsoft.SemanticKernel.Agents to Microsoft.Agents.AI you need to determine for which projects you need to do it.
If a single project is specified - do it for that project only. If you are asked to do it for a solution, migrate all projects in the solution
that reference Microsoft.SemanticKernel.Agents or related Semantic Kernel agent packages. If you don't know which projects to migrate, ask the user.
Things to consider while doing migration
- NuGet package names, assembly names, projects names or other dependencies names are case insensitive(!). You must take it into account when doing something with project dependencies, like searching for dependencies or when removing them from projects etc.
- Agent Framework uses different namespace patterns and API structures compared to Semantic Kernel Agents
- Text-based heuristics should be avoided in favor of proper content type inspection when available.
Planning
For each project that needs to be migrated, you need to do the following:
<agent_type_identification>
-
Find projects depending on
Microsoft.SemanticKernel.Agentsor related Semantic Kernel agent packages (when searching for projects, if some projects are not part of the solution or you could not find the project, notify user and continue with other projects). -
Identify the specific Semantic Kernel agent types being used:
ChatCompletionAgent→ChatClientAgentOpenAIAssistantAgent→assistantsClient.CreateAIAgent()(via OpenAI Assistants client extension)AzureAIAgent→persistentAgentsClient.CreateAIAgent()(via Azure AI Foundry client extension)OpenAIResponseAgent→responsesClient.CreateAIAgent()(via OpenAI Responses client extension)A2AAgent→AIAgent(via A2A card resolver)BedrockAgent→ Custom implementation required (not supported)
-
Determine if agents are being created new or retrieved from hosted services:
- New agents: Use
CreateAIAgent()methods - Existing hosted agents: Use
GetAIAgent(agentId)methods for OpenAI Assistants and Azure AI Foundry </agent_type_identification>
- New agents: Use
-
Determine the AI provider being used (OpenAI, Azure OpenAI, Azure AI Foundry, etc.)
-
Analyze tool/function registration patterns
-
Review thread management and invocation patterns
Execution
Important: when running steps in this section you must not pause, you must continue until you are done with all steps or you are truly unable to continue and need user's interaction (you will be penalized if you stop unnecessarily).
Keep in mind information in the next section about differences and follow these steps in the order they are specified (you will be penalized if you do steps below in wrong order or skip any of them):
- For each project that has an explicit package dependency to Semantic Kernel agent packages in the project file or some imported MSBuild targets (some project could receive package dependencies transitively, so avoid adding new package dependencies for such projects), do the following:
- Remove the Semantic Kernel agent package references from the project file:
Microsoft.SemanticKernel.Agents.CoreMicrosoft.SemanticKernel.Agents.OpenAIMicrosoft.SemanticKernel.Agents.AzureAIMicrosoft.SemanticKernel(if only used for agents)
- Add the appropriate Agent Framework package references based on the provider being used:
Microsoft.Agents.AI.Abstractions(always required)Microsoft.Agents.AI.OpenAI(for OpenAI and Azure OpenAI providers)- For unsupported providers (Bedrock, CopilotStudio), note in the report that custom implementation is required
- If projects use Central Package Management, update the
Directory.Packages.propsfile to remove the Semantic Kernel agent package versions in addition to removing package reference from projects. When adding the Agent Framework PackageReferences, add them to affected project files without a version and add PackageVersion elements to the Directory.Packages.props file with the version that supports the project's target framework.
- Update code files using Semantic Kernel Agents in the selected projects (and in projects that depend on them since they could receive Semantic Kernel transitively):
- Find all code files in the selected projects (and in projects that depend on them since they could receive Semantic Kernel transitively).
When doing search of code files that need changes, prefer calling search tools with
upgrade_prefix if available. Also do pass project's root folder for all selected projects or projects that depend on them. - Update the code files that use Semantic Kernel Agents to use Agent Framework instead. You never should add placeholders when updating code, or remove any comments in the code files,
you must keep the business logic as close as possible to the original code but use new API. When checking if code file needs to be updated, you should check for
using statements, types and API from
Microsoft.SemanticKernel.Agentsnamespace (skip comments and string literal constants). - Ensure that you replace all Semantic Kernel agent using statements with Agent Framework using statements (always check if there are any other Semantic Kernel agent API used in the file having any of the Semantic Kernel agent using statements; if no other API detected, Semantic Kernel agent using statements should be just removed instead of replaced). If there were no Semantic Kernel agent using statements in the file, do not add Agent Framework using statements.
- When replacing types you must ensure that you add using statements for them, since some types that lived in main
Microsoft.SemanticKernel.Agentsnamespace live in other namespaces underMicrosoft.Agents.AI. For example,Microsoft.SemanticKernel.Agents.ChatCompletionAgentis replaced withMicrosoft.Agents.AI.ChatClientAgent, when that happens using statement withMicrosoft.Agents.AIneeds to be added (unless you use fully qualified type name) - If you see some code that really cannot be converted or will have potential behavior changes at runtime, remember files and code lines where it happens at the end of the migration process you will generate a report markdown file and list all follow up steps user would have to do.
-
Validate that all places where Semantic Kernel Agents were used are migrated. To do that search for
Microsoft.SemanticKernel.Agentsin all affected projects and projects that depend on them again and if still see any Semantic Kernel agent presence go back to step 2. Steps 2 and 3 should be repeated until you see no Semantic Kernel agent references. -
Build all modified projects to ensure that they compile without errors. If there are any build errors, you must fix them all yourself one by one and don't stop until all errors are fixed without breaking any of the migration guidance.
-
Validate Migration: Use the validation checklist below to ensure complete migration.
-
Generate the report file under
<solution root>\.github folder, the file name should beSemanticKernelToAgentFrameworkReport.md, it is highly important that you generate report when migration complete. Report should contain:- all project dependencies changes (mention what was changed, added or removed, including provider-specific packages)
- all code files that were changed (mention what was changed in the file, if it was not changed, just mention that the file was not changed)
- provider-specific migration patterns used (OpenAI, Azure OpenAI, Azure AI Foundry, A2A, ONNX, etc.)
- all cases where you could not convert the code because of unsupported features and you were unable to find a workaround
- unsupported providers that require custom implementation (Bedrock, CopilotStudio)
- breaking glass pattern migrations (InnerContent → RawRepresentation) and any CodeInterpreter or advanced tool usage
- all behavioral changes that have to be verified at runtime
- provider-specific configuration changes that may affect behavior
- all follow up steps that user would have to do in the report markdown file
Migration Validation Checklist
After completing migration, verify these specific items:
- Compilation: Execute
dotnet buildon all modified projects - zero errors required - Namespace Updates: Confirm all
using Microsoft.SemanticKernel.Agentsstatements are replaced - Method Calls: Verify all
InvokeAsynccalls are changed toRunAsync - Return Types: Confirm handling of
AgentResponseinstead ofIAsyncEnumerable<AgentResponseItem<ChatMessageContent>> - Thread Creation: Validate all thread creation uses
agent.GetNewThread()pattern - Tool Registration: Ensure
[KernelFunction]attributes are removed andAIFunctionFactory.Create()is used - Options Configuration: Verify
AgentRunOptionsorChatClientAgentRunOptionsreplacesAgentInvokeOptions - Breaking Glass: Test
RawRepresentationaccess replacesInnerContentaccess
Detailed information about differences in Semantic Kernel Agents and Agent Framework
<api_changes> Agent Framework provides functionality for creating and managing AI agents through the Microsoft.Extensions.AI package ecosystem. The framework uses different APIs and patterns compared to Semantic Kernel Agents.
Key API differences:
- Agent creation: Remove Kernel dependency, use direct client-based creation
- Method names:
InvokeAsync→RunAsync,InvokeStreamingAsync→RunStreamingAsync - Return types:
IAsyncEnumerable<AgentResponseItem<ChatMessageContent>>→AgentResponse - Thread creation: Provider-specific constructors →
agent.GetNewThread() - Tool registration:
KernelPluginsystem → DirectAIFunctionregistration - Options:
AgentInvokeOptions→ Provider-specific run options (e.g.,ChatClientAgentRunOptions) </api_changes>
<configuration_changes> Configuration patterns have changed from Kernel-based to direct client configuration:
- Remove
Kernel.CreateBuilder()patterns - Replace with provider-specific client creation
- Update namespace imports from
Microsoft.SemanticKernel.AgentstoMicrosoft.Agents.AI - Change tool registration from attribute-based to factory-based </configuration_changes>
Exact API Mappings
<agent_type_identification> Replace these Semantic Kernel agent classes with their Agent Framework equivalents:
| Semantic Kernel Class | Agent Framework Replacement | Constructor Changes |
|---|---|---|
IChatCompletionService |
IChatClient |
Convert to IChatClient using chatService.AsChatClient() extensions |
ChatCompletionAgent |
ChatClientAgent |
Remove Kernel parameter, add IChatClient parameter |
OpenAIAssistantAgent |
AIAgent (via extension) |
⚠️ Deprecated - Use Responses API instead. New: OpenAIClient.GetAssistantClient().CreateAIAgent() Existing: OpenAIClient.GetAssistantClient().GetAIAgent(assistantId) |
AzureAIAgent |
AIAgent (via extension) |
New: PersistentAgentsClient.CreateAIAgent() Existing: PersistentAgentsClient.GetAIAgent(agentId) |
OpenAIResponseAgent |
AIAgent (via extension) |
Replace with OpenAIClient.GetOpenAIResponseClient(modelId).CreateAIAgent() |
A2AAgent |
AIAgent (via extension) |
Replace with A2ACardResolver.GetAIAgentAsync() |
BedrockAgent |
Not supported | Custom implementation required |
Important distinction:
- CreateAIAgent(): Use when creating new agents in the hosted service
- GetAIAgent(agentId): Use when retrieving existing agents from the hosted service </agent_type_identification>
<api_changes> Replace these method calls:
| Semantic Kernel Method | Agent Framework Method | Parameter Changes |
|---|---|---|
agent.InvokeAsync(message, thread, options) |
agent.RunAsync(message, thread, options) |
Same parameters, different return type |
agent.InvokeStreamingAsync(message, thread, options) |
agent.RunStreamingAsync(message, thread, options) |
Same parameters, different return type |
new ChatHistoryAgentThread() |
agent.GetNewThread() |
No parameters needed |
new OpenAIAssistantAgentThread(client) |
agent.GetNewThread() |
No parameters needed |
new AzureAIAgentThread(client) |
agent.GetNewThread() |
No parameters needed |
thread.DeleteAsync() |
Provider-specific cleanup | Use provider client directly |
Return type changes:
IAsyncEnumerable<AgentResponseItem<ChatMessageContent>>→AgentResponseIAsyncEnumerable<StreamingChatMessageContent>→IAsyncEnumerable<AgentResponseUpdate></api_changes>
<configuration_changes> Replace these configuration patterns:
| Semantic Kernel Pattern | Agent Framework Pattern |
|---|---|
AgentInvokeOptions |
AgentRunOptions ChatClientAgent: ChatClientAgentRunOptions |
KernelArguments |
If no arguments are provided, do nothing. If arguments are provided, template is not supported and the prompt must be rendered before calling agent |
[KernelFunction] attribute |
Remove attribute, use AIFunctionFactory.Create() |
KernelPlugin registration |
Direct function list in agent creation |
InnerContent property |
RawRepresentation property |
content.Metadata property |
AdditionalProperties property |
| </configuration_changes> |
<behavioral_changes>
Functional Differences
Agent Framework changes these behaviors compared to Semantic Kernel Agents:
-
Thread Management: Agent Framework automatically manages thread state. Semantic Kernel required manual thread updates in some scenarios (e.g., OpenAI Responses).
-
Return Types:
- Non-streaming: Returns single
AgentResponseinstead ofIAsyncEnumerable<AgentResponseItem<ChatMessageContent>> - Streaming: Returns
IAsyncEnumerable<AgentResponseUpdate>instead ofIAsyncEnumerable<StreamingChatMessageContent>
- Non-streaming: Returns single
-
Tool Registration: Agent Framework uses direct function registration without requiring
[KernelFunction]attributes. -
Usage Metadata: Agent Framework provides unified
UsageDetailsaccess viaresponse.Usageandupdate.Contents.OfType<UsageContent>(). -
Breaking Glass: Access underlying SDK objects via
RawRepresentationinstead ofInnerContent. </behavioral_changes>
Namespace Updates
<configuration_changes> Replace these exact namespace imports:
Remove these Semantic Kernel namespaces:
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.Agents.AzureAI;
using Microsoft.SemanticKernel.Agents.A2A;
using Microsoft.SemanticKernel.Connectors.OpenAI;
Add these Agent Framework namespaces:
using Microsoft.Extensions.AI;
using Microsoft.Agents.AI;
// Provider-specific namespaces (add only if needed):
using OpenAI; // For OpenAI provider
using Azure.AI.OpenAI; // For Azure OpenAI provider
using Azure.AI.Agents.Persistent; // For Azure AI Foundry provider
using Azure.Identity; // For Azure authentication
</configuration_changes>
Chat Completion Abstractions
<configuration_changes>
Replace this Semantic Kernel pattern:
Kernel kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId, apiKey)
.Build();
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
With this Agent Framework pattern:
// Method 1: Direct constructor
IChatClient chatClient = new OpenAIClient(apiKey).GetChatClient(modelId).AsIChatClient();
AIAgent agent = new ChatClientAgent(chatClient, instructions: "You are a helpful assistant");
// Method 2: Extension method (recommended)
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: "You are a helpful assistant");
</configuration_changes>
Chat Completion Service
<configuration_changes>
Replace this Semantic Kernel pattern:
IChatCompletionService completionService = kernel.GetService<IChatCompletionService>();
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
With this Agent Framework pattern:
Agent Framework does not support IChatCompletionService directly. Instead, use IChatClient as the common abstraction
converting from IChatCompletionService to IChatClient via AsChatClient() extension method or creating a new IChatClient
instance directly using the provider package dedicated extensions.
IChatCompletionService completionService = kernel.GetService<IChatCompletionService>();
IChatClient chatClient = completionService.AsChatClient();
var agent = new ChatClientAgent(chatClient, instructions: "You are a helpful assistant");
</configuration_changes>
Agent Creation Transformation
<configuration_changes>
Replace this Semantic Kernel pattern:
Kernel kernel = Kernel.CreateBuilder()
.AddOpenAIChatClient(modelId, apiKey)
.Build();
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
With this Agent Framework pattern:
// Method 1: Direct constructor (OpenAI/AzureOpenAI Package specific)
IChatClient chatClient = new OpenAIClient(apiKey).GetChatClient(modelId).AsIChatClient();
AIAgent agent = new ChatClientAgent(chatClient, instructions: "You are a helpful assistant");
// Method 2: Extension method (recommended)
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: "You are a helpful assistant");
Required changes:
- Remove
Kernel.CreateBuilder()and.Build()calls - Replace
ChatCompletionAgentwithChatClientAgentor use extension methods - Remove
Kernelproperty assignment - Pass
IChatClientdirectly to constructor or use extension methods </configuration_changes>
Thread Management Transformation
<api_changes> Replace these Semantic Kernel thread creation patterns:
// Remove these provider-specific thread constructors:
AgentThread thread = new ChatHistoryAgentThread();
AgentThread thread = new OpenAIAssistantAgentThread(assistantClient);
AgentThread thread = new AzureAIAgentThread(azureClient);
With this unified Agent Framework pattern:
// Use this single pattern for all agent types:
AgentThread thread = agent.GetNewThread();
Required changes:
- Remove all
new [Provider]AgentThread()constructor calls - Replace with
agent.GetNewThread()method call - Remove provider client parameters from thread creation
- Use the same pattern regardless of agent provider type </api_changes>
Tool Registration Transformation
<configuration_changes> Replace this Semantic Kernel tool registration pattern:
[KernelFunction] // Remove this attribute
[Description("Get the weather for a location")]
static string GetWeather(string location) => $"Weather in {location}";
KernelFunction kernelFunction = KernelFunctionFactory.CreateFromMethod(GetWeather);
KernelPlugin kernelPlugin = KernelPluginFactory.CreateFromFunctions("WeatherPlugin", [kernelFunction]);
kernel.Plugins.Add(kernelPlugin);
ChatCompletionAgent agent = new() { Kernel = kernel };
With this Agent Framework pattern:
[Description("Get the weather for a location")] // Keep Description attribute
static string GetWeather(string location) => $"Weather in {location}";
AIAgent agent = chatClient.CreateAIAgent(
instructions: "You are a helpful assistant",
tools: [AIFunctionFactory.Create(GetWeather)]);
Required changes:
- Remove
[KernelFunction]attributes from methods - Keep
[Description]attributes for function descriptions - Remove
KernelFunctionFactory.CreateFromMethod()calls - Remove
KernelPluginFactory.CreateFromFunctions()calls - Remove
kernel.Plugins.Add()calls - Replace with
AIFunctionFactory.Create()in tools parameter - Pass tools directly to agent creation method </configuration_changes>
Invocation Method Transformation
<api_changes> Replace this Semantic Kernel non-streaming pattern:
await foreach (AgentResponseItem<ChatMessageContent> item in agent.InvokeAsync(userInput, thread, options))
{
Console.WriteLine(item.Message);
}
With this Agent Framework non-streaming pattern:
AgentResponse result = await agent.RunAsync(userInput, thread, options);
Console.WriteLine(result);
Replace this Semantic Kernel streaming pattern:
await foreach (StreamingChatMessageContent update in agent.InvokeStreamingAsync(userInput, thread, options))
{
Console.Write(update.Message);
}
With this Agent Framework streaming pattern:
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(userInput, thread, options))
{
Console.Write(update);
}
Required changes:
- Replace
agent.InvokeAsync()withagent.RunAsync() - Replace
agent.InvokeStreamingAsync()withagent.RunStreamingAsync() - Change return type handling from
IAsyncEnumerable<AgentResponseItem<ChatMessageContent>>toAgentResponse - Change streaming type from
StreamingChatMessageContenttoAgentResponseUpdate - Remove
await foreachfor non-streaming calls - Access message content directly from result object instead of iterating </api_changes>
Options and Configuration Transformation
<configuration_changes> Replace this Semantic Kernel options pattern:
OpenAIPromptExecutionSettings settings = new() { MaxTokens = 1000 };
AgentInvokeOptions options = new() { KernelArguments = new(settings) };
With this Agent Framework options pattern:
ChatClientAgentRunOptions options = new(new ChatOptions { MaxOutputTokens = 1000 });
Required changes:
- Remove
OpenAIPromptExecutionSettings(or other provider-specific settings) - Remove
AgentInvokeOptionswrapper - Remove
KernelArgumentswrapper - Replace with
ChatClientAgentRunOptionscontainingChatOptions - Update property names:
MaxTokens→MaxOutputTokens - Pass options directly to
RunAsync()orRunStreamingAsync()methods </configuration_changes>
Dependency Injection Transformation
<configuration_changes> Replace this Semantic Kernel DI pattern:
Different providers require different kernel extensions:
services.AddKernel().AddOpenAIChatClient(modelId, apiKey);
services.AddTransient<ChatCompletionAgent>(sp => new()
{
Kernel = sp.GetRequiredService<Kernel>(),
Instructions = "You are helpful"
});
With this Agent Framework DI pattern:
services.AddTransient<AIAgent>(sp =>
new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: "You are helpful"));
Required changes:
- Remove
services.AddKernel()registration - Remove provider-specific kernel extensions (e.g.,
.AddOpenAIChatClient()) - Replace
ChatCompletionAgentwithAIAgentin service registration - Remove
Kerneldependency from constructor - Use direct client creation and extension methods
- Remove
sp.GetRequiredService<Kernel>()calls </configuration_changes>
Thread Cleanup Transformation
<api_changes> Replace this Semantic Kernel cleanup pattern:
await thread.DeleteAsync(); // For hosted threads
With these Agent Framework cleanup patterns:
For every thread created if there's intent to cleanup, the caller should track all the created threads for the provider that support hosted threads for cleanup purposes.
// For OpenAI Assistants (when cleanup is needed):
var assistantClient = new OpenAIClient(apiKey).GetAssistantClient();
await assistantClient.DeleteThreadAsync(thread.ConversationId);
// For Azure AI Foundry (when cleanup is needed):
var persistentClient = new PersistentAgentsClient(endpoint, credential);
await persistentClient.Threads.DeleteThreadAsync(thread.ConversationId);
// No thread and agent cleanup is needed for non-hosted agent providers like
// - Azure OpenAI Chat Completion
// - OpenAI Chat Completion
// - Azure OpenAI Responses
// - OpenAI Responses
Required changes:
- Remove
thread.DeleteAsync()calls - Use provider-specific client for cleanup when required
- Access thread ID via
thread.ConversationIdproperty - Only implement cleanup for providers that require it (Assistants, Azure AI Foundry) </api_changes>
Provider-Specific Creation Patterns
<configuration_changes> Use these exact patterns for each provider:
OpenAI Chat Completion:
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: instructions);
OpenAI Assistants (New): ⚠️ Deprecated - Use Responses API instead
AIAgent agent = new OpenAIClient(apiKey)
.GetAssistantClient()
.CreateAIAgent(modelId, instructions: instructions);
OpenAI Assistants (Existing): ⚠️ Deprecated - Use Responses API instead
AIAgent agent = new OpenAIClient(apiKey)
.GetAssistantClient()
.GetAIAgent(assistantId);
Azure OpenAI:
AIAgent agent = new AzureOpenAIClient(endpoint, credential)
.GetChatClient(deploymentName)
.CreateAIAgent(instructions: instructions);
Azure AI Foundry (New):
AIAgent agent = new PersistentAgentsClient(endpoint, credential)
.CreateAIAgent(model: deploymentName, instructions: instructions);
Azure AI Foundry (Existing):
AIAgent agent = await new PersistentAgentsClient(endpoint, credential)
.GetAIAgentAsync(agentId);
OpenAI Responses: (Recommended for OpenAI)
AIAgent agent = new OpenAIClient(apiKey)
.GetOpenAIResponseClient(modelId)
.CreateAIAgent(instructions: instructions);
Azure OpenAI Responses: (Recommended for Azure OpenAI)
AIAgent agent = new AzureOpenAIClient(endpoint, credential)
.GetOpenAIResponseClient(deploymentName)
.CreateAIAgent(instructions: instructions);
A2A:
A2ACardResolver resolver = new(new Uri(agentHost));
AIAgent agent = await resolver.GetAIAgentAsync();
</configuration_changes>
Complete Migration Examples
Basic Agent Creation Transformation
<configuration_changes> Replace this complete Semantic Kernel pattern:
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
Kernel kernel = Kernel.CreateBuilder()
.AddOpenAIChatClient(modelId, apiKey)
.Build();
ChatCompletionAgent agent = new()
{
Instructions = "You are helpful",
Kernel = kernel
};
AgentThread thread = new ChatHistoryAgentThread();
With this complete Agent Framework pattern:
using Microsoft.Agents.AI;
using OpenAI;
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: "You are helpful");
AgentThread thread = agent.GetNewThread();
</configuration_changes>
Tool Registration Transformation
<configuration_changes> Replace this complete Semantic Kernel tool pattern:
[KernelFunction] // Remove this attribute
[Description("Get weather information")]
static string GetWeather([Description("Location")] string location)
=> $"Weather in {location}";
KernelFunction function = KernelFunctionFactory.CreateFromMethod(GetWeather);
KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Weather", [function]);
kernel.Plugins.Add(plugin);
With this complete Agent Framework tool pattern:
[Description("Get weather information")] // Keep this attribute
static string GetWeather([Description("Location")] string location)
=> $"Weather in {location}";
AIAgent agent = chatClient.CreateAIAgent(
instructions: "You are a helpful assistant",
tools: [AIFunctionFactory.Create(GetWeather)]);
</configuration_changes>
Agent Invocation Transformation
<api_changes> Replace this complete Semantic Kernel invocation pattern:
OpenAIPromptExecutionSettings settings = new() { MaxTokens = 1000 };
AgentInvokeOptions options = new() { KernelArguments = new(settings) };
await foreach (var result in agent.InvokeAsync(input, thread, options))
{
Console.WriteLine(result.Message);
}
With this complete Agent Framework invocation pattern:
ChatClientAgentRunOptions options = new(new ChatOptions { MaxOutputTokens = 1000 });
AgentResponse result = await agent.RunAsync(input, thread, options);
Console.WriteLine(result);
// Access underlying content when needed:
var chatResponse = result.RawRepresentation as ChatResponse;
// Access underlying SDK objects via chatResponse?.RawRepresentation
</api_changes>
Usage Metadata Transformation
<api_changes> Replace this Semantic Kernel non-streaming usage pattern:
await foreach (var result in agent.InvokeAsync(input, thread, options))
{
if (result.Message.Metadata?.TryGetValue("Usage", out object? usage) ?? false)
{
if (usage is ChatTokenUsage openAIUsage)
{
Console.WriteLine($"Tokens: {openAIUsage.TotalTokenCount}");
}
}
}
With this Agent Framework non-streaming usage pattern:
AgentResponse result = await agent.RunAsync(input, thread, options);
Console.WriteLine($"Tokens: {result.Usage.TotalTokenCount}");
Replace this Semantic Kernel streaming usage pattern:
await foreach (StreamingChatMessageContent response in agent.InvokeStreamingAsync(message, agentThread))
{
if (response.Metadata?.TryGetValue("Usage", out object? usage) ?? false)
{
if (usage is ChatTokenUsage openAIUsage)
{
Console.WriteLine($"Tokens: {openAIUsage.TotalTokenCount}");
}
}
}
With this Agent Framework streaming usage pattern:
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, thread, options))
{
if (update.Contents.OfType<UsageContent>().FirstOrDefault() is { } usageContent)
{
Console.WriteLine($"Tokens: {usageContent.Details.TotalTokenCount}");
}
}
</api_changes>
Breaking Glass Pattern Transformation
<api_changes> Replace this Semantic Kernel breaking glass pattern:
await foreach (var content in agent.InvokeAsync(userInput, thread))
{
UnderlyingSdkType? underlyingChatMessage = content.Message.InnerContent as UnderlyingSdkType;
}
With this Agent Framework breaking glass pattern:
var agentRunResponse = await agent.RunAsync(userInput, thread);
// If the agent uses a ChatClient the first breaking glass probably will be a Microsoft.Extensions.AI.ChatResponse
ChatResponse? chatResponse = agentRunResponse.RawRepresentation as ChatResponse;
// If thats the case, to access the underlying SDK types you will need to break glass again.
UnderlyingSdkType? underlyingChatMessage = chatResponse?.RawRepresentation as UnderlyingSdkType;
Required changes:
- Replace
InnerContentproperty access withRawRepresentationproperty access - Cast
RawRepresentationto appropriate type expected - If the
RawRepresentationis aMicrosoft.Extensions.AItype, break glass again to access the underlying SDK types </api_changes>
CodeInterpreter Tool Transformation
<behavioral_changes> Replace this Semantic Kernel CodeInterpreter pattern:
await foreach (var content in agent.InvokeAsync(userInput, thread))
{
bool isCode = content.Message.Metadata?.ContainsKey(AzureAIAgent.CodeInterpreterMetadataKey) ?? false;
Console.WriteLine($"# {content.Message.Role}{(isCode ? "\n# Generated Code:\n" : ":")}{content.Message.Content}");
// Process annotations
foreach (var item in content.Message.Items)
{
if (item is AnnotationContent annotation)
{
Console.WriteLine($"[{item.GetType().Name}] {annotation.Label}: File #{annotation.ReferenceId}");
}
else if (item is FileReferenceContent fileReference)
{
Console.WriteLine($"[{item.GetType().Name}] File #{fileReference.FileId}");
}
}
}
With this Agent Framework CodeInterpreter pattern:
using System.Text;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
var result = await agent.RunAsync(userInput, thread);
Console.WriteLine(result);
// Get the CodeInterpreterToolCallContent (code input)
CodeInterpreterToolCallContent? toolCallContent = result.Messages
.SelectMany(m => m.Contents)
.OfType<CodeInterpreterToolCallContent>()
.FirstOrDefault();
if (toolCallContent?.Inputs is not null)
{
DataContent? codeInput = toolCallContent.Inputs.OfType<DataContent>().FirstOrDefault();
if (codeInput?.HasTopLevelMediaType("text") ?? false)
{
Console.WriteLine($"Code Input: {Encoding.UTF8.GetString(codeInput.Data.ToArray())}");
}
}
// Get the CodeInterpreterToolResultContent (code output)
CodeInterpreterToolResultContent? toolResultContent = result.Messages
.SelectMany(m => m.Contents)
.OfType<CodeInterpreterToolResultContent>()
.FirstOrDefault();
if (toolResultContent?.Outputs is not null)
{
TextContent? resultOutput = toolResultContent.Outputs.OfType<TextContent>().FirstOrDefault();
if (resultOutput is not null)
{
Console.WriteLine($"Code Tool Result: {resultOutput.Text}");
}
}
// Getting any annotations generated by the tool
foreach (AIAnnotation annotation in result.Messages
.SelectMany(m => m.Contents)
.SelectMany(c => c.Annotations ?? []))
{
Console.WriteLine($"Annotation: {annotation}");
}
Functional differences:
- Code interpreter content is now available via MEAI abstractions - no breaking glass required
- Use
CodeInterpreterToolCallContentto access code inputs (the generated code) - Use
CodeInterpreterToolResultContentto access code outputs (execution results) - Annotations are accessible via
AIAnnotationon content items </behavioral_changes>
Provider-Specific Options Configuration
<configuration_changes>
For advanced model settings not available in ChatOptions, use the RawRepresentationFactory property:
var agentOptions = new ChatClientAgentRunOptions(new ChatOptions
{
MaxOutputTokens = 8000,
// Breaking glass to access provider-specific options
RawRepresentationFactory = (_) => new OpenAI.Responses.CreateResponseOptions()
{
ReasoningOptions = new()
{
ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.High,
ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
}
}
});
Use this pattern when:
- Standard
ChatOptionsproperties don't cover required model settings - Provider-specific configuration is needed (e.g., reasoning effort level)
- Advanced SDK features need to be accessed </configuration_changes>
Type-Safe Extension Methods
<api_changes> Use provider-specific extension methods for safer breaking glass access:
using OpenAI; // Brings in extension methods
// Type-safe extraction of OpenAI ChatCompletion
var chatCompletion = result.AsChatCompletion();
// Access underlying OpenAI objects safely
var openAIResponse = chatCompletion.GetRawResponse();
Available extension methods:
result.AsChatCompletion()for OpenAI providersresult.GetRawResponse()for accessing underlying SDK responses- Provider-specific extensions for type-safe casting </api_changes>
Common Migration Issues and Solutions
<configuration_changes> Issue: Missing Using Statements
- Problem: Compilation errors due to missing namespace imports
- Solution: Add
using Microsoft.Agents.AI;and removeusing Microsoft.SemanticKernel.Agents;
Issue: Tool Function Signatures
- Problem:
[KernelFunction]attributes cause compilation errors - Solution: Remove
[KernelFunction]attributes, keep[Description]attributes
Issue: Thread Type Mismatches
- Problem: Provider-specific thread constructors not found
- Solution: Replace all thread constructors with
agent.GetNewThread()
Issue: Options Configuration
- Problem:
AgentInvokeOptionstype not found - Solution: Replace with
AgentRunOptionsorChatClientAgentRunOptionscontainingChatOptions
Issue: Dependency Injection
- Problem:
Kernelservice registration not found - Solution: Remove
services.AddKernel(), use direct client registration </configuration_changes>
Migration Execution Steps
<configuration_changes>
- Update Package References: Remove SK packages, add AF packages per provider
- Update Namespaces: Replace SK namespaces with AF namespaces
- Update Agent Creation: Remove Kernel, use direct client creation
- Update Method Calls: Replace
InvokeAsyncwithRunAsync - Update Thread Creation: Replace provider-specific constructors with
GetNewThread() - Update Tool Registration: Remove attributes, use
AIFunctionFactory.Create() - Update Options: Replace
AgentInvokeOptionswith provider-specific options - Test and Validate: Compile and test all functionality </configuration_changes>
Provider-Specific Migration Patterns
<configuration_changes> The following sections provide detailed migration patterns for each supported provider, covering package references, agent creation patterns, and provider-specific configurations. </configuration_changes>
1. OpenAI Chat Completion Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.OpenAI" />
</configuration_changes>
Before (Semantic Kernel):
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
Kernel kernel = Kernel.CreateBuilder()
.AddOpenAIChatClient(modelId, apiKey)
.Build();
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
AgentThread thread = new ChatHistoryAgentThread();
After (Agent Framework):
using Microsoft.Agents.AI;
using OpenAI;
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: "You are a helpful assistant");
AgentThread thread = agent.GetNewThread();
2. Azure OpenAI Chat Completion Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
Note: If not using AzureCliCredential, you can use ApiKeyCredential instead without the Azure.Identity package.
</configuration_changes>
Before (Semantic Kernel):
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Azure.Identity;
Kernel kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential())
.Build();
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
After (Agent Framework):
using Microsoft.Agents.AI;
using Azure.AI.OpenAI;
using Azure.Identity;
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
.GetChatClient(deploymentName)
.CreateAIAgent(instructions: "You are a helpful assistant");
3. OpenAI Assistants Migration
⚠️ DEPRECATION WARNING: The OpenAI Assistants API has been deprecated. The Agent Framework extension methods for Assistants are marked as
[Obsolete]. Please use the Responses API instead (see Section 6: OpenAI Responses Migration).
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.OpenAI" />
</configuration_changes>
<api_changes> Replace this Semantic Kernel pattern:
using Microsoft.SemanticKernel.Agents.OpenAI;
using OpenAI.Assistants;
AssistantClient assistantClient = new(apiKey);
Assistant assistant = await assistantClient.CreateAssistantAsync(
modelId,
instructions: "You are a helpful assistant");
OpenAIAssistantAgent agent = new(assistant, assistantClient)
{
Kernel = kernel
};
AgentThread thread = new OpenAIAssistantAgentThread(assistantClient);
With this Agent Framework pattern:
Creating a new assistant:
using Microsoft.Agents.AI;
using OpenAI;
AIAgent agent = new OpenAIClient(apiKey)
.GetAssistantClient()
.CreateAIAgent(modelId, instructions: "You are a helpful assistant");
AgentThread thread = agent.GetNewThread();
// Cleanup when needed
await assistantClient.DeleteThreadAsync(thread.ConversationId);
Retrieving an existing assistant:
using Microsoft.Agents.AI;
using OpenAI;
AIAgent agent = new OpenAIClient(apiKey)
.GetAssistantClient()
.GetAIAgent(assistantId); // Use existing assistant ID
AgentThread thread = agent.GetNewThread();
</api_changes>
4. Azure AI Foundry (AzureAIAgent) Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.AzureAI" />
<PackageReference Include="Azure.Identity" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.AzureAI.Persistent" />
<PackageReference Include="Azure.Identity" />
</configuration_changes>
<api_changes> Replace these Semantic Kernel patterns:
Pattern 1: Direct AzureAIAgent creation
using Microsoft.SemanticKernel.Agents.AzureAI;
using Azure.Identity;
AzureAIAgent agent = new(
endpoint: new Uri(endpoint),
credential: new AzureCliCredential(),
projectId: projectId)
{
Instructions = "You are a helpful assistant"
};
AgentThread thread = new AzureAIAgentThread(agent);
Pattern 2: PersistentAgent definition creation
// Define the agent
PersistentAgent definition = await client.Administration.CreateAgentAsync(
deploymentName,
tools: [new CodeInterpreterToolDefinition()]);
AzureAIAgent agent = new(definition, client);
// Create a thread for the agent conversation.
AgentThread thread = new AzureAIAgentThread(client);
With these Agent Framework patterns:
Creating a new agent:
using Microsoft.Agents.AI;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
var client = new PersistentAgentsClient(endpoint, new AzureCliCredential());
// Create a new AIAgent using Agent Framework
AIAgent agent = client.CreateAIAgent(
model: deploymentName,
instructions: "You are a helpful assistant",
tools: [/* List of specialized Azure.AI.Agents.Persistent.ToolDefinition types */]);
AgentThread thread = agent.GetNewThread();
Retrieving an existing agent:
using Microsoft.Agents.AI;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
var client = new PersistentAgentsClient(endpoint, new AzureCliCredential());
// Retrieve an existing AIAgent using its ID
AIAgent agent = await client.GetAIAgentAsync(agentId);
AgentThread thread = agent.GetNewThread();
</api_changes>
5. A2A Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.A2A" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.A2A" />
</configuration_changes>
<api_changes> Replace this Semantic Kernel pattern:
// Create an A2A agent instance
using var httpClient = CreateHttpClient();
var client = new A2AClient(url, httpClient);
var cardResolver = new A2ACardResolver(url, httpClient);
var agentCard = await cardResolver.GetAgentCardAsync();
var agent = new A2AAgent(client, agentCard);
With this Agent Framework pattern:
// Initialize an A2ACardResolver to get an A2A agent card.
A2ACardResolver agentCardResolver = new(new Uri(a2aAgentHost));
// Create an instance of the AIAgent for an existing A2A agent specified by the agent card.
AIAgent agent = await agentCardResolver.GetAIAgentAsync();
</api_changes>
6. OpenAI Responses Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.OpenAI" />
</configuration_changes>
<api_changes> Replace this Semantic Kernel pattern:
The thread management is done manually with OpenAI Responses in Semantic Kernel, where the thread
needs to be passed to the InvokeAsync method and updated with the item.Thread from the response.
using Microsoft.SemanticKernel.Agents.OpenAI;
// Define the agent
OpenAIResponseAgent agent = new(new OpenAIClient(apiKey))
{
Name = "ResponseAgent",
Instructions = "Answer all queries in English and French.",
};
// Initial thread can be null as it will be automatically created
AgentThread? agentThread = null;
var responseItems = agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, "Input message."), agentThread);
await foreach (AgentResponseItem<ChatMessageContent> responseItem in responseItems)
{
// Update the thread to maintain the conversation for future interaction
agentThread = responseItem.Thread;
WriteAgentChatMessage(responseItem.Message);
}
With this Agent Framework pattern:
Agent Framework automatically manages the thread, so there's no need to manually update it.
using Microsoft.Agents.AI.OpenAI;
AIAgent agent = new OpenAIClient(apiKey)
.GetOpenAIResponseClient(modelId)
.CreateAIAgent(
name: "ResponseAgent",
instructions: "Answer all queries in English and French.",
tools: [/* AITools */]);
AgentThread thread = agent.GetNewThread();
var result = await agent.RunAsync(userInput, thread);
// The thread will be automatically updated with the new response id from this point
</api_changes>
7. Azure OpenAI Responses Migration
<configuration_changes> Remove Semantic Kernel Packages:
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" />
Add Agent Framework Packages:
<PackageReference Include="Microsoft.Agents.AI.OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" />
</configuration_changes>
<api_changes> Replace this Semantic Kernel pattern:
Azure OpenAI Responses uses AzureOpenAIClient instead of OpenAIClient. The thread management is done manually where the thread needs to be passed to the InvokeAsync method and updated with the item.Thread from the response.
using Microsoft.SemanticKernel.Agents.OpenAI;
using Azure.AI.OpenAI;
// Define the agent
OpenAIResponseAgent agent = new(new AzureOpenAIClient(endpoint, new AzureCliCredential()))
{
Name = "ResponseAgent",
Instructions = "Answer all queries in English and French.",
};
// Initial thread can be null as it will be automatically created
AgentThread? agentThread = null;
var responseItems = agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, "Input message."), agentThread);
await foreach (AgentResponseItem<ChatMessageContent> responseItem in responseItems)
{
// Update the thread to maintain the conversation for future interaction
agentThread = responseItem.Thread;
WriteAgentChatMessage(responseItem.Message);
}
With this Agent Framework pattern:
Agent Framework automatically manages the thread, so there's no need to manually update it.
using Microsoft.Agents.AI.OpenAI;
using Azure.AI.OpenAI;
AIAgent agent = new AzureOpenAIClient(endpoint, new AzureCliCredential())
.GetOpenAIResponseClient(modelId)
.CreateAIAgent(
name: "ResponseAgent",
instructions: "Answer all queries in English and French.",
tools: [/* AITools */]);
AgentThread thread = agent.GetNewThread();
var result = await agent.RunAsync(userInput, thread);
// The thread will be automatically updated with the new response id from this point
</api_changes>
8. Unsupported Providers (Require Custom Implementation)
<behavioral_changes>
BedrockAgent Migration
Status: Hosted Agents is not directly supported in Agent Framework
Status: Non-Hosted AI Model Agents supported via ChatClientAgent
Replace this Semantic Kernel pattern:
using Microsoft.SemanticKernel.Agents.Bedrock;
// Create a new agent on the Bedrock Agent service and prepare it for use
using var client = new AmazonBedrockAgentClient();
using var runtimeClient = new AmazonBedrockAgentRuntimeClient();
var agentModel = await client.CreateAndPrepareAgentAsync(new CreateAgentRequest()
{
AgentName = agentName,
Description = "AgentDescription",
Instruction = "You are a helpful assistant",
AgentResourceRoleArn = TestConfiguration.BedrockAgent.AgentResourceRoleArn,
FoundationModel = TestConfiguration.BedrockAgent.FoundationModel,
});
// Create a new BedrockAgent instance with the agent model and the client
// so that we can interact with the agent using Semantic Kernel contents.
var agent = new BedrockAgent(agentModel, client, runtimeClient);
With this Agent Framework workaround:
Currently there's no support for the Hosted Bedrock Agent service in Agent Framework.
For providers like AWS Bedrock that have an IChatClient implementation available, use the ChatClientAgent directly by providing the IChatClient instance to the agent.
Those agents will be purely backed by the AI chat models behavior and will not store any state in the server.
using Microsoft.Agents.AI;
services.TryAddAWSService<IAmazonBedrockRuntime>();
var serviceProvider = services.BuildServiceProvider();
IAmazonBedrockRuntime runtime = serviceProvider.GetRequiredService<IAmazonBedrockRuntime>();
using var bedrockChatClient = runtime.AsIChatClient();
AIAgent agent = new ChatClientAgent(bedrockChatClient, instructions: "You are a helpful assistant");
</behavioral_changes>
Unsupported Features that need workarounds
<behavioral_changes> The following Semantic Kernel Agents features currently don't have direct equivalents in Agent Framework:
Plugins Migration
Problem: Semantic Kernel plugins allowed multiple functions to be registered under a type or object instance
Semantic Kernel pattern
// Create plugin with multiple functions
public class WeatherPlugin
{
[KernelFunction, Description("Get current weather")]
public string GetCurrentWeather(string location)
=> $"Weather in {location}: Sunny";
[KernelFunction, Description("Get weather forecast")]
public static Task<string> GetForecastAsync(string location, int days)
=> Task.FromResult($"Forecast for {location}: {days} days");
}
kernel.Plugins.AddFromType<WeatherPlugin>();
// OR
kernel.Plugins.AddFromObject(new WeatherPlugin());
Agent Framework workaround:
// Create individual functions (no plugin grouping)
public class WeatherFunctions
{
[Description("Get current weather")]
public static string GetCurrentWeather(string location)
=> $"Weather in {location}: Sunny";
[Description("Get weather forecast")]
public Task<string> GetForecastAsync(string location, int days)
=> Task.FromResult($"Forecast for {location}: {days} days");
}
var weatherService = new WeatherFunctions();
// Register functions individually as tools
AITool[] tools = [
AIFunctionFactory.Create(WeatherFunctions.GetCurrentWeather), // Get from type static method
AIFunctionFactory.Create(weatherService.GetForecastAsync) // Get from instance method
];
// OR Iterate over the type or instance if many functions are needed for registration
AITool[] tools =
[
.. typeof(WeatherFunctions)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Select((m) => AIFunctionFactory.Create(m, target: null)), // Get from type static methods
.. weatherService.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Select((m) => AIFunctionFactory.Create(m, target: weatherService)) // Get from instance methods
];
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(
instructions: "You are a weather assistant",
tools: tools);
Prompt Template Migration
Problem: Agent prompt templating is not yet supported in Agent Framework
Semantic Kernel pattern
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
var template = "Tell a story about {{$topic}} that is {{$length}} sentences long.";
ChatCompletionAgent agent =
new(templateFactory: new KernelPromptTemplateFactory(),
templateConfig: new(template) { TemplateFormat = PromptTemplateConfig.SemanticKernelTemplateFormat })
{
Kernel = kernel,
Name = "StoryTeller",
Arguments = new KernelArguments()
{
{ "topic", "Dog" },
{ "length", "3" },
}
};
Agent Framework workaround
using Microsoft.Agents.AI;
using Microsoft.SemanticKernel;
// Manually render template
var template = "Tell a story about {{$topic}} that is {{$length}} sentences long.";
var renderedTemplate = await new KernelPromptTemplateFactory()
.Create(new PromptTemplateConfig(template))
.RenderAsync(new Kernel(), new KernelArguments()
{
["topic"] = "Dog",
["length"] = "3"
});
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(modelId)
.CreateAIAgent(instructions: renderedTemplate);
// No template variables in invocation - use plain string
var result = await agent.RunAsync("What's the weather?", thread);
Console.WriteLine(result);
</behavioral_changes>
9. Function Invocation Filtering
Invocation Context
Semantic Kernel's IAutoFunctionInvocationFilter provides a AutoFunctionInvocationContext where Agent Framework provides FunctionInvocationContext
The property mapping guide from a AutoFunctionInvocationContext to a FunctionInvocationContext is as follows:
| SK | AF |
|---|---|
| RequestSequenceIndex | Iteration |
| FunctionSequenceIndex | FunctionCallIndex |
| ToolCallId | CallContent.CallId |
| ChatMessageContent | Messages[0] |
| ExecutionSettings | Options |
| ChatHistory | Messages |
| Function | Function |
| Kernel | N/A |
| Result | Use return from the delegate |
| Terminate | Terminate |
| CancellationToken | provided via argument to middleware delegate |
| Arguments | Arguments |
Semantic Kernel
// Filter specifically for functions calling
public sealed class CustomAutoFunctionInvocationFilter : IAutoFunctionInvocationFilter
{
public async Task OnAutoFunctionInvocationAsync(AutoFunctionInvocationContext context, Func<AutoFunctionInvocationContext, Task> next)
{
Console.WriteLine($"[SK Auto Filter] Auto-invoking function: {context.Function.Name}");
// Check if function should be auto-invoked
if (context.Function.Name.Contains("Dangerous"))
{
Console.WriteLine($"[SK Auto Filter] Skipping dangerous function: {context.Function.Name}");
context.Terminate = true;
return;
}
await next(context);
Console.WriteLine($"[SK Auto Filter] Auto-invocation completed for: {context.Function.Name}");
}
}
var builder = Kernel.CreateBuilder()
.AddOpenAIChatClient(modelId, apiKey);
// via builder DI
var builder = Kernel.CreateBuilder()
.AddOpenAIChatClient(modelId, apiKey)
.Services
.AddSingleton<IAutoFunctionInvocationFilter, CustomAutoFunctionInvocationFilter>();
// OR via DI
services
.AddKernel()
.AddOpenAIChatClient(modelId, apiKey)
.AddSingleton<IAutoFunctionInvocationFilter, CustomAutoFunctionInvocationFilter>();
// OR register auto function filter directly with the kernel instance
kernel.AutoFunctionInvocationFilters.Add(new CustomAutoFunctionInvocationFilter());
// Create agent with filtered kernel
ChatCompletionAgent agent = new()
{
Instructions = "You are a helpful assistant",
Kernel = kernel
};
Agent Framework
Agent Framework provides function calling middleware that offers equivalent capabilities to Semantic Kernel's auto function invocation filters:
// Function calling middleware equivalent to CustomAutoFunctionInvocationFilter
async ValueTask<object?> CustomAutoFunctionMiddleware(
AIAgent agent,
FunctionInvocationContext context,
Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
CancellationToken cancellationToken)
{
Console.WriteLine($"[AF Middleware] Auto-invoking function: {context.Function.Name}");
// Check if function should be auto-invoked
if (context.Function.Name.Contains("Dangerous"))
{
Console.WriteLine($"[AF Middleware] Skipping dangerous function: {context.Function.Name}");
context.Terminate = true;
return "Function execution blocked for security reasons";
}
var result = await next(context, cancellationToken);
Console.WriteLine($"[AF Middleware] Auto-invocation completed for: {context.Function.Name}");
return result;
}
// Apply middleware to agent
var filteredAgent = originalAgent
.AsBuilder()
.Use(CustomAutoFunctionMiddleware)
.Build();