Compartir a través de


Compactación

A medida que crecen las conversaciones, el recuento de tokens del historial de chat puede superar las ventanas de contexto del modelo o aumentar los costos. Las estrategias de compactación reducen el tamaño del historial de conversaciones al tiempo que conservan un contexto importante, por lo que los agentes pueden seguir funcionando en interacciones de larga duración.

Importante

El marco de compactación es actualmente experimental. Para usarlo, deberá agregar #pragma warning disable MAAI001.

Importante

El marco de compactación es actualmente experimental en Python. Importe estrategias de agent_framework._compaction.

¿Por qué importa la compactación?

Cada llamada a un LLM incluye el historial de conversaciones completo. Sin compactación:

  • Límites de token : las conversaciones superan finalmente la ventana de contexto del modelo, lo que provoca errores.
  • Costo : las solicitudes más grandes consumen más tokens, lo que aumenta los costos de API.
  • Latencia : más tokens de entrada significa tiempos de respuesta más lentos.

La compactación resuelve estos problemas mediante la eliminación selectiva, la contracción o el resumen de partes más antiguas de la conversación.

Conceptos principales

Aplicabilidad: solo agentes de historial en memoria

La compactación solo se aplica a los agentes que administran su propio historial de conversaciones en la memoria. Los agentes que dependen del contexto gestionado por el servicio o del estado de la conversación no reciben beneficios de la optimización, ya que el servicio ya se encarga de la administración del contexto. Entre los ejemplos de agentes administrados por el servicio se incluyen:

  • Agentes de Foundry — el contexto se gestiona del lado del servidor por el servicio Azure AI Foundry.
  • API de respuestas con el almacén habilitado (valor predeterminado): el servicio OpenAI almacena y administra el estado de conversación.
  • Agentes de Copilot Studio : el servicio Copilot Studio mantiene el contexto de conversación.

Para estos tipos de agente, la configuración de una estrategia de compactación no tiene ningún efecto. La compactación solo es relevante cuando el agente mantiene su propia lista de mensajes en memoria y pasa el historial completo al modelo en cada llamada.

La compactación opera en una MessageIndex vista estructurada de la lista de mensajes planos que agrupa los mensajes en unidades atómicas llamadas instancias MessageGroup. Cada grupo realiza un seguimiento del recuento de mensajes, el recuento de bytes y el recuento de tokens estimados.

Grupos de mensajes

MessageGroup representa mensajes relacionados lógicamente que se deben mantener o quitar juntos. Por ejemplo, un mensaje de asistente que contiene llamadas a herramientas y sus mensajes de resultados de herramientas correspondientes forman un grupo atómico, ya que quitar uno sin el otro produciría errores de la API de LLM.

Cada grupo tiene:MessageGroupKind

Kind Descripción
System Uno o varios mensajes del sistema. Siempre se preserva durante la compactación.
User Mensaje de usuario único que inicia un nuevo turno.
AssistantText Respuesta sencilla de texto del asistente (sin llamadas a herramientas).
ToolCall Mensaje del asistente con llamadas a herramientas y los mensajes de resultados de la herramienta correspondientes, tratados como una unidad atómica.
Summary Un mensaje condensado generado mediante la compresión de resumen.

Desencadenadores

CompactionTrigger es un delegado que evalúa si la compactación debe continuar en función de las métricas actuales MessageIndex.

public delegate bool CompactionTrigger(MessageIndex index);

La CompactionTriggers clase proporciona métodos de fábrica comunes:

Trigger Se activa cuando
CompactionTriggers.Always Cada vez (incondicionalmente).
CompactionTriggers.Never Nunca (deshabilita la compactación).
CompactionTriggers.TokensExceed(maxTokens) El número total de tokens incluidos supera el umbral.
CompactionTriggers.MessagesExceed(maxMessages) El recuento de mensajes incluido supera el umbral.
CompactionTriggers.TurnsExceed(maxTurns) El recuento de turnos de usuario incluido supera el umbral.
CompactionTriggers.GroupsExceed(maxGroups) El recuento de grupos incluidos supera el umbral.
CompactionTriggers.HasToolCalls() Existe al menos un grupo de llamada de herramientas que no está excluido.

Combinar desencadenadores con CompactionTriggers.All(...) (AND lógico) o CompactionTriggers.Any(...) (OR lógico):

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

Activador versus objetivo

Cada estrategia tiene dos predicados:

  • Desencadenador : controla cuándo comienza la compactación. Si el desencadenador devuelve false, la estrategia se omite por completo.
  • Destino : controla cuándo se detiene la compactación. Las estrategias excluyen de manera incremental a los grupos y reevalúan el objetivo después de cada paso, deteniéndose tan pronto como el objetivo devuelve true.

