Agentikus alkalmazásminták

Az ügynöki alkalmazások AI-vel történő létrehozásának két általános megközelítése van:

  • Determinisztikus munkafolyamatok – A kód határozza meg a vezérlőfolyamatot. A lépések sorozatát, az elágaztatást, a párhuzamosságot és a hibakezelést szabványos programozási szerkezetekkel kell megírni. Az LLM minden lépésen belül végez munkát, de nem szabályozza a teljes folyamatot.
  • Ügynök által irányított munkafolyamatok (ügynökhurokok) – Az LLM vezérli a vezérlőfolyamatot. Az ügynök dönti el, hogy mely eszközöket hívja meg, milyen sorrendben és mikor fejezze be a feladatot. Eszközöket és utasításokat ad meg, de az ügynök futásidőben határozza meg a végrehajtási útvonalat.

Mindkét megközelítés kihasználja a tartós végrehajtás előnyeit, és a Durable Task programozási modellel valósítható meg. Ez a cikk bemutatja, hogyan hozhat létre minden mintát használatra kész kódmintákkal C#, Python, JavaScript/TypeScript és Java.

Jótanács

Prerequisites: A minták használata előtt állítsa be a Durable Task SDK-t vagy a Durable Functions. Első lépésként tekintse meg a SDK áttekintését vagy Durable Functions áttekintését.

Ezen a lapon:

Jótanács

Ezek a minták összhangban vannak az Anthropic Building Effective Agents című cikkében ismertetett ügynöki munkafolyamat-tervekkel. A Durable Task programozási modell természetesen megfelel ezeknek a mintáknak: a vezénylések határozzák meg a munkafolyamat-vezérlési folyamatot, és automatikusan ellenőrzik őket, míg a tevékenységek nem determinisztikus műveleteket, például LLM-hívásokat, eszközhívásokat és API-kéréseket csomagolnak be.

Megközelítés kiválasztása

Az alábbi táblázat segít eldönteni, hogy mikor érdemes használni az egyes megközelítéseket.

Használj determinisztikus munkafolyamatokat, amikor... Ügynökhurokok használata...
A lépések sorrendje előre ismert. A tevékenység befejezetlen, és a lépések nem jelezhetők előre.
Explicit védőkorlátokra van szüksége az ügynök viselkedése felett. Azt szeretné, hogy az LLM döntse el, hogy mely eszközöket és mikor használja.
A megfelelőséghez vagy az auditáláshoz áttekinthető vezérlési folyamat szükséges. Az ügynöknek a köztes eredmények alapján kell módosítania a megközelítését.
Több AI-keretrendszert szeretne egyesíteni egyetlen munkafolyamatban. Eszközhívási képességekkel rendelkező csevegőrobotot hoz létre.

Mindkét megközelítés automatikus ellenőrzőpontozást, újrapróbálkozási szabályzatokat, elosztott skálázást és human-in-the-loop támogatást biztosít tartós végrehajtással.

Determinisztikus munkafolyamat-minták

Egy determinisztikus munkafolyamatban a kód vezérli a végrehajtási útvonalat. Az LLM meghívása lépésként történik a munkafolyamaton belül, de nem dönti el, hogy mi történjen. A Durable Task programozási modell természetesen megfelel ennek a megközelítésnek:

  • A vezénylések határozzák meg a munkafolyamat-vezérlési folyamatot (sorrend, elágaztatás, párhuzamosság, hibakezelés), és automatikusan ellenőrzik.
  • A tevékenységek nem determinisztikus műveleteket, például LLM-hívásokat, eszközhívásokat és API-kéréseket csomagolnak be. A tevékenységek bármely elérhető számítási példányon futtathatók.

Az alábbi példák a Durable Functions használatát mutatják be, amely az Azure Functions platformon fut kiszolgáló nélküli üzemeltetéssel.

Az alábbi példák a hordozható Durable Task SDK-kat használják, amelyek bármilyen gazdagép-számításon futnak, beleértve az Azure Container Apps-t, a Kubernetes-t, a virtuális gépeket, vagy működhetnek helyileg is.

Rákérdezés láncolási mintára

Az utasításláncolás a legegyszerűbb ügynöki minta. Egy összetett feladatot szekvenciális LLM-interakciók sorozatára bonthat, ahol az egyes lépések kimenete a következő lépés bemenetébe kerül. Mivel minden tevékenységhívás automatikusan ellenőrzőponttal rendelkezik, a folyamat közbeni összeomlás nem kényszeríti az alapoktól való újraindításra és a költséges LLM-jogkivonatok újbóli felhasználására – a végrehajtás az utolsó befejezett lépéstől folytatódik.

