Condividi tramite


Compattazione

Man mano che le conversazioni aumentano, il numero di token della cronologia delle chat può superare le finestre del contesto del modello o aumentare i costi. Le strategie di compattazione riducono le dimensioni della cronologia delle conversazioni mantenendo al tempo stesso un contesto importante, in modo che gli agenti possano continuare a funzionare con interazioni a esecuzione prolungata.

Importante

Il framework di compattazione è al momento in fase sperimentale. Per usarlo, è necessario aggiungere #pragma warning disable MAAI001.

Importante

Il framework di compattazione è attualmente sperimentale in Python. Importare strategie da agent_framework._compaction.

Perché la compattazione è importante

Ogni chiamata a un LLM include la cronologia completa della conversazione. Senza compattazione:

  • Limiti dei token : le conversazioni superano infine la finestra di contesto del modello, causando errori.
  • Costo : richieste più grandi usano più token, aumentando i costi dell'API.
  • Latenza : più token di input indicano tempi di risposta più lenti.

La compattazione risolve questi problemi rimuovendo, comprimendo o riepilogando parti precedenti della conversazione in modo selettivo.

Concetti di base

Applicabilità: solo agenti di cronologia in memoria

La compattazione si applica solo agli agenti che gestiscono la propria cronologia delle conversazioni in memoria. Gli agenti che si basano sul contesto gestito dal servizio o sullo stato della conversazione non traggono vantaggio dalla compattazione perché il servizio gestisce già la gestione del contesto. Esempi di agenti gestiti dal servizio includono:

  • Agenti Foundry — il contesto viene gestito sul lato server dal servizio Azure AI Foundry.
  • API delle risposte con l'archivio abilitato (impostazione predefinita): lo stato della conversazione viene archiviato e gestito dal servizio OpenAI.
  • Agenti di Copilot Studio : il contesto di conversazione viene gestito dal servizio Copilot Studio.

Per questi tipi di agente, la configurazione di una strategia di compattazione non ha alcun effetto. La compattazione è rilevante solo quando l'agente mantiene un proprio elenco di messaggi in memoria e passa la cronologia completa al modello in ogni chiamata.

La compattazione opera su un oggetto MessageIndex, ovvero una visualizzazione strutturata dell'elenco di messaggi flat che raggruppa i messaggi in unità atomiche chiamate MessageGroup istanze. Ogni gruppo tiene traccia del numero di messaggi, del numero di byte e del numero di token stimato.

Gruppi di messaggi

Un MessageGroup oggetto rappresenta messaggi correlati logicamente che devono essere mantenuti o rimossi insieme. Ad esempio, un messaggio di assistente contenente le chiamate agli strumenti e i messaggi di risultato dello strumento corrispondenti formano un gruppo atomico, rimuovendone uno senza l'altro causerebbe errori dell'API LLM.

Ogni gruppo ha un MessageGroupKind:

Kind Descrizione
System Uno o più messaggi di sistema. Viene sempre mantenuta durante la compattazione.
User Messaggio utente singolo che avvia un nuovo turno.
AssistantText Risposta di testo di assistente normale (nessuna chiamata di strumento).
ToolCall Messaggio di assistente con chiamate degli strumenti e messaggi di risultato dello strumento corrispondenti, considerati come unità atomiche.
Summary Messaggio condensato prodotto dalla compattazione di sintesi.

Attivatori

Un CompactionTrigger è un delegato che valuta se la compattazione deve continuare in base alle metriche correnti MessageIndex :

public delegate bool CompactionTrigger(MessageIndex index);

La CompactionTriggers classe fornisce metodi factory comuni:

Attivatore Si attiva quando
CompactionTriggers.Always Ogni volta (incondizionatamente)
CompactionTriggers.Never Mai (disabilita la compattazione).
CompactionTriggers.TokensExceed(maxTokens) Il numero di token inclusi supera la soglia.
CompactionTriggers.MessagesExceed(maxMessages) Il numero di messaggi inclusi supera la soglia.
CompactionTriggers.TurnsExceed(maxTurns) Il conteggio incluso dei turni utente supera la soglia.
CompactionTriggers.GroupsExceed(maxGroups) Il numero di gruppi inclusi supera la soglia.
CompactionTriggers.HasToolCalls() Esiste almeno un gruppo di chiamate dello strumento non escluso.