Cuando no se especifica ningún destino, el valor predeterminado es el inverso del desencadenador: la compactación se detiene tan pronto como la condición del desencadenador ya no se activaría.

La compactación funciona en una lista plana de Message objetos. Los mensajes se anotan con metadatos de grupo ligeros y las estrategias mutan esas anotaciones en su lugar para marcar los grupos como excluidos antes de que la lista de mensajes se proyecta en el modelo.

Grupos de mensajes

Los mensajes se agrupan en unidades atómicas. A cada grupo se le asigna un GroupKind:

Kind Descripción
system Mensajes del sistema. Siempre se preserva durante la compactación.
user Un único mensaje de usuario.
assistant_text Respuesta de texto del asistente sin formato (sin llamadas a funciones).
tool_call Mensaje de asistente con llamadas de función más los mensajes de resultados de la herramienta correspondientes, tratados como una unidad atómica.

Estrategias de compactación

Un CompactionStrategy es un protocolo, es cualquier async invocable que acepta un list[Message] y lo modifique en el lugar, devolviendo True cuando ha cambiado algo:

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

Tokenizador

Las estrategias sensibles a tokens aceptan una TokenizerProtocol implementación. La función integrada CharacterEstimatorTokenizer usa una heurística de cuatro caracteres por token:

from agent_framework._compaction import CharacterEstimatorTokenizer

tokenizer = CharacterEstimatorTokenizer()

Proporcione un tokenizador personalizado cuando necesite contar tokens de forma precisa para la codificación de un modelo específico.

Estrategias de compactación

Todas las estrategias heredan de la clase base abstracta CompactionStrategy . Cada estrategia conserva los mensajes del sistema y respeta un MinimumPreserved nivel de base que protege a los grupos más recientes que no son del sistema contra la eliminación.

Las estrategias de compactación se importan desde agent_framework._compaction.

Estrategia de Compactación de Truncación

TruncationStrategy

El enfoque más sencillo: quita los grupos de mensajes que no son del sistema más antiguos hasta que se cumple la condición de destino.

  • Respeta los límites del grupo atómico (los mensajes de llamada a herramientas y resultados se quitan juntos).
  • Mejor opción para los topes presupuestarios de tokens rígidos.
  • MinimumPreserved se establece de forma predeterminada en 32.
// Drop oldest groups when tokens exceed 32K, keeping at least 10 recent groups
TruncationCompactionStrategy truncation = new(
    trigger: CompactionTriggers.TokensExceed(0x8000),
    minimumPreserved: 10);
  • Cuando se proporciona tokenizer, la métrica es el recuento de tokens; de lo contrario, la métrica es el recuento de mensajes.
  • preserve_system se establece de forma predeterminada en 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

EstrategiaDeVentanaDeslizante

Quita el contenido de conversación anterior para mantener solo la ventana más reciente de intercambios, respetando las unidades de conversación lógicas en lugar de recuentos arbitrarios de mensajes. Los mensajes del sistema se conservan en todo el mundo.

  • Mejor para limitar la longitud de la conversación de manera predecible.

Quita los turnos de usuario más antiguos y sus grupos de respuesta asociados, trabajando en límites de turno lógico en lugar de grupos individuales.

  • Un turno comienza con un mensaje de usuario e incluye todos los grupos de asistentes y llamadas a herramientas posteriores hasta el siguiente mensaje de usuario.
  • MinimumPreserved el valor predeterminado es 1 (conserva al menos el grupo no del sistema más reciente).
// Keep only the last 4 user turns
SlidingWindowCompactionStrategy slidingWindow = new(
    trigger: CompactionTriggers.TurnsExceed(4));

Mantiene solo los grupos más recientes que no son del sistema keep_last_groups, excluyendo todo lo más antiguo.

  • preserve_system se establece de forma predeterminada en True.
from agent_framework._compaction import SlidingWindowStrategy

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

Estrategia de Compactación de Resultados de Herramientas (ToolResultCompactionStrategy)

Condensa los grupos de instrucciones de herramientas más antiguos en mensajes de resumen compactos, manteniendo un seguimiento legible sin el exceso de información completa.

  • No toca los mensajes del usuario ni las respuestas simples del asistente.
  • Es mejor como una estrategia de primer paso para recuperar espacio de los resultados verbosos de la herramienta.
  • Reemplaza los grupos de llamadas de la herramienta de varios mensajes (llamadas de asistente y resultados de herramientas) por un breve resumen como [Tool calls: get_weather, search_docs].
  • MinimumPreserved el valor predeterminado es 2, lo que garantiza que las interacciones de la herramienta del turno actual permanezcan visibles.
