mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Fix samples, improve readme language and add readme to sln. (#942)
* Fix samples, improve readme language and add readme to sln. * Fix threading in openai samples too.
This commit is contained in:
committed by
GitHub
Unverified
parent
28ad256931
commit
499838565e
@@ -112,7 +112,9 @@
|
||||
<Project Path="samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj" />
|
||||
<Project Path="samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/SemanticKernelMigration/" />
|
||||
<Folder Name="/Samples/SemanticKernelMigration/">
|
||||
<File Path="samples/SemanticKernelMigration/README.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/SemanticKernelMigration/AzureAIFoundry/">
|
||||
<Project Path="samples/SemanticKernelMigration/AzureAIFoundry/Step01_Basics/AzureAIFoundry_Step01_Basics.csproj" />
|
||||
<Project Path="samples/SemanticKernelMigration/AzureAIFoundry/Step02_ToolCall/AzureAIFoundry_Step02_ToolCall.csproj" />
|
||||
|
||||
+4
-3
@@ -22,8 +22,9 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName))
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -31,7 +32,7 @@ async Task SKAgentAsync()
|
||||
|
||||
var agentOptions = new OpenAIResponseAgentInvokeOptions() { ResponseCreationOptions = new() { MaxOutputTokenCount = 1000 } };
|
||||
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = new OpenAIResponseAgentThread(responseClient);
|
||||
await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
|
||||
{
|
||||
Console.WriteLine(item.Message);
|
||||
|
||||
+4
-3
@@ -49,8 +49,9 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName))
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetOpenAIResponseClient(deploymentName);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -69,7 +70,7 @@ async Task SKAgentAsync()
|
||||
}
|
||||
};
|
||||
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = new OpenAIResponseAgentThread(responseClient);
|
||||
await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
|
||||
{
|
||||
foreach (var content in item.Message.Items)
|
||||
|
||||
@@ -20,7 +20,8 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId))
|
||||
var responseClient = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -28,7 +29,7 @@ async Task SKAgentAsync()
|
||||
|
||||
var agentOptions = new OpenAIResponseAgentInvokeOptions() { ResponseCreationOptions = new() { MaxOutputTokenCount = 1000 } };
|
||||
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = new OpenAIResponseAgentThread(responseClient);
|
||||
await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
|
||||
{
|
||||
Console.WriteLine(item.Message);
|
||||
|
||||
+3
-2
@@ -47,7 +47,8 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId))
|
||||
var responseClient = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -66,7 +67,7 @@ async Task SKAgentAsync()
|
||||
}
|
||||
};
|
||||
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
|
||||
Microsoft.SemanticKernel.Agents.AgentThread? thread = new OpenAIResponseAgentThread(responseClient);
|
||||
await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
|
||||
{
|
||||
foreach (var content in item.Message.Items)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Semantic Kernel to Agent Framework Migration Guide
|
||||
|
||||
## What's Changed?
|
||||
- **Namespace Updates**: From `Microsoft.SemanticKernel.Agents` to `Microsoft.Extensions.AI.Agents`
|
||||
- **Namespace Updates**: From `Microsoft.SemanticKernel.Agents` to `Microsoft.Agents.AI`
|
||||
- **Agent Creation**: Single fluent API calls vs multi-step builder patterns
|
||||
- **Thread Management**: Built-in thread management vs manual thread creation
|
||||
- **Tool Registration**: Direct function registration vs plugin wrapper systems
|
||||
@@ -27,12 +27,8 @@ using Microsoft.SemanticKernel.Agents;
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
Agent Framework namespaces are now under `Microsoft.Extensions.AI`.
|
||||
|
||||
- `Microsoft.Extensions.AI` for core AI types
|
||||
- `Microsoft.Extensions.AI.Agents` for core agent types
|
||||
OR just
|
||||
- `Microsoft.Extensions.AI.Agents.Abstractions` if your
|
||||
Agent Framework namespaces are under `Microsoft.Agents.AI`.
|
||||
Agent Framework uses the core AI message and content types from `Microsoft.Extensions.AI` for communication between components.
|
||||
|
||||
```csharp
|
||||
using Microsoft.Extensions.AI;
|
||||
@@ -54,7 +50,7 @@ an empty `Kernel` if not provided.
|
||||
ChatCompletionAgent agent = new() { Instructions = ParrotInstructions, Kernel = kernel };
|
||||
```
|
||||
|
||||
Azure AI Foundry requires a strong setup before creating an agent
|
||||
Azure AI Foundry requires an agent resource to be created in the cloud before creating a local agent class that uses it.
|
||||
|
||||
```csharp
|
||||
PersistentAgentsClient azureAgentClient = AzureAIAgent.CreateAgentsClient(azureEndpoint, new AzureCliCredential());
|
||||
@@ -72,8 +68,8 @@ Agent creation in Agent Framework is made simpler with extensions provided by al
|
||||
|
||||
```csharp
|
||||
AIAgent openAIAgent = chatClient.CreateAIAgent(instructions: ParrotInstructions);
|
||||
AIAgent azureFoundryAgent = persistentAgentsClient.CreateAIAgent(instructions: ParrotInstructions);
|
||||
AIAgent openAIAssistantAgent = assistantClient.CreateAIAgent(instructions: ParrotInstructions);
|
||||
AIAgent azureFoundryAgent = await persistentAgentsClient.CreateAIAgentAsync(instructions: ParrotInstructions);
|
||||
AIAgent openAIAssistantAgent = await assistantClient.CreateAIAgentAsync(instructions: ParrotInstructions);
|
||||
```
|
||||
|
||||
Additionally for hosted agent providers you can also use the `GetAIAgent` to retrieve an agent from an existing hosted agent.
|
||||
@@ -82,8 +78,7 @@ Additionally for hosted agent providers you can also use the `GetAIAgent` to ret
|
||||
AIAgent azureFoundryAgent = await persistentAgentsClient.GetAIAgentAsync(agentId);
|
||||
```
|
||||
|
||||
|
||||
### Agent Thread Creation
|
||||
### 3. Agent Thread Creation
|
||||
|
||||
#### Semantic Kernel
|
||||
|
||||
@@ -94,7 +89,7 @@ The caller has to know the thread type and create it manually.
|
||||
AgentThread thread = new OpenAIAssistantAgentThread(this.AssistantClient);
|
||||
AgentThread thread = new AzureAIAgentThread(this.Client);
|
||||
AgentThread thread = new OpenAIResponseAgentThread(this.Client);
|
||||
```
|
||||
```
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
@@ -105,7 +100,7 @@ The agent is responsible for creating the thread.
|
||||
AgentThread thread = agent.GetNewThread();
|
||||
```
|
||||
|
||||
### Hosted Agent Thread Cleanup
|
||||
### 4. Hosted Agent Thread Cleanup
|
||||
|
||||
This case applies exclusively to a few AI providers that still provide hosted threads.
|
||||
|
||||
@@ -121,28 +116,26 @@ await thread.DeleteAsync();
|
||||
#### Agent Framework
|
||||
|
||||
> [!NOTE]
|
||||
> OpenAI Responses introduced a new conversation model that simplifies completely how conversations are handled avoiding any previous hosted thread management complexities that were initially introduced by the now deprecated OpenAI Assistants model well documented in https://platform.openai.com/docs/assistants/migration
|
||||
> OpenAI Responses introduced a new conversation model that simplifies how conversations are handled. This simplifies hosted thread management compared to the now deprecated OpenAI Assistants model. For more information see the [OpenAI Assistants migration guide](https://platform.openai.com/docs/assistants/migration).
|
||||
|
||||
Agent Framework doesn't have a thread deletion API in the `AgentThread` type as not all providers support hosted threads or thread deletion and this will become more common as more providers shift to responses based architectures.
|
||||
|
||||
|
||||
Agent Framework doesn't have thread deletion API in the `AgentThread` type as not all providers require hosted thread cleanup and this will become more common as more providers shift to conversation based architectures.
|
||||
|
||||
**When the provider allow thread deletion** the caller **should** keep track of the created threads and delete them later when necessary.
|
||||
If you require thread deletion and the provider allows this, the caller **should** keep track of the created threads and delete them later when necessary via the provider's sdk.
|
||||
|
||||
i.e: OpenAI Assistants Provider
|
||||
```csharp
|
||||
await assistantClient.DeleteThreadAsync(thread.ConversationId);
|
||||
```
|
||||
|
||||
### Tool Registration
|
||||
### 5. Tool Registration
|
||||
|
||||
#### Semantic Kernel
|
||||
|
||||
In semantic kernel to expose a function as a tool you must:
|
||||
|
||||
1. Decorate the function with `[KernelFunction]` attribute.
|
||||
1. Decorate the function with a `[KernelFunction]` attribute.
|
||||
2. Have a `Plugin` class or use the `KernelPluginFactory` to wrap the function.
|
||||
3. Have a `Kernel` to use add your plugin.
|
||||
3. Have a `Kernel` to add your plugin to.
|
||||
4. Pass the `Kernel` to the agent.
|
||||
|
||||
```csharp
|
||||
@@ -162,7 +155,7 @@ In agent framework in a single call you can register tools directly in the agent
|
||||
AIAgent agent = chatClient.CreateAIAgent(tools: [AIFunctionFactory.Create(GetWeather)]);
|
||||
```
|
||||
|
||||
### Agent Non-Streaming Invocation
|
||||
### 6. Agent Non-Streaming Invocation
|
||||
|
||||
Key differences can be seen in the method names from `Invoke` to `Run`, return types and parameters `AgentRunOptions`.
|
||||
|
||||
@@ -179,15 +172,16 @@ await foreach (AgentResponseItem<ChatMessageContent> result in agent.InvokeAsync
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
The Non-Streaming returns a single `AgentRunResponse` with the agent response that can contain multiple messages.
|
||||
The Non-Streaming returns a single `AgentRunResponse` with the agent response that can contain multiple messages.
|
||||
The text result of the run is available in `AgentRunResponse.Text` or `AgentRunResponse.ToString()`.
|
||||
All intermediate messages that lead up to creating the result is returned in the `AgentRunResponse.Messages` list.
|
||||
All messages created as part of the response is returned in the `AgentRunResponse.Messages` list.
|
||||
This may include tool call messages, function results, reasoning updates and final results.
|
||||
|
||||
```csharp
|
||||
AgentRunResponse agentResponse = await agent.RunAsync(userInput, thread);
|
||||
```
|
||||
|
||||
### Agent Streaming Invocation
|
||||
### 7. Agent Streaming Invocation
|
||||
|
||||
Key differences in the method names from `Invoke` to `Run`, return types and parameters `AgentRunOptions`.
|
||||
|
||||
@@ -202,7 +196,7 @@ await foreach (StreamingChatMessageContent update in agent.InvokeStreamingAsync(
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
Similar streaming API pattern with the key difference being that it `AgentRunResponseUpdate` including more agent related information per update.
|
||||
Similar streaming API pattern with the key difference being that it returns `AgentRunResponseUpdate` objects including more agent related information per update.
|
||||
|
||||
All updates produced by any service underlying the AIAgent is returned. The textual result of the agent is available by concatenating the `AgentRunResponse.Text` values.
|
||||
|
||||
@@ -211,9 +205,12 @@ await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(userInpu
|
||||
{
|
||||
Console.Write(update); // Update is ToString() friendly
|
||||
}
|
||||
``
|
||||
### Tool Function Signatures
|
||||
```
|
||||
|
||||
### 8. Tool Function Signatures
|
||||
|
||||
**Problem**: SK plugin methods need `[KernelFunction]` attributes
|
||||
|
||||
```csharp
|
||||
public class MenuPlugin
|
||||
{
|
||||
@@ -223,34 +220,42 @@ public class MenuPlugin
|
||||
```
|
||||
|
||||
**Solution**: AF can use methods directly without attributes
|
||||
|
||||
```csharp
|
||||
public class MenuTools
|
||||
{
|
||||
[Description("Get menu items")] // Only Description needed
|
||||
[Description("Get menu items")] // Optional description
|
||||
public static MenuItem[] GetMenu() => ...;
|
||||
}
|
||||
```
|
||||
|
||||
### Options Configuration
|
||||
### 9. Options Configuration
|
||||
|
||||
**Problem**: Complex options setup in SK
|
||||
|
||||
```csharp
|
||||
OpenAIPromptExecutionSettings settings = new() { MaxTokens = 1000 };
|
||||
AgentInvokeOptions options = new() { KernelArguments = new(settings) };
|
||||
```
|
||||
|
||||
**Solution**: Simplified options in AF
|
||||
|
||||
```csharp
|
||||
ChatClientAgentRunOptions options = new(new() { MaxOutputTokens = 1000 });
|
||||
```
|
||||
|
||||
### Dependency Injection
|
||||
> [!IMPORTANT]
|
||||
> This example shows passing implementation specific options to a `ChatClientAgent`. Not all `AIAgents` support `ChatClientAgentRunOptions`.
|
||||
> `ChatClientAgent` is provided to build agents based on underlying inference services, and therefore supports inference options like `MaxOutputTokens`.
|
||||
|
||||
### 10. Dependency Injection
|
||||
|
||||
#### Semantic Kernel
|
||||
|
||||
A `Kernel` registration is required in the service container to be able to create an agent
|
||||
as every agent abstractions needs to be initialized with a `Kernel` property.
|
||||
|
||||
Semantic Kernel uses `Agent` type as the lower level abstraction for agents.
|
||||
Semantic Kernel uses the `Agent` type as the base abstraction class for agents.
|
||||
|
||||
```csharp
|
||||
services.AddKernel().AddProvider(...);
|
||||
@@ -264,32 +269,47 @@ serviceContainer.AddKeyedSingleton<SemanticKernel.Agents.Agent>(
|
||||
});
|
||||
```
|
||||
|
||||
### 11. **Agent Type Consolidation**
|
||||
|
||||
#### Semantic Kernel
|
||||
|
||||
Semantic kernel provides specific agent classes for various services, e.g.
|
||||
|
||||
- `ChatCompletionAgent` for use with chat-completion-based inference services.
|
||||
- `OpenAIAssistantAgent` for use with the OpenAI Assistants service.
|
||||
- `AzureAIAgent` for use with the Azure AI Foundry Agents service.
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
Agent framework lower level agents abstraction are defined as `AIAgent` type to avoid potential type clashes with other `Agent` types
|
||||
not necessarily related to AI Agents.
|
||||
The agent framework supports all the abovementioned services via a single agent type, `ChatClientAgent`.
|
||||
|
||||
`ChatClientAgent` can be used to build agents using any underlying service that provides an SDK implementing the `Microsoft.Extensions.AI.IChatClient` interface.
|
||||
|
||||
#### Agent Framework
|
||||
|
||||
The Agent framework provides the `AIAgent` type as the base abstraction class.
|
||||
|
||||
```csharp
|
||||
services.AddKeyedSingleton<AIAgent>(() => client.CreateAIAgent(...));
|
||||
```
|
||||
|
||||
# Migration Samples
|
||||
## Migration Samples
|
||||
|
||||
This folder contains **separate console application projects** demonstrating how to transition from **Semantic Kernel (SK)** to the new **Agent Framework (AF)**.
|
||||
This folder contains **separate console application projects** demonstrating how to transition from **Semantic Kernel (SK)** to the new **Agent Framework (AF)**.
|
||||
|
||||
Each project shows side-by-side comparisons of equivalent functionality in both frameworks and can be run independently.
|
||||
|
||||
Each sample code contains the following:
|
||||
1. **SK Agent** (Semantic Kernel before)
|
||||
2. **AF Agent** (Agent Framework after)
|
||||
2. **AF Agent** (Agent Framework after)
|
||||
|
||||
## Running the samples from Visual Studio
|
||||
### Running the samples from Visual Studio
|
||||
|
||||
Open the solution in Visual Studio and set the desired sample project as the startup project. Then, run the project using the built-in debugger or by pressing `F5`.
|
||||
|
||||
You will be prompted for any required environment variables if they are not already set.
|
||||
|
||||
## Prerequisites
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following:
|
||||
|
||||
@@ -298,7 +318,7 @@ Before you begin, ensure you have the following:
|
||||
- For OpenAI samples: OpenAI API key
|
||||
- For OpenAI Assistants samples: OpenAI API key with Assistant API access
|
||||
|
||||
## Environment Variables
|
||||
### Environment Variables
|
||||
|
||||
Set the appropriate environment variables based on the sample type you want to run:
|
||||
|
||||
@@ -325,7 +345,7 @@ $env:AF_SHOW_ALL_DEMO_SETTING_VALUES = "Y"
|
||||
|
||||
If environment variables are not set, the demos will prompt you to enter values interactively.
|
||||
|
||||
## Samples
|
||||
### Samples
|
||||
|
||||
The migration samples are organized into three categories, each demonstrating different AI service integrations:
|
||||
|
||||
@@ -370,4 +390,4 @@ dotnet run
|
||||
# Azure OpenAI Assistants Examples
|
||||
cd "AzureOpenAIAssistants\Step01_Basics"
|
||||
dotnet run
|
||||
```
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user