Agentische toepassingspatronen

Er zijn twee algemene benaderingen voor het bouwen van agentische toepassingen met AI:

  • Deterministische werkstromen : uw code definieert de controlestroom. U schrijft de reeks stappen, vertakking, parallelle uitvoering en foutafhandeling met behulp van standaardprogrammeerconstructies. De LLM voert in elke stap werk uit, maar heeft geen controle over de algehele stroom.
  • Agent-gestuurde werkstromen (agentlussen) — de LLM stuurt de besturingsstroom aan. De agent bepaalt welke hulpprogramma's moeten worden aangeroepen, in welke volgorde en wanneer de taak is voltooid. U geeft hulpprogramma's en instructies op, maar de agent bepaalt het uitvoeringspad tijdens runtime.

Beide benaderingen profiteren van duurzame uitvoering en kunnen worden geïmplementeerd met behulp van het Durable Task-programmeermodel. In dit artikel wordt beschreven hoe u elk patroon bouwt met behulp van codevoorbeelden.

Aanbeveling

Deze patronen zijn afgestemd op de agentische werkstroomontwerpen die worden beschreven in de Building Effective Agents van Lantropic. Het durable task programmeermodel wordt op natuurlijke wijze toegewezen aan deze patronen: indelingen definiëren de stroom voor werkstroombeheer en worden automatisch gecontroleerd, terwijl activiteiten niet-deterministische bewerkingen verpakken, zoals LLM-aanroepen, aanroepen van hulpprogramma's en API-aanvragen.

Een benadering kiezen

In de volgende tabel kunt u bepalen wanneer u elke benadering wilt gebruiken.

Deterministische werkstromen gebruiken wanneer... Agentlussen gebruiken wanneer...
De volgorde van de stappen is van tevoren bekend. De taak is geopend en de stappen kunnen niet worden voorspeld.
U hebt expliciete kaders nodig voor agentgedrag. U wilt dat de LLM bepaalt welke hulpprogramma's moeten worden gebruikt en wanneer.
Naleving of controlebaarheid vereist een controleerbare controlestroom. De agent moet de benadering aanpassen op basis van tussenliggende resultaten.
U wilt meerdere AI-frameworks combineren in één werkstroom. U bouwt een gespreksagent met mogelijkheden voor het aanroepen van hulpprogramma's.

Beide benaderingen bieden automatische controlepunten, opnieuw probeerbeleid, gedistribueerd schalen en ondersteuning voor menselijke tussenkomst via bestendige uitvoering.

Deterministische werkstroompatronen

In een deterministische werkstroom bepaalt uw code het uitvoeringspad. De LLM wordt aangeroepen als een stap binnen de werkstroom, maar bepaalt niet wat er vervolgens gebeurt. Het Durable Task programmeermodel past van nature bij de aanpak.

  • Indelingen definiëren de stroom voor werkstroombeheer (volgorde, vertakking, parallellisme, foutafhandeling) en worden automatisch gecontroleerd.
  • Activiteiten verpakken niet-deterministische bewerkingen, zoals LLM-aanroepen, aanroepen van hulpprogramma's en API-aanvragen. Activiteiten kunnen worden uitgevoerd op elk beschikbaar rekenproces.

In de volgende voorbeelden worden Durable Functions gebruikt, die wordt uitgevoerd op Azure Functions met serverloze hosting.

In de volgende voorbeelden worden de draagbare Durable Taak-SDK's gebruikt, die worden uitgevoerd op elke host-compute, waaronder Azure Container Apps, Kubernetes, virtuele machines of lokaal.

Promptkoppeling

Prompt-chaining is het eenvoudigste agentuele patroon. U breekt een complexe taak op in een reeks opeenvolgende LLM-interacties, waarbij de uitvoer van elke stap wordt ingevoerd in de invoer van de volgende stap. Omdat elke activiteitsoproep automatisch wordt gecontroleerd, dwingt een crash halverwege de pijplijn u niet opnieuw op te starten en dure LLM-tokens opnieuw te gebruiken. De uitvoering wordt hervat vanaf de laatste voltooide stap.

