Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions demos/module-04/demo04/AvailableRoom.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace modulerag;

public static partial class HotelBookingFunctions
{
public class AvailableRoom
{
public int RoomId { get; set; }
public string RoomType { get; set; }
public decimal PricePerNight { get; set; }
public string HotelName { get; set; }
public int NumberOfAdultsAllowedInRoom { get; set; }
public string Amenities { get; set; }
public string City { get; set; }
}
}
117 changes: 41 additions & 76 deletions demos/module-04/demo04/ChatWithAgent.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

namespace modulerag;

internal class ChatWithAgent
internal class ChatWithAgent(IChatClient chatClient)
{
public async Task LetAgentFindRideAndHotel(IConfiguration config)
public async Task LetAgentFindRideAndHotel()
{
var question =
"""
I am going to a concert that is held at the Seattle Kraken Stadium. The Concert starts at 7:30 pm and is November 20th this year.
""";

Console.WriteLine("******** Create the ride agent ***********");
var rideAgent = CreateTransportationAgent(config);
rideAgent.Kernel.ImportPluginFromType<RideInformationSystemService>();
var rideAgent = CreateTransportationAgent();

Console.WriteLine("******** Create the hotel agent ***********");
var hotelAgent = HotelBookingAgent.CreateChatCompletionAgent(config);
hotelAgent.Kernel.ImportPluginFromType<HotelBookingFunctions>();
var hotelAgent = HotelBookingAgent.CreateChatCompletionAgent(chatClient);

// create the chat history that starts the agent thread
var history = new ChatHistory();
history.AddMessage(AuthorRole.User, question);

AgentThread thread = new ChatHistoryAgentThread(history);
// TODO how to deal with multiple agents? AgentSession is created for a specific agent.
// Sessions are agent/service-specific.
// Reusing a session with a different agent configuration or provider can lead to invalid context.
AgentSession thread = new ChatHistoryAgentThread(history);

await RunUntilGoalReached(hotelAgent, thread);
Console.WriteLine("******** hotel agent done ***********");
Expand All @@ -36,68 +34,56 @@ public async Task LetAgentFindRideAndHotel(IConfiguration config)
Console.WriteLine("******** Done ***********");
}

private async Task RunUntilGoalReached(ChatCompletionAgent agent, AgentThread thread)
private async Task RunUntilGoalReached(AIAgent agent, AgentSession session)
{
var agentResponse = agent.InvokeAsync("", thread);
var agentResponse = await agent.RunAsync("", session);

await PrintResult(agentResponse);
while (!await IsGoalReached(agentResponse))
PrintResult(agentResponse);
while (!IsGoalReached(agentResponse))
{
var input = Console.ReadLine();

agentResponse = agent.InvokeAsync(input, thread);
agentResponse = await agent.RunAsync(input, session);

await PrintResult(agentResponse);
PrintResult(agentResponse);
}
}

public async Task LetAgentFindRide(IConfiguration config)
public async Task LetAgentFindRide()
{
var question =
"""
I stay at the WestIn Seattle and the venue is the Seattle Kraken stadium.
I stay at the Westin Seattle and the venue is the Seattle Kraken stadium.
the Concert starts at 7:30 pm and is November 20th this year.
""";

Console.WriteLine("******** Create the agent ***********");
var transportationAgent = CreateTransportationAgent(config);
transportationAgent.Kernel.ImportPluginFromType<RideInformationSystemService>();
var transportationAgent = CreateTransportationAgent();

Console.WriteLine("******** Start the agent ***********");
var session = await transportationAgent.CreateSessionAsync();

var thread = new ChatHistoryAgentThread();
var agentresult = transportationAgent.InvokeAsync(question, thread);
var agentResponse = await transportationAgent.RunAsync(question, session);

Console.WriteLine("******** RESPONSE 1 ***********");
await PrintResult(agentresult);
PrintResult(agentResponse);

while (!await IsGoalReached(agentresult))
while (!IsGoalReached(agentResponse))
{
var input = Console.ReadLine();

agentresult = transportationAgent.InvokeAsync(input, thread);
agentResponse = await transportationAgent.RunAsync(input, session);

Console.WriteLine("******** RESPONSE ***********");
await PrintResult(agentresult);
PrintResult(agentResponse);
}
Console.WriteLine("******** Terminating, goal reached ***********");
}

private async Task<bool> IsGoalReached(IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> agentResponse)
{
await foreach(var item in agentResponse)
{
if(item.Message.Content.Contains("[** GOAL REACHED **]"))
{
return true;
}
}
return false;
}
private bool IsGoalReached(AgentResponse agentResponse) => agentResponse.Text.Contains("[** GOAL REACHED **]");

private ChatCompletionAgent CreateTransportationAgent(IConfiguration config)
private AIAgent CreateTransportationAgent()
{
var kernel = CreateKernel(config);

var instructions = """
You are an expert in finding transportation options from a given hotel location to the concert location.
You will try to get the best options available for an affordable price. Make sure the customer will be there at least 30 minutes before the concert starts at the venue.
Expand All @@ -108,44 +94,23 @@ You are not allowed to make a booking without user confirmation!
After you successfully booked the ride you will respond with [** GOAL REACHED **] in your message.
""";

ChatCompletionAgent agent = new()
{
Name = "TransportationAgent",
Instructions = instructions,
Description = "An agent that finds transportation options from hotel to concert location",
Kernel = kernel,
Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),
}),
};