A lépések között programozott érvényesítési kapukat is beszúrhat. A vázlat létrehozása után például ellenőrizheti, hogy megfelel-e egy hossz- vagy témakörkorlátnak, mielőtt átadja azt a vázlatkészítési lépésnek.

Ez a minta közvetlenül megfelel a Durable Task programozási modell függvénylánc-mintájának.

Mikor érdemes használni: Tartalomgenerálási folyamatok, többlépéses dokumentumfeldolgozás, szekvenciális adatbővítés, köztes érvényesítési kapukat igénylő munkafolyamatok.

Megjegyzés:

Az alábbi példákban a vezénylési állapot automatikusan ellenőrzés alá kerül minden await/yield utasításnál. Ha a gazdafolyamat összeomlik, vagy a virtuális gépet újraindítják, a vezénylés az utolsó befejezett lépéstől folytatódik az újrakezdés helyett.

[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;
}
[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;
    }
}

Útválasztás

Az útválasztás egy besorolási lépéssel határozza meg, hogy melyik alsóbb rétegbeli ügynök vagy modell kezelje a kéréseket. Az orchestration először egy osztályozó tevékenységet hív meg, majd az eredmény alapján a megfelelő kezelőhöz ágazik. Ez a módszer lehetővé teszi, hogy az egyes kezelők parancssorát, modelljét és eszközkészletét egymástól függetlenül szabja testre – például a számlázási kérdéseket egy speciális ügynökhöz irányíthatja, amely hozzáfér a fizetési API-khoz, miközben általános kérdéseket küld egy kisebb súlyú modellnek.

Mikor érdemes használni: Ügyféltámogatási osztályozás, speciális ügynökök szándékbesorolása, dinamikus modellválasztás a feladat összetettsége alapján.

[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),
        };
    }
}

Párhuzamosítás

Ha több független altevékenysége van, párhuzamos tevékenységhívásként elküldheti őket, és megvárhatja az összes eredményt a folytatás előtt. A Durable Task Scheduler automatikusan osztja el ezeket a tevékenységeket az összes rendelkezésre álló számítási példány között, ami azt jelenti, hogy több feldolgozó hozzáadása közvetlenül csökkenti a teljes falióra-időt.

A gyakori változat a többmodelles szavazás: ugyanazt a kérdést több modellnek (vagy ugyanazon modellnek különböző hőmérsékletekkel) küldi el párhuzamosan, majd összesíti vagy kiválasztja a válaszokat. Mivel minden párhuzamos ág egymástól függetlenül van ellenőrizve, az egyik ág átmeneti hibája nem érinti a többit.

Ez a minta közvetlenül a fan-out/fan-in mintára képez le a Durable Task keretrendszerben.

Mikor érdemes használni: Dokumentumok kötegelt elemzése, párhuzamos eszközhívások, többmodelles értékelés, tartalommoderálás több véleményezővel.

[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;
    }
}

Vezérlő-munkavégző minta

Ebben a mintában egy központi vezénylő először meghív egy LLM-et (egy tevékenységen keresztül), hogy megtervezze a munkát. Az LLM kimenete alapján a vezénylő ezután meghatározza, hogy milyen altevékenységekre van szükség. A vezénylő ezután ezeket az altevékenységeket speciális feldolgozó vezénylésekre küldi. A párhuzamosítás fő különbsége, hogy az altevékenységek halmaza nincs rögzítve a tervezéskor; a vezénylő futásidőben dinamikusan határozza meg őket.

Ez a minta részvezényléseket használ, amelyek egymástól függetlenül ellenőrzőponttal rendelkező gyermek-munkafolyamatok. Az egyes feldolgozói vezénylések több lépést, újrapróbálkozást és beágyazott párhuzamosságot tartalmazhatnak.

Mikor érdemes használni: Mély kutatási folyamatok, több fájlt módosító kódolási ügynök-munkafolyamatok, többügynökök közötti együttműködés, ahol minden ügynöknek külön szerepköre van.

[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;
    }
}

Kiértékelő-optimalizáló minta

A kiértékelő-optimalizáló minta egy generátorügynököt és egy kiértékelőügynököt párosít egy finomítási ciklusban. A generátor kimenetet állít elő, a kiértékelő a minőségi kritériumok alapján értékeli, és visszajelzést ad, és a ciklus addig ismétlődik, amíg a kimenet el nem éri a maximális iterációszámot. Mivel minden ciklus iterációja ellenőrzőponttal rendelkezik, a három sikeres finomítási kör után történt összeomlás nem veszíti el ezt a folyamatot.