Combinare i trigger con l'operatore logico CompactionTriggers.All(...) (AND) o l'operatore logico CompactionTriggers.Any(...) (OR):

// Compact only when there are tool calls AND tokens exceed 2000
CompactionTrigger trigger = CompactionTriggers.All(
    CompactionTriggers.HasToolCalls(),
    CompactionTriggers.TokensExceed(2000));

Trigger e obiettivo

Ogni strategia ha due predicati:

  • Trigger : controlla l'avvio della compattazione. Se il trigger restituisce false, la strategia viene ignorata completamente.
  • Target : controlla quando si arresta la compattazione. Le strategie escludono in modo incrementale i gruppi e rivalutano l'obiettivo dopo ogni passaggio, interrompere non appena l'obiettivo ritorna true.

Quando non viene specificata alcuna destinazione, l'impostazione predefinita è l'inversa del trigger. La compattazione si arresta non appena la condizione del trigger non viene più attivata.

La compattazione opera su un elenco semplice di Message oggetti. I messaggi vengono annotati con metadati di gruppo leggeri e le strategie modificano tali annotazioni sul posto per contrassegnare i gruppi come esclusi prima che l'elenco dei messaggi venga proiettato nel modello.

Gruppi di messaggi

I messaggi vengono raggruppati in unità atomiche. A ogni gruppo viene assegnato un oggetto GroupKind:

Kind Descrizione
system Messaggi di sistema. Viene sempre mantenuta durante la compattazione.
user Un singolo messaggio utente.
assistant_text Risposta di testo di assistente normale (nessuna chiamata di funzione).
tool_call Messaggio di assistente con chiamate di funzione più i messaggi di risultato dello strumento corrispondenti, considerati come un'unità atomica.

Strategie di compattazione

Un CompactionStrategy è un protocollo: qualsiasi async chiamabile che accetta un oggetto list[Message] e lo modifica sul posto, restituendo True quando è cambiato qualcosa:

class CompactionStrategy(Protocol):
    async def __call__(self, messages: list[Message]) -> bool: ...

Tokenizzatore

Le strategie con riconoscimento dei token accettano un'implementazione TokenizerProtocol . L'interfaccia predefinita CharacterEstimatorTokenizer usa un'euristica a 4 caratteri per token:

from agent_framework._compaction import CharacterEstimatorTokenizer

tokenizer = CharacterEstimatorTokenizer()

Utilizza un tokenizzatore personalizzato quando hai bisogno di conteggi di token accurati per la codifica di un modello specifico.

Strategie di compattazione

Tutte le strategie ereditano dalla classe base astratta CompactionStrategy . Ogni strategia mantiene i messaggi di sistema e rispetta una MinimumPreserved base che protegge i gruppi non di sistema più recenti dalla rimozione.

Le strategie di compattazione vengono importate da agent_framework._compaction.

TruncationCompactionStrategy

StrategiaTroncamento

L'approccio più semplice: rimuove i gruppi di messaggi non di sistema meno recenti fino a quando non viene soddisfatta la condizione di destinazione.

  • Rispetta i confini dei gruppi atomici (i messaggi di chiamata dello strumento e quelli dei risultati vengono rimossi insieme).
  • Ideale per i meccanismi di protezione del budget dei token rigidi.
  • Il valore predefinito di MinimumPreserved è 32.
// Drop oldest groups when tokens exceed 32K, keeping at least 10 recent groups
TruncationCompactionStrategy truncation = new(
    trigger: CompactionTriggers.TokensExceed(0x8000),
    minimumPreserved: 10);
  • Quando viene fornito un tokenizer, la metrica è il numero di token; in caso contrario, viene incluso il conteggio dei messaggi.
  • Il valore predefinito di preserve_system è True.
from agent_framework._compaction import CharacterEstimatorTokenizer, TruncationStrategy

# Exclude oldest groups when tokens exceed 32 000, trimming to 16 000
truncation = TruncationStrategy(
    max_n=32_000,
    compact_to=16_000,
    tokenizer=CharacterEstimatorTokenizer(),
)