return agent;
return chatClient.AsAIAgent(
name: "TransportationAgent",
description: "An agent that finds transportation options for the user from their hotel to the concert venue.",
instructions: instructions,
tools: [AIFunctionFactory.Create(RideInformationSystemService.GetAvailableRides),
AIFunctionFactory.Create(RideInformationSystemService.BookARide)]
);
}

private static async Task PrintResult(IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> agentResponse)
private static void PrintResult(AgentResponse agentResponse)
{
await
foreach (var item in agentResponse)
foreach (var message in agentResponse.Messages)
{
Console.WriteLine($"Thread: {item.Thread.Id}");
Console.WriteLine($"Thread data: {item.Thread}");
Console.WriteLine($"Author: {item.Message.AuthorName}");
Console.WriteLine($"Message:{item.Message}");
//Console.WriteLine($"Thread: {session.Id}");
//Console.WriteLine($"Thread data: {message.}");
Console.WriteLine($"Author: {message.AuthorName}");
Console.WriteLine($"Message:{message.Text}");
}
}

private static Kernel CreateKernel(IConfiguration config)
{
var model = config["OpenAI:Model"];
var endpoint = config["OpenAI:EndPoint"];
var token = config["OpenAI:ApiKey"];

var kernelBuilder = Kernel
.CreateBuilder()
.AddOpenAIChatCompletion(model, new Uri(endpoint), token);

var kernel = kernelBuilder.Build();
return kernel;
}
}
42 changes: 11 additions & 31 deletions demos/module-04/demo04/HotelBookingAgent.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

namespace modulerag;

