diff --git a/WORKSHOP.txt b/WORKSHOP.txt index adeb10f..8e20f28 100644 --- a/WORKSHOP.txt +++ b/WORKSHOP.txt @@ -1,7 +1,7 @@ -Document-aware coaching -The complete single agent, connected to both InterviewData and MarkItDown MCP servers. +Your first handoff +Triage and the receptionist share document intake in a runnable two-agent workflow. -Checkpoint: 06-documents +Checkpoint: 07-first-handoff Source tag: workshop-v3-reference.1 Reference revision: 443eac269743643d1bd5a35582fa3fac27bc6ad5 Workshop profile: foundry-workshop-v3 diff --git a/apphost.settings.json b/apphost.settings.json index 70567b4..7588450 100644 --- a/apphost.settings.json +++ b/apphost.settings.json @@ -7,7 +7,7 @@ "Microsoft.EntityFrameworkCore": "Warning" } }, - "AgentMode": "Single", + "AgentMode": "HandOff", "LlmProvider": "MicrosoftFoundry", "MicrosoftFoundry": { "DeploymentName": "gpt-5-mini", diff --git a/src/InterviewCoach.Agent/AgentDelegateFactory.cs b/src/InterviewCoach.Agent/AgentDelegateFactory.cs index 8abdef6..d6ea684 100644 --- a/src/InterviewCoach.Agent/AgentDelegateFactory.cs +++ b/src/InterviewCoach.Agent/AgentDelegateFactory.cs @@ -47,7 +47,17 @@ public static IHostedAgentBuilder AddAIAgent(this IHostApplicationBuilder builde } private static IHostedAgentBuilder AddHandOffWorkflow(this IHostApplicationBuilder builder, string key, Func createWorkflowDelegate) - => throw new NotSupportedException("Complete Make your first handoff before enabling the workflow."); + { + builder.AddWorkflow(key, createWorkflowDelegate); + + return builder.AddAIAgent(key, (sp, name) => + { + var workflow = sp.GetRequiredKeyedService(key); + + return workflow.AsAIAgent(name: key) + .CreateFixedAgent(); + }); + } private static AIAgent CreateProviderAgent( IServiceProvider services, @@ -116,6 +126,65 @@ Always maintain a supportive and encouraging tone. // ============================================================================ // MODE 2: Handoff workflow. private static Workflow CreateHandOffWorkflow(IServiceProvider sp, string key) - => throw new NotSupportedException("Complete Make your first handoff before enabling the workflow."); + { + var markitdown = sp.GetRequiredKeyedService("mcp-markitdown"); + var interviewData = sp.GetRequiredKeyedService("mcp-interview-data"); + + var markitdownTools = markitdown.ListToolsAsync().GetAwaiter().GetResult(); + var interviewDataTools = interviewData.ListToolsAsync().GetAwaiter().GetResult(); + + // --- Triage Agent --- + var triageAgent = CreateProviderAgent( + services: sp, + name: "triage", + description: "Routes the conversation to the correct interview specialist.", + instructions: """ + You are the Triage agent for an interview intake workflow. + The only available specialist is "receptionist". + Hand off session setup and document intake to the receptionist. + If intake is already complete, explain that the interview specialists are not connected yet. + Do not restart completed intake or route to unavailable specialists. + """); + + // --- Receptionist Agent --- + var receptionistAgent = CreateProviderAgent( + services: sp, + name: "receptionist", + description: "Sets up interview sessions and collects resumes and job descriptions.", + instructions: """ + You are the Receptionist for an AI Interview Coach system. + Your job is to set up the interview session and collect documents. + + Process: + 1. Call get_interview_session with the application-provided SessionId. If no record exists, + call add_interview_session with that exact ID before any update. An update cannot create a record. + Let the user know the session ID after lookup or creation succeeds. + 2. Ask the user to provide their resume (link or text). Use MarkItDown to parse document links into markdown. + 3. Ask the user to provide the job description (link or text). Use MarkItDown to parse document links into markdown. + 4. Save parsed or pasted text in ResumeText and JobDescriptionText; saving a URL alone is insufficient. + A failed fetch does not complete intake. Ask for corrected input or explicit permission to skip it. + Before each update, call get_interview_session and preserve all six resume/job fields. + Set Transcript to ONLY new text to append, and verify the returned document fields before handoff. + 5. Once document intake is complete, let the user know. Interview specialists are not connected yet. + Only hand off to "triage" if the user wants to do something unexpected. + + The user may choose to proceed without a resume or job description — that's fine. + Always maintain a supportive and encouraging tone. + """, + tools: [.. markitdownTools, .. interviewDataTools]); + + // Connect only the two agents that exist at this checkpoint. +#pragma warning disable MAAIW001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + var workflow = AgentWorkflowBuilder + .CreateHandoffBuilderWith(triageAgent) + .WithHandoff(triageAgent, receptionistAgent) + .WithHandoff(receptionistAgent, triageAgent) + .Build(); +#pragma warning restore MAAIW001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + + return workflow.SetName(key); + } }