U kunt ook programmatische validatiepoorten invoegen tussen stappen. Nadat u bijvoorbeeld een overzicht hebt gegenereerd, kunt u controleren of deze voldoet aan een lengte- of onderwerpbeperking voordat u deze doorgeeft aan de ontwerpstap.

Dit patroon wordt rechtstreeks toegewezen aan het patroon voor functiekoppeling in het Durable Task-programmeermodel.

Wanneer gebruikt u: Pijplijnen voor het genereren van inhoud, documentverwerking met meerdere stappen, sequentiële gegevensverrijking, werkstromen waarvoor tussenliggende validatiepoorten zijn vereist.

[Function(nameof(PromptChainingOrchestration))]
public async Task<string> PromptChainingOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var topic = context.GetInput<string>();

    // Step 1: Generate research outline
    string outline = await context.CallActivityAsync<string>(
        nameof(GenerateOutlineAgent), topic);

    // Step 2: Write first draft from outline
    string draft = await context.CallActivityAsync<string>(
        nameof(WriteDraftAgent), outline);

    // Step 3: Refine and polish the draft
    string finalContent = await context.CallActivityAsync<string>(
        nameof(RefineDraftAgent), draft);

    return finalContent;
}

Opmerking

De status van de orkestratie wordt automatisch vastgelegd bij elke await instructie. Als het hostproces vastloopt of de VM wordt gerecycled, wordt de indeling automatisch hervat vanaf de laatste voltooide stap in plaats van opnieuw te beginnen.

[DurableTask]
public class PromptChainingOrchestration : TaskOrchestrator<string, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, string topic)
    {
        // Step 1: Generate research outline
        string outline = await context.CallActivityAsync<string>(
            nameof(GenerateOutlineAgent), topic);

        // Step 2: Write first draft from outline
        string draft = await context.CallActivityAsync<string>(
            nameof(WriteDraftAgent), outline);

        // Step 3: Refine and polish the draft
        string finalContent = await context.CallActivityAsync<string>(
            nameof(RefineDraftAgent), draft);

        return finalContent;
    }
}

Opmerking

De status van de orkestratie wordt automatisch vastgelegd bij elke await instructie. Als het hostproces vastloopt of de VM wordt gerecycled, wordt de indeling automatisch hervat vanaf de laatste voltooide stap in plaats van opnieuw te beginnen.

Routebepaling

Routering maakt gebruik van een classificatiestap om te bepalen welke downstreamagent of welk model een aanvraag moet verwerken. De orkestratie roept eerst een classificatieactiviteit aan en tak vervolgens naar de juiste handler op basis van het resultaat. Met deze aanpak kunt u de prompt, het model en de toolset van elke handler onafhankelijk aanpassen, bijvoorbeeld het doorsturen van factureringsvragen naar een gespecialiseerde agent met toegang tot betalings-API's terwijl algemene vragen worden verzonden naar een lichter model.

Wanneer gebruikt u: Klantenondersteuning triage, intentieclassificatie voor gespecialiseerde agents, dynamische modelselectie op basis van taakcomplexiteit.

[Function(nameof(RoutingOrchestration))]
public async Task<string> RoutingOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var request = context.GetInput<SupportRequest>();

    // Classify the request type
    string category = await context.CallActivityAsync<string>(
        nameof(ClassifyRequestAgent), request.Message);

    // Route to the appropriate specialized agent
    return category switch
    {
        "billing" => await context.CallActivityAsync<string>(
            nameof(BillingAgent), request),
        "technical" => await context.CallActivityAsync<string>(
            nameof(TechnicalSupportAgent), request),
        "general" => await context.CallActivityAsync<string>(
            nameof(GeneralInquiryAgent), request),
        _ => await context.CallActivityAsync<string>(
            nameof(GeneralInquiryAgent), request),
    };
}
[DurableTask]
public class RoutingOrchestration : TaskOrchestrator<SupportRequest, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, SupportRequest request)
    {
        // Classify the request type
        string category = await context.CallActivityAsync<string>(
            nameof(ClassifyRequestAgent), request.Message);

        // Route to the appropriate specialized agent
        return category switch
        {
            "billing" => await context.CallActivityAsync<string>(
                nameof(BillingAgent), request),
            "technical" => await context.CallActivityAsync<string>(
                nameof(TechnicalSupportAgent), request),
            _ => await context.CallActivityAsync<string>(
                nameof(GeneralInquiryAgent), request),
        };
    }
}