Ez a minta különösen akkor hasznos, ha a minőség programozott módon mérhető – például a létrehozott kód fordításának ellenőrzése, vagy hogy a fordítás megőrzi az elnevezett entitásokat.

Mikor érdemes használni: Kódlétrehozás automatizált áttekintéssel, műfordítással, iteratív tartalom finomításával, összetett keresési feladatokkal, amelyek több elemzési kört igényelnek.

[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
    }
}

Ügynökhurokok

Egy tipikus AI-ügynök implementációjában a rendszer egy LLM-et hív meg egy ciklusban, eszközöket használ, és döntéseket hoz a feladat befejezéséig vagy a leállítási feltétel eléréséig. A determinisztikus munkafolyamatoktól eltérően a végrehajtási útvonal nincs előre definiálva. Az ügynök az előző lépések eredményei alapján határozza meg, hogy mi a teendő az egyes lépésekben.

Az ügynökhurokok olyan feladatokhoz ideálisak, ahol a lépések száma vagy sorrendje nem jelezhető előre. Ilyenek például a nyíltvégű kódolási ügynökök, az autonóm kutatás és az eszközhívó képességekkel rendelkező beszélgetési robotok.

Az ügynökhurkok durable task programozási modellel való implementálásának két ajánlott módszere van:

Megközelítés Leírás Mikor érdemes használni?
Orkesztráció-alapú Az ügynökök hurokja tartós összehangolás. Az eszközhívások tevékenységek; az emberi bemenet külső eseményeket használ. Részletes vezérlés, eszközenkénti újrapróbálkozási szabályzatok, elosztott eszközvégrehajtás, IDE-hibakeresés.
Entitásalapú Minden ügynökpéldány egy tartós entitás. Az ügynök-keretrendszer kezeli a hurkot; az entitás biztosítja a munkamenetek megőrzését. Olyan keretrendszert használ (például Microsoft Ügynök-keretrendszer), amely már rendelkezik ügynök ciklussal, és minimális módosításokkal szeretné növelni a tartósságot.

Orchestráció-alapú ügynökciklusok

A vezénylésen alapuló ügynök ciklus számos Durable Task-képességet kombinál: örök vezényléseket (újrakezdés), hogy a memória korlátozott maradjon, a szétosztás/konszolidálás a párhuzamos eszközök végrehajtásához, és külső eseményeket az embert bevonó interakciókhoz. A hurok minden iterációja:

  1. Az aktuális beszélgetési környezetet egy tevékenységen vagy állapotalapú entitáson keresztül küldi el az LLM-nek.
  2. Megkapja az LLM válaszát, amely eszközhívásokat is tartalmazhat.
  3. Az eszközhívásokat tevékenységekként hajtja végre (az elérhető számítások között elosztva).
  4. Igény szerint megvárja az emberi bemenetet külső események használatával.
  5. A ciklust frissített állapotban folytatja, vagy befejeződik, amikor az ügynök jelzi, hogy elkészült.
[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.";
    }
}

Entitásalapú ügynökhurokok

Ha olyan ügynök-keretrendszert használ, amely már implementálja a saját ügynökhurkát, a cikluslogika újraírása nélkül tartós entitásba csomagolhatja, hogy tartósságot adjon hozzá. Minden entitáspéldány egyetlen ügynök-munkamenetet jelöl. Az entitás fogadja az üzeneteket, az ügynöki keretrendszeren belül delegálja őket, és az interakciók során megőrzi a beszélgetés állapotát.

Ennek a megközelítésnek a fő előnye az egyszerűség: az ügynököt az előnyben részesített keretrendszer használatával írja meg, és az ügynök vezérlési folyamatának újratervezése helyett tartósságot ad hozzá üzemeltetési szempontként. Az entitás tartós burkolóként működik, és automatikusan kezeli a munkamenetek megőrzését és helyreállítását.

Az alábbi példák bemutatják, hogyan csomagolhat be egy meglévő ügynök SDK-t tartós entitásként. Az entitás egy message olyan műveletet tesz elérhetővé, amelyet az ügyfelek meghívnak, hogy felhasználói bemenetet küldjenek. Az entitás belsőleg az ügynök-keretrendszernek delegál, amely a saját eszközhívó ciklusát kezeli.

// 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;
    }
}

A Durable Task extension for Microsoft Agent Framework ezt a megközelítést használja. Tartós entitásokként burkolja Microsoft Agent Framework-ügynököket, állandó munkameneteket, automatikus ellenőrzőpontozást és beépített API-végpontokat biztosít egyetlen konfigurálási sortal.

Következő lépések