compact 이후 복원 메모 계층화와 compact UI 메타 축소
- post_compact_context 메시지에 compact summary 수와 structured tool history 블록 수를 추가해 compact 뒤 첫 query turn의 복원 맥락을 더 명확히 전달함 - compact 메타 카드를 더 짧은 한 줄 요약과 파일 개수 중심으로 줄여 transcript에서 운영 메타 밀도를 낮춤 - 컨텍스트 사용 팝업의 compact 디테일을 짧은 한국어 표현으로 정리해 claw-code 스타일의 얇은 운영 표현에 가깝게 맞춤 - README.md 및 docs/DEVELOPMENT.md를 2026-04-12 23:23 (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:
@@ -1669,3 +1669,8 @@ MIT License
|
|||||||
- compact 이후 query view에 복원된 파일/이미지 참조를 짧게 다시 주입해, `claw-code`의 post-compact attachment continuity에 더 가깝게 맞췄습니다.
|
- compact 이후 query view에 복원된 파일/이미지 참조를 짧게 다시 주입해, `claw-code`의 post-compact attachment continuity에 더 가깝게 맞췄습니다.
|
||||||
- [AgentQueryContextBuilder.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentQueryContextBuilder.cs)는 compact boundary가 적용된 query view에 `post_compact_context` system 메시지를 추가해, 복원된 파일 참조와 이미지 참조 개수를 함께 전달합니다.
|
- [AgentQueryContextBuilder.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentQueryContextBuilder.cs)는 compact boundary가 적용된 query view에 `post_compact_context` system 메시지를 추가해, 복원된 파일 참조와 이미지 참조 개수를 함께 전달합니다.
|
||||||
- [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs)의 final-report 품질 프롬프트는 일반 작업에서는 더 짧고 명확한 3줄 요약 중심으로 축소하고, review/high-impact 작업에만 구조적 상세 보고를 유지하도록 조정했습니다.
|
- [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs)의 final-report 품질 프롬프트는 일반 작업에서는 더 짧고 명확한 3줄 요약 중심으로 축소하고, review/high-impact 작업에만 구조적 상세 보고를 유지하도록 조정했습니다.
|
||||||
|
- 업데이트: 2026-04-12 23:23 (KST)
|
||||||
|
- compact 뒤 query view의 복원 메모를 한 단계 더 구조화해, 요약 경계 수와 구조화된 tool history 블록 수도 함께 실어주도록 보강했습니다.
|
||||||
|
- [AgentQueryContextBuilder.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentQueryContextBuilder.cs)는 `post_compact_context` 메시지에 `restored compact summaries`, `restored tool history blocks`, `restored file refs`, `restored image refs`를 분리해 담습니다.
|
||||||
|
- [ChatWindow.TimelinePresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs)는 compact 메타 카드를 더 짧은 설명과 파일 개수 정도만 보이도록 줄였습니다.
|
||||||
|
- [ChatWindow.ContextUsagePresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.ContextUsagePresentation.cs)는 컨텍스트 사용 팝업의 compact 디테일 문구를 짧고 읽기 쉬운 표현으로 다시 정리했습니다.
|
||||||
|
|||||||
@@ -719,3 +719,18 @@ owKindCounts를 함께 남겨 %APPDATA%\\AxCopilot\\perf 기준으로 transcript
|
|||||||
- compact 직후 첫 query turn이 복원된 파일/이미지 참조를 더 안정적으로 이어받습니다.
|
- compact 직후 첫 query turn이 복원된 파일/이미지 참조를 더 안정적으로 이어받습니다.
|
||||||
- 일반 Cowork/Code 작업의 최종 응답이 `claw-code`처럼 더 짧고 메타 밀도가 낮아집니다.
|
- 일반 Cowork/Code 작업의 최종 응답이 `claw-code`처럼 더 짧고 메타 밀도가 낮아집니다.
|
||||||
|
|
||||||
|
## post-compact 복원 메모 계층화 / compact UI 메타 축소 (2026-04-12 23:23 KST)
|
||||||
|
|
||||||
|
- `claw-code`는 compact 뒤 첫 요청에서 attachment/tool context를 다시 붙여주고, transcript에는 compact 운영 메타를 과하게 드러내지 않습니다. AX도 그 방향으로 한 단계 더 정리했습니다.
|
||||||
|
- `src/AxCopilot/Services/Agent/AgentQueryContextBuilder.cs`
|
||||||
|
- `post_compact_context` system 메시지에 단순 파일/이미지 참조뿐 아니라 `restored compact summaries`, `restored tool history blocks`도 함께 넣어, compact 뒤 첫 query turn이 어떤 종류의 맥락을 이어받는지 더 명확히 전달합니다.
|
||||||
|
- `src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs`
|
||||||
|
- compact 메타 카드는 이전보다 더 짧은 요약 한 줄과 첨부 파일 수 정도만 보여주도록 줄였습니다.
|
||||||
|
- 운영 설명을 줄이고 transcript 안에서 일반 메시지 흐름을 덜 방해하게 맞췄습니다.
|
||||||
|
- `src/AxCopilot/Views/ChatWindow.ContextUsagePresentation.cs`
|
||||||
|
- 컨텍스트 사용 카드와 팝업의 compact 관련 문구를 짧고 읽기 쉬운 한국어 표현으로 정리했습니다.
|
||||||
|
- 마지막 압축 정보는 `자동/수동 압축 A -> B`, 누적 정보는 `누적 압축 N회 · 절감 X` 형태만 남겨 과한 운영 메타를 줄였습니다.
|
||||||
|
- 기대 효과
|
||||||
|
- compact 뒤 첫 LLM 호출이 복원된 맥락의 종류를 더 안정적으로 전달받습니다.
|
||||||
|
- transcript와 usage UI가 `claw-code`처럼 더 얇고 조용한 운영 메타 표현을 유지합니다.
|
||||||
|
|
||||||
|
|||||||
@@ -153,11 +153,25 @@ public static class AgentQueryContextBuilder
|
|||||||
.Take(5)
|
.Take(5)
|
||||||
.ToList();
|
.ToList();
|
||||||
var imageCount = messages.Sum(m => m.Images?.Count ?? 0);
|
var imageCount = messages.Sum(m => m.Images?.Count ?? 0);
|
||||||
|
var compactSummaryCount = messages.Count(m =>
|
||||||
|
string.Equals(m.MetaKind, "microcompact_boundary", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(m.MetaKind, "session_memory_compaction", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(m.MetaKind, "collapsed_boundary", StringComparison.OrdinalIgnoreCase));
|
||||||
|
var structuredToolHistoryCount = messages.Count(m =>
|
||||||
|
{
|
||||||
|
var content = m.Content ?? "";
|
||||||
|
return content.StartsWith("{\"_tool_use_blocks\"", StringComparison.Ordinal)
|
||||||
|
|| content.StartsWith("{\"type\":\"tool_result\"", StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
|
||||||
if (attachedFiles.Count == 0 && imageCount == 0)
|
if (attachedFiles.Count == 0 && imageCount == 0 && compactSummaryCount == 0 && structuredToolHistoryCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var lines = new List<string> { "[post-compact context]" };
|
var lines = new List<string> { "[post-compact context]" };
|
||||||
|
if (compactSummaryCount > 0)
|
||||||
|
lines.Add($"restored compact summaries: {compactSummaryCount}");
|
||||||
|
if (structuredToolHistoryCount > 0)
|
||||||
|
lines.Add($"restored tool history blocks: {structuredToolHistoryCount}");
|
||||||
if (attachedFiles.Count > 0)
|
if (attachedFiles.Count > 0)
|
||||||
lines.Add("restored file refs: " + string.Join(", ", attachedFiles));
|
lines.Add("restored file refs: " + string.Join(", ", attachedFiles));
|
||||||
if (imageCount > 0)
|
if (imageCount > 0)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace AxCopilot.Views;
|
|||||||
|
|
||||||
public partial class ChatWindow
|
public partial class ChatWindow
|
||||||
{
|
{
|
||||||
// 토큰 추정 캐시: 메시지 수/대화 ID가 바뀔 때만 재계산
|
// 토큰 추정 캐시: 메시지 수나 대화 ID가 달라질 때만 재계산
|
||||||
private int _cachedMessageTokens;
|
private int _cachedMessageTokens;
|
||||||
private int _cachedMessageCountForTokens = -1;
|
private int _cachedMessageCountForTokens = -1;
|
||||||
private string? _cachedConvIdForTokens;
|
private string? _cachedConvIdForTokens;
|
||||||
@@ -32,8 +32,6 @@ public partial class ChatWindow
|
|||||||
var triggerPercent = Math.Clamp(llm.ContextCompactTriggerPercent, 10, 95);
|
var triggerPercent = Math.Clamp(llm.ContextCompactTriggerPercent, 10, 95);
|
||||||
var triggerRatio = triggerPercent / 100.0;
|
var triggerRatio = triggerPercent / 100.0;
|
||||||
|
|
||||||
// 메시지 토큰 추정: 메시지 수나 대화 ID가 바뀔 때만 재계산 (타이핑 중 반복 계산 방지)
|
|
||||||
// 스트리밍 중에는 매번 재계산 (도구 결과 메시지가 실시간으로 추가됨)
|
|
||||||
int messageTokens;
|
int messageTokens;
|
||||||
lock (_convLock)
|
lock (_convLock)
|
||||||
{
|
{
|
||||||
@@ -53,8 +51,6 @@ public partial class ChatWindow
|
|||||||
var draftText = InputBox?.Text ?? "";
|
var draftText = InputBox?.Text ?? "";
|
||||||
var draftTokens = string.IsNullOrWhiteSpace(draftText) ? 0 : Services.TokenEstimator.Estimate(draftText) + 4;
|
var draftTokens = string.IsNullOrWhiteSpace(draftText) ? 0 : Services.TokenEstimator.Estimate(draftText) + 4;
|
||||||
|
|
||||||
// 시스템 프롬프트 + 도구 정의 오버헤드: 첫 메시지 전송 이후에만 포함
|
|
||||||
// 새 대화(메시지 0개)에서는 0% 표시 — 사용자 혼동 방지
|
|
||||||
var hasAnyMessages = messageTokens > 0 || _isStreaming;
|
var hasAnyMessages = messageTokens > 0 || _isStreaming;
|
||||||
int baseOverhead = 0;
|
int baseOverhead = 0;
|
||||||
if (hasAnyMessages)
|
if (hasAnyMessages)
|
||||||
@@ -63,6 +59,7 @@ public partial class ChatWindow
|
|||||||
var toolCount = _toolRegistry?.GetActiveToolsForTab(_activeTab ?? "Chat")?.Count ?? 0;
|
var toolCount = _toolRegistry?.GetActiveToolsForTab(_activeTab ?? "Chat")?.Count ?? 0;
|
||||||
baseOverhead = Services.TokenEstimator.EstimateBaseOverhead(sysPromptLen, toolCount);
|
baseOverhead = Services.TokenEstimator.EstimateBaseOverhead(sysPromptLen, toolCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentTokens = Math.Max(0, messageTokens + draftTokens + baseOverhead);
|
var currentTokens = Math.Max(0, messageTokens + draftTokens + baseOverhead);
|
||||||
var usageRatio = Services.TokenEstimator.GetContextUsage(currentTokens, maxContextTokens);
|
var usageRatio = Services.TokenEstimator.GetContextUsage(currentTokens, maxContextTokens);
|
||||||
|
|
||||||
@@ -80,7 +77,7 @@ public partial class ChatWindow
|
|||||||
else if (usageRatio >= triggerRatio)
|
else if (usageRatio >= triggerRatio)
|
||||||
{
|
{
|
||||||
progressBrush = Brushes.DarkOrange;
|
progressBrush = Brushes.DarkOrange;
|
||||||
summary = llm.EnableProactiveContextCompact ? "곧 자동 압축" : "압축 한계 도달";
|
summary = llm.EnableProactiveContextCompact ? "곧 자동 압축" : "압축 임계 도달";
|
||||||
compactLabel = "압축 권장";
|
compactLabel = "압축 권장";
|
||||||
}
|
}
|
||||||
else if (usageRatio >= triggerRatio * 0.7)
|
else if (usageRatio >= triggerRatio * 0.7)
|
||||||
@@ -99,7 +96,7 @@ public partial class ChatWindow
|
|||||||
if (_lastCompactionAt.HasValue && _lastCompactionBeforeTokens.HasValue && _lastCompactionAfterTokens.HasValue)
|
if (_lastCompactionAt.HasValue && _lastCompactionBeforeTokens.HasValue && _lastCompactionAfterTokens.HasValue)
|
||||||
{
|
{
|
||||||
var compactType = _lastCompactionWasAutomatic ? "자동" : "수동";
|
var compactType = _lastCompactionWasAutomatic ? "자동" : "수동";
|
||||||
detailText = $"{compactType} 압축 {Services.TokenEstimator.Format(_lastCompactionBeforeTokens.Value)} → {Services.TokenEstimator.Format(_lastCompactionAfterTokens.Value)}";
|
detailText = $"{compactType} 압축 {Services.TokenEstimator.Format(_lastCompactionBeforeTokens.Value)} -> {Services.TokenEstimator.Format(_lastCompactionAfterTokens.Value)}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -107,7 +104,7 @@ public partial class ChatWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
TokenUsageArc.Stroke = progressBrush;
|
TokenUsageArc.Stroke = progressBrush;
|
||||||
var percentText = $"{Math.Round(usageRatio * 100):0}%";
|
var percentText = $"{System.Math.Round(usageRatio * 100):0}%";
|
||||||
TokenUsagePercentText.Text = percentText;
|
TokenUsagePercentText.Text = percentText;
|
||||||
TokenUsageSummaryText.Text = $"컨텍스트 {percentText}";
|
TokenUsageSummaryText.Text = $"컨텍스트 {percentText}";
|
||||||
TokenUsageHintText.Text = $"{Services.TokenEstimator.Format(currentTokens)} / {Services.TokenEstimator.Format(maxContextTokens)}";
|
TokenUsageHintText.Text = $"{Services.TokenEstimator.Format(currentTokens)} / {Services.TokenEstimator.Format(maxContextTokens)}";
|
||||||
@@ -124,8 +121,8 @@ public partial class ChatWindow
|
|||||||
TokenUsagePopupDetail.Text = detailText;
|
TokenUsagePopupDetail.Text = detailText;
|
||||||
if (TokenUsagePopupCompact != null)
|
if (TokenUsagePopupCompact != null)
|
||||||
TokenUsagePopupCompact.Text = _sessionCompactionCount > 0
|
TokenUsagePopupCompact.Text = _sessionCompactionCount > 0
|
||||||
? $"누적 압축 {_sessionCompactionCount}회 · 절감 {FormatTokenCount(_sessionCompactionSavedTokens)} tokens"
|
? $"누적 압축 {_sessionCompactionCount}회 · 절감 {FormatTokenCount(_sessionCompactionSavedTokens)}"
|
||||||
: "AX Agent가 컨텍스트를 자동으로 관리합니다";
|
: "AX Agent가 컨텍스트를 자동 관리합니다";
|
||||||
|
|
||||||
TokenUsageCard.ToolTip = null;
|
TokenUsageCard.ToolTip = null;
|
||||||
|
|
||||||
|
|||||||
@@ -393,14 +393,21 @@ public partial class ChatWindow
|
|||||||
|
|
||||||
var summary = message.MetaKind switch
|
var summary = message.MetaKind switch
|
||||||
{
|
{
|
||||||
"session_memory_compaction" => "이전 요약과 실행 경계를 하나의 세션 메모로 정리했습니다.",
|
"session_memory_compaction" => "이전 기록을 세션 메모로 정리했습니다.",
|
||||||
"collapsed_boundary" => "이전 compact 경계를 합쳐 transcript를 더 가볍게 유지했습니다.",
|
"collapsed_boundary" => "이전 압축 경계를 합쳤습니다.",
|
||||||
_ => "오래된 실행/도구 결과를 줄여 다음 요청 컨텍스트를 가볍게 만들었습니다.",
|
_ => "오래된 실행 기록을 줄였습니다.",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var detailBits = new List<string>();
|
||||||
|
if (message.AttachedFiles?.Count > 0)
|
||||||
|
detailBits.Add($"파일 {message.AttachedFiles.Count}개");
|
||||||
|
var compactDetail = detailBits.Count > 0
|
||||||
|
? $"{summary} · {string.Join(", ", detailBits)}"
|
||||||
|
: summary;
|
||||||
|
|
||||||
stack.Children.Add(new TextBlock
|
stack.Children.Add(new TextBlock
|
||||||
{
|
{
|
||||||
Text = summary,
|
Text = compactDetail,
|
||||||
FontSize = 10.5,
|
FontSize = 10.5,
|
||||||
Foreground = secondaryText,
|
Foreground = secondaryText,
|
||||||
TextWrapping = TextWrapping.Wrap,
|
TextWrapping = TextWrapping.Wrap,
|
||||||
|
|||||||
Reference in New Issue
Block a user