From ca006972b219cfe87ff186557c116acae47ae8f3 Mon Sep 17 00:00:00 2001 From: lacvet Date: Sat, 4 Apr 2026 23:54:59 +0900 Subject: [PATCH] =?UTF-8?q?post-compaction=20=EC=B6=94=EC=A0=81=EA=B3=BC?= =?UTF-8?q?=20=EB=8C=80=ED=99=94=EB=B3=84=20=EC=82=AC=EC=9A=A9=EB=9F=89=20?= =?UTF-8?q?=EA=B3=84=EC=B8=A1=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - claude-code의 post-compaction 플래그 흐름을 참고해 compact 직후 첫 응답을 별도로 추적하고 대화 단위 후속 사용량을 집계함 - ChatConversation에 pending post-compaction, 응답 회수, prompt/completion token 누적 필드를 추가하고 AX Agent 컨텍스트 카드 hover에 표시함 - README 및 docs/DEVELOPMENT.md에 2026-04-04 23:47 (KST) 기준 이력을 반영함 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0) --- README.md | 2 ++ docs/DEVELOPMENT.md | 1 + src/AxCopilot/Models/ChatModels.cs | 12 +++++++ src/AxCopilot/Views/ChatWindow.xaml.cs | 46 ++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ab18344..33f8565 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저 - AX Agent 컨텍스트 압축 경로에 `session memory compact`, `microcompact`, `collapse/snip` 단계를 추가해 오래된 요약·실행 로그·도구 결과를 LLM 요약 전에 더 세밀하게 줄이도록 보강했습니다. - 현재 대화 기준 compact 누적 회수, 자동/수동 비중, 절감 토큰, session memory 적용 횟수, microcompact/snipped 메시지 수를 하단 컨텍스트 카드 hover에서 함께 확인할 수 있게 했습니다. - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 +- compact 직후 첫 응답을 별도로 추적하는 post-compaction 흐름을 추가해, 압축 다음 턴의 응답 횟수와 사용 토큰을 현재 대화 기준으로 누적 집계하도록 보강했습니다. +- AX Agent 하단 컨텍스트 카드 hover에는 이제 `compact 후 첫 응답 대기 중`, `compact 이후 응답 수`, `compact 이후 사용 토큰`까지 함께 표시됩니다. - 업데이트: 2026-04-04 23:32 (KST) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 9400e9b..23979d2 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -2,6 +2,7 @@ - Document update: 2026-04-04 23:47 (KST) - Added AX-side session-memory compaction, collapse/snip trimming, and per-conversation compaction usage tracking so compact now records staged reductions instead of only the last before/after snapshot. - Document update: 2026-04-04 23:47 (KST) - The AX Agent context tooltip now shows session totals for compaction count, saved tokens, session-memory passes, microcompact boundaries, and snipped messages for the current conversation. +- Document update: 2026-04-04 23:47 (KST) - Added a post-compaction flag and conversation-level follow-up usage counters so AX can distinguish the first response after compaction and report its prompt/completion token cost separately. - Document update: 2026-04-04 23:32 (KST) - Upgraded ContextCondenser from a two-step path to a three-step path (tool-result truncate -> microcompact -> summarize) so long sessions can strip older execution/tool chatter before invoking the LLM summary stage. - Document update: 2026-04-04 23:32 (KST) - Added AX-side microcompact boundaries that compress older tool results, execution metadata, and oversized messages into lighter boundary summaries, bringing the compact flow closer to the staged approach used in claude-code. diff --git a/src/AxCopilot/Models/ChatModels.cs b/src/AxCopilot/Models/ChatModels.cs index bd9ecf7..34f1b32 100644 --- a/src/AxCopilot/Models/ChatModels.cs +++ b/src/AxCopilot/Models/ChatModels.cs @@ -175,6 +175,18 @@ public class ChatConversation [JsonPropertyName("lastCompactionStageSummary")] public string? LastCompactionStageSummary { get; set; } + + [JsonPropertyName("pendingPostCompaction")] + public bool PendingPostCompaction { get; set; } + + [JsonPropertyName("postCompactionResponseCount")] + public int PostCompactionResponseCount { get; set; } + + [JsonPropertyName("postCompactionPromptTokens")] + public int PostCompactionPromptTokens { get; set; } + + [JsonPropertyName("postCompactionCompletionTokens")] + public int PostCompactionCompletionTokens { get; set; } } public class ChatAgentRunRecord diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index 0c1d28a..ada096f 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -107,6 +107,10 @@ public partial class ChatWindow : Window private int _sessionMemoryCompactionCount; private int _sessionMicrocompactBoundaryCount; private int _sessionSnipCompactionCount; + private bool _pendingPostCompaction; + private int _sessionPostCompactionResponseCount; + private int _sessionPostCompactionPromptTokens; + private int _sessionPostCompactionCompletionTokens; private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg) { if (button?.Content is not string text) @@ -1732,6 +1736,10 @@ public partial class ChatWindow : Window _sessionMemoryCompactionCount = conv?.SessionMemoryCompactionCount ?? 0; _sessionMicrocompactBoundaryCount = conv?.MicrocompactBoundaryCount ?? 0; _sessionSnipCompactionCount = conv?.SnipCompactionCount ?? 0; + _pendingPostCompaction = conv?.PendingPostCompaction ?? false; + _sessionPostCompactionResponseCount = conv?.PostCompactionResponseCount ?? 0; + _sessionPostCompactionPromptTokens = conv?.PostCompactionPromptTokens ?? 0; + _sessionPostCompactionCompletionTokens = conv?.PostCompactionCompletionTokens ?? 0; _lastCompactionAt = conv?.LastCompactionAt; _lastCompactionWasAutomatic = conv?.LastCompactionWasAutomatic ?? false; _lastCompactionBeforeTokens = conv?.LastCompactionBeforeTokens; @@ -10939,10 +10947,12 @@ public partial class ChatWindow : Window ? _agentCumulativeOutputTokens : usage?.CompletionTokens ?? 0; + var wasPostCompactionResponse = _pendingPostCompaction; if (displayInput > 0 || displayOutput > 0) { UpdateStatusTokens(displayInput, displayOutput); Services.UsageStatisticsService.RecordTokens(displayInput, displayOutput); + ConsumePostCompactionUsageIfNeeded(displayInput, displayOutput); } string tokenText; if (displayInput > 0 || displayOutput > 0) @@ -10952,9 +10962,10 @@ public partial class ChatWindow : Window else tokenText = $"~{FormatTokenCount(EstimateTokenCount(finalContent))} tokens"; + var postCompactSuffix = wasPostCompactionResponse ? " · compact 직후" : ""; var metaText = new TextBlock { - Text = $"{elapsedText} · {tokenText}", + Text = $"{elapsedText} · {tokenText}{postCompactSuffix}", FontSize = 9.5, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, HorizontalAlignment = HorizontalAlignment.Right, @@ -16532,6 +16543,11 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi $"세션 절감: {Services.TokenEstimator.Format(_sessionCompactionSavedTokens)} tokens\n" + $"세션 메모리 {_sessionMemoryCompactionCount:N0}회 · 경계 {_sessionMicrocompactBoundaryCount:N0}건 · snip {_sessionSnipCompactionCount:N0}건" : ""; + var postCompactionUsage = _sessionPostCompactionResponseCount > 0 + ? $"\ncompact 이후 응답: {_sessionPostCompactionResponseCount:N0}회\n" + + $"compact 이후 사용량: {Services.TokenEstimator.Format(_sessionPostCompactionPromptTokens)} + {Services.TokenEstimator.Format(_sessionPostCompactionCompletionTokens)} = {Services.TokenEstimator.Format(_sessionPostCompactionPromptTokens + _sessionPostCompactionCompletionTokens)} tokens" + : ""; + var pendingPostCompaction = _pendingPostCompaction ? "\ncompact 후 첫 응답 대기 중" : ""; TokenUsageCard.ToolTip = $"상태: {summary}\n" + @@ -16540,7 +16556,9 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi $"자동 압축 시작: {triggerPercent}%\n" + $"현재 입력 초안 포함" + compactHistory + - compactSession; + compactSession + + postCompactionUsage + + pendingPostCompaction; UpdateCircularUsageArc(TokenUsageArc, usageRatio, 18, 18, 14); PositionThresholdMarker(TokenUsageThresholdMarker, triggerRatio, 18, 18, 14, 3); @@ -16605,6 +16623,7 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi _sessionMemoryCompactionCount += result.SessionMemoryApplied ? 1 : 0; _sessionMicrocompactBoundaryCount += result.MicrocompactBoundaryCount; _sessionSnipCompactionCount += result.SnippedMessageCount + result.CollapsedBoundaryCount; + _pendingPostCompaction = true; if (wasAutomatic) _sessionAutomaticCompactionCount++; else @@ -16627,6 +16646,29 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi conv.LastCompactionBeforeTokens = beforeTokens; conv.LastCompactionAfterTokens = afterTokens; conv.LastCompactionStageSummary = _lastCompactionStageSummary; + conv.PendingPostCompaction = true; + try { _storage.Save(conv); } catch { } + } + + private void ConsumePostCompactionUsageIfNeeded(int promptTokens, int completionTokens) + { + if (!_pendingPostCompaction) + return; + + _pendingPostCompaction = false; + _sessionPostCompactionResponseCount++; + _sessionPostCompactionPromptTokens += Math.Max(0, promptTokens); + _sessionPostCompactionCompletionTokens += Math.Max(0, completionTokens); + + ChatConversation? conv; + lock (_convLock) conv = _currentConversation; + if (conv == null) + return; + + conv.PendingPostCompaction = false; + conv.PostCompactionResponseCount = _sessionPostCompactionResponseCount; + conv.PostCompactionPromptTokens = _sessionPostCompactionPromptTokens; + conv.PostCompactionCompletionTokens = _sessionPostCompactionCompletionTokens; try { _storage.Save(conv); } catch { } }