Skip to content

Commit ec48ad5

Browse files
committed
group chat fun
1 parent 311bae8 commit ec48ad5

4 files changed

Lines changed: 319 additions & 76 deletions

File tree

Helpers/AIProcessor.cs

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
using Microsoft.SemanticKernel;
1818
using Microsoft.SemanticKernel.Agents;
1919
using Microsoft.SemanticKernel.Agents.Chat;
20+
using Microsoft.SemanticKernel.Agents.Orchestration.GroupChat;
21+
using Microsoft.SemanticKernel.Agents.Orchestration.Handoff;
22+
using Microsoft.SemanticKernel.Agents.Runtime.InProcess;
2023
using Microsoft.SemanticKernel.AudioToText;
2124
using Microsoft.SemanticKernel.ChatCompletion;
2225
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
@@ -36,6 +39,7 @@
3639
using Qdrant.Client.Grpc;
3740
using SemanticKernelFun.Data;
3841
using SemanticKernelFun.Models;
42+
using SemanticKernelFun.Plugins;
3943
using SemanticKernelFun.Plugins.TransferOrderPlanner;
4044
using SemanticKernelFun.Plugins.TripPlanner;
4145
using Spectre.Console;
@@ -1499,12 +1503,220 @@ You are limited to American cities only. Keep your responses clear and concise."
14991503
}
15001504
}
15011505

