-
Notifications
You must be signed in to change notification settings - Fork 4k
.Net: Remote Chat Completion Agent Demo #11554
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
Closed
tommasodotNET
wants to merge
27
commits into
microsoft:main
from
tommasodotNET:demo/remote-chat-completion-agent
Closed
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
5a12075
adds remote chat completion agent demo
tommasodotNET ff62810
adds readme
tommasodotNET 58e3945
updates aspire app host version
tommasodotNET 87234b7
remove blank line
tommasodotNET 1154f23
fixes missing } in readme
tommasodotNET 244ebdd
Merge branch 'main' into demo/remote-chat-completion-agent
tommasodotNET 64d2d32
set dotnet target to 8.0
tommasodotNET 16c02a6
Merge branch 'demo/remote-chat-completion-agent' of github.com:tommas…
tommasodotNET 44ac51c
Merge branch 'main' into demo/remote-chat-completion-agent
crickman de38e06
Clean-up for build
crickman af3fb3e
Typos
crickman 96a21f1
Typo syntax
crickman cb477aa
Readme
crickman 5433445
Encoding
crickman 93e5a6a
Encoding
crickman 14d6e14
More encoding
crickman 60737a5
Fix readme links
crickman f91f1da
Suppress comments for autogenerated code
crickman ffdb417
Fix project
crickman 916f50b
adds images to doc
tommasodotNET 203c54e
Merge branch 'demo/remote-chat-completion-agent' of github.com:tommas…
tommasodotNET 80c6167
Merge branch 'main' into demo/remote-chat-completion-agent
crickman 467e4a8
Fix link
crickman 9ccbc80
Merge branch 'demo/remote-chat-completion-agent' of https://github.co…
crickman 025bd95
Merge branch 'main' into demo/remote-chat-completion-agent
crickman 4ee0705
Update _typos.toml
crickman acc056f
Merge branch 'main' into demo/remote-chat-completion-agent
crickman 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
Large diffs are not rendered by default.
Oops, something went wrong.
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,75 @@ | ||
# Remote Chat Completion Agent Test | ||
|
||
This repo shows how we could implement a [`RemoteChatCompletionAgent`](./RemoteChatCompletionAgentDemo.GroupChat/RemoteChatCompletionAgent.cs) that can be used to interact with a remote Semantic Kernel `ChatCompletionAgent`. | ||
|
||
The new type [`RemoteChatCompletionAgent`](./RemoteChatCompletionAgentDemo.GroupChat/RemoteChatCompletionAgent.cs) implements the `ChatHistoryKernelAgent` methods using a custom HTTP client to send requests to an API that hosts the `ChatCompletionAgent` functionality. This allows us to use the same interface as the local `ChatCompletionAgent`, but with the added benefit of being able to interact with a remote service. | ||
|
||
## Benefits of having a remote agent | ||
- **Polyglot Support**: The remote agent can be implemented with different programming languages and frameworks, allowing for better integration and support for different platforms. | ||
- **Reusability**: The remote agent can be reused across different applications and services, allowing for better code reuse and maintainability. | ||
- **Scalability**: The remote agent can be hosted on a powerful server, allowing for more complex computations and larger models. | ||
- **Resource Management**: The remote agent can be managed and monitored more easily, allowing for better resource allocation and usage tracking. | ||
- **Security**: The remote agent can be secured and monitored more easily, allowing for better protection of sensitive data and computations. | ||
- **Cost Efficiency**: The remote agent can be hosted on a pay-as-you-go basis, allowing for better cost management and resource allocation. | ||
- **Flexibility**: The remote agent can be updated and maintained more easily, allowing for better support for new features and improvements. | ||
|
||
## Details about the sample | ||
|
||
I am using an `AgentGroupChat` to show how to use different remote agents togethere in the same chat. It's a useful use case. Since the`RemoteChatCompletionAgent` extends the type `ChatHistoryKernelAgent`, it can be used in the same way as the local agent. | ||
|
||
In this simple Group Chat, we have two agents: | ||
- `TranslatorAgent`: this agent translates the text to English. [Program.cs](./RemoteChatCompletionAgentDemo.TranslatorAgent/Program.cs) | ||
- `SummaryAgent`: this agent summarize the text. [Program.cs](./RemoteChatCompletionAgentDemo.SummaryAgent/Program.cs) | ||
|
||
The Group Chat will call the `TranslatorAgent` first, and then the `SummaryAgent`. | ||
|
||
I am also using [.NET Aspire]https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) to have all the different microservices start and run together. I am also leveraging the .NET Service Discovery to have the correct endpoints for each agent. | ||
|
||
.NET Aspire is not required. It just make it easier to run the sample and monitoring all the different microservices. You can run the `RemoteChatCompletionAgent` in any .NET application. | ||
|
||
## How to run the example | ||
|
||
1. Configure the OpenAI integration for .NET Aspire according to the [documentation](https://learn.microsoft.com/en-us/dotnet/aspire/azureai/azureai-openai-integration?tabs=dotnet-cli#connect-to-an-existing-azure-openai-service). | ||
|
||
Note that you can use either DefaultAzureCredentials or API Keys for authentication. | ||
|
||
Using DefaultAzureCredentials: | ||
|
||
```json | ||
{ | ||
"ConnectionStrings": { | ||
"openAiConnectionName": "https://{account_name}.openai.azure.com/" | ||
} | ||
} | ||
``` | ||
|
||
Using API Keys: | ||
|
||
```json | ||
{ | ||
"ConnectionStrings": { | ||
"openAiConnectionName": "Endpoint=https://{account_name}.openai.azure.com/;Key={api_key};" | ||
}, | ||
} | ||
``` | ||
|
||
2. Run the sample | ||
|
||
```bash | ||
cd RemoteAgentTest.AppHost | ||
dotnet run | ||
``` | ||
|
||
3. Invoke the AgentGroupChat via http: | ||
|
||
``` | ||
http://localhost:{PORT}/remote-group-chat | ||
``` | ||
|
||
You will see Traces for the request, showing that the Group Chat is actually calling each remote agent as intructed. | ||
|
||
 | ||
|
||
The output of the Group Chat can be seen in the `groupChat` service logs: | ||
|
||
 |
24 changes: 24 additions & 0 deletions
24
.../samples/Demos/RemoteChatCompletionAgent/RemoteChatCompletionAgentDemo.AppHost/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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
var builder = DistributedApplication.CreateBuilder(args); | ||
|
||
var openai = builder.AddConnectionString("openAiConnectionName"); | ||
|
||
var translatorAgent = builder.AddProject<Projects.RemoteChatCompletionAgentDemo_TranslatorAgent>("translatoragent") | ||
.WithReference(openai); | ||
|
||
var summaryAgent = builder.AddProject<Projects.RemoteChatCompletionAgentDemo_SummaryAgent>("summaryagent") | ||
.WithReference(openai); | ||
|
||
var remoteChatCompletionAgent = builder.AddProject<Projects.RemoteChatCompletionAgentDemo_GroupChat>("groupChat") | ||
.WithReference(openai) | ||
.WithReference(translatorAgent) | ||
.WithReference(summaryAgent) | ||
.WithHttpCommand("/remote-group-chat", "Invoke Chat", | ||
commandOptions: new() | ||
{ | ||
Method = HttpMethod.Get | ||
} | ||
); | ||
|
||
builder.Build().Run(); |
29 changes: 29 additions & 0 deletions
29
...nAgent/RemoteChatCompletionAgentDemo.AppHost/RemoteChatCompletionAgentDemo.AppHost.csproj
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,29 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<IsAspireHost>true</IsAspireHost> | ||
<UserSecretsId>4e4d92b3-c7a6-4269-9119-5831f36a8063</UserSecretsId> | ||
<NoWarn>$(NoWarn);CS1591</NoWarn> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Aspire.Hosting.AppHost" /> | ||
<PackageReference Include="Aspire.Hosting.Azure.CognitiveServices" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference | ||
Include="..\RemoteChatCompletionAgentDemo.TranslatorAgent\RemoteChatCompletionAgentDemo.TranslatorAgent.csproj" /> | ||
<ProjectReference | ||
Include="..\RemoteChatCompletionAgentDemo.SummaryAgent\RemoteChatCompletionAgentDemo.SummaryAgent.csproj" /> | ||
<ProjectReference | ||
Include="..\RemoteChatCompletionAgentDemo.GroupChat\RemoteChatCompletionAgentDemo.GroupChat.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
9 changes: 9 additions & 0 deletions
9
...es/Demos/RemoteChatCompletionAgent/RemoteChatCompletionAgentDemo.AppHost/appsettings.json
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,9 @@ | ||
{ | ||
"Logging": { | ||
"LogLevel": { | ||
"Default": "Information", | ||
"Microsoft.AspNetCore": "Warning", | ||
"Aspire.Hosting.Dcp": "Warning" | ||
} | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
...amples/Demos/RemoteChatCompletionAgent/RemoteChatCompletionAgentDemo.GroupChat/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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using Microsoft.SemanticKernel; | ||
using Microsoft.SemanticKernel.Agents; | ||
using Microsoft.SemanticKernel.Agents.Chat; | ||
using Microsoft.SemanticKernel.ChatCompletion; | ||
using RemoteChatCompletionAgentDemo.GroupChat; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true); | ||
|
||
// Add services to the container. | ||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi | ||
builder.AddServiceDefaults(); | ||
builder.AddAzureOpenAIClient("openAiConnectionName"); | ||
builder.Services.AddHttpClient<TranslatorAgentHttpClient>(client => { client.BaseAddress = new("https+http://translatoragent"); }); | ||
builder.Services.AddHttpClient<SummaryAgentHttpClient>(client => { client.BaseAddress = new("https+http://summaryagent"); }); | ||
builder.Services.AddKernel().AddAzureOpenAIChatCompletion("gpt-4o"); | ||
var app = builder.Build(); | ||
|
||
app.UseHttpsRedirection(); | ||
|
||
app.MapGet("/remote-group-chat", async (Kernel kernel, TranslatorAgentHttpClient translatorAgentHttpClient, SummaryAgentHttpClient summaryAgentHttpClient) => | ||
{ | ||
// Use the clients as needed here | ||
var translatorAgent = new RemoteChatCompletionAgent(translatorAgentHttpClient); | ||
var summaryAgent = new RemoteChatCompletionAgent(summaryAgentHttpClient); | ||
|
||
var terminateFunction = KernelFunctionFactory.CreateFromPrompt( | ||
""" | ||
Determine if the text has been summarized. If so, respond with a single word: yes. | ||
|
||
History: | ||
|
||
{{$history}} | ||
""" | ||
); | ||
|
||
var selectionFunction = KernelFunctionFactory.CreateFromPrompt( | ||
$$$""" | ||
Your job is to determine which participant takes the next turn in a conversation according to the action of the most recent participant. | ||
State only the name of the participant to take the next turn. | ||
|
||
Choose only from these participants: | ||
- {{{translatorAgent.Name}}} | ||
- {{{summaryAgent.Name}}} | ||
|
||
Always follow these steps when selecting the next participant: | ||
1) After user input, it is {{{translatorAgent.Name}}}'s turn. | ||
2) After {{{translatorAgent.Name}}} replies, it's {{{summaryAgent.Name}}}'s turn. | ||
|
||
History: | ||
{{$history}} | ||
""" | ||
); | ||
|
||
var chat = new AgentGroupChat(translatorAgent, summaryAgent) | ||
{ | ||
ExecutionSettings = new() | ||
{ | ||
TerminationStrategy = new KernelFunctionTerminationStrategy(terminateFunction, kernel) | ||
{ | ||
Agents = [summaryAgent], | ||
ResultParser = (result) => result.GetValue<string>()?.Contains("yes", StringComparison.OrdinalIgnoreCase) ?? false, | ||
HistoryVariableName = "history", | ||
MaximumIterations = 10 | ||
}, | ||
SelectionStrategy = new KernelFunctionSelectionStrategy(selectionFunction, kernel) | ||
{ | ||
HistoryVariableName = "history" | ||
} | ||
} | ||
}; | ||
|
||
var prompt = "COME I FORNITORI INFLUENZANO I TUOI COSTI Quando scegli un piano di assicurazione sanitaria, uno dei fattori più importanti da considerare è la rete di fornitori in convenzione disponibili con il piano. Northwind Standard offre un'ampia varietà di fornitori in convenzione, tra cui medici di base, specialisti, ospedali e farmacie. Questo ti permette di scegliere un fornitore comodo per te e la tua famiglia, contribuendo al contempo a mantenere bassi i tuoi costi. Se scegli un fornitore in convenzione con il tuo piano, pagherai generalmente copay e franchigie più basse rispetto a un fornitore fuori rete. Inoltre, molti servizi, come l'assistenza preventiva, possono essere coperti senza alcun costo aggiuntivo se ricevuti da un fornitore in convenzione. È importante notare, tuttavia, che Northwind Standard non copre i servizi di emergenza, l'assistenza per la salute mentale e l'abuso di sostanze, né i servizi fuori rete. Questo significa che potresti dover pagare di tasca tua per questi servizi se ricevuti da un fornitore fuori rete. Quando scegli un fornitore in convenzione, ci sono alcuni suggerimenti da tenere a mente. Verifica che il fornitore sia in convenzione con il tuo piano. Puoi confermarlo chiamando l'ufficio del fornitore e chiedendo se è in rete con Northwind Standard. Puoi anche utilizzare lo strumento di ricerca fornitori sul sito web di Northwind Health per verificare la copertura. Assicurati che il fornitore stia accettando nuovi pazienti. Alcuni fornitori potrebbero essere in convenzione ma non accettare nuovi pazienti. Considera la posizione del fornitore. Se il fornitore è troppo lontano, potrebbe essere difficile raggiungere gli appuntamenti. Valuta gli orari dell'ufficio del fornitore. Se lavori durante il giorno, potresti aver bisogno di trovare un fornitore con orari serali o nel fine settimana. Scegliere un fornitore in convenzione può aiutarti a risparmiare sui costi sanitari. Seguendo i suggerimenti sopra e facendo ricerche sulle opzioni disponibili, puoi trovare un fornitore conveniente, accessibile e in rete con il tuo piano Northwind Standard."; | ||
chat.AddChatMessage(new ChatMessageContent(AuthorRole.User, prompt)); | ||
await foreach (var content in chat.InvokeAsync().ConfigureAwait(false)) | ||
{ | ||
Console.WriteLine(); | ||
Console.WriteLine($"# {content.Role} - {content.AuthorName ?? "*"}: '{content.Content}'"); | ||
Console.WriteLine(); | ||
} | ||
|
||
return Results.Ok(); | ||
}) | ||
.WithName("InvokeRemoteGroupChat"); | ||
|
||
app.MapDefaultEndpoints(); | ||
|
||
app.Run(); | ||
|
||
internal sealed class TranslatorAgentHttpClient : RemoteAgentHttpClient | ||
{ | ||
public TranslatorAgentHttpClient(HttpClient httpClient) : base(httpClient) | ||
{ | ||
} | ||
} | ||
|
||
internal sealed class SummaryAgentHttpClient : RemoteAgentHttpClient | ||
{ | ||
public SummaryAgentHttpClient(HttpClient httpClient) : base(httpClient) | ||
{ | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
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.