모델 프로파일 기반 Cowork/Code 루프와 진행 UX 고도화 반영

- 등록 모델 실행 프로파일을 검증 게이트, 문서 fallback, post-tool verification까지 확장 적용

- Cowork/Code 진행 카드에 계획/도구/검증/압축/폴백/재시도 단계 메타를 추가해 대기 상태 가시성 강화

- OpenAI/vLLM tool 요청에 병렬 도구 호출 힌트를 추가하고 회귀 프롬프트 문서를 프로파일 기준으로 전면 정리

- 검증: 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-08 13:41:57 +09:00
parent b391dfdfb3
commit a2c952879d
552 changed files with 8094 additions and 13595 deletions

View File

@@ -147,7 +147,19 @@ public sealed class AxAgentExecutionEngine
if (session != null)
{
session.AppendMessage(tab, assistant, storage);
// session.CurrentConversation이 전달된 conversation과 다른 경우 (새 대화 시작 등),
// session을 통하지 않고 conversation에 직접 추가하여 새 대화가 오염되지 않도록 함.
if (session.CurrentConversation == null ||
string.Equals(session.CurrentConversation.Id, conversation.Id, StringComparison.Ordinal))
{
session.AppendMessage(tab, assistant, storage);
}
else
{
conversation.Messages.Add(assistant);
conversation.UpdatedAt = DateTime.Now;
try { storage?.Save(conversation); } catch { }
}
return assistant;
}
@@ -197,23 +209,7 @@ public sealed class AxAgentExecutionEngine
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 "(빈 응답)";
return BuildFallbackCompletionMessage(conversation, runTab);
}
public FinalizedContent FinalizeExecutionContent(string? currentContent, Exception? error = null, bool cancelled = false)
@@ -276,23 +272,59 @@ public sealed class AxAgentExecutionEngine
if (!string.IsNullOrWhiteSpace(content))
return content;
var latestEventSummary = conversation.ExecutionEvents?
.Where(evt => !string.IsNullOrWhiteSpace(evt.Summary))
return BuildFallbackCompletionMessage(conversation, runTab);
}
/// <summary>
/// LLM 응답이 비어있을 때 실행 이벤트에서 의미 있는 완료 메시지를 구성합니다.
/// UserPromptSubmit/Paused/Resumed 같은 내부 운영 이벤트는 제외합니다.
/// </summary>
private static string BuildFallbackCompletionMessage(ChatConversation conversation, string runTab)
{
static bool IsSignificantEventType(string t)
=> !string.Equals(t, "UserPromptSubmit", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(t, "Paused", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(t, "Resumed", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(t, "SessionStart", StringComparison.OrdinalIgnoreCase);
var completionLine = runTab switch
{
"Cowork" => "코워크 작업이 완료되었습니다.",
"Code" => "코드 작업이 완료되었습니다.",
_ => null,
};
// 파일 경로가 있는 이벤트를 최우선으로 — 산출물 파일을 명시적으로 표시
var artifactEvent = conversation.ExecutionEvents?
.Where(evt => !string.IsNullOrWhiteSpace(evt.FilePath) && IsSignificantEventType(evt.Type))
.OrderByDescending(evt => evt.Timestamp)
.FirstOrDefault();
if (artifactEvent != null)
{
var fileLine = string.IsNullOrWhiteSpace(artifactEvent.Summary)
? $"생성된 파일: {artifactEvent.FilePath}"
: $"{artifactEvent.Summary}\n경로: {artifactEvent.FilePath}";
return completionLine != null
? $"{completionLine}\n\n{fileLine}"
: fileLine;
}
// 파일 없으면 가장 최근 의미 있는 이벤트 요약 사용
var latestSummary = conversation.ExecutionEvents?
.Where(evt => !string.IsNullOrWhiteSpace(evt.Summary) && IsSignificantEventType(evt.Type))
.OrderByDescending(evt => evt.Timestamp)
.Select(evt => evt.Summary.Trim())
.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(latestEventSummary))
if (!string.IsNullOrWhiteSpace(latestSummary))
{
return runTab switch
{
"Cowork" => $"코워크 작업이 완료되었습니다.\n\n{latestEventSummary}",
"Code" => $"코드 작업이 완료되었습니다.\n\n{latestEventSummary}",
_ => latestEventSummary,
};
return completionLine != null
? $"{completionLine}\n\n{latestSummary}"
: latestSummary;
}
return "(빈 응답)";
return completionLine ?? "(빈 응답)";
}
private static ChatMessage CloneMessage(ChatMessage source)