This commit is contained in:
Shyju Krishnankutty
2026-01-15 11:10:46 -08:00
parent 6e029eb039
commit ff230c86ce
13 changed files with 111 additions and 133 deletions
@@ -26,7 +26,7 @@ AzureOpenAIClient client = !string.IsNullOrEmpty(azureOpenAiKey)
// Set up an AI agent following the standard Microsoft Agent Framework pattern.
const string JokerName = "Joker";
const string JokerInstructions = "You are good at telling jokes.";
const string AnalysisInstructions = """
const string FeedbackAnalysisInstructions = """
You are a Customer Feedback Analyzer. Your task is to analyze customer survey responses and categorize them accurately.
INPUT: You will receive customer feedback text that may include a rating and comments.
@@ -44,31 +44,44 @@ CATEGORIZATION RULES:
- "Support Incident Status": Follow-ups on existing tickets, status inquiries about previous issues
RESPONSE FORMAT: Return only the category name exactly as shown above, with no additional text or explanation.
Examples:
- "The app crashes when I try to export" Bug Report
- "Love the new design! Great work" General Feedback
- "Why was I charged twice this month?" Billing Question
- "What's the status of ticket #12345?" Support Incident Status
""";
AIAgent agent = client.GetChatClient(deploymentName).CreateAIAgent(JokerInstructions, JokerName);
AIAgent surveyFeedbackAgent = client.GetChatClient(deploymentName).CreateAIAgent(AnalysisInstructions, "FeedbackAnalysisBot");
AIAgent agent = client.GetChatClient(deploymentName).CreateAIAgent(JokerInstructions, JokerName);
// 3 Executors usd by the workflow.
// 1. Class based executor to parse survey response.
SurveyResponseParserExecutor surveyResponseParserExecutor = new();
ResponseRouterExecutor responseRouterExecutor = new();
// 2. AI Agent to analyze feedback and categorize it.
AIAgent surveyFeedbackAgent = client.GetChatClient(deploymentName).CreateAIAgent(FeedbackAnalysisInstructions, "FeedbackAnalyzerAgent");
// 3. Function delegate executor to route response based on category.
Func<string, string> responseHandlingExecutorFunc = input => input switch
{
var s when s.Contains("billing", StringComparison.OrdinalIgnoreCase)
=> "Will notify Billing Team",
var s when s.Contains("Bug Report", StringComparison.OrdinalIgnoreCase)
=> "Will notify Technical Support Team",
_ => "Will notify General Support Team"
};
var responseRouterExecutor = responseHandlingExecutorFunc.BindAsExecutor("ResponseRouterExecutor");
WorkflowBuilder builder = new(surveyResponseParserExecutor);
builder.AddEdge(surveyResponseParserExecutor, surveyFeedbackAgent);
builder.AddEdge(surveyFeedbackAgent, responseRouterExecutor).WithOutputFrom(responseRouterExecutor);
var workflow = builder.WithName("HandleSurveyResponse").Build();
// Configure the function app to host AI agents and workflows in a unified way.
// This will automatically generate HTTP API endpoints for agents and workflows.
var functionBuilder = FunctionsApplication.CreateBuilder(args);
functionBuilder.ConfigureFunctionsWebApplication().ConfigureDurableOptions(options =>
{
// Configure workflows
options.Workflows.AddWorkflow(workflow);
});
functionBuilder.Build().Run();
FunctionsApplication.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
//.ConfigureDurableAgents(options => options.AddAIAgent(agent))
.ConfigureDurableOptions(options =>
{
// Configure workflows
options.Workflows.AddWorkflow(workflow);
// Optional - Configure AI agents
// options.Agents.AddAIAgent(agent);
})
.Build().Run();
@@ -1,27 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows;
namespace SingleAgent;
/// <summary>
/// Routes survey responses to appropriate teams based on rating and category.
/// </summary>
public sealed class ResponseRouterExecutor() : Executor<string, string>("ResponseRouterExecutor")
{
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
{
if (message.Contains("billing", StringComparison.OrdinalIgnoreCase))
{
return ValueTask.FromResult("Routed to Billing Team");
}
else if (message.Contains("technical", StringComparison.OrdinalIgnoreCase))
{
return ValueTask.FromResult("Routed to Technical Support Team");
}
else
{
return ValueTask.FromResult("Routed to General Support Team");
}
}
}
@@ -23,12 +23,12 @@ internal sealed partial class SurveyResponseParserExecutor() : Executor<string,
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
{
SurveyResponse response = this.ParseSurveyResponse(message);
SurveyResponse response = ParseSurveyResponse(message);
string jsonResult = JsonSerializer.Serialize(response, s_jsonOptions);
return ValueTask.FromResult(jsonResult);
}
private SurveyResponse ParseSurveyResponse(string message)
private static SurveyResponse ParseSurveyResponse(string message)
{
// Parse the message to extract rating and comment
int? rating = null;
@@ -6,3 +6,9 @@ POST {{authority}}/api/workflows/HandleSurveyResponse/run
Content-Type: text/plain
Rating: 10. Why was I charged $99 when my plan should be $49? I need a refund for the overcharge
POST {{authority}}/api/workflows/HandleSurveyResponse/run
Content-Type: text/plain
Rating: 10. Why was I charged $99 when my plan should be $49? I need a refund for the overcharge