Architecture du pipeline d’agent

Les agents de Microsoft Agent Framework utilisent une architecture de pipeline en couches pour traiter les demandes. La compréhension de cette architecture vous aide à personnaliser le comportement de l’agent en ajoutant des middlewares, des fournisseurs de contexte ou des modifications au niveau du client au niveau de la couche appropriée.

ChatClientAgent Pipeline

Architecture du pipeline de l’agent C#

Le ChatClientAgent construit un pipeline avec trois couches principales :

  1. Agent middleware - Décorateurs facultatifs qui encapsulent l’agent pour .Use() la journalisation, la validation ou la transformation
  2. Couche de contexte : gère l’historique des conversations (ChatHistoryProvider) et injecte un contexte supplémentaire (AIContextProviders)
  3. Client de chat : IChatClient avec des décorateurs d’intergiciels facultatifs qui gèrent la communication LLM

Lorsque vous appelez RunAsync(), votre demande transite par chaque couche en séquence.

Pipeline d’agent

Architecture du pipeline de l’agent Python

La Agent classe génère un pipeline par le biais de la composition de classes avec deux composants principaux :

Agent (composant externe) :

  1. Intergiciel Agent + télémétrie : les classes AgentMiddlewareLayer et AgentTelemetryLayer gèrent les appels d'intergiciels et l'instrumentation OpenTelemetry.
  2. RawAgent - Logique de base de l'agent qui appelle des fournisseurs de contexte et collecte le middleware ajouté par le fournisseur
  3. Fournisseurs de contexte - La liste unifiée context_providers assure la gestion de l'historique, du contexte supplémentaire et du middleware de conversation/fonction pour chaque exécution.

ChatClient (composant distinct et interchangeable) :

  1. FunctionInvocation - Gère la boucle d’appel de l’outil, en invoquant le middleware de fonction + la télémétrie pour chaque appel d’outil
  2. Intergiciel de conversation + télémétrie - Couches d’intergiciels et d’instrumentation facultatives, y compris tout intergiciel de conversation ajouté par les fournisseurs de contexte, en cours d’exécution par appel de modèle
  3. RawChatClient - Implémentation spécifique au fournisseur (Azure OpenAI, OpenAI, Anthropic, etc.) qui communique avec le LLM

Lorsque vous appelez run(), votre requête transite par les couches Agent, puis dans le pipeline ChatClient pour la communication LLM.

La fonctionnalité De hooks d’agent facultative installe un ensemble d’intergiciels sur l’agent, la conversation et les couches de fonction. Les portes de diffusion en continu et de persistance de base étendent cette limite afin que la sortie ne soit pas libérée ou stockée avant que le verdict applicable l’autorise.

Architecture du pipeline d’agent

Architecture du pipeline de l’agent Go

Dans Go, les agents utilisent un pipeline d’intergiciels en couches. Les middlewares encapsulent la fonction Run de l’agent, chacun appelant next pour transmettre le contrôle à la couche suivante.

Lorsqu’un agent s’exécute, son cycle de vie s’applique dans l’ordre suivant :

  1. Intergiciel d’agent personnalisé - Votre agent.Config.Middlewares enregistré, appliqué dans l’ordre de déclaration sur l’ensemble du cycle de vie de l’agent
  2. Fournisseur d’historique : charge les messages antérieurs et stocke les messages de requête/réponse ultérieures
  3. Fournisseurs de contexte : injecter le contexte, les options et l’état à partir d’instances inscrites agent.ContextProvider
  4. Intergiciel fournisseur - Intergiciel inscrit par le fournisseur, comme l’appel automatique des outils, les sorties structurées et la création de réponses
  5. Fournisseur : fournisseur LLM sous-jacent, tel qu’OpenAI ou Anthropic

Couche d'agent intermédiaire

L’intergiciel agent intercepte chaque appel à la méthode d’exécution de l’agent, ce qui vous permet d’inspecter ou de modifier des entrées et des sorties.

Ajoutez un intergiciel à l’aide du modèle de générateur d’agents :