SlidingWindowCompactionStrategy

SlidingWindowStrategy

Rimuove il contenuto della conversazione meno recente per mantenere solo la finestra di scambio più recente, rispettando le unità di conversazione logiche anziché i conteggi arbitrari dei messaggi. I messaggi di sistema vengono mantenuti in tutto.

  • Ideale per delimitare la lunghezza della conversazione in modo prevedibile.

Rimuove i turni dell'utente meno recenti e i relativi gruppi di risposta associati, operando sui limiti dei turni logici anziché sui singoli gruppi.

  • Un turno inizia con un messaggio utente e include tutti i gruppi di assistente e chiamata agli strumenti successivi fino al messaggio utente successivo.
  • MinimumPreserved predefinito a 1 (mantiene almeno il gruppo non di sistema più recente).
// Keep only the last 4 user turns
SlidingWindowCompactionStrategy slidingWindow = new(
    trigger: CompactionTriggers.TurnsExceed(4));

Mantiene solo i gruppi non di sistema più recenti keep_last_groups , esclusi tutti gli elementi precedenti.

  • Il valore predefinito di preserve_system è True.
from agent_framework._compaction import SlidingWindowStrategy

# Keep only the last 20 non-system groups
sliding_window = SlidingWindowStrategy(keep_last_groups=20)

ToolResultCompactionStrategy

Comprime i gruppi di chiamate a strumenti più datati in messaggi riassuntivi compatti, mantenendo una traccia leggibile senza l'overhead completo del messaggio.

  • Non tocca i messaggi utente o le risposte di assistente normale.
  • Ideale come strategia iniziale per recuperare spazio dai risultati dettagliati degli strumenti.
  • Sostituisce i gruppi di chiamate dello strumento multi-messaggio (chiamata assistente + risultati dello strumento) con un breve riepilogo, ad esempio [Tool calls: get_weather, search_docs].
  • MinimumPreserved è impostato su 2, assicurando che le interazioni dello strumento del turno corrente rimangano visibili.
// Collapse old tool results when tokens exceed 512
ToolResultCompactionStrategy toolCompaction = new(
    trigger: CompactionTriggers.TokensExceed(0x200));
  • Comprime in messaggi di riepilogo compatti, ad esempio [Tool results: get_weather: sunny, 18°C].
  • I gruppi di chiamate agli strumenti più recenti keep_last_tool_call_groups vengono lasciati invariati.
from agent_framework._compaction import ToolResultCompactionStrategy

# Collapse all but the newest tool-call group
tool_result = ToolResultCompactionStrategy(keep_last_tool_call_groups=1)

RiepilogoCompactionStrategy

Strategia di Riepilogo

Usa un LLM per riepilogare le parti precedenti della conversazione, sostituendole con un singolo messaggio di riepilogo.

  • Una richiesta predefinita mantiene i fatti chiave, le decisioni, le preferenze degli utenti e i risultati delle chiamate degli strumenti.
  • Richiede un client LLM separato per il riepilogo. È consigliabile un modello più piccolo e veloce.
  • Ideale per mantenere il contesto di conversazione riducendo significativamente il numero di token.
  • È possibile fornire un prompt di riepilogo personalizzato.
  • Protegge i messaggi di sistema e i gruppi non di sistema più recenti MinimumPreserved (impostazione predefinita: 4).
  • Invia i messaggi meno recenti a una destinazione separata IChatClient con un prompt di riepilogo, successivamente inserisce il riepilogo come gruppo MessageGroupKind.Summary.
// Summarize older messages when tokens exceed 1280, keeping the last 4 groups
SummarizationCompactionStrategy summarization = new(
    chatClient: summarizerChatClient,
    trigger: CompactionTriggers.TokensExceed(0x500),
    minimumPreserved: 4);

È possibile fornire un prompt di riepilogo personalizzato:

SummarizationCompactionStrategy summarization = new(
    chatClient: summarizerChatClient,
    trigger: CompactionTriggers.TokensExceed(0x500),
    summarizationPrompt: "Summarize the key decisions and user preferences only.");
  • Si attiva quando il conteggio dei messaggi non di sistema incluso supera target_count + threshold.
  • Mantiene i messaggi più recenti target_count . Riepiloga tutti gli elementi precedenti.
  • Richiede un SupportsChatGetResponse client.
from agent_framework._compaction import SummarizationStrategy

# Summarize when non-system message count exceeds 6, retaining the 4 newest
summarization = SummarizationStrategy(
    client=summarizer_client,
    target_count=4,
    threshold=2,
)

Fornire un prompt di riepilogo personalizzato:

summarization = SummarizationStrategy(
    client=summarizer_client,
    target_count=4,
    prompt="Summarize the key decisions and user preferences only.",
)

PipelineCompactionStrategy

Compone più strategie in una pipeline sequenziale. Ogni strategia opera sul risultato della precedente, consentendo la compattazione stratificata dal delicato all'aggressivo.

  • Il trigger della pipeline è CompactionTriggers.Always : ogni strategia figlia valuta il proprio trigger in modo indipendente.
  • Le strategie si eseguono in ordine, quindi mettere prima le strategie più delicate.
PipelineCompactionStrategy pipeline = new(
    new ToolResultCompactionStrategy(CompactionTriggers.TokensExceed(0x200)),
    new SummarizationCompactionStrategy(summarizerChatClient, CompactionTriggers.TokensExceed(0x500)),
    new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(4)),
    new TruncationCompactionStrategy(CompactionTriggers.TokensExceed(0x8000)));

Questa pipeline:

  1. Comprime i risultati dei vecchi strumenti (delicato).
  2. Riepiloga gli intervalli di conversazione meno recenti (moderata).
  3. Mantiene solo gli ultimi 4 turni utente (aggressivi).
  4. Elimina i gruppi meno recenti se ancora oltre il budget (backstop di emergenza).

Strategia di Compattazione delle Chiamate di Strumenti Selettivi

Esclude i gruppi di chiamate agli strumenti meno recenti completamente, conservando solo l'ultimo oggetto keep_last_tool_call_groups.

  • Non tocca i messaggi utente o assistente normale.
  • Meglio quando lo strumento chatter domina l'utilizzo dei token e la cronologia completa degli strumenti non è necessaria.
from agent_framework._compaction import SelectiveToolCallCompactionStrategy

# Keep only the most recent tool-call group
selective_tool = SelectiveToolCallCompactionStrategy(keep_last_tool_call_groups=1)

TokenBudgetComposedStrategy

Compone più strategie in una pipeline sequenziale basata su un budget di token. Ogni strategia figlia viene eseguita in ordine, interrompendosi anticipatamente una volta soddisfatto il budget. Un meccanismo di fallback incorporato esclude i gruppi meno recenti se le strategie da sole non riescono a raggiungere l'obiettivo.

  • Eseguire le strategie in ordine; posizionare prima le strategie più lievi.
  • early_stop=True (impostazione predefinita) si arresta non appena viene soddisfatto il budget dei token.
from agent_framework._compaction import (
    CharacterEstimatorTokenizer,
    SelectiveToolCallCompactionStrategy,
    SlidingWindowStrategy,
    SummarizationStrategy,
    TokenBudgetComposedStrategy,
    ToolResultCompactionStrategy,
)

tokenizer = CharacterEstimatorTokenizer()

pipeline = TokenBudgetComposedStrategy(
    token_budget=16_000,
    tokenizer=tokenizer,
    strategies=[
        ToolResultCompactionStrategy(keep_last_tool_call_groups=1),
        SummarizationStrategy(client=summarizer_client, target_count=4, threshold=2),
        SlidingWindowStrategy(keep_last_groups=20),
    ],
)

Questa pipeline:

  1. Comprime i risultati dei vecchi strumenti (in modo graduale).
  2. Riepiloga gli intervalli di conversazione meno recenti (moderata).
  3. Mantiene solo gli ultimi 20 gruppi (aggressivi).
  4. Torna all'esclusione meno recente se ancora oltre il budget (backstop di emergenza).

Uso della compattazione con un agente