1502-
OpenAI.Chat.ChatCompletion finalCompletion = await chatClient.CompleteChatAsync(
1506+
ChatCompletion finalCompletion = await chatClient.CompleteChatAsync(
15031507
conversationMessages,
15041508
options
15051509
);
15061510

15071511
Console.WriteLine(finalCompletion.Content.First().Text);
15081512
}
15091513
}
1514+
1515+
public static async Task RunMultiAgentSloganOrchestration(AzureAIConfig azureAIConfig)
1516+
{
1517+
// Set up kernel (adjust this as needed for your config)
1518+
var kernel = KernelHelper.GetKernelChatCompletion(azureAIConfig);
1519+
1520+
// Define the agents inline
1521+
var writer = new ChatCompletionAgent
1522+
{
1523+
Name = "CopyWriter",
1524+
Description = "A copy writer",
1525+
Instructions = """
1526+
You are a copywriter with ten years of experience and are known for brevity and a dry humor.
1527+
The goal is to refine and decide on the single best copy as an expert in the field.
1528+
Only provide a single proposal per response.
1529+
You're laser focused on the goal at hand.
1530+
Don't waste time with chit chat.
1531+
Consider suggestions when refining an idea.
1532+
""",
1533+
Kernel = kernel,
1534+
};
1535+
1536+
var editor = new ChatCompletionAgent
1537+
{
1538+
Name = "Reviewer",
1539+
Description = "An editor.",
1540+
Instructions = """
1541+
You are an art director who has opinions about copywriting born of a love for David Ogilvy.
1542+
The goal is to determine if the given copy is acceptable to print.
1543+
If so, state that it is approved.
1544+
If not, provide insight on how to refine suggested copy without example.
1545+
""",
1546+
Kernel = kernel,
1547+
};
1548+
1549+
// Define orchestration with a custom group manager and simple console output
1550+
var orchestration = new GroupChatOrchestration(
1551+
new CustomRoundRobinGroupChatManager
1552+
{
1553+
MaximumInvocationCount = 5,
1554+
InteractiveCallback = () =>
1555+
{
1556+
var input = new Microsoft.SemanticKernel.ChatMessageContent(
1557+
AuthorRole.User,
1558+
"I like it"
1559+
);
1560+
Console.WriteLine($"\n# USER INPUT: {input.Content}\n");
1561+
return ValueTask.FromResult(input);
1562+
},
1563+
},
1564+
writer,
1565+
editor
1566+
)
1567+
{
1568+
ResponseCallback = async content =>
1569+
{
1570+
Console.WriteLine($"\n📨 {content.Role} [{content.AuthorName}]: {content.Content}");
1571+
await Task.CompletedTask;
1572+
},
1573+
};
1574+
1575+
// Start the runtime
1576+
var runtime = new InProcessRuntime();
1577+
await runtime.StartAsync();
1578+
1579+
// Initial input
1580+
string input =
1581+
"Create a slogan for a new electric SUV that is affordable and fun to drive.";
1582+
Console.WriteLine($"\n🚗 Initial Prompt: {input}");
1583+
1584+
var result = await orchestration.InvokeAsync(input, runtime);
1585+
string output = await result.GetValueAsync(TimeSpan.FromSeconds(30));
1586+
1587+
Console.WriteLine($"\n🎯 Final Output:\n{output}");
1588+
1589+
await runtime.RunUntilIdleAsync();
1590+
1591+
Console.WriteLine("\n✅ Done. Press any key to exit.");
1592+
Console.ReadKey();
1593+
}
1594+
1595+
private class CustomRoundRobinGroupChatManager : RoundRobinGroupChatManager
1596+
{
1597+
public override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInput(
1598+
ChatHistory history,
1599+
CancellationToken cancellationToken = default
1600+
)
1601+
{
1602+
var last = history.LastOrDefault();
1603+
if (last?.AuthorName == "Reviewer")
1604+
{
1605+
return ValueTask.FromResult(
1606+
new GroupChatManagerResult<bool>(true)
1607+
{
1608+
Reason = "Reviewer has spoken; requesting user input.",
1609+
}
1610+
);
1611+
}
1612+
1613+
return ValueTask.FromResult(
1614+
new GroupChatManagerResult<bool>(false) { Reason = "User input not yet needed." }
1615+
);
1616+
}
1617+
}
1618+
1619+
public static async Task RunMultiAgentTriageOrchestration(AzureAIConfig azureAIConfig)
1620+
{
1621+
var kernel = KernelHelper.GetKernelChatCompletion(azureAIConfig);
1622+
1623+
kernel.ImportPluginFromObject(new TicketPlugin());
1624+
kernel.ImportPluginFromObject(new KnowledgePlugin());
1625+
1626+
// AGENTS
1627+
var triageAgent = new ChatCompletionAgent
1628+
{
1629+
Name = "TriageAgent",
1630+
Description =
1631+
"Responsible for triaging incoming support requests and routing them to the appropriate department.",
1632+
Instructions = """
1633+
You triage employee requests. Route to ITAgent (tech), HRAgent (HR), or FinanceAgent (money-related).
1634+
""",
1635+
Kernel = kernel,
1636+
};
1637+
1638+
var itAgent = new ChatCompletionAgent
1639+
{
1640+
Name = "ITAgent",
1641+
Description =
1642+
"Handles IT-related support requests such as software, hardware, network, or login issues.",
1643+
Instructions = """
1644+
You're an IT support agent. Troubleshoot technical issues.
1645+
If unresolved, call TicketPlugin.CreateTicket(issue, "IT").
1646+
""",
1647+
Kernel = kernel,
1648+
};
1649+
1650+
var hrAgent = new ChatCompletionAgent
1651+
{
1652+
Name = "HRAgent",
1653+
Description =
1654+
"Handles human resources support like PTO, benefits, job roles, and employee policies.",
1655+
Instructions = """
1656+
You're an HR support agent. Help with PTO, benefits, and policies.
1657+
Use KnowledgePlugin.GetPolicy if asked about HR policy.
1658+
""",
1659+
Kernel = kernel,
1660+
};
1661+
1662+
var financeAgent = new ChatCompletionAgent
1663+
{
1664+
Name = "FinanceAgent",
1665+
Description =
1666+
"Handles finance-related support such as payroll, reimbursements, and budgeting policies.",
1667+
Instructions = """
1668+
You're a finance agent. Help with payroll, reimbursements, and expense policies.
1669+
Use KnowledgePlugin.GetPolicy or TicketPlugin as needed.
1670+
""",
1671+
Kernel = kernel,
1672+
};
1673+
1674+
var handoffs = OrchestrationHandoffs
1675+
.StartWith(triageAgent)
1676+
.Add(triageAgent, itAgent, hrAgent, financeAgent)
1677+
.Add(itAgent, triageAgent, "Hand back if not a tech issue")
1678+
.Add(hrAgent, triageAgent, "Hand back if not an HR issue")
1679+
.Add(financeAgent, triageAgent, "Hand back if not finance");
1680+
1681+
var orchestration = new HandoffOrchestration(
1682+
handoffs,
1683+
triageAgent,
1684+
itAgent,
1685+
hrAgent,
1686+
financeAgent
1687+
)
1688+
{
1689+
InteractiveCallback = async () =>
1690+
{
1691+
Console.Write("\n🧑‍💼 You: ");
1692+
var input = await Task.Run(() => Console.ReadLine());
1693+
1694+
return new Microsoft.SemanticKernel.ChatMessageContent(
1695+
AuthorRole.User,
1696+
input ?? ""
1697+
);
1698+
},
1699+
1700+
ResponseCallback = async msg =>
1701+
{
1702+
Console.WriteLine($"\n📣 {msg.Role} [{msg.AuthorName}]: {msg.Content}");
1703+
await Task.CompletedTask;
1704+
},
1705+
};
1706+
1707+
// RUN IT
1708+
Console.WriteLine("Enter a support request:");
1709+
Console.Write("> ");
1710+
var input = Console.ReadLine() ?? "I need help accessing my pay stubs.";
1711+
1712+
var runtime = new InProcessRuntime();
1713+
await runtime.StartAsync();
1714+
1715+
var result = await orchestration.InvokeAsync(input, runtime);
1716+
var final = await result.GetValueAsync();
1717+
1718+
Console.WriteLine($"\n✅ Final result: {final}");
1719+
Console.WriteLine("\nPress any key to exit...");
1720+
Console.ReadKey();
1721+
}
15101722
}

Plugins/Functions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.ComponentModel;
2+
using Microsoft.SemanticKernel;
3+
4+
namespace SemanticKernelFun.Plugins;
5+
6+
// PLUGINS
7+
public class TicketPlugin
8+
{
9+
[KernelFunction, Description("Creates a support ticket for unresolved issues")]
10+
public string CreateTicket(string issue, string department) =>
11+
$"📩 Ticket created in {department} for '{issue}' (ID: {Guid.NewGuid()})";
12+
}
13+
14+
public class KnowledgePlugin
15+
{
16+
[KernelFunction, Description("Returns the company policy for a topic and department")]
17+
public string GetPolicy(string topic, string department) =>
18+
$"📚 Policy for '{topic}' in {department}: [Sample policy text here]";
19+
}

0 commit comments

Comments
 (0)