From ed1b8497c6deb41fca616bf57d29d9527aab9092 Mon Sep 17 00:00:00 2001 From: lacvet Date: Sun, 5 Apr 2026 15:55:28 +0900 Subject: [PATCH] =?UTF-8?q?AX=20Agent=20=EC=9E=AC=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=9D=90=EB=A6=84=EA=B3=BC=20=EC=97=94=EC=A7=84=20=EB=A7=88?= =?UTF-8?q?=EA=B0=90=20=EB=AC=B8=EA=B5=AC=EB=A5=BC=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - regenerate/retry가 MessagePanel 직접 삭제 대신 conversation state 수정 후 RenderMessages 경로를 타도록 정리 - AxAgentExecutionEngine에 UI용 마감 helper를 추가해 취소/오류/빈 응답과 Cowork·Code 완료 문구를 정상 한국어 기준으로 정규화 - Paused/Resumed 실행 이벤트는 debug가 아닐 때 기본 타임라인에서 숨겨 Cowork/Code 노이즈를 축소 - README와 docs/DEVELOPMENT.md에 2026-04-05 18:20 (KST) 기준 작업 이력 반영 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0) --- README.md | 5 +++ docs/DEVELOPMENT.md | 5 +++ .../Services/Agent/AxAgentExecutionEngine.cs | 43 ++++++++++++++++++- src/AxCopilot/Views/ChatWindow.xaml.cs | 28 +++++++----- 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0b47501..cc38916 100644 --- a/README.md +++ b/README.md @@ -857,6 +857,11 @@ ow + toggle 시각 언어로 통일했습니다. - 업데이트: 2026-04-05 16:33 (KST) - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 - 업데이트: 2026-04-05 15:16 (KST) +- AX Agent 엔진 마감 2차로 [AxAgentExecutionEngine.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs)에 UI용 마감 helper `FinalizeExecutionContentForUi(...)`, `NormalizeAssistantContentForUi(...)`를 추가했습니다. 취소/오류/빈 응답, Cowork/Code 완료 문구를 깨진 문자열이 아닌 정상 한국어 기준으로 정규화하도록 분리했습니다. +- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에서는 `RegenerateLastAsync()`와 `RetryWithFeedbackAsync(...)`가 더 이상 `MessagePanel.Children.RemoveAt(...)`로 마지막 assistant 버블을 직접 지우지 않고, 대화 상태를 먼저 수정한 뒤 `RenderMessages()`와 자동 스크롤로 다시 그리게 바꿨습니다. 재생성/피드백 재시도 흐름이 세션 상태 기준으로 더 일관되게 닫힙니다. +- 같은 변경에서 `Paused/Resumed` 실행 이벤트는 `debug`가 아닐 때 본문 타임라인에 기본 노출되지 않게 줄여 Cowork/Code 실행 중 시각적 노이즈를 더 낮췄습니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 +- 업데이트: 2026-04-05 18:20 (KST) --- diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 687c9f9..e0f6138 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4616,3 +4616,8 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - 같은 변경에서 계획 이벤트는 기본적으로 큰 카드가 아니라 얇은 요약 pill로만 보이고, `debug` 로그일 때만 `AddPlanningCard(...)`가 펼쳐지도록 바꿨습니다. 문서형 Cowork/Code 작업에서도 기본 노출이 더 최소화됐습니다. - 검증 예정: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` - 업데이트: 2026-04-05 18:08 (KST) +- AX Agent 엔진 마감 2차로 [AxAgentExecutionEngine.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs)에 `FinalizeExecutionContentForUi(...)`, `NormalizeAssistantContentForUi(...)`를 추가해 취소/오류/빈 응답과 Cowork/Code 완료 문구를 UI용 한국어 기준으로 다시 정규화했습니다. 기존 깨진 문자열을 직접 건드리기보다 호출 축을 새 helper로 교체해 위험을 낮췄습니다. +- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에서는 `RegenerateLastAsync()`와 `RetryWithFeedbackAsync(...)`가 마지막 assistant 응답을 더 이상 `MessagePanel.Children.RemoveAt(...)`로 직접 지우지 않고, 세션/대화 모델을 먼저 갱신한 뒤 `RenderMessages(preserveViewport: true)`로 다시 그리게 바꿨습니다. retry/regenerate가 화면 구조보다 conversation state를 우선하도록 정리한 단계입니다. +- 같은 변경에서 `AddAgentEventBanner(...)`는 `Paused/Resumed`를 `debug`가 아닐 때 기본 타임라인에 그리지 않게 바꿔 Cowork/Code 실행 중 흔한 상태 전환 배너 노출을 더 줄였습니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` 경고 0 / 오류 0 +- 업데이트: 2026-04-05 18:20 (KST) diff --git a/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs b/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs index dd343f9..56a0597 100644 --- a/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs +++ b/src/AxCopilot/Services/Agent/AxAgentExecutionEngine.cs @@ -155,7 +155,7 @@ public sealed class AxAgentExecutionEngine string? content, ChatStorageService? storage = null) { - var normalized = NormalizeAssistantContent(conversation, tab, content); + var normalized = NormalizeAssistantContentForUi(conversation, tab, content); if (tab is "Cowork" or "Code") conversation.ShowExecutionHistory = false; @@ -163,6 +163,47 @@ public sealed class AxAgentExecutionEngine return normalized; } + public FinalizedContent FinalizeExecutionContentForUi(string? currentContent, Exception? error = null, bool cancelled = false) + { + if (cancelled) + { + var content = string.IsNullOrWhiteSpace(currentContent) ? "(취소됨)" : currentContent!; + return new FinalizedContent(content, true, "사용자가 작업을 중단했습니다."); + } + + if (error != null) + return new FinalizedContent($"오류: {error.Message}", false, error.Message); + + return new FinalizedContent(currentContent ?? string.Empty, false, null); + } + + public string NormalizeAssistantContentForUi( + ChatConversation conversation, + string runTab, + string? content) + { + if (!string.IsNullOrWhiteSpace(content)) + return content; + + var latestEventSummary = conversation.ExecutionEvents? + .Where(evt => !string.IsNullOrWhiteSpace(evt.Summary)) + .OrderByDescending(evt => evt.Timestamp) + .Select(evt => evt.Summary.Trim()) + .FirstOrDefault(); + + if (!string.IsNullOrWhiteSpace(latestEventSummary)) + { + return runTab switch + { + "Cowork" => $"코워크 작업이 완료되었습니다.\n\n{latestEventSummary}", + "Code" => $"코드 작업이 완료되었습니다.\n\n{latestEventSummary}", + _ => latestEventSummary, + }; + } + + return "(빈 응답)"; + } + public FinalizedContent FinalizeExecutionContent(string? currentContent, Exception? error = null, bool cancelled = false) { if (cancelled) diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index 952441b..a4c3534 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -8581,14 +8581,14 @@ public partial class ChatWindow : Window } catch (OperationCanceledException) { - var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true); + var finalized = _chatEngine.FinalizeExecutionContentForUi(assistantContent, cancelled: true); assistantContent = finalized.Content; draftCancelled = finalized.Cancelled; draftFailure = finalized.FailureReason; } catch (Exception ex) { - var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, ex); + var finalized = _chatEngine.FinalizeExecutionContentForUi(assistantContent, ex); assistantContent = finalized.Content; draftFailure = finalized.FailureReason; ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600); @@ -10393,6 +10393,10 @@ public partial class ChatWindow : Window return; } + if (!string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase) + && evt.Type is AgentEventType.Paused or AgentEventType.Resumed) + return; + // simple 모드: ToolCall은 건너뜀 (ToolResult만 한 줄로 표시) if (logLevel == "simple" && evt.Type == AgentEventType.ToolCall) return; @@ -10799,9 +10803,8 @@ public partial class ChatWindow : Window } } - // UI에서 마지막 AI 응답 제거 - if (MessagePanel.Children.Count > 0) - MessagePanel.Children.RemoveAt(MessagePanel.Children.Count - 1); + RenderMessages(preserveViewport: true); + AutoScrollIfNeeded(); // 재전송 await SendRegenerateAsync(conv); @@ -10913,14 +10916,19 @@ public partial class ChatWindow : Window // 마지막 assistant 메시지 제거 lock (_convLock) { - if (conv.Messages.Count > 0 && conv.Messages[^1].Role == "assistant") + var session = ChatSession; + if (session != null) + { + session.RemoveLastAssistantMessage(_activeTab, _storage); + _currentConversation = session.CurrentConversation; + conv = _currentConversation!; + } + else if (conv.Messages.Count > 0 && conv.Messages[^1].Role == "assistant") + { conv.Messages.RemoveAt(conv.Messages.Count - 1); + } } - // UI에서 마지막 AI 응답 제거 - if (MessagePanel.Children.Count > 0) - MessagePanel.Children.RemoveAt(MessagePanel.Children.Count - 1); - // 피드백을 사용자 메시지로 추가 var feedbackMsg = new ChatMessage {