Incapsulare una strategia di compattazione in un CompactionProvider e registrarla come AIContextProvider. Passa una singola strategia o un PipelineCompactionStrategy al costruttore.

Registrazione con l'API del generatore

Registrare il fornitore nell'oggetto ChatClientBuilder utilizzando UseAIContextProviders. Il provider viene eseguito all'interno del ciclo di chiamata degli strumenti, compattando i messaggi prima di ogni chiamata LLM.

IChatClient agentChatClient = openAIClient.GetChatClient(deploymentName).AsIChatClient();
IChatClient summarizerChatClient = openAIClient.GetChatClient(deploymentName).AsIChatClient();

PipelineCompactionStrategy compactionPipeline =
    new(
        new ToolResultCompactionStrategy(CompactionTriggers.TokensExceed(0x200)),
        new SummarizationCompactionStrategy(summarizerChatClient, CompactionTriggers.TokensExceed(0x500)),
        new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(4)),
        new TruncationCompactionStrategy(CompactionTriggers.TokensExceed(0x8000)));

AIAgent agent =
    agentChatClient
        .AsBuilder()
        .UseAIContextProviders(new CompactionProvider(compactionPipeline))
        .BuildAIAgent(
            new ChatClientAgentOptions
            {
                Name = "ShoppingAssistant",
                ChatOptions = new()
                {
                    Instructions = "You are a helpful shopping assistant.",
                    Tools = [AIFunctionFactory.Create(LookupPrice)],
                },
            });

AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("What's the price of a laptop?", session));

Suggerimento

Usare un modello più piccolo e più economico (ad esempio gpt-4o-mini) per il client di chat di riepilogo per ridurre i costi mantenendo al tempo stesso la qualità di riepilogo.

Se è necessaria una sola strategia, passarla direttamente a CompactionProvider senza inserirla in un oggetto PipelineCompactionStrategy.

agentChatClient
    .AsBuilder()
    .UseAIContextProviders(new CompactionProvider(
        new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(20))))
    .BuildAIAgent(...);

Registrazione tramite ChatClientAgentOptions

Il provider può anche essere specificato direttamente in ChatClientAgentOptions.AIContextProviders:

AIAgent agent = agentChatClient
    .AsBuilder()
    .BuildAIAgent(new ChatClientAgentOptions
    {
        AIContextProviders = [new CompactionProvider(compactionPipeline)]
    });

Annotazioni

Se registrato tramite ChatClientAgentOptions, CompactionProvider non viene attivato durante il ciclo di chiamata degli strumenti. I provider di contesto a livello di agente vengono eseguiti prima dell'archiviazione della cronologia delle chat, quindi qualsiasi messaggio di riepilogo sintetico prodotto da CompactionProvider può diventare parte della cronologia persistente quando si usa ChatHistoryProvider. Per compattare solo il contesto della richiesta in transito preservando la cronologia originale archiviata, registrare invece il provider su ChatClientBuilder tramite UseAIContextProviders(...).

Compattazione ad hoc

CompactionProvider.CompactAsync applica una strategia a un elenco di messaggi arbitrari senza una sessione agente attiva:

IEnumerable<ChatMessage> compacted = await CompactionProvider.CompactAsync(
    new TruncationCompactionStrategy(CompactionTriggers.TokensExceed(8000)),
    existingMessages);

CompactionProvider è un provider di contesto che applica strategie di compattazione prima e dopo l'esecuzione di ogni agente. Aggiungerlo insieme a un provider di cronologia nell'elenco dell'agente context_providers .

  • before_strategy : viene eseguito prima della chiamata del modello, compattando i messaggi già caricati nel contesto.
  • after_strategy: viene eseguito dopo la chiamata al modello, compattando i messaggi archiviati dal fornitore di cronologia in modo che il turno successivo risulti più piccolo.
  • history_source_id — il fornitore della cronologia i cui messaggi archiviati after_strategy devono essere compattati (valore predefinito "in_memory").

Registrazione con un agente

from agent_framework import Agent, CompactionProvider, InMemoryHistoryProvider
from agent_framework._compaction import (
    CharacterEstimatorTokenizer,
    SlidingWindowStrategy,
    SummarizationStrategy,
    TokenBudgetComposedStrategy,
    ToolResultCompactionStrategy,
)

