AX Agent 계획 선행 잔재 제거 및 최종 후속 권유 조건부 완화
이번 커밋은 claude-code와 비교했을 때 AX에 남아 있던 계획 선행 흐름과 과한 최종 후속 권유를 줄이는 데 집중했다. 핵심 변경 사항: - AgentLoopService에서 사용되지 않는 plan prelude/승인용 계획 생성 블록 제거 - FinalReportGate를 review 작업 및 고영향 변경에만 적용하도록 축소 - FinalReportQuality 프롬프트에서 remaining risk/next action은 실제 미해결 사항이 있을 때만 쓰도록 완화 - Cowork 프롬프트에서 document_plan을 기본 선행 단계처럼 밀지 않고 필요 시만 사용하도록 조정 - Code REPORT 단계도 변경 내용과 검증 요약 중심으로 정리하고 미해결 리스크만 선택적으로 언급하도록 수정 문서 반영: - README.md, docs/DEVELOPMENT.md에 2026-04-10 00: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:
@@ -239,9 +239,6 @@ public partial class AgentLoopService
|
||||
var recentTaskRetryQuality = TryGetRecentTaskRetryQuality(taskPolicy.TaskType);
|
||||
maxRetry = ComputeQualityAwareMaxRetry(maxRetry, recentTaskRetryQuality, taskPolicy.TaskType);
|
||||
|
||||
// 플랜 prelude는 현재 정책상 비활성
|
||||
var shouldGeneratePlanPrelude = false;
|
||||
|
||||
var context = BuildContext();
|
||||
InjectTaskTypeGuidance(messages, taskPolicy);
|
||||
InjectExplorationScopeGuidance(messages, explorationState.Scope);
|
||||
@@ -340,148 +337,6 @@ public partial class AgentLoopService
|
||||
workFolder = context.WorkFolder
|
||||
}));
|
||||
|
||||
// ── 과거 plan mode 잔재. 현재 정책상 비활성 ──
|
||||
if (shouldGeneratePlanPrelude)
|
||||
{
|
||||
iteration++;
|
||||
EmitEvent(AgentEventType.Thinking, "", "실행 계획 생성 중...");
|
||||
|
||||
// 계획 생성 전용 시스템 지시를 임시 추가
|
||||
var planInstruction = new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content = "[System] 도구를 호출하지 마세요. 먼저 실행 계획을 번호 매긴 단계로 작성하세요. " +
|
||||
"각 단계에 사용할 도구와 대상을 구체적으로 명시하세요. " +
|
||||
"계획만 제시하고 실행은 하지 마세요."
|
||||
};
|
||||
messages.Add(planInstruction);
|
||||
|
||||
// 도구 없이 텍스트만 요청
|
||||
string planText;
|
||||
try
|
||||
{
|
||||
planText = await _llm.SendAsync(messages, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EmitEvent(AgentEventType.Error, "", $"LLM 오류: {ex.Message}");
|
||||
return $"⚠ LLM 오류: {ex.Message}";
|
||||
}
|
||||
|
||||
// 계획 지시 메시지 제거 (실제 실행 시 혼란 방지)
|
||||
messages.Remove(planInstruction);
|
||||
|
||||
// 계획 추출
|
||||
planSteps = TaskDecomposer.ExtractSteps(planText);
|
||||
planExtracted = true;
|
||||
|
||||
if (planSteps.Count > 0)
|
||||
{
|
||||
EmitEvent(AgentEventType.Planning, "", $"작업 계획: {planSteps.Count}단계",
|
||||
steps: planSteps);
|
||||
|
||||
// 사용자 승인 대기
|
||||
if (UserDecisionCallback != null)
|
||||
{
|
||||
EmitEvent(
|
||||
AgentEventType.Decision,
|
||||
"",
|
||||
$"계획 확인 대기 · {planSteps.Count}단계",
|
||||
steps: planSteps);
|
||||
|
||||
var decision = await UserDecisionCallback(
|
||||
planText,
|
||||
new List<string> { "승인", "수정 요청", "취소" });
|
||||
EmitPlanDecisionResultEvent(decision, planSteps);
|
||||
|
||||
if (decision == "취소")
|
||||
{
|
||||
EmitEvent(AgentEventType.Complete, "", "작업이 중단되었습니다");
|
||||
return "작업이 중단되었습니다.";
|
||||
}
|
||||
else if (TryParseApprovedPlanDecision(decision, out var approvedPlanText, out var approvedPlanSteps))
|
||||
{
|
||||
planText = approvedPlanText;
|
||||
planSteps = approvedPlanSteps;
|
||||
}
|
||||
else if (decision != null && decision != "승인")
|
||||
{
|
||||
// 수정 요청 — 피드백으로 계획 재생성
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = planText });
|
||||
messages.Add(new ChatMessage { Role = "user", Content = decision + "\n위 피드백을 반영하여 실행 계획을 다시 작성하세요." });
|
||||
|
||||
// 재생성 루프 (최대 3회)
|
||||
for (int retry = 0; retry < 3; retry++)
|
||||
{
|
||||
try { planText = await _llm.SendAsync(messages, ct); }
|
||||
catch { break; }
|
||||
|
||||
planSteps = TaskDecomposer.ExtractSteps(planText);
|
||||
if (planSteps.Count > 0)
|
||||
{
|
||||
EmitEvent(AgentEventType.Planning, "", $"수정된 계획: {planSteps.Count}단계",
|
||||
steps: planSteps);
|
||||
}
|
||||
|
||||
EmitEvent(
|
||||
AgentEventType.Decision,
|
||||
"",
|
||||
$"수정 계획 확인 대기 · {planSteps.Count}단계",
|
||||
steps: planSteps);
|
||||
|
||||
decision = await UserDecisionCallback(
|
||||
planText,
|
||||
new List<string> { "승인", "수정 요청", "취소" });
|
||||
EmitPlanDecisionResultEvent(decision, planSteps);
|
||||
|
||||
if (decision == "취소")
|
||||
{
|
||||
EmitEvent(AgentEventType.Complete, "", "작업이 중단되었습니다");
|
||||
return "작업이 중단되었습니다.";
|
||||
}
|
||||
if (TryParseApprovedPlanDecision(decision, out var revisedPlanText, out var revisedPlanSteps))
|
||||
{
|
||||
planText = revisedPlanText;
|
||||
planSteps = revisedPlanSteps;
|
||||
break;
|
||||
}
|
||||
if (decision == null || decision == "승인") break;
|
||||
|
||||
// 재수정
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = planText });
|
||||
messages.Add(new ChatMessage { Role = "user", Content = decision + "\n위 피드백을 반영하여 실행 계획을 다시 작성하세요." });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 승인된 계획을 컨텍스트에 포함하여 실행 유도
|
||||
// 도구 호출을 명확히 강제하여 텍스트 응답만 반환하는 경우 방지
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = planText });
|
||||
|
||||
// 1차 계획의 단계들을 document_plan의 sections_hint로 전달하도록 지시
|
||||
// → BuildSections() 하드코딩 대신 LLM이 잡은 섹션 구조가 문서에 반영됨
|
||||
var planSectionsHint = planSteps.Count > 0
|
||||
? string.Join(", ", planSteps)
|
||||
: "";
|
||||
var sectionInstruction = !string.IsNullOrEmpty(planSectionsHint)
|
||||
? $"document_plan 도구를 호출할 때 sections_hint 파라미터에 위 계획의 섹션/단계를 그대로 넣으세요: \"{planSectionsHint}\""
|
||||
: "";
|
||||
|
||||
messages.Add(new ChatMessage { Role = "user",
|
||||
Content = "계획이 승인되었습니다. 지금 즉시 1단계부터 도구(tool)를 호출하여 실행을 시작하세요. " +
|
||||
"텍스트로 설명하지 말고 반드시 도구를 호출하세요." +
|
||||
(string.IsNullOrEmpty(sectionInstruction) ? "" : "\n" + sectionInstruction) });
|
||||
}
|
||||
else
|
||||
{
|
||||
// 계획 추출 실패 — assistant 응답으로 추가하고 일반 모드로 진행
|
||||
if (!string.IsNullOrEmpty(planText))
|
||||
messages.Add(new ChatMessage { Role = "assistant", Content = planText });
|
||||
}
|
||||
}
|
||||
|
||||
while (iteration < maxIterations && !ct.IsCancellationRequested)
|
||||
{
|
||||
iteration++;
|
||||
@@ -3538,8 +3393,8 @@ public partial class AgentLoopService
|
||||
private static string BuildFinalReportQualityPrompt(TaskTypePolicy taskPolicy, bool highImpact)
|
||||
{
|
||||
var taskLine = taskPolicy.FinalReportTaskLine;
|
||||
var riskLine = highImpact
|
||||
? "고영향 변경이므로 남은 리스크나 추가 확인 필요 사항도 반드시 적으세요.\n"
|
||||
var riskLine = taskPolicy.IsReviewTask || highImpact
|
||||
? "남은 리스크나 추가 확인 필요 사항이 실제로 남아 있을 때만 짧게 적으세요.\n"
|
||||
: "";
|
||||
|
||||
return "[System:FinalReportQuality] 최종 답변을 더 구조적으로 정리하세요.\n" +
|
||||
@@ -3551,7 +3406,7 @@ public partial class AgentLoopService
|
||||
"6. review 작업이면 이슈별로 수정 완료/미수정 상태를 분리해 적으세요\n" +
|
||||
taskLine +
|
||||
riskLine +
|
||||
"가능하면 짧고 명확하게 요약하세요.";
|
||||
"후속 권유는 실제 미해결 위험이나 추가 확인이 남았을 때만 포함하세요. 가능하면 짧고 명확하게 요약하세요.";
|
||||
}
|
||||
|
||||
private static string BuildFinalReportQualityPrompt(string taskType, bool highImpact)
|
||||
|
||||
@@ -55,9 +55,11 @@ public partial class AgentLoopService
|
||||
var hasDiffEvidence = HasDiffEvidenceAfterLastModification(messages);
|
||||
var hasRecentBuildOrTestEvidence = HasBuildOrTestEvidenceAfterLastModification(messages);
|
||||
var hasSuccessfulBuildAndTestEvidence = HasSuccessfulBuildAndTestAfterLastModification(messages);
|
||||
var hasLightweightCompletionEvidence = hasCodeVerificationEvidence
|
||||
|| hasDiffEvidence
|
||||
|| hasRecentBuildOrTestEvidence;
|
||||
if (executionPolicy.CodeVerificationGateMaxRetries > 0
|
||||
&& !hasCodeVerificationEvidence
|
||||
&& !(hasDiffEvidence && hasRecentBuildOrTestEvidence && !requireHighImpactCodeVerification)
|
||||
&& !(requireHighImpactCodeVerification ? hasCodeVerificationEvidence : hasLightweightCompletionEvidence)
|
||||
&& runState.CodeVerificationGateRetry < executionPolicy.CodeVerificationGateMaxRetries)
|
||||
{
|
||||
runState.CodeVerificationGateRetry++;
|
||||
@@ -93,9 +95,12 @@ public partial class AgentLoopService
|
||||
return true;
|
||||
}
|
||||
|
||||
var hasBlockingCodeEvidenceGap = !hasCodeVerificationEvidence
|
||||
var hasBlockingCodeEvidenceGap = !(requireHighImpactCodeVerification ? hasCodeVerificationEvidence : hasLightweightCompletionEvidence)
|
||||
|| (requireHighImpactCodeVerification && !hasSuccessfulBuildAndTestEvidence);
|
||||
var shouldRequestStructuredFinalReport =
|
||||
taskPolicy.IsReviewTask || requireHighImpactCodeVerification;
|
||||
if (executionPolicy.FinalReportGateMaxRetries > 0
|
||||
&& shouldRequestStructuredFinalReport
|
||||
&& !hasBlockingCodeEvidenceGap
|
||||
&& !HasSufficientFinalReportEvidence(textResponse, taskPolicy, requireHighImpactCodeVerification, messages)
|
||||
&& runState.FinalReportGateRetry < executionPolicy.FinalReportGateMaxRetries)
|
||||
|
||||
Reference in New Issue
Block a user