diff --git a/dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipeline/README.md b/dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipeline/README.md index 0ec61789d6..e613a46d1c 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipeline/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipeline/README.md @@ -14,6 +14,56 @@ This sample demonstrates how to use a `CompactionProvider` with a `PipelineCompa ## Concepts +### Choosing between `CompactionProvider` and `IChatReducer` + +Both abstractions reduce the messages sent to a model, but they run at different layers and have different effects on stored history. + +| Choose | When you need | Effect on stored history | Function-calling loop | +|---|---|---|---| +| `CompactionProvider` on `ChatClientBuilder.UseAIContextProviders(...)` | Request-context management that preserves the original conversation | The compacted view is forwarded to the inner chat client; the source history remains unchanged | Runs for each inner chat-client call, including calls made while tools are being invoked | +| `CompactionProvider` in `ChatClientAgentOptions.AIContextProviders` | Agent-specific compaction without decorating a shared chat client | Runs before chat history is stored, so generated replacement messages can become part of the persisted history | Runs at the agent boundary, not for each call inside the tool loop | +| `IChatReducer` in `InMemoryChatHistoryProviderOptions.ChatReducer` | Storage management where the reduced list should replace the session's in-memory history | Permanently replaces the provider's stored message list with the reducer output | Runs at the configured history-provider event, not for each call inside the tool loop | + +Use a builder-level `CompactionProvider` when the primary goal is to fit each model request within a context window while retaining the complete conversation for auditing, replay, or a different downstream policy. Use an `IChatReducer` when the primary goal is to bound the history retained in `InMemoryChatHistoryProvider` itself. If the reduced history is serialized with the session, the discarded messages are no longer present after the session is restored. + +`InMemoryChatHistoryProvider` can run its reducer at either of these events: + +- `BeforeMessagesRetrieval` (the default) reduces stored history immediately before it is supplied to the agent. +- `AfterMessageAdded` reduces stored history after each request/response pair is added. + +The event controls *when* reduction occurs; the `IChatReducer` implementation controls *how* messages are reduced. By contrast, a `CompactionStrategy` supplies its own `CompactionTrigger` and operates on message groups that preserve tool-call/result pairs. + +#### Adapting between the abstractions + +The adapters support existing implementations at either integration point. Pick the direction that matches the layer where you want reduction to run. + +To use a `CompactionStrategy` for persistent in-memory history reduction, adapt it to `IChatReducer`: + +```csharp +CompactionStrategy strategy = + new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(20)); + +InMemoryChatHistoryProvider historyProvider = new(new() +{ + ChatReducer = strategy.AsChatReducer(), + ReducerTriggerEvent = InMemoryChatHistoryProviderOptions.ChatReducerTriggerEvent.BeforeMessagesRetrieval +}); +``` + +To use an existing `IChatReducer` in a compaction pipeline or for in-run request compaction, adapt it to `CompactionStrategy`: + +```csharp +IChatReducer existingReducer = /* your MEAI reducer */; + +CompactionStrategy strategy = new ChatReducerCompactionStrategy( + existingReducer, + CompactionTriggers.TokensExceed(4000)); + +CompactionProvider provider = new(strategy); +``` + +Do not wrap a strategy with `AsChatReducer()` and immediately wrap that reducer in `ChatReducerCompactionStrategy`. That round trip adds no capability; choose the original strategy directly and register it at the appropriate layer. + ### Message groups The compaction engine organizes messages into atomic *groups* that are treated as indivisible units during compaction. A group is either: diff --git a/dotnet/samples/02-agents/Agents/README.md b/dotnet/samples/02-agents/Agents/README.md index 0fe7c94403..c7cc43a877 100644 --- a/dotnet/samples/02-agents/Agents/README.md +++ b/dotnet/samples/02-agents/Agents/README.md @@ -44,7 +44,7 @@ Before you begin, ensure you have the following prerequisites: |[Deep research with an agent](./Agent_Step15_DeepResearch/)|This sample demonstrates how to use the Deep Research Tool to perform comprehensive research on complex topics| |[Declarative agent](./Agent_Step16_Declarative/)|This sample demonstrates how to declaratively define an agent.| |[Providing additional AI Context to an agent using multiple AIContextProviders](./Agent_Step17_AdditionalAIContext/)|This sample demonstrates how to inject additional AI context into a ChatClientAgent using multiple custom AIContextProvider components that are attached to the agent.| -|[Using compaction pipeline with an agent](./Agent_Step18_CompactionPipeline/)|This sample demonstrates how to use a compaction pipeline to efficiently limit the size of the conversation history for an agent.| +|[Using compaction pipeline with an agent](./Agent_Step18_CompactionPipeline/)|This sample demonstrates how to use a compaction pipeline and how to choose between request-level `CompactionProvider` and persistent-history `IChatReducer` integration.| |[In-function-loop checkpointing](./Agent_Step19_InFunctionLoopCheckpointing/)|This sample demonstrates how to persist chat history after each service call during a tool-calling loop, enabling crash recovery and mid-run observability.| |[Dynamic function tools](./Agent_Step20_DynamicFunctionTools/)|This sample demonstrates how to dynamically expand the set of function tools available to an agent during a function-calling loop using the ambient FunctionInvocationContext.| |[Shell tool with environment-aware system prompt](./Agent_Step21_ShellWithEnvironment/)|This sample demonstrates how to use the shell tool together with the ShellEnvironmentProvider to run commands in stateless and persistent modes, injecting environment-aware instructions so the agent emits commands in the right shell idiom.|