AX Agent 실행 이벤트 저장을 배치형으로 조정
Some checks failed
Release Gate / gate (push) Has been cancelled

- execution event와 agent run 기록이 들어올 때마다 즉시 저장하지 않고 conversationPersistTimer로 220ms 단위 지연 저장하도록 변경함

- Cowork/Code 연속 이벤트 구간의 디스크 I/O를 줄여 체감 끊김과 잦은 저장 부하를 낮추는 방향으로 정리함

- 검증: 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-05 12:39:54 +09:00
parent d24596a8ea
commit abc355c451
3 changed files with 48 additions and 4 deletions

View File

@@ -85,6 +85,7 @@ public partial class ChatWindow : Window
private readonly DispatcherTimer _inputUiRefreshTimer;
private readonly DispatcherTimer _executionHistoryRenderTimer;
private readonly DispatcherTimer _taskSummaryRefreshTimer;
private readonly DispatcherTimer _conversationPersistTimer;
private CancellationTokenSource? _gitStatusRefreshCts;
private int _displayedLength; // 현재 화면에 표시된 글자 수
private ResourceDictionary? _agentThemeDictionary;
@@ -119,6 +120,7 @@ public partial class ChatWindow : Window
private int _sessionPostCompactionPromptTokens;
private int _sessionPostCompactionCompletionTokens;
private bool _pendingExecutionHistoryAutoScroll;
private readonly Dictionary<string, ChatConversation> _pendingConversationPersists = new(StringComparer.OrdinalIgnoreCase);
private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg)
{
if (button?.Content is not string text)
@@ -251,6 +253,12 @@ public partial class ChatWindow : Window
_taskSummaryRefreshTimer.Stop();
UpdateTaskSummaryIndicators();
};
_conversationPersistTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(220) };
_conversationPersistTimer.Tick += (_, _) =>
{
_conversationPersistTimer.Stop();
FlushPendingConversationPersists();
};
KeyDown += ChatWindow_KeyDown;
UpdateConversationFailureFilterUi();
@@ -8557,6 +8565,7 @@ public partial class ChatWindow : Window
_executionHistoryRenderTimer.Stop();
_pendingExecutionHistoryAutoScroll = false;
_taskSummaryRefreshTimer.Stop();
_conversationPersistTimer.Stop();
HideStickyProgress();
StopRainbowGlow();
_activeStreamText = null;
@@ -8626,6 +8635,30 @@ public partial class ChatWindow : Window
_taskSummaryRefreshTimer.Start();
}
private void ScheduleConversationPersist(ChatConversation? conversation)
{
if (conversation == null || string.IsNullOrWhiteSpace(conversation.Id))
return;
_pendingConversationPersists[conversation.Id] = conversation;
_conversationPersistTimer.Stop();
_conversationPersistTimer.Start();
}
private void FlushPendingConversationPersists()
{
if (_pendingConversationPersists.Count == 0)
return;
foreach (var conversation in _pendingConversationPersists.Values.ToList())
{
try { _storage.Save(conversation); }
catch (Exception ex) { Services.LogService.Debug($"대화 지연 저장 실패: {ex.Message}"); }
}
_pendingConversationPersists.Clear();
}
// ─── 코워크 에이전트 지원 ────────────────────────────────────────────
private string BuildCoworkSystemPrompt()
@@ -9106,15 +9139,18 @@ public partial class ChatWindow : Window
var normalizedTarget = NormalizeTabName(targetTab);
var normalizedActive = NormalizeTabName(_activeTab);
ChatConversation updatedConversation;
if (string.Equals(normalizedTarget, normalizedActive, StringComparison.OrdinalIgnoreCase))
{
_currentConversation = session.AppendAgentRun(normalizedTarget, evt, status, summary, _storage);
_currentConversation = updatedConversation = session.AppendAgentRun(normalizedTarget, evt, status, summary, null);
ScheduleConversationPersist(updatedConversation);
return;
}
var activeSnapshot = _currentConversation;
var previousSessionConversation = session.CurrentConversation;
session.AppendAgentRun(normalizedTarget, evt, status, summary, _storage);
updatedConversation = session.AppendAgentRun(normalizedTarget, evt, status, summary, null);
ScheduleConversationPersist(updatedConversation);
if (activeSnapshot != null && string.Equals(NormalizeTabName(activeSnapshot.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
{
@@ -9156,15 +9192,18 @@ public partial class ChatWindow : Window
var normalizedTarget = NormalizeTabName(targetTab);
var normalizedActive = NormalizeTabName(_activeTab);
ChatConversation updatedConversation;
if (string.Equals(normalizedTarget, normalizedActive, StringComparison.OrdinalIgnoreCase))
{
_currentConversation = session.AppendExecutionEvent(normalizedTarget, evt, _storage);
_currentConversation = updatedConversation = session.AppendExecutionEvent(normalizedTarget, evt, null);
ScheduleConversationPersist(updatedConversation);
return;
}
var activeSnapshot = _currentConversation;
var previousSessionConversation = session.CurrentConversation;
session.AppendExecutionEvent(normalizedTarget, evt, _storage);
updatedConversation = session.AppendExecutionEvent(normalizedTarget, evt, null);
ScheduleConversationPersist(updatedConversation);
if (activeSnapshot != null && string.Equals(NormalizeTabName(activeSnapshot.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
{