var middlewareAgent = originalAgent
    .AsBuilder()
    .Use(runFunc: MyAgentMiddleware, runStreamingFunc: MyStreamingMiddleware)
    .Build();

Vous pouvez également utiliser MessageAIContextProvider comme intergiciel agent pour injecter des messages supplémentaires dans la demande. Cela fonctionne avec n’importe quel type d’agent, pas seulement ChatClientAgent:

var contextAgent = originalAgent
    .AsBuilder()
    .UseAIContextProviders(new MyMessageContextProvider())
    .Build();

Cette couche encapsule l’intégralité de l’exécution de l’agent, y compris la résolution de contexte et les appels clients de conversation. Cela présente des avantages, car ces décorateurs peuvent être utilisés avec n’importe quel type d’agent, par exemple A2AAgent , ou GitHubCopilotAgentpas seulement ChatClientAgent. Cela implique également que les décorateurs à ce niveau ne peuvent pas nécessairement faire des suppositions concernant l'agent qu'ils décorent, ce qui limite leur capacité à personnaliser ou à modifier les fonctionnalités communes.

Ajoutez un intergiciel lors de la création de l’agent :

from agent_framework import Agent

agent = Agent(
    client=my_client,
    instructions="You are helpful.",
    middleware=[my_middleware_func],
)

La Agent classe hérite de AgentMiddlewareLayer, qui gère l’appel d’intergiciel avant de déléguer à la logique de l’agent principal. Il hérite également de AgentTelemetryLayer, ce qui gère l’émission d’étendues, d’événements et de métriques vers un back-end OpenTelemetry configuré. Ces deux couches, ne font rien quand elles ne sont pas configurées.

Ajoutez un intergiciel en implémentant l’interface Middleware ou en utilisant agent.MiddlewareFunc un intergiciel léger :

type Middleware interface {
    Run(next RunFunc, ctx context.Context, messages []*message.Message,
        options ...agent.Option) iter.Seq2[*agent.ResponseUpdate, error]
}

Chaque intergiciel reçoit la next fonction dans la chaîne et peut modifier les messages ou options avant d’appeler next, traiter les réponses après l’appel nextou court-circuiter le pipeline.

timing := agent.MiddlewareFunc(
    func(next agent.RunFunc, ctx context.Context, messages []*message.Message, options ...agent.Option) iter.Seq2[*agent.ResponseUpdate, error] {
        start := time.Now()
        return func(yield func(*agent.ResponseUpdate, error) bool) {
            defer log.Printf("agent run completed in %s", time.Since(start))
            for update, err := range next(ctx, messages, options...) {
                if !yield(update, err) {
                    return
                }
            }
        }
    },
)

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Config: agent.Config{
        Middlewares: []agent.Middleware{timing},
    },
})

Pour obtenir des modèles d’intergiciel et d’observabilité détaillés, consultez Intergiciel Agent et Observabilité.

Couche de contexte

La couche de contexte s’exécute avant chaque appel LLM pour générer l’historique complet des messages et injecter un contexte supplémentaire.

ChatClientAgent a deux types de fournisseurs distincts :

  • ChatHistoryProvider (unique) - Gère le stockage et la récupération de l’historique des conversations
  • AIContextProviders (liste) : injecte un contexte supplémentaire comme des souvenirs, des documents récupérés ou des instructions dynamiques
var agent = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
    ChatHistoryProvider = new InMemoryChatHistoryProvider(),
    AIContextProviders = [new MyMemoryProvider(), new MyRagProvider()],
});

L’agent appelle la méthode de InvokingAsync() de chaque fournisseur avant d’envoyer des messages au client de messagerie, en passant la sortie de chaque fournisseur comme entrée au fournisseur suivant.

La Agent classe utilise une liste unifiée context_providers qui peut inclure à la fois des fournisseurs d’historique et des fournisseurs de contexte :

from agent_framework import Agent, InMemoryHistoryProvider

agent = Agent(
    client=my_client,
    context_providers=[
        InMemoryHistoryProvider(),
        MyMemoryProvider(),
        MyRagProvider(),
    ],
)