Parallellisatie

Wanneer u meerdere onafhankelijke subtaken hebt, kunt u deze verzenden als parallelle activiteitsoproepen en wachten op alle resultaten voordat u doorgaat. De Durable Task Scheduler distribueert deze activiteiten automatisch over alle beschikbare rekeninstanties, wat betekent dat het toevoegen van meer werkers de totale doorlooptijd vermindert.

Een veelvoorkomende variant is stemmen met meerdere modellen: u verzendt dezelfde prompt naar verschillende modellen (of hetzelfde model met verschillende temperaturen) parallel en voegt vervolgens samen of selecteert u deze uit de antwoorden. Omdat elke parallelle vertakking onafhankelijk van elkaar wordt gecontroleerd, heeft een tijdelijke fout in de ene vertakking geen invloed op de andere.

Dit patroon komt direct overeen met het fan-out/fan-in patroon in Durable Task.

Wanneer gebruikt u: Batchanalyse van documenten, parallelle hulpprogramma-aanroepen, evaluatie van meerdere modellen, inhoudsbeheer met meerdere revisoren.

[Function(nameof(ParallelResearchOrchestration))]
public async Task<string> ParallelResearchOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var request = context.GetInput<ResearchRequest>();

    // Fan-out: research multiple subtopics in parallel
    var researchTasks = request.Subtopics
        .Select(subtopic => context.CallActivityAsync<string>(
            nameof(ResearchSubtopicAgent), subtopic))
        .ToList();
    string[] researchResults = await Task.WhenAll(researchTasks);

    // Aggregate: synthesize all research into a single summary
    string summary = await context.CallActivityAsync<string>(
        nameof(SynthesizeAgent),
        new { request.Topic, Research = researchResults });

    return summary;
}
[DurableTask]
public class ParallelResearchOrchestration : TaskOrchestrator<ResearchRequest, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, ResearchRequest request)
    {
        // Fan-out: research multiple subtopics in parallel
        var researchTasks = request.Subtopics
            .Select(subtopic => context.CallActivityAsync<string>(
                nameof(ResearchSubtopicAgent), subtopic))
            .ToList();
        string[] researchResults = await Task.WhenAll(researchTasks);

        // Aggregate: synthesize all research into a single summary
        string summary = await context.CallActivityAsync<string>(
            nameof(SynthesizeAgent),
            new { request.Topic, Research = researchResults });

        return summary;
    }
}

Orchestrator-werkers

In dit patroon roept een centrale orchestrator eerst een LLM (via een activiteit) aan om het werk te plannen. Op basis van de uitvoer van de LLM bepaalt de orchestrator vervolgens welke subtaken er nodig zijn. De orchestrator verzendt die subtaken vervolgens naar gespecialiseerde werkprocessen. Het belangrijkste verschil met parallellisatie is dat de set van subtaken niet tijdens het ontwerp is vastgesteld; de orchestrator bepaalt deze dynamisch tijdens de uitvoeringstijd.

Dit patroon maakt gebruik van subindelingen, die onafhankelijk van controlepunten onderliggende werkstromen zijn. Elke werknemerscoördinatie kan zelf meerdere stappen, herhalingen en geneste parallelle processen bevatten.

Wanneer gebruikt u: Pijplijnen voor diep onderzoek, het coderen van agentwerkstromen die meerdere bestanden wijzigen, samenwerking met meerdere agents waarbij elke agent een afzonderlijke rol heeft.