// Collapse old tool results when tokens exceed 512
ToolResultCompactionStrategy toolCompaction = new(
    trigger: CompactionTriggers.TokensExceed(0x200));
  • Se contrae en mensajes de resumen compactos, como [Tool results: get_weather: sunny, 18°C].
  • Los grupos más recientes de llamadas a herramientas keep_last_tool_call_groups se dejan intactos.
from agent_framework._compaction import ToolResultCompactionStrategy

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

ResumenCompactionStrategy

EstrategiaDeResumen

Usa un LLM para resumir las partes anteriores de la conversación y reemplazarlas por un único mensaje de resumen.

  • Un aviso predeterminado conserva los hechos clave, las decisiones, las preferencias del usuario y los resultados de las llamadas a herramientas.
  • Requiere un cliente LLM independiente para el resumen: se recomienda un modelo más pequeño y más rápido.
  • Lo mejor para conservar el contexto conversacional al tiempo que reduce significativamente el recuento de tokens.
  • Puede proporcionar un mensaje de resumen personalizado.
  • Protege los mensajes del sistema y los grupos que no son del sistema más recientes MinimumPreserved (valor predeterminado: 4).
  • Envía los mensajes más antiguos a otro IChatClient con una indicación para resumirlos y, a continuación, inserta el resumen como parte de un grupo 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);

Puede proporcionar un mensaje de resumen personalizado:

SummarizationCompactionStrategy summarization = new(
    chatClient: summarizerChatClient,
    trigger: CompactionTriggers.TokensExceed(0x500),
    summarizationPrompt: "Summarize the key decisions and user preferences only.");
  • Se activa cuando el recuento incluído de mensajes que no son del sistema supera target_count + threshold.
  • Conserva los mensajes target_count más recientes; resume todo lo más antiguo.
  • Requiere un SupportsChatGetResponse cliente.
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,
)

Proporcione un mensaje de resumen personalizado:

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

PipelineCompactionStrategy

Compone varias estrategias en una canalización secuencial. Cada estrategia opera sobre el resultado de la anterior, lo que permite la compactación estratificada de suave a agresiva.

  • El propio desencadenador de la canalización es CompactionTriggers.Always : cada estrategia secundaria evalúa su propio desencadenador de forma independiente.
  • Las estrategias se ejecutan en orden, así que ponga primero las estrategias más suaves.
PipelineCompactionStrategy pipeline = new(
    new ToolResultCompactionStrategy(CompactionTriggers.TokensExceed(0x200)),
    new SummarizationCompactionStrategy(summarizerChatClient, CompactionTriggers.TokensExceed(0x500)),
    new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(4)),
    new TruncationCompactionStrategy(CompactionTriggers.TokensExceed(0x8000)));

Esta tubería:

  1. Contrae los resultados antiguos de la herramienta (suave).
  2. Resume los intervalos de conversación más antiguos (moderados).
  3. Mantiene solo las últimas 4 interacciones del usuario (agresiva).
  4. Elimina los grupos más antiguos si aún se supera el presupuesto (respaldo de emergencia).

Estrategia de Compacción de Llamada de Herramienta Selectiva

Excluye completamente los grupos de llamadas a herramientas más antiguos, manteniendo solo el último keep_last_tool_call_groups.

  • No toca mensajes de usuario ni de asistente simples.
  • Lo mejor cuando el chatter de herramientas domina el uso de tokens y no se necesita el historial completo de herramientas.
from agent_framework._compaction import SelectiveToolCallCompactionStrategy

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

EstrategiaCompuestaDePresupuestoDeFichas

Combina varias estrategias en una canalización secuencial controlada por un presupuesto de token. Cada estrategia infantil se ejecuta en orden, deteniendo temprano una vez satisfecho el presupuesto. Un mecanismo de respaldo integrado excluye los grupos más antiguos si las estrategias por sí solas no pueden alcanzar el objetivo.

  • Ejecute las estrategias en orden; coloque primero las estrategias más suaves.
  • early_stop=True (valor predeterminado) se detiene tan pronto como se cumpla el presupuesto del 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),
    ],
)

Esta canalización:

  1. Contrae los resultados antiguos de la herramienta (suave).
  2. Resume de manera moderada los fragmentos de conversación más antiguos.
  3. Mantiene solo los últimos 20 grupos (agresivos).
  4. Vuelve a la exclusión más antigua si aún supera el presupuesto (de emergencia).

Uso de la compactación con un agente

