AX Agent 진행 시간·글로우 경로 정리 및 최근 로컬 변경 일괄 반영

- AX Agent 스트리밍 경과 시간을 공용 helper로 통일해 비정상적인 수천만 시간 표시를 방지함

- 채팅 입력창 글로우를 런처와 같은 표시/숨김 중심의 얇은 외곽 글로우로 정리하고 런처 글로우 설정은 일반 설정에 유지함

- README와 DEVELOPMENT 문서를 2026-04-08 12:02 (KST) 기준으로 갱신하고 Release 빌드 경고 0 / 오류 0을 확인함
This commit is contained in:
2026-04-08 23:20:53 +09:00
parent 6e99837a4c
commit 1b4a2bfb1c
24 changed files with 1103 additions and 173 deletions

View File

@@ -539,14 +539,15 @@ public partial class AgentLoopService
sendMessages = [.. messages, new ChatMessage
{
Role = "user",
Content = "[TOOL_REQUIRED] 지금 즉시 도구를 1개 이상 호출하세요. 텍스트만 반환하면 거부됩니다. " +
"Call at least one tool RIGHT NOW. Text-only response is rejected."
Content = "[TOOL_REQUIRED] 지금 즉시 <tool_call> 형식으로 도구를 호출하세요. 텍스트만 반환하면 거부됩니다.\n" +
"Output format:\n<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>"
}];
}
// 워크플로우 상세 로그: LLM 요청
llmCallSw.Restart();
var (_, currentModel) = _llm.GetCurrentModelInfo();
WorkflowLogService.SetCallContext(_conversationId, _currentRunId, iteration);
WorkflowLogService.LogLlmRequest(_conversationId, _currentRunId, iteration,
currentModel, sendMessages.Count, activeTools.Count, forceFirst);
var streamedTextPreview = new StringBuilder();
@@ -807,17 +808,14 @@ public partial class AgentLoopService
"[System:ToolCallRequired] " +
"⚠ 경고: 이전 응답에서 도구를 호출하지 않았습니다. " +
"텍스트 설명만 반환하는 것은 허용되지 않습니다. " +
"지금 즉시 도구를 1개 이상 호출하세요. " +
"할 말이 있다면 도구 호출 이후에 하세요 — 도구 호출 전 설명 금지. " +
"한 응답에서 여러 도구를 동시에 호출할 수 있고, 그렇게 해야 합니다. " +
$"지금 사용 가능한 도구: {activeToolPreview}",
"지금 즉시 아래 형식으로 도구를 호출하세요:\n" +
"<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n" +
$"사용 가능한 도구: {activeToolPreview}",
_ =>
"[System:ToolCallRequired] " +
"🚨 최종 경고: 도구를 계속 호출하지 않고 있습니다. 이것이 마지막 기회입니다. " +
"지금 응답은 반드시 도구 호출만 포함해야 합니다. 텍스트는 한 글자도 쓰지 마세요. " +
"작업을 완료하려면 도구를 호출하는 것 외에 다른 방법이 없습니다. " +
"도구 이름을 모른다면 아래 목록에서 골라 즉시 호출하세요. " +
"여러 도구를 한꺼번에 호출할 수 있습니다 — 지금 그렇게 하세요. " +
"텍스트는 한 글자도 쓰지 마세요. 반드시 아래 형식으로 도구를 호출하세요:\n" +
"<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n" +
$"반드시 사용해야 할 도구 목록: {activeToolPreview}"
};
messages.Add(new ChatMessage { Role = "user", Content = recoveryContent });
@@ -850,15 +848,13 @@ public partial class AgentLoopService
{
1 =>
"[System:ToolCallRequired] 계획을 세웠지만 도구를 호출하지 않았습니다. " +
"계획은 이미 수립되었으므로 지금 당장 실행 단계로 넘어가세요. " +
"텍스트 설명 없이 계획의 첫 번째 단계를 도구(tool call)로 즉시 실행하세요. " +
"한 응답에서 여러 도구를 동시에 호출할 수 있습니다. " +
"지금 당장 실행하세요. 아래 형식으로 도구를 호출하세요:\n" +
"<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n" +
$"사용 가능한 도구: {planToolList}",
_ =>
"[System:ToolCallRequired] 🚨 도구 호출 없이 계획만 반복하고 있습니다. " +
"이제 계획 설명은 완전히 금지됩니다. 오직 도구 호출만 하세요. " +
"지금 이 응답에 텍스트를 포함하지 마세요. 도구만 호출하세요. " +
"독립적인 작업은 한 번에 여러 도구를 병렬 호출하세요. " +
"텍스트를 한 글자도 쓰지 마세요. 오직 아래 형식의 도구 호출만 출력하세요:\n" +
"<tool_call>\n{\"name\": \"TOOL_NAME\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n" +
$"사용 가능한 도구: {planToolList}"
};
messages.Add(new ChatMessage { Role = "user", Content = planRecoveryContent });
@@ -884,7 +880,8 @@ public partial class AgentLoopService
messages.Add(new ChatMessage { Role = "user",
Content = "html_create 도구를 호출하지 않았습니다. " +
"document_plan 결과의 body 골격을 바탕으로 각 섹션에 충분한 내용을 채워서 " +
"html_create 도구를 지금 즉시 호출하세요. 설명 없이 도구 호출하세요." });
"지금 즉시 아래 형식으로 호출하세요:\n" +
"<tool_call>\n{\"name\": \"html_create\", \"arguments\": {\"file_name\": \"...\", \"html_body\": \"...\"}}\n</tool_call>" });
EmitEvent(AgentEventType.Thinking, "", $"html_create 미호출 재시도 {postDocumentPlanRetry}/{documentPlanRetryMax}...");
continue; // 루프 재시작
}
@@ -1475,6 +1472,26 @@ public partial class AgentLoopService
{
failedToolHistogram.TryGetValue(effectiveCall.ToolName, out var failedCount);
failedToolHistogram[effectiveCall.ToolName] = failedCount + 1;
// 같은 도구가 5회 이상 실패하면 해당 도구를 포기하고 LLM에 알림
if (failedCount + 1 >= 5)
{
var abortMsg = $"도구 '{effectiveCall.ToolName}'이(가) {failedCount + 1}회 실패했습니다. 이 도구를 더 이상 호출하지 마세요. 다른 방법을 시도하거나 사용자에게 결과를 보고하세요.";
EmitEvent(AgentEventType.Error, effectiveCall.ToolName, abortMsg);
messages.Add(LlmService.CreateToolResultMessage(
effectiveCall.ToolId, effectiveCall.ToolName, abortMsg));
messages.Add(new ChatMessage { Role = "user", Content = abortMsg });
continue;
}
// 전체 실패 횟수가 총 도구 호출의 60% 이상이면 조기 중단
var totalFails = failedToolHistogram.Values.Sum();
if (totalToolCalls > 6 && totalFails > totalToolCalls * 0.6)
{
EmitEvent(AgentEventType.Error, "",
$"전체 도구 호출 중 실패율이 높아 작업을 중단합니다 (실패 {totalFails}/{totalToolCalls})");
return $"도구 실행 실패율이 높아 작업을 중단했습니다. {totalFails}개 실패 / {totalToolCalls}개 호출. 요청을 다시 시도하거나 작업 방식을 변경해 주세요.";
}
}
// UI 스레드가 이벤트를 렌더링할 시간 확보