AX Agent loop 정책을 분리하고 transcript 가상화·성능 계측 구조를 강화한다
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- AgentLoop 검증/문서/compact 정책 메서드를 partial 파일로 분리해 loop 본문의 책임을 줄임 - transcript 렌더에 cache pruning과 deferred scrolling을 적용해 긴 세션의 UI 부담을 낮춤 - AgentPerformanceLogService를 추가해 transcript 렌더와 agent loop 실행 요약을 perf 로그로 남김 - README와 DEVELOPMENT 문서에 2026-04-09 10:08 (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:
@@ -0,0 +1,233 @@
|
||||
using AxCopilot.Models;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
public partial class AgentLoopService
|
||||
{
|
||||
private void ApplyCodeQualityFollowUpTransition(
|
||||
LlmService.ContentBlock call,
|
||||
ToolResult result,
|
||||
List<ChatMessage> messages,
|
||||
TaskTypePolicy taskPolicy,
|
||||
ref bool requireHighImpactCodeVerification,
|
||||
ref string? lastModifiedCodeFilePath)
|
||||
{
|
||||
var highImpactCodeChange = IsHighImpactCodeModification(ActiveTab ?? "", call.ToolName, result);
|
||||
if (ShouldInjectCodeQualityFollowUp(ActiveTab ?? "", call.ToolName, result))
|
||||
{
|
||||
requireHighImpactCodeVerification = highImpactCodeChange;
|
||||
lastModifiedCodeFilePath = result.FilePath;
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = BuildCodeQualityFollowUpPrompt(
|
||||
call.ToolName,
|
||||
result,
|
||||
highImpactCodeChange,
|
||||
HasAnyBuildOrTestEvidence(messages),
|
||||
taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", highImpactCodeChange
|
||||
? "고영향 코드 변경으로 분류돼 참조 검증과 build/test 검증을 더 엄격하게 이어갑니다."
|
||||
: "코드 변경 후 build/test/diff 검증을 이어갑니다.");
|
||||
}
|
||||
else if (HasCodeVerificationEvidenceAfterLastModification(messages, requireHighImpactCodeVerification))
|
||||
{
|
||||
requireHighImpactCodeVerification = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryApplyCodeCompletionGateTransition(
|
||||
List<ChatMessage> messages,
|
||||
string? textResponse,
|
||||
TaskTypePolicy taskPolicy,
|
||||
bool requireHighImpactCodeVerification,
|
||||
int totalToolCalls,
|
||||
RunState runState,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy)
|
||||
{
|
||||
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase) || totalToolCalls <= 0)
|
||||
return false;
|
||||
|
||||
var hasCodeVerificationEvidence = HasCodeVerificationEvidenceAfterLastModification(
|
||||
messages,
|
||||
requireHighImpactCodeVerification);
|
||||
if (executionPolicy.CodeVerificationGateMaxRetries > 0
|
||||
&& !hasCodeVerificationEvidence
|
||||
&& runState.CodeVerificationGateRetry < executionPolicy.CodeVerificationGateMaxRetries)
|
||||
{
|
||||
runState.CodeVerificationGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = requireHighImpactCodeVerification
|
||||
? "[System:CodeQualityGate] 공용/핵심 코드 변경 이후 검증 근거가 부족합니다. 종료하지 말고 file_read, grep/glob, git diff, build/test까지 확인한 뒤에만 마무리하세요."
|
||||
: "[System:CodeQualityGate] 마지막 코드 수정 이후 build/test/file_read/diff 근거가 부족합니다. 종료하지 말고 검증 근거를 보강한 뒤에만 마무리하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", requireHighImpactCodeVerification
|
||||
? "핵심 코드 변경의 검증 근거가 부족해 추가 검증을 진행합니다..."
|
||||
: "코드 결과 검증 근거가 부족해 추가 검증을 진행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requireHighImpactCodeVerification
|
||||
&& executionPolicy.HighImpactBuildTestGateMaxRetries > 0
|
||||
&& !HasSuccessfulBuildAndTestAfterLastModification(messages)
|
||||
&& runState.HighImpactBuildTestGateRetry < executionPolicy.HighImpactBuildTestGateMaxRetries)
|
||||
{
|
||||
runState.HighImpactBuildTestGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[System:HighImpactBuildTestGate] 핵심 코드 변경입니다. 종료하지 말고 build_run과 test_loop를 모두 실행해 성공 근거를 확보한 뒤에만 마무리하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "핵심 변경이라 build+test 성공 근거를 모두 확보할 때까지 진행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (executionPolicy.FinalReportGateMaxRetries > 0
|
||||
&& !HasSufficientFinalReportEvidence(textResponse, taskPolicy, requireHighImpactCodeVerification, messages)
|
||||
&& runState.FinalReportGateRetry < executionPolicy.FinalReportGateMaxRetries)
|
||||
{
|
||||
runState.FinalReportGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = BuildFinalReportQualityPrompt(taskPolicy, requireHighImpactCodeVerification)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "최종 보고에 변경·검증·리스크 요약이 부족해 한 번 더 정리합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryApplyCodeDiffEvidenceGateTransition(
|
||||
List<ChatMessage> messages,
|
||||
string? textResponse,
|
||||
RunState runState,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy)
|
||||
{
|
||||
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (executionPolicy.CodeDiffGateMaxRetries <= 0 || runState.CodeDiffGateRetry >= executionPolicy.CodeDiffGateMaxRetries)
|
||||
return false;
|
||||
|
||||
if (HasDiffEvidenceAfterLastModification(messages))
|
||||
return false;
|
||||
|
||||
runState.CodeDiffGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[System:CodeDiffGate] 코드 변경 이후 diff 근거가 부족합니다. git_tool 도구로 변경 파일과 핵심 diff를 먼저 확인하고 요약하세요. 지금 즉시 git_tool 도구를 호출하세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "코드 diff 근거가 부족해 git diff 검증을 추가합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryApplyRecentExecutionEvidenceGateTransition(
|
||||
List<ChatMessage> messages,
|
||||
string? textResponse,
|
||||
TaskTypePolicy taskPolicy,
|
||||
RunState runState,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy)
|
||||
{
|
||||
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (executionPolicy.RecentExecutionGateMaxRetries <= 0 || runState.RecentExecutionGateRetry >= executionPolicy.RecentExecutionGateMaxRetries)
|
||||
return false;
|
||||
|
||||
if (!HasAnyBuildOrTestEvidence(messages))
|
||||
return false;
|
||||
|
||||
if (HasBuildOrTestEvidenceAfterLastModification(messages))
|
||||
return false;
|
||||
|
||||
runState.RecentExecutionGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = BuildRecentExecutionEvidencePrompt(taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "최근 수정 이후 실행 근거가 부족해 build/test 재검증을 수행합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryApplyExecutionSuccessGateTransition(
|
||||
List<ChatMessage> messages,
|
||||
string? textResponse,
|
||||
TaskTypePolicy taskPolicy,
|
||||
RunState runState,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy)
|
||||
{
|
||||
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (executionPolicy.ExecutionSuccessGateMaxRetries <= 0 || runState.ExecutionSuccessGateRetry >= executionPolicy.ExecutionSuccessGateMaxRetries)
|
||||
return false;
|
||||
|
||||
if (!HasAnyBuildOrTestAttempt(messages))
|
||||
return false;
|
||||
|
||||
if (HasAnyBuildOrTestEvidence(messages))
|
||||
return false;
|
||||
|
||||
runState.ExecutionSuccessGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = BuildExecutionSuccessGatePrompt(taskPolicy)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", "실패한 실행 근거만 있어 build/test 성공 결과를 다시 검증합니다...");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryApplyTerminalEvidenceGateTransition(
|
||||
List<ChatMessage> messages,
|
||||
string? textResponse,
|
||||
TaskTypePolicy taskPolicy,
|
||||
string userQuery,
|
||||
int totalToolCalls,
|
||||
string? lastArtifactFilePath,
|
||||
RunState runState,
|
||||
int retryMax)
|
||||
{
|
||||
if (totalToolCalls <= 0)
|
||||
return false;
|
||||
|
||||
if (ShouldSkipTerminalEvidenceGateForAnalysisQuery(userQuery, taskPolicy))
|
||||
return false;
|
||||
|
||||
if (runState.TerminalEvidenceGateRetry >= retryMax)
|
||||
return false;
|
||||
|
||||
if (HasTerminalProgressEvidence(taskPolicy, messages, lastArtifactFilePath))
|
||||
return false;
|
||||
|
||||
runState.TerminalEvidenceGateRetry++;
|
||||
if (!string.IsNullOrEmpty(textResponse))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = BuildTerminalEvidenceGatePrompt(taskPolicy, lastArtifactFilePath)
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", $"종료 전 실행 증거가 부족해 보강 단계를 진행합니다 ({runState.TerminalEvidenceGateRetry}/{retryMax})");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user