[Function(nameof(OrchestratorWorkersOrchestration))]
public async Task<string> OrchestratorWorkersOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var request = context.GetInput<ResearchRequest>();

    // Central orchestrator: determine what research is needed
    string[] subtasks = await context.CallActivityAsync<string[]>(
        nameof(PlanResearchAgent), request.Topic);

    // Delegate to worker orchestrations in parallel
    var workerTasks = subtasks
        .Select(subtask => context.CallSubOrchestratorAsync<string>(
            nameof(ResearchWorkerOrchestration), subtask))
        .ToList();
    string[] results = await Task.WhenAll(workerTasks);

    // Synthesize results
    string finalReport = await context.CallActivityAsync<string>(
        nameof(SynthesizeAgent),
        new { request.Topic, Research = results });

    return finalReport;
}
[DurableTask]
public class OrchestratorWorkersOrchestration : TaskOrchestrator<ResearchRequest, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, ResearchRequest request)
    {
        // Central orchestrator: determine what research is needed
        string[] subtasks = await context.CallActivityAsync<string[]>(
            nameof(PlanResearchAgent), request.Topic);

        // Delegate to worker orchestrations in parallel
        var workerTasks = subtasks
            .Select(subtask => context.CallSubOrchestratorAsync<string>(
                nameof(ResearchWorkerOrchestration), subtask))
            .ToList();
        string[] results = await Task.WhenAll(workerTasks);

        // Synthesize results
        string finalReport = await context.CallActivityAsync<string>(
            nameof(SynthesizeAgent),
            new { request.Topic, Research = results });

        return finalReport;
    }
}

Evaluator-optimizer

Het patroon evaluator-optimizer paart een generator-agent met een evaluator-agent in een verfijningslus. De generator produceert uitvoer, de evaluator beoordeelt deze op basis van kwaliteitscriteria en geeft feedback en de lus wordt herhaald totdat de uitvoer is verstreken of een maximum aantal iteraties is bereikt. Omdat elke herhaling van de lus wordt gecontroleerd, verliest een crash na drie geslaagde verfijningsrondes die voortgang niet.

Dit patroon is vooral handig wanneer kwaliteit programmatisch kan worden gemeten, bijvoorbeeld het valideren van de gegenereerde codecompilaties of dat een vertaling benoemde entiteiten behoudt.

Wanneer gebruikt u: Codegeneratie met geautomatiseerde beoordeling, literaire vertaling, iteratieve inhoudsverfijning, complexe zoektaken waarvoor meerdere analyserondes nodig zijn.

[Function(nameof(EvaluatorOptimizerOrchestration))]
public async Task<string> EvaluatorOptimizerOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var request = context.GetInput<ContentRequest>();
    int maxIterations = 5;
    string content = "";
    string feedback = "";

    for (int i = 0; i < maxIterations; i++)
    {
        // Generate or refine content
        content = await context.CallActivityAsync<string>(
            nameof(GenerateContentAgent),
            new { request.Prompt, PreviousContent = content, Feedback = feedback });

        // Evaluate quality
        var evaluation = await context.CallActivityAsync<EvaluationResult>(
            nameof(EvaluateContentAgent), content);

        if (evaluation.MeetsQualityBar)
            return content;

        feedback = evaluation.Feedback;
    }

    return content; // Return best effort after max iterations
}
[DurableTask]
public class EvaluatorOptimizerOrchestration : TaskOrchestrator<ContentRequest, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, ContentRequest request)
    {
        int maxIterations = 5;
        string content = "";
        string feedback = "";

        for (int i = 0; i < maxIterations; i++)
        {
            // Generate or refine content
            content = await context.CallActivityAsync<string>(
                nameof(GenerateContentAgent),
                new { request.Prompt, PreviousContent = content, Feedback = feedback });

            // Evaluate quality
            var evaluation = await context.CallActivityAsync<EvaluationResult>(
                nameof(EvaluateContentAgent), content);

            if (evaluation.MeetsQualityBar)
                return content;

            feedback = evaluation.Feedback;
        }

        return content; // Return best effort after max iterations
    }
}

Agentlussen

In een typische implementatie van een AI-agent wordt een LLM in een loop aangeroepen, waarbij hulpprogramma's worden gebruikt en beslissingen worden genomen totdat de taak is voltooid of een stopvoorwaarde is bereikt. In tegenstelling tot deterministische werkstromen is het uitvoeringspad niet vooraf gedefinieerd. De agent bepaalt wat u moet doen bij elke stap op basis van de resultaten uit de vorige stappen.

Agentlussen zijn geschikt voor taken waarbij het aantal of de volgorde van de stappen niet kan worden voorspeld. Veelvoorkomende voorbeelden zijn open-ended codeeragents, autonoom onderzoek en gespreksbots met mogelijkheden om tools aan te roepen.