tokenizer = CharacterEstimatorTokenizer()

pipeline = TokenBudgetComposedStrategy(
    token_budget=16_000,
    tokenizer=tokenizer,
    strategies=[
        ToolResultCompactionStrategy(keep_last_tool_call_groups=1),
        SummarizationStrategy(client=summarizer_client, target_count=4, threshold=2),
        SlidingWindowStrategy(keep_last_groups=20),
    ],
)

history = InMemoryHistoryProvider()
compaction = CompactionProvider(
    before_strategy=pipeline,
    history_source_id=history.source_id,
)

agent = Agent(
    client=client,
    name="ShoppingAssistant",
    instructions="You are a helpful shopping assistant.",
    context_providers=[history, compaction],
)

session = agent.create_session()
print(await agent.run("What's the price of a laptop?", session=session))

Suggerimento

Usare un modello più piccolo e più economico (ad esempio gpt-4o-mini) per il client di riepilogo per ridurre i costi mantenendo al tempo stesso la qualità di riepilogo.

Se è necessaria una sola strategia, passarla direttamente come before_strategy:

compaction = CompactionProvider(
    before_strategy=SlidingWindowStrategy(keep_last_groups=20),
    history_source_id=history.source_id,
)

Compattazione della cronologia persistente dopo ogni esecuzione

Usare after_strategy per compattare i messaggi archiviati dal provider di cronologia in modo che i turni futuri inizino con un contesto ridotto:

compaction = CompactionProvider(
    before_strategy=SlidingWindowStrategy(keep_last_groups=20),
    after_strategy=ToolResultCompactionStrategy(keep_last_tool_call_groups=1),
    history_source_id=history.source_id,
)

Compattazione ad hoc

apply_compaction applica una strategia a un elenco di messaggi arbitrari all'esterno di una sessione dell'agente attivo:

from agent_framework._compaction import apply_compaction, TruncationStrategy, CharacterEstimatorTokenizer

tokenizer = CharacterEstimatorTokenizer()

compacted = await apply_compaction(
    messages,
    strategy=TruncationStrategy(
        max_n=8_000,
        compact_to=4_000,
        tokenizer=tokenizer,
    ),
    tokenizer=tokenizer,
)

Scelta di una strategia

Strategia Aggressività Mantiene il contesto Richiede LLM Ideale per
ToolResultCompactionStrategy Low Alto: comprime solo i risultati degli strumenti No Riappropriazione di spazio dall'output eccessivo dello strumento
SummarizationCompactionStrategy Medium Medium : sostituisce la cronologia con un riepilogo Conversazioni lunghe in cui il contesto è importante
SlidingWindowCompactionStrategy Alto Basso : elimina interi turni No Limiti di conteggio dei turni rigidi
TruncationCompactionStrategy Alto Basso: elimina i gruppi meno recenti No Dispositivo di sicurezza per la riserva di token d'emergenza
PipelineCompactionStrategy Configurable Dipende dalle strategie dei figli Dipende da Compattazione stratificata con più opzioni di fallback
Strategia Aggressività Mantiene il contesto Richiede LLM Ideale per
ToolResultCompactionStrategy Low Alto: comprime i risultati degli strumenti in messaggi di riepilogo No Recupero di spazio dall'output prolisso dello strumento
SelectiveToolCallCompactionStrategy Basso-Medio Medio: esclude completamente i gruppi di chiamata degli strumenti precedenti No Rimozione della cronologia degli strumenti quando i risultati non sono più necessari
SummarizationStrategy Medium Medium : sostituisce la cronologia con un riepilogo Conversazioni lunghe in cui il contesto è importante
SlidingWindowStrategy Alto Basso - elimina i gruppi più vecchi No Limiti di conteggio dei gruppi rigidi
TruncationStrategy Alto Basso - elimina i gruppi più vecchi No Backstop del budget di token o messaggi di emergenza
TokenBudgetComposedStrategy Configurable Dipende dalle strategie figlie Dipende da Compattazione a più livelli con un obiettivo di budget di token e diversi fallback

Passaggi successivi