Les fournisseurs de contexte peuvent également attacher des intergiciels de conversation ou de fonction à un appel unique via SessionContext.extend_middleware(). L'agent aplatit ces ajouts selon l'ordre du fournisseur avant d'entrer dans le pipeline du client de discussion.

Les fournisseurs de contexte s’exécutent à l’intérieur du cycle de vie de l’agent après que l’intergiciel personnalisé a entré l’exécution et avant que le middleware du fournisseur appelle le modèle. Les fournisseurs de contexte peuvent ajouter des messages ou des options avant l’appel du fournisseur et conserver l’état après l’exécution.

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Config: agent.Config{
        ContextProviders: []agent.ContextProvider{memoryProvider},
    },
})

Pour obtenir des modèles de fournisseur de contexte détaillés, consultez Fournisseurs de contexte.

Couche client de chat

La couche cliente de conversation gère la communication réelle avec le service LLM.

ChatClientAgent utilise une IChatClient instance, qui peut être décorée avec un intergiciel supplémentaire :

var chatClient = new AIProjectClient(endpoint, credential)
    .GetProjectOpenAIClient()
    .GetProjectResponsesClient()
    .AsIChatClient(deploymentName)
    .AsBuilder()
    .Use(CustomChatClientMiddleware)
    .Build();

var agent = new ChatClientAgent(chatClient, instructions: "You are helpful.");

Vous pouvez également utiliser AIContextProvider comme intergiciel client de conversation pour enrichir les messages, les outils et les instructions au niveau du client. Cela doit être utilisé dans le contexte d’une exécution AIAgent:

var chatClient = new AIProjectClient(endpoint, credential)
    .GetProjectOpenAIClient()
    .GetProjectResponsesClient()
    .AsIChatClient(deploymentName)
    .AsBuilder()
    .UseAIContextProviders(new MyContextProvider())
    .Build();

var agent = new ChatClientAgent(chatClient, instructions: "You are helpful.");

Par défaut, ChatClientAgent encapsule le client de conversation fourni avec la prise en charge des appels de fonction. Définissez UseProvidedChatClientAsIs = true dans les options pour ignorer cet enveloppement par défaut.

La Agent classe accepte tout client qui implémente SupportsChatGetResponse. Le pipeline ChatClient gère les intergiciels, les données de télémétrie, l’appel de fonction et la communication spécifique au fournisseur :

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient

client = FoundryChatClient(
    credential=credential,
    project_endpoint=endpoint,
    model=model,
)

agent = Agent(client=client, instructions="You are helpful.")

Le RawChatClient dans le ChatClient implémente la logique spécifique au fournisseur pour communiquer avec différents services LLM.

Le middleware du fournisseur s’exécute après les fournisseurs d’historique et de contexte, immédiatement avant le fournisseur de LLM sous-jacent. Les assistances au niveau de l’agent, telles que OpenTelemetry et la journalisation des exécutions, sont inscrites en tant qu’intergiciels d’agent personnalisés et encapsulent les étapes de cycle de vie antérieures.

Composant Inscription Couche Purpose
Appel automatique agent/harness/toolautocall Intergiciel fournisseur Appelle automatiquement les outils de fonction
Sortie structurée agent.WithStructuredOutput Intergiciel fournisseur Gère l’analyse de sortie structurée
OpenTelemetry provider/otelprovider Intergiciel agent Traces des invocations de l’agent
Exécuter l’enregistreur d’événements agent.Config.Logger Intergiciel agent Journalise les interactions de l’agent

agent.ContextProvider correspond à des composants du cycle de vie, et non à des implémentations agent.Middleware. Ils s’exécutent entre l’intergiciel d’agent personnalisé et l’intergiciel fournisseur.

Flux d’exécution