Er zijn twee aanbevolen benaderingen voor het implementeren van agentlussen met het Durable Task-programmeermodel:

Methode Beschrijving Wanneer gebruiken
Op orkestratie gebaseerde Schrijf de agentlus als een veerkrachtige orkestratie. Hulpprogrammaaanroepen worden geïmplementeerd als activiteiten en menselijke invoer maakt gebruik van externe gebeurtenissen. De orkestratie bepaalt de lusstructuur, terwijl de LLM de beslissingen hierin beheerst. U hebt nauwkeurige controle over de lus nodig, beleid voor opnieuw proberen per hulpprogramma, gedistribueerde taakverdeling van hulpprogrammaaanroepen of de mogelijkheid om fouten in de lus in uw IDE op te sporen met onderbrekingspunten.
Op entiteit gebaseerd Elk agentexemplaar is een duurzame entiteit. Het agentframework beheert de lus intern en de entiteit biedt duurzame status- en sessiepersistentie. U gebruikt een agentframework (zoals Microsoft Agent Framework) dat de agentlus al implementeert en u duurzaamheid wilt toevoegen met minimale codewijzigingen.

Agentlussen op basis van orchestratie

Een op orkestratie gebaseerde agentlus combineert verschillende Durable Task-capaciteiten: eeuwigdurende orkestraties (doorgaan als nieuw) om het geheugen binnen grenzen te houden, fan-out/fan-in voor parallelle uitvoering van hulpprogramma's, en externe gebeurtenissen voor menselijke interacties binnen de lus. Elke herhaling van de lus:

  1. Verzendt de huidige gesprekscontext naar de LLM via een activiteit of stateful entiteit.
  2. Ontvangt het antwoord van de LLM, waarbij mogelijk tool-aanroepen inbegrepen zijn.
  3. Hiermee worden hulpprogramma-aanroepen uitgevoerd als activiteiten (gedistribueerd over beschikbare rekenkracht).
  4. Wacht eventueel op menselijke invoer met behulp van externe gebeurtenissen.
  5. Hiermee wordt de lus voortgezet met de bijgewerkte status of voltooid wanneer de agent aangeeft dat deze klaar is.
[Function(nameof(AgentLoopOrchestration))]
public async Task<string> AgentLoopOrchestration(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    // Get state from input (supports continue-as-new)
    var state = context.GetInput<AgentState>() ?? new AgentState();

    int maxIterations = 100;
    while (state.Iteration < maxIterations)
    {
        // Send conversation history to the LLM
        var llmResponse = await context.CallActivityAsync<LlmResponse>(
            nameof(CallLlmAgent), state.Messages);

        state.Messages.Add(llmResponse.Message);

        // If the LLM returned tool calls, execute them in parallel
        if (llmResponse.ToolCalls is { Count: > 0 })
        {
            var toolTasks = llmResponse.ToolCalls
                .Select(tc => context.CallActivityAsync<ToolResult>(
                    nameof(ExecuteTool), tc))
                .ToList();
            ToolResult[] toolResults = await Task.WhenAll(toolTasks);

            foreach (var result in toolResults)
                state.Messages.Add(result.ToMessage());
        }
        // If the LLM needs human input, wait for it
        else if (llmResponse.NeedsHumanInput)
        {
            string humanInput = await context.WaitForExternalEvent<string>("HumanInput");
            state.Messages.Add(new Message("user", humanInput));
        }
        // LLM is done
        else
        {
            return llmResponse.FinalAnswer;
        }

        state.Iteration++;

        // Periodically continue-as-new to keep the history bounded
        if (state.Iteration % 10 == 0)
        {
            context.ContinueAsNew(state);
            return null!; // Orchestration will restart with updated state
        }
    }

    return "Max iterations reached.";
}
[DurableTask]
public class AgentLoopOrchestration : TaskOrchestrator<AgentState, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, AgentState? state)
    {
        state ??= new AgentState();

        int maxIterations = 100;
        while (state.Iteration < maxIterations)
        {
            // Send conversation history to the LLM
            var llmResponse = await context.CallActivityAsync<LlmResponse>(
                nameof(CallLlmAgent), state.Messages);

            state.Messages.Add(llmResponse.Message);

            // If the LLM returned tool calls, execute them
            if (llmResponse.ToolCalls is { Count: > 0 })
            {
                var toolTasks = llmResponse.ToolCalls
                    .Select(tc => context.CallActivityAsync<ToolResult>(
                        nameof(ExecuteTool), tc))
                    .ToList();
                ToolResult[] toolResults = await Task.WhenAll(toolTasks);

                foreach (var result in toolResults)
                    state.Messages.Add(result.ToMessage());
            }
            // If the LLM needs human input, wait for it
            else if (llmResponse.NeedsHumanInput)
            {
                string humanInput = await context.WaitForExternalEvent<string>("HumanInput");
                state.Messages.Add(new Message("user", humanInput));
            }
            // LLM is done
            else
            {
                return llmResponse.FinalAnswer;
            }

            state.Iteration++;

            // Periodically continue-as-new to keep the history bounded
            if (state.Iteration % 10 == 0)
            {
                context.ContinueAsNew(state);
                return null!;
            }
        }

        return "Max iterations reached.";
    }
}