Encapsula una estrategia de compactación en CompactionProvider y la registra como .AIContextProvider Pase como parámetro una sola estrategia o un PipelineCompactionStrategy al constructor.

Registro en la API del constructor

Registra al proveedor en ChatClientBuilder mediante UseAIContextProviders. El proveedor se ejecuta dentro del bucle de llamadas a herramientas, compactando mensajes antes de cada llamada 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));

Sugerencia

Use un modelo más pequeño y barato (como gpt-4o-mini) para el cliente de chat de resumen para reducir los costos al tiempo que mantiene la calidad del resumen.

Si solo se necesita una estrategia, pásela directamente a CompactionProvider sin encapsularla en :PipelineCompactionStrategy

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

Registrándose a través de ChatClientAgentOptions

El proveedor también se puede especificar directamente en ChatClientAgentOptions.AIContextProviders:

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

Nota:

Cuando se registra a través de ChatClientAgentOptions, el CompactionProviderno se activa durante el bucle de llamada a herramienta. Los proveedores de contexto de nivel de agente se ejecutan antes de almacenar el historial de chat, por lo que los mensajes de resumen sintéticos generados por CompactionProvider pueden formar parte del historial persistente al usar ChatHistoryProvider. Para compactar solo el contexto de la solicitud en curso mientras conserva el historial almacenado original, registre al proveedor en la ChatClientBuilder utilizando UseAIContextProviders(...) en su lugar.

Compactación ad hoc

CompactionProvider.CompactAsync aplica una estrategia a una lista de mensajes arbitraria sin una sesión de agente activa:

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

CompactionProvider es un proveedor de contexto que aplica estrategias de compactación antes y después de ejecutar cada agente. Agréguelo junto con un proveedor de historial en la lista del context_providers agente.

  • before_strategy : se ejecuta antes de la llamada del modelo, compactando los mensajes ya cargados en el contexto.
  • after_strategy : se ejecuta después de la llamada al modelo, compactando los mensajes almacenados por el proveedor de historial para que el siguiente turno se inicie más pequeño.
  • history_source_id : del source_id proveedor de historial cuyos mensajes after_strategy almacenados deben compactar (el valor predeterminado es "in_memory").

Registro 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))

Sugerencia

Use un modelo más pequeño y económico (como gpt-4o-mini) para el cliente de resumen para reducir los costos al tiempo que mantiene la calidad del resumen.

Si solo se necesita una estrategia, pásela directamente como before_strategy:

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

Compactación del historial persistente después de cada ejecución

Use after_strategy para compactar los mensajes almacenados por el proveedor de historial para que los turnos futuros comiencen con un contexto reducido:

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

Compactación ad hoc

apply_compaction aplica una estrategia a una lista arbitraria de mensajes fuera de una sesión del agente activo:

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,
)

Elección de una estrategia

Estrategia Agresividad Conserva el contexto Requiere LLM Más adecuado para
ToolResultCompactionStrategy Bajo Alto: solo contrae los resultados de la herramienta No Recuperar espacio de la salida verborrea de la herramienta
SummarizationCompactionStrategy Medio Medio: reemplaza el historial por un resumen Conversaciones largas en las que importa el contexto
SlidingWindowCompactionStrategy Alto Baja: cae turnos enteros No Límites de número de turnos estrictos
TruncationCompactionStrategy Alto Bajo: elimina los grupos más antiguos No Soporte de emergencia del presupuesto de tokens
PipelineCompactionStrategy Configurable Depende de las estrategias hijas Depende Compactación en capas con varias opciones de respaldo
Estrategia Agresividad Conserva el contexto Requiere LLM Más adecuado para
ToolResultCompactionStrategy Bajo Alto: reduce los resultados de la herramienta a mensajes de resumen No Recuperar espacio de la verbosidad de la salida de la herramienta
SelectiveToolCallCompactionStrategy Bajo y medio Medio: excluye completamente los grupos antiguos de llamadas a herramientas No Eliminación del historial de herramientas cuando los resultados ya no son necesarios
SummarizationStrategy Medio Medio: reemplaza el historial por un resumen Conversaciones largas en las que importa el contexto
SlidingWindowStrategy Alto Bajo - elimina los grupos más antiguos No Límites estrictos de recuento de grupos
TruncationStrategy Alto Bajo: elimina los grupos más antiguos No Respaldo del presupuesto de tokens o mensajes de emergencia
TokenBudgetComposedStrategy Configurable Depende de las estrategias secundarias Depende Compactación por capas con un objetivo de presupuesto de tokens y múltiples alternativas secundarias

Pasos siguientes