-
Notifications
You must be signed in to change notification settings - Fork 1.6k
.NET Compaction - Introducing compaction strategies and pipeline #4533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 37 commits
Commits
Show all changes
108 commits
Select commit
Hold shift + click to select a range
23cf75b
Checkpoint
crickman fcd60da
Checkpoint
crickman eb84062
Stable
crickman 0e8b9b2
Strategies
crickman b275d34
Merge branch 'main' into crickman/feature-compaction-deux
crickman dda15ea
Updated
crickman 7608005
Encoding
crickman 1428286
Formatting
crickman f70423b
Cleanup
crickman defb9dd
Formatting
crickman 6ce0447
Tests
crickman 7e2c5ad
Tuning
crickman 06f55c0
Update tests
crickman f42863e
Test update
crickman c513694
Remove working solution
crickman 1a8a58f
Merge branch 'main' into crickman/feature-compaction-deux
crickman 43d226f
Add sample to solution
crickman 5ef100c
Sample readyme
crickman 4d6e1ff
Experimental
crickman 2f443a1
Format
crickman 209d0e3
Formatting
crickman 84aa392
Encoding
crickman 7c88b20
Merge branch 'main' into crickman/feature-compaction-deux
crickman 9c1165f
Support IChatReducer
crickman 6ef397e
Merge branch 'main' into crickman/feature-compaction-deux
crickman 7afed95
Sample output formatting
crickman 36ba6b0
Merge branch 'main' into crickman/feature-compaction-deux
crickman 1598991
Initial plan
Copilot dc2bb4d
Replace CompactingChatClient with MessageCompactionContextProvider
Copilot 601eddb
Boundary condition
crickman cc441d2
Merge branch 'main' into crickman/feature-compaction-deux
crickman 14aae1f
Merge branch 'crickman/feature-compaction-deux' into copilot/create-m…
crickman 094b415
Fix encoding
crickman 93728c1
Fix cast
crickman aff1d06
Test coverage
crickman 04f29e6
Merge branch 'crickman/feature-compaction-deux' into copilot/create-m…
crickman 278912b
Namespace
crickman 576f750
Improvements
crickman aa47a14
Efficiency
crickman b202b7c
Cleanup
crickman 9f0cc62
Resolve merge
crickman da4886f
Detect service managed conversation
crickman dcf4b1a
Fix namespace
crickman fe09a1e
Fix merge
crickman b6070fd
Fix test expectation
crickman 06787d0
Merge branch 'main' into crickman/feature-compaction-deux
crickman cf4fe99
Merge branch 'crickman/feature-compaction-deux' into copilot/create-m…
crickman ff3d9e5
Update dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistor…
crickman 1fffafe
Address PR comments (x1)
crickman e157b5f
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman d6d2331
Update comment
crickman ee936d3
Update comments
crickman f42ebb0
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman b6cbf62
Clean-up
crickman 6aeb295
Format output
crickman f88fed0
Sync sample comment
crickman 9edd440
Fix condition
crickman f105ae0
Adjust data-flow
crickman f40710f
Address comments (x2)
crickman 39bd2a5
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 5c406b8
Direct compaction
crickman b0138dc
Fix summarization content
crickman 8179e2e
Argument check / fix count calculation
crickman 4629cc3
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 011995b
Minor follow-up
crickman c68a7d3
Diagnostics
crickman c010d70
Minor updates
crickman 6df1e23
Fix state test
crickman 08354f4
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman 133a631
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman 2c37cfa
Fix sliding window perf
crickman 7640783
Stable state keys
crickman 9a46d18
Increase size computation
crickman 60c4b7a
Formatting
crickman 1287881
Add README.md for Agent_Step18_CompactionPipeline sample (#4574)
Copilot 19643d3
Sample comments
crickman cc8cfc7
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman e79ca62
Updated
crickman 302b4f4
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 182cefb
Update dotnet/src/Microsoft.Agents.AI/Compaction/MessageIndex.cs
crickman 4b64b28
Update dotnet/tests/Microsoft.Agents.AI.UnitTests/Compaction/Compacti…
crickman 2d8f53f
Update dotnet/src/Microsoft.Agents.AI/Compaction/MessageIndex.cs
crickman 10f3538
Address copilot comments
crickman 9e78a1e
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman 1ae8d25
Fix namespace
crickman 2c7e1c8
Merge branch 'main' into copilot/create-message-compaction-provider
crickman dfff5fc
Comments / convensions
crickman 48fbe27
Prefix `MessageGroup` and `MessageIndex`
crickman d8d0d1c
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 1c5820e
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 70ae245
Fix sliding window
crickman d1cadea
Update dotnet/src/Microsoft.Agents.AI/Compaction/SummarizationCompact…
crickman efe5f67
Update dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistor…
crickman 3821ae7
Python alignment
crickman 235876e
Resolve merge
crickman 6d663f2
Fix merge
crickman ee25c42
Merge branch 'main' into copilot/create-message-compaction-provider
crickman 6fbe1ec
Fix equality, readme, and sample
crickman 3cdb4c0
Readme update and ToolResult fix
crickman 45d689b
Update dotnet/src/Microsoft.Agents.AI/Compaction/SummarizationCompact…
crickman e79cfd1
Update dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipelin…
crickman 5023ffc
Simplify readme
crickman 932385b
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman e4beb84
Update dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipelin…
crickman 07c9af7
Remove example
crickman ffc1603
Merge branch 'copilot/create-message-compaction-provider' of https://…
crickman f75f796
Remove unused
crickman 339117e
Merge branch 'main' into copilot/create-message-compaction-provider
crickman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
...s/02-agents/Agents/Agent_Step18_CompactionPipeline/Agent_Step18_CompactionPipeline.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFrameworks>net10.0</TargetFrameworks> | ||
|
|
||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Azure.AI.OpenAI" /> | ||
| <PackageReference Include="Azure.Identity" /> | ||
| <PackageReference Include="Microsoft.Extensions.AI.OpenAI" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
113 changes: 113 additions & 0 deletions
113
dotnet/samples/02-agents/Agents/Agent_Step18_CompactionPipeline/Program.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| // This sample demonstrates how to use a MessageCompactionContextProvider with a compaction pipeline | ||
| // as an AIContextProvider for an agent's in-run context management. The pipeline chains multiple | ||
| // compaction strategies from gentle to aggressive: | ||
| // 1. ToolResultCompactionStrategy - Collapses old tool-call groups into concise summaries | ||
|
crickman marked this conversation as resolved.
|
||
| // 2. SummarizationCompactionStrategy - LLM-compresses older conversation spans | ||
| // 3. SlidingWindowCompactionStrategy - Keeps only the most recent N user turns | ||
| // 4. TruncationCompactionStrategy - Emergency token-budget backstop | ||
|
|
||
| using System.ComponentModel; | ||
| using Azure.AI.OpenAI; | ||
| using Azure.Identity; | ||
| using Microsoft.Agents.AI; | ||
| using Microsoft.Agents.AI.Compaction; | ||
| using Microsoft.Extensions.AI; | ||
|
|
||
| var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); | ||
| var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; | ||
|
|
||
| // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. | ||
| // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid | ||
| // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. | ||
| AzureOpenAIClient openAIClient = new(new Uri(endpoint), new DefaultAzureCredential()); | ||
|
|
||
| // Create a chat client for the agent and a separate one for the summarization strategy. | ||
| // Using the same model for simplicity; in production, use a smaller/cheaper model for summarization. | ||
| IChatClient agentChatClient = openAIClient.GetChatClient(deploymentName).AsIChatClient(); | ||
| IChatClient summarizerChatClient = openAIClient.GetChatClient(deploymentName).AsIChatClient(); | ||
|
|
||
| // Define a tool the agent can use, so we can see tool-result compaction in action. | ||
| [Description("Look up the current price of a product by name.")] | ||
| static string LookupPrice([Description("The product name to look up.")] string productName) => | ||
| productName.ToUpperInvariant() switch | ||
| { | ||
| "LAPTOP" => "The laptop costs $999.99.", | ||
| "KEYBOARD" => "The keyboard costs $79.99.", | ||
| "MOUSE" => "The mouse costs $29.99.", | ||
| _ => $"Sorry, I don't have pricing for '{productName}'." | ||
| }; | ||
|
|
||
| // Configure the compaction pipeline with one of each strategy, ordered least to most aggressive. | ||
| PipelineCompactionStrategy compactionPipeline = | ||
| new(// 1. Gentle: collapse old tool-call groups into short summaries like "[Tool calls: LookupPrice]" | ||
| new ToolResultCompactionStrategy(CompactionTriggers.MessagesExceed(7)), | ||
|
crickman marked this conversation as resolved.
|
||
|
|
||
| // 2. Moderate: use an LLM to summarize older conversation spans into a concise message | ||
| new SummarizationCompactionStrategy(summarizerChatClient, CompactionTriggers.TokensExceed(0x500)), | ||
|
|
||
| // 3. Aggressive: keep only the last N user turns and their responses | ||
| new SlidingWindowCompactionStrategy(CompactionTriggers.TurnsExceed(4)), | ||
|
|
||
| // 4. Emergency: drop oldest groups until under the token budget | ||
| new TruncationCompactionStrategy(CompactionTriggers.TokensExceed(0x8000))); | ||
|
|
||
| // Create the agent with a MessageCompactionContextProvider that uses the compaction pipeline. | ||
| AIAgent agent = | ||
| agentChatClient.AsAIAgent( | ||
| new ChatClientAgentOptions | ||
| { | ||
| Name = "ShoppingAssistant", | ||
| ChatOptions = new() | ||
| { | ||
| Instructions = | ||
| """ | ||
| You are a helpful, but long winded, shopping assistant. | ||
| Help the user look up prices and compare products. | ||
| When responding, Be sure to be extra descriptive and use as | ||
| many words as possible without sounding ridiculous. | ||
| """, | ||
| Tools = [AIFunctionFactory.Create(LookupPrice)], | ||
| }, | ||
| AIContextProviders = [new MessageCompactionContextProvider(compactionPipeline)], | ||
|
crickman marked this conversation as resolved.
Outdated
|
||
| }); | ||
|
|
||
| AgentSession session = await agent.CreateSessionAsync(); | ||
|
|
||
| // Helper to print chat history size | ||
| void PrintChatHistory() | ||
| { | ||
| if (session.TryGetInMemoryChatHistory(out var history)) | ||
| { | ||
| Console.ForegroundColor = ConsoleColor.Cyan; | ||
| Console.WriteLine($"\n[Messages: x{history.Count}]\n"); | ||
|
crickman marked this conversation as resolved.
Outdated
|
||
| Console.ResetColor(); | ||
| } | ||
| } | ||
|
|
||
| // Run a multi-turn conversation with tool calls to exercise the pipeline. | ||
| string[] prompts = | ||
| [ | ||
| "What's the price of a laptop?", | ||
| "How about a keyboard?", | ||
| "And a mouse?", | ||
| "Which product is the cheapest?", | ||
| "Can you compare the laptop and the keyboard for me?", | ||
| "What was the first product I asked about?", | ||
| "Thank you!", | ||
| ]; | ||
|
|
||
| foreach (string prompt in prompts) | ||
| { | ||
| Console.ForegroundColor = ConsoleColor.Cyan; | ||
| Console.Write("\n[User] "); | ||
| Console.ResetColor(); | ||
| Console.WriteLine(prompt); | ||
| Console.ForegroundColor = ConsoleColor.Cyan; | ||
| Console.Write("\n[Agent] "); | ||
| Console.ResetColor(); | ||
| Console.WriteLine(await agent.RunAsync(prompt, session)); | ||
|
|
||
| PrintChatHistory(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
dotnet/src/Microsoft.Agents.AI/Compaction/ChatReducerCompactionStrategy.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Extensions.AI; | ||
| using Microsoft.Shared.DiagnosticIds; | ||
| using Microsoft.Shared.Diagnostics; | ||
|
|
||
| namespace Microsoft.Agents.AI.Compaction; | ||
|
|
||
| /// <summary> | ||
| /// A compaction strategy that delegates to an <see cref="IChatReducer"/> to reduce the conversation's | ||
| /// included messages. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// This strategy bridges the <see cref="IChatReducer"/> abstraction from <c>Microsoft.Extensions.AI</c> | ||
| /// into the compaction pipeline. It collects the currently included messages from the | ||
| /// <see cref="MessageIndex"/>, passes them to the reducer, and rebuilds the index from the | ||
| /// reduced message list when the reducer produces fewer messages. | ||
| /// </para> | ||
| /// <para> | ||
| /// The <see cref="CompactionTrigger"/> controls when reduction is attempted. | ||
| /// Use <see cref="CompactionTriggers"/> for common trigger conditions such as token or message thresholds. | ||
| /// </para> | ||
| /// <para> | ||
| /// Use this strategy when you have an existing <see cref="IChatReducer"/> implementation | ||
| /// (such as <c>MessageCountingChatReducer</c>) and want to apply it as part of a | ||
| /// <see cref="CompactionStrategy"/> pipeline or as an in-run compaction strategy. | ||
| /// </para> | ||
| /// </remarks> | ||
| [Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)] | ||
| public sealed class ChatReducerCompactionStrategy : CompactionStrategy | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ChatReducerCompactionStrategy"/> class. | ||
| /// </summary> | ||
| /// <param name="chatReducer"> | ||
| /// The <see cref="IChatReducer"/> that performs the message reduction. | ||
| /// </param> | ||
| /// <param name="trigger"> | ||
| /// The <see cref="CompactionTrigger"/> that controls when compaction proceeds. | ||
| /// </param> | ||
| /// <param name="target"> | ||
| /// An optional target condition that controls when compaction stops. When <see langword="null"/>, | ||
| /// defaults to the inverse of the <paramref name="trigger"/> — compaction stops as soon as the trigger would no longer fire. | ||
| /// Note that the <see cref="IChatReducer"/> performs reduction in a single call, so the target is | ||
| /// not evaluated incrementally; it is available for composition with other strategies via | ||
| /// <see cref="PipelineCompactionStrategy"/>. | ||
| /// </param> | ||
| public ChatReducerCompactionStrategy(IChatReducer chatReducer, CompactionTrigger trigger, CompactionTrigger? target = null) | ||
| : base(trigger, target) | ||
| { | ||
| this.ChatReducer = Throw.IfNull(chatReducer); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the chat reducer used to reduce messages. | ||
| /// </summary> | ||
| public IChatReducer ChatReducer { get; } | ||
|
|
||
| /// <inheritdoc/> | ||
| protected override async Task<bool> ApplyCompactionAsync(MessageIndex index, CancellationToken cancellationToken) | ||
| { | ||
| // No need to short-circuit on empty conversations, this is handled by <see cref="CompactionStrategy.CompactAsync"/>. | ||
| List<ChatMessage> includedMessages = [.. index.GetIncludedMessages()]; | ||
|
|
||
| IEnumerable<ChatMessage> reduced = await this.ChatReducer.ReduceAsync(includedMessages, cancellationToken).ConfigureAwait(false); | ||
| IList<ChatMessage> reducedMessages = reduced as IList<ChatMessage> ?? [.. reduced]; | ||
|
|
||
| if (reducedMessages.Count >= includedMessages.Count) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // Rebuild the index from the reduced messages | ||
| MessageIndex rebuilt = MessageIndex.Create(reducedMessages, index.Tokenizer); | ||
| index.Groups.Clear(); | ||
| foreach (MessageGroup group in rebuilt.Groups) | ||
| { | ||
| index.Groups.Add(group); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.