Agentlussen op basis van entiteiten

Als u een agentframework gebruikt dat al een eigen agentlus implementeert, kunt u deze verpakken in een duurzame entiteit om duurzaamheid toe te voegen zonder de luslogica opnieuw te schrijven. Elk entiteitsexemplaar vertegenwoordigt één agentsessie. De entiteit ontvangt berichten, delegeert aan het agentframework intern en legt de gespreksstatus vast over meerdere interacties heen.

Het belangrijkste voordeel van deze benadering is eenvoud: u schrijft uw agent met behulp van uw favoriete framework en voegt duurzaamheid toe als hostingprobleem in plaats van de controlestroom van de agent opnieuw te ontwerpen. De entiteit fungeert als een duurzame wrapper, verwerkt sessiepersistentie en herstel automatisch.

In de volgende voorbeelden ziet u hoe u een bestaande agent-SDK verpakt als een duurzame entiteit. De entiteit maakt een message bewerking beschikbaar die clients aanroepen om gebruikersinvoer te verzenden. Intern wordt de entiteit gedelegeerd aan het agentframework, dat een eigen lus voor het aanroepen van hulpprogramma's beheert.

// Define the entity that wraps an existing agent SDK
public class ChatAgentEntity : TaskEntity<ChatAgentState>
{
    private readonly IChatClient _chatClient;

    public ChatAgentEntity(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    // Called by clients to send a message to the agent
    public async Task<string> Message(string userMessage)
    {
        // Add the user message to the conversation history
        State.Messages.Add(new ChatMessage(ChatRole.User, userMessage));

        // Delegate to the agent SDK for the LLM call (with tool loop)
        ChatResponse response = await _chatClient.GetResponseAsync(
            State.Messages, State.Options);

        // Persist the response in the entity state
        State.Messages.AddRange(response.Messages);

        return response.Text;
    }

    // Azure Functions entry point for the entity
    [Function(nameof(ChatAgentEntity))]
    public Task RunEntityAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
    {
        return dispatcher.DispatchAsync<ChatAgentEntity>();
    }
}
// Define the entity that wraps an existing agent SDK
[DurableTask(Name = "ChatAgent")]
public class ChatAgentEntity : TaskEntity<ChatAgentState>
{
    private readonly IChatClient _chatClient;

    public ChatAgentEntity(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    // Called by clients to send a message to the agent
    public async Task<string> Message(string userMessage)
    {
        // Add the user message to the conversation history
        State.Messages.Add(new ChatMessage(ChatRole.User, userMessage));

        // Delegate to the agent SDK for the LLM call (with tool loop)
        ChatResponse response = await _chatClient.GetResponseAsync(
            State.Messages, State.Options);

        // Persist the response in the entity state
        State.Messages.AddRange(response.Messages);

        return response.Text;
    }
}

De Durable Taakextensie voor Microsoft Agent Framework maakt gebruik van deze methode. Het omvat Microsoft Agent Framework-agents als duurzame entiteiten, die permanente sessies, automatische controlepunten en ingebouwde API-eindpunten bieden door middel van een enkele configuratieregel.

Volgende stappen