AX Agent 루프 마감 복구 규칙을 서비스화하고 회귀 검증을 고정합니다
- AgentLoopService 안에 섞여 있던 도구 미호출 루프/계획 미실행 복구 문구와 재시도 규칙을 AgentLoopNoToolResponseRecoveryService로 분리했습니다. - probe-only 즉시 복구, 최종 경고 전환, 계획 미실행 재시도 규칙을 별도 테스트로 고정해 루프 마감 품질을 높였습니다. - README.md, docs/DEVELOPMENT.md, docs/NEXT_ROADMAP.md에 2026-04-15 10:57 (KST) 기준 변경 이력과 검증 결과를 반영했습니다. - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_closeout\\ -p:IntermediateOutputPath=obj\\verify_closeout\\ (경고 0 / 오류 0) - 검증: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentLoopNoToolResponseRecoveryServiceTests|AgentLoopIterationPreparationServiceTests|AgentLoopLlmRequestPreparationServiceTests|AgentQueuedCommandProjectorTests|AgentMessageInvariantHelperTests|AgentToolResultBudgetTests|AgentQueryContextBuilderTests|ChatStorageServiceTests|HtmlSkillGoldenReportTests|PptxSkillGoldenDeckTests|DocxSkillGoldenDocumentTests|ExcelSkillGoldenWorkbookTests -p:OutputPath=bin\\verify_closeout_tests\\ -p:IntermediateOutputPath=obj\\verify_closeout_tests\\ (통과 27)
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class AgentLoopNoToolResponseRecoveryServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildNoToolLoopRecovery_ShouldTriggerImmediatelyForProbeOnlyRuns()
|
||||
{
|
||||
var result = AgentLoopNoToolResponseRecoveryService.BuildNoToolLoopRecovery(
|
||||
requiresConcreteArtifactOrEdit: true,
|
||||
onlyProbeToolsUsed: true,
|
||||
totalToolCalls: 1,
|
||||
consecutiveNoToolResponses: 1,
|
||||
noToolResponseThreshold: 2,
|
||||
currentRetryCount: 0,
|
||||
maxRetryCount: 2,
|
||||
preferredInitialToolSequence: "file_read -> file_edit",
|
||||
activeToolNames: ["dev_env_detect", "file_read", "file_edit"]);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result!.NextRetryCount.Should().Be(1);
|
||||
result.RecoveryContent.Should().Contain("도구를 호출하지 않았습니다");
|
||||
result.RecoveryContent.Should().Contain("file_read -> file_edit");
|
||||
result.RecoveryContent.Should().Contain("dev_env_detect, file_read, file_edit");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildNoToolLoopRecovery_ShouldReturnFinalWarningOnLastRetry()
|
||||
{
|
||||
var result = AgentLoopNoToolResponseRecoveryService.BuildNoToolLoopRecovery(
|
||||
requiresConcreteArtifactOrEdit: true,
|
||||
onlyProbeToolsUsed: false,
|
||||
totalToolCalls: 0,
|
||||
consecutiveNoToolResponses: 2,
|
||||
noToolResponseThreshold: 2,
|
||||
currentRetryCount: 1,
|
||||
maxRetryCount: 2,
|
||||
preferredInitialToolSequence: "file_read -> file_edit",
|
||||
activeToolNames: ["file_read", "file_edit", "file_read"]);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result!.NextRetryCount.Should().Be(2);
|
||||
result.RecoveryContent.Should().Contain("최종 경고");
|
||||
result.RecoveryContent.Should().Contain("file_read, file_edit");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPlanExecutionRecovery_ShouldGeneratePlanSpecificRetryPrompt()
|
||||
{
|
||||
var result = AgentLoopNoToolResponseRecoveryService.BuildPlanExecutionRecovery(
|
||||
requiresConcreteArtifactOrEdit: true,
|
||||
forceToolCallAfterPlan: true,
|
||||
planStepCount: 3,
|
||||
totalToolCalls: 0,
|
||||
currentRetryCount: 0,
|
||||
maxRetryCount: 2,
|
||||
preferredInitialToolSequence: "file_read -> file_edit",
|
||||
activeToolNames: ["file_read", "file_edit", "git_tool"]);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result!.NextRetryCount.Should().Be(1);
|
||||
result.RecoveryContent.Should().Contain("계획을 세웠지만 도구를 호출하지 않았습니다");
|
||||
result.EventSummary.Should().Contain("실행 재시도 1/2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPlanExecutionRecovery_ShouldReturnNullWhenRetryIsExhausted()
|
||||
{
|
||||
var result = AgentLoopNoToolResponseRecoveryService.BuildPlanExecutionRecovery(
|
||||
requiresConcreteArtifactOrEdit: true,
|
||||
forceToolCallAfterPlan: true,
|
||||
planStepCount: 2,
|
||||
totalToolCalls: 0,
|
||||
currentRetryCount: 2,
|
||||
maxRetryCount: 2,
|
||||
preferredInitialToolSequence: "file_read -> file_edit",
|
||||
activeToolNames: ["file_read", "file_edit"]);
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user