post-compaction 추적과 대화별 사용량 계측 보강
Some checks failed
Release Gate / gate (push) Has been cancelled

- 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)
This commit is contained in:
2026-04-04 23:54:59 +09:00
parent d2f8e39d2b
commit ca006972b2
4 changed files with 59 additions and 2 deletions

View File

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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 { }
}