internal class HotelBookingAgent
{
public static ChatCompletionAgent CreateChatCompletionAgent(IConfiguration config)
public static AIAgent CreateChatCompletionAgent(IChatClient chatClient)
{
Kernel kernel = CreateKernel(config);
ChatCompletionAgent hotelReservationAgent =
new()
{
Name = "HotelReservationAgent",
Instructions = """
return chatClient.AsAIAgent(
name: "HotelReservationAgent",
instructions: """
You are an expert in finding hotel rooms close to music concert locations.
You provide some options what you have found and ask for approval before you
make the booking. You always suggest 3 options with different price ranges.
Expand All @@ -23,25 +18,10 @@ You are not allowed to make a booking without user confirmation!

After you succesfully booked the ride you will respond with [** GOAL REACHED **] in your message.
""",
Description = "An agent that finds and books a hotel room close to the concert location",
Kernel = kernel,
Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
})
};
return hotelReservationAgent;
}

private static Kernel CreateKernel(IConfiguration config)
{
var model = config["OpenAI:Model"];
var endpoint = config["OpenAI:EndPoint"];
var token = config["OpenAI:ApiKey"];
var kernelBuilder = Kernel
.CreateBuilder()
.AddOpenAIChatCompletion(model, new Uri(endpoint), token);
var kernel = kernelBuilder.Build();
return kernel;
description: "An agent that finds and books a hotel room close to the concert location",
tools: [AIFunctionFactory.Create(HotelBookingFunctions.SelectRoomPreference),
AIFunctionFactory.Create(HotelBookingFunctions.BookSelectedRoom),
AIFunctionFactory.Create(HotelBookingFunctions.GetApprovalForBooking)]
);
}
}
27 changes: 8 additions & 19 deletions demos/module-04/demo04/HotelBookingFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

namespace modulerag;

public class HotelBookingFunctions
public static partial class HotelBookingFunctions
{
[KernelFunction, Description("Returns available rooms in a city for a date")]
[Description("Returns available rooms in a city for a date")]
[return: Description("The available room information, containing Type, Price per night, name of the hotel, number of people per room, amenities and location")]
public AvailableRoom[] SelectRoomPreference(
public static AvailableRoom[] SelectRoomPreference(
[Description("City where you need an hotel")]
string city,
[Description("Date the hotel night is required")]
Expand All @@ -21,9 +21,9 @@ public AvailableRoom[] SelectRoomPreference(
.ToArray();
}

[KernelFunction, Description("Books a room previously selected from available rooms")]
[Description("Books a room previously selected from available rooms")]
[return: Description("Indication if the booking was successful")]
public bool BookSelectedRoom(
public static bool BookSelectedRoom(
[Description("id of the room to be booked")]
int id)
{
Expand All @@ -34,9 +34,9 @@ public bool BookSelectedRoom(
return selectedRoom != null;
}

[KernelFunction, Description("Provides approval from the user for making a suggested booking based on the room id")]
[Description("Provides approval from the user for making a suggested booking based on the room id")]
[return: Description("Indication if it is allowed to make the booking")]
public bool GetApprovalForBooking(
public static bool GetApprovalForBooking(
[Description("id of the room to be booked")]
int id)
{
Expand All @@ -47,18 +47,7 @@ public bool GetApprovalForBooking(
return consent.Equals("yes", StringComparison.OrdinalIgnoreCase);
}

public class AvailableRoom
{
public int RoomId { get; set; }
public string RoomType { get; set; }
public decimal PricePerNight { get; set; }
public string HotelName { get; set; }
public int NumberOfAdultsAllowedInRoom { get; set; }
public string Amenities { get; set; }
public string City { get; set; }
}

private readonly AvailableRoom[] availableRooms =
private static readonly AvailableRoom[] availableRooms =
[
// --- 50 room options below ---
new AvailableRoom { RoomId = 304, RoomType = "Standard", PricePerNight = 110.00m, HotelName = "Harbor View", NumberOfAdultsAllowedInRoom = 2, Amenities = "Free WiFi, TV", City = "San Francisco" },
Expand Down
23 changes: 19 additions & 4 deletions demos/module-04/demo04/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using System.ClientModel;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using OpenAI;
using modulerag;

var builder = new ConfigurationBuilder();
Expand All @@ -9,5 +11,18 @@

IConfiguration config = builder.Build();

//await new ChatWithAgent().LetAgentFindRide(config);
await new ChatWithAgent().LetAgentFindRideAndHotel(config);
var model = config["OpenAI:Model"] ?? throw new InvalidOperationException("Missing OpenAI model setting.");
var endpoint = config["OpenAI:Endpoint"] ?? config["OpenAI:EndPoint"] ??
throw new InvalidOperationException("Missing OpenAI endpoint setting.");
var apiKey = config["OpenAI:ApiKey"] ?? throw new InvalidOperationException("Missing OpenAI API key.");

// Create OpenAI client with custom endpoint
var openAIClient = new OpenAIClient(new ApiKeyCredential(apiKey), new OpenAIClientOptions
{
Endpoint = new Uri(endpoint)
});

var chatClient = openAIClient.GetChatClient(model).AsIChatClient();

//await new ChatWithAgent(chatClient).LetAgentFindRide();
await new ChatWithAgent(chatClient).LetAgentFindRideAndHotel();
3 changes: 3 additions & 0 deletions demos/module-04/demo04/Ride.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace modulerag;

public record Ride(int RideId, string RideType, decimal Price, string ServiceName, string City);
Loading