Lorsque vous appelez un agent, la requête transite par le pipeline :

  1. L’intergiciel de l’agent s’exécute (s’il est configuré)
  2. ChatHistoryProvider charge l’historique des conversations dans la liste des messages de requête
  3. AIContextProviders ajoute des messages, des outils ou des instructions à la demande
  4. L’intergiciel IChatClient s’exécute (s’il est décoré)
  5. IChatClient envoie la requête au LLM
  6. La réponse revient à travers les mêmes couches
  7. ChatHistoryProvider et AIContextProviders sont avertis des nouveaux messages

Pipeline d’agent :

  1. L’agent Middleware + Télémétrie exécute l’intergiciel (s’il est configuré) et enregistre les spans
  2. RawAgent invoque des fournisseurs de contexte pour charger l'historique, ajouter un contexte et collecter le middleware de chat/fonction ajouté par le fournisseur.
  3. La demande est transmise à ChatClient

Pipeline ChatClient :

  1. FunctionInvocation gère la boucle d’appel d’outil
    • Pour chaque appel d’outil, l’intergiciel de fonction + télémétrie s’exécute, y compris tout intergiciel de fonction ajouté par les fournisseurs de contexte
  2. Chat Middleware + Télémétrie s’exécute par appel de modèle (s’il est configuré), y compris tout intergiciel de conversation ajouté par les fournisseurs de contexte
  3. RawChatClient gère la communication LLM spécifique au fournisseur
  4. La réponse revient à travers les mêmes couches
  5. Les fournisseurs de contexte sont avertis des nouveaux messages en vue de leur stockage

Note

Les agents spécialisés peuvent fonctionner différemment du pipeline décrit ici.

  1. L’intergiciel d’agent personnalisé s’exécute en premier et encapsule le cycle de vie complet de l’agent.
  2. Le fournisseur d’historique charge l’historique des conversations pour la session active lorsque l’historique local est actif.
  3. Les fournisseurs de contexte ajoutent des messages, des options ou un état avant l’appel du fournisseur.
  4. L’intergiciel fournisseur s’exécute, y compris l’intergiciel d’appel automatique des outils et la gestion de sortie structurée lorsqu’il est activé.
  5. Le fournisseur envoie la requête au modèle.
  6. Les mises à jour de réponse remontent via l’intergiciel du fournisseur et l’intergiciel d’agent personnalisé.
  7. Les fournisseurs d’historique et les fournisseurs de contexte stockent l’état de réponse après une exécution réussie.

Autres types d’agents

Tous les agents n’utilisent pas le pipeline complet ChatClientAgent . Les agents tels que A2AAgent, GitHubCopilotAgentou CopilotStudioAgent communiquent avec des services distants plutôt que d’utiliser un agent local IChatClient. Toutefois, ils prennent toujours en charge les intergiciels au niveau de l’agent.

Pipeline des autres types d’agents

Étant donné que ces agents dérivent de AIAgent, vous pouvez utiliser les mêmes modèles de middleware d'agent :

// Agent middleware works with any AIAgent
var a2aAgent = originalA2AAgent
    .AsBuilder()
    .Use(runFunc: LoggingMiddleware)
    .UseAIContextProviders(new MyMessageContextProvider())
    .Build();

// Same pattern works for GitHubCopilotAgent
var copilotAgent = originalCopilotAgent
    .AsBuilder()
    .Use(runFunc: AuditMiddleware)
    .Build();

Note

Vous ne pouvez pas ajouter d’intergiciel client de conversation à ces agents, car ils n’utilisent IChatClientpas .

Autres types d’agents

Tous les agents Python n’utilisent pas le pipeline complet Agent + ChatClient . GitHubCopilotAgent, par exemple, envoie des requêtes via l’interface CLI GitHub Copilot au lieu d’un client de conversation local.

Même si Python GitHubCopilotAgent supporte toujours le middleware d’agent et exécute maintenant context_providers autour de chaque appel. Les messages et instructions ajoutés par le fournisseur sont inclus dans l’invite envoyée à Copilot, et les fournisseurs reçoivent le rappel correspondant after_run une fois qu’une réponse est disponible.

Note

Étant donné que GitHubCopilotAgent n’utilise pas de client de conversation local, le middleware client de conversation ne s’applique toujours pas.

Étapes suivantes