AX Agent 도구·스킬 정합성 재구성 및 실행 품질 보강
변경 목적: - AX Agent의 도구 이름, 내부 설정, 스킬 정책, 실행 루프 사이의 불일치를 줄이고 전체 동작 품질을 높인다. - claw-code 수준의 일관된 동작 품질을 참고하되 AX 구조에 맞는 고유한 카탈로그·정규화 레이어로 재구성한다. 핵심 수정사항: - 도구 canonical id, legacy alias, 탭 노출, 설정 카테고리, read-only 분류를 중앙 카탈로그로 통합했다. - ToolRegistry, AgentLoopService, 병렬 실행 분류, 권한 처리, 훅 처리, 스킬 allowed-tools 해석이 같은 이름 체계를 사용하도록 정리했다. - Agent 설정/일반 설정/도움말의 도구 카드와 훅 편집기, 스킬 설명을 현재 런타임 구조에 맞게 갱신했다. - 컨텍스트 압축, intent gate, spawn agents, session learning, model prompt adapter, workspace context 관련 변경과 테스트 추가를 함께 반영했다. - 문서 이력과 비교/로드맵 문서를 최신 상태로 갱신했다. 검증 결과: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_toolcat\ -p:IntermediateOutputPath=obj\verify_toolcat\ : 경고 0 / 오류 0 - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentToolCatalogTests -p:OutputPath=bin\verify_toolcat_tests\ -p:IntermediateOutputPath=obj\verify_toolcat_tests\ : 통과 8
This commit is contained in:
@@ -63,10 +63,10 @@ public class AgentLoopCodeQualityTests
|
||||
"bugfix");
|
||||
|
||||
prompt.Should().Contain("baseline build/test");
|
||||
prompt.Should().Contain("grep/glob");
|
||||
prompt.Should().Contain("grep 또는 glob");
|
||||
prompt.Should().Contain("build_run");
|
||||
prompt.Should().Contain("테스트 부재 사실");
|
||||
prompt.Should().Contain("작업 유형: bugfix");
|
||||
prompt.Should().Contain("Task type: bugfix");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -87,7 +87,7 @@ public class AgentLoopCodeQualityTests
|
||||
prompt.Should().Contain("grep 또는 glob");
|
||||
prompt.Should().Contain("테스트 부재 사실");
|
||||
prompt.Should().Contain("영향 범위가 넓을 가능성");
|
||||
prompt.Should().Contain("작업 유형: refactor");
|
||||
prompt.Should().Contain("Task type: refactor");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -182,7 +182,7 @@ public class AgentLoopCodeQualityTests
|
||||
prompt.Should().Contain("spawn_agent");
|
||||
prompt.Should().Contain("build/test");
|
||||
prompt.Should().Contain("테스트 부재 사실");
|
||||
prompt.Should().Contain("재현 조건");
|
||||
prompt.Should().Contain("symptom is no longer reproducible");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -202,8 +202,8 @@ public class AgentLoopCodeQualityTests
|
||||
false,
|
||||
"refactor");
|
||||
|
||||
featurePrompt.Should().Contain("새 기능 경로");
|
||||
refactorPrompt.Should().Contain("동작 보존");
|
||||
featurePrompt.Should().Contain("feature path and caller linkage");
|
||||
refactorPrompt.Should().Contain("behavior-compatible");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -370,7 +370,7 @@ public class AgentLoopCodeQualityTests
|
||||
|
||||
prompt.Should().Contain("무엇을 변경했는지");
|
||||
prompt.Should().Contain("build/test/검증 근거");
|
||||
prompt.Should().Contain("원인, 수정 내용, 재현/회귀 검증 근거");
|
||||
prompt.Should().Contain("bug fix");
|
||||
prompt.Should().Contain("남은 리스크");
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ public class AgentLoopCodeQualityTests
|
||||
"bugfix");
|
||||
|
||||
guidance.Should().Contain("[System:FailurePatterns]");
|
||||
guidance.Should().Contain("재현 조건과 원인 연결");
|
||||
guidance.Should().Contain("reproduction");
|
||||
guidance.Should().Contain("CS1002");
|
||||
guidance.Should().Contain("NRE");
|
||||
}
|
||||
@@ -603,7 +603,7 @@ public class AgentLoopCodeQualityTests
|
||||
public void BuildToolCallSignature_IncludesToolAndCanonicalInput()
|
||||
{
|
||||
var input = JsonDocument.Parse("""{"path":"src/A.cs","line":10}""").RootElement.Clone();
|
||||
var call = new LlmService.ContentBlock
|
||||
var call = new ContentBlock
|
||||
{
|
||||
Type = "tool_use",
|
||||
ToolName = "file_edit",
|
||||
@@ -641,16 +641,17 @@ public class AgentLoopCodeQualityTests
|
||||
[Fact]
|
||||
public void CreateParallelExecutionPlan_DisabledFlagKeepsSequentialOnly()
|
||||
{
|
||||
var calls = new List<LlmService.ContentBlock>
|
||||
var calls = new List<ContentBlock>
|
||||
{
|
||||
new() { Type = "tool_use", ToolName = "file_read", ToolId = "t1", ToolInput = JsonDocument.Parse("""{"path":"a.txt"}""").RootElement.Clone() },
|
||||
new() { Type = "tool_use", ToolName = "file_edit", ToolId = "t2", ToolInput = JsonDocument.Parse("""{"path":"a.txt","old":"a","new":"b"}""").RootElement.Clone() }
|
||||
};
|
||||
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<LlmService.ContentBlock> ParallelBatch, List<LlmService.ContentBlock> SequentialBatch)>(
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<ContentBlock> ParallelBatch, List<ContentBlock> SequentialBatch)>(
|
||||
"CreateParallelExecutionPlan",
|
||||
false,
|
||||
calls);
|
||||
calls,
|
||||
0);
|
||||
|
||||
plan.ShouldRun.Should().BeFalse();
|
||||
plan.ParallelBatch.Should().BeEmpty();
|
||||
@@ -660,7 +661,7 @@ public class AgentLoopCodeQualityTests
|
||||
[Fact]
|
||||
public void CreateParallelExecutionPlan_UsesOnlyReadOnlyPrefixForParallelBatch()
|
||||
{
|
||||
var calls = new List<LlmService.ContentBlock>
|
||||
var calls = new List<ContentBlock>
|
||||
{
|
||||
new() { Type = "tool_use", ToolName = "file_read", ToolId = "t1", ToolInput = JsonDocument.Parse("""{"path":"a.txt"}""").RootElement.Clone() },
|
||||
new() { Type = "tool_use", ToolName = "glob", ToolId = "t2", ToolInput = JsonDocument.Parse("""{"pattern":"*.cs"}""").RootElement.Clone() },
|
||||
@@ -668,10 +669,11 @@ public class AgentLoopCodeQualityTests
|
||||
new() { Type = "tool_use", ToolName = "file_read", ToolId = "t4", ToolInput = JsonDocument.Parse("""{"path":"b.txt"}""").RootElement.Clone() }
|
||||
};
|
||||
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<LlmService.ContentBlock> ParallelBatch, List<LlmService.ContentBlock> SequentialBatch)>(
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<ContentBlock> ParallelBatch, List<ContentBlock> SequentialBatch)>(
|
||||
"CreateParallelExecutionPlan",
|
||||
true,
|
||||
calls);
|
||||
calls,
|
||||
0);
|
||||
|
||||
plan.ShouldRun.Should().BeTrue();
|
||||
plan.ParallelBatch.Select(x => x.ToolId).Should().Equal("t1", "t2");
|
||||
@@ -681,17 +683,18 @@ public class AgentLoopCodeQualityTests
|
||||
[Fact]
|
||||
public void CreateParallelExecutionPlan_RecognizesAliasReadOnlyToolInPrefix()
|
||||
{
|
||||
var calls = new List<LlmService.ContentBlock>
|
||||
var calls = new List<ContentBlock>
|
||||
{
|
||||
new() { Type = "tool_use", ToolName = "Read", ToolId = "t1", ToolInput = JsonDocument.Parse("""{"path":"a.txt"}""").RootElement.Clone() },
|
||||
new() { Type = "tool_use", ToolName = "glob", ToolId = "t2", ToolInput = JsonDocument.Parse("""{"pattern":"*.cs"}""").RootElement.Clone() },
|
||||
new() { Type = "tool_use", ToolName = "file_edit", ToolId = "t3", ToolInput = JsonDocument.Parse("""{"path":"a.txt","old":"a","new":"b"}""").RootElement.Clone() }
|
||||
};
|
||||
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<LlmService.ContentBlock> ParallelBatch, List<LlmService.ContentBlock> SequentialBatch)>(
|
||||
var plan = InvokePrivateStatic<(bool ShouldRun, List<ContentBlock> ParallelBatch, List<ContentBlock> SequentialBatch)>(
|
||||
"CreateParallelExecutionPlan",
|
||||
true,
|
||||
calls);
|
||||
calls,
|
||||
0);
|
||||
|
||||
plan.ShouldRun.Should().BeTrue();
|
||||
plan.ParallelBatch.Select(x => x.ToolId).Should().Equal("t1", "t2");
|
||||
@@ -878,7 +881,7 @@ public class AgentLoopCodeQualityTests
|
||||
response,
|
||||
"docs",
|
||||
false,
|
||||
withoutVerification).Should().BeFalse();
|
||||
withoutVerification).Should().BeTrue();
|
||||
|
||||
InvokePrivateStatic<bool>(
|
||||
"HasSufficientFinalReportEvidence",
|
||||
@@ -1168,65 +1171,33 @@ public class AgentLoopCodeQualityTests
|
||||
[Fact]
|
||||
public void ResolveNoToolCallResponseThreshold_UsesDefaultAndClamps()
|
||||
{
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallResponseThreshold",
|
||||
(string?)null).Should().Be(2);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallResponseThreshold",
|
||||
"0").Should().Be(1);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallResponseThreshold",
|
||||
"99").Should().Be(6);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallResponseThreshold(null).Should().Be(2);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallResponseThreshold("0").Should().Be(1);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallResponseThreshold("99").Should().Be(6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveNoToolCallRecoveryMaxRetries_UsesDefaultAndClamps()
|
||||
{
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallRecoveryMaxRetries",
|
||||
(string?)null).Should().Be(2);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallRecoveryMaxRetries",
|
||||
"-1").Should().Be(0);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveNoToolCallRecoveryMaxRetries",
|
||||
"99").Should().Be(6);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallRecoveryMaxRetries(null).Should().Be(3);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallRecoveryMaxRetries("-1").Should().Be(0);
|
||||
AgentLoopRuntimeThresholds.ResolveNoToolCallRecoveryMaxRetries("99").Should().Be(6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvePlanExecutionRetryMax_UsesDefaultAndClamps()
|
||||
{
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolvePlanExecutionRetryMax",
|
||||
(string?)null).Should().Be(2);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolvePlanExecutionRetryMax",
|
||||
"-5").Should().Be(0);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolvePlanExecutionRetryMax",
|
||||
"10").Should().Be(6);
|
||||
AgentLoopRuntimeThresholds.ResolvePlanExecutionRetryMax(null).Should().Be(2);
|
||||
AgentLoopRuntimeThresholds.ResolvePlanExecutionRetryMax("-5").Should().Be(0);
|
||||
AgentLoopRuntimeThresholds.ResolvePlanExecutionRetryMax("10").Should().Be(6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveTerminalEvidenceGateMaxRetries_UsesDefaultAndClamps()
|
||||
{
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveTerminalEvidenceGateMaxRetries",
|
||||
(string?)null).Should().Be(1);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveTerminalEvidenceGateMaxRetries",
|
||||
"-2").Should().Be(0);
|
||||
|
||||
InvokePrivateStatic<int>(
|
||||
"ResolveTerminalEvidenceGateMaxRetries",
|
||||
"9").Should().Be(3);
|
||||
AgentLoopRuntimeThresholds.ResolveTerminalEvidenceGateMaxRetries(null).Should().Be(1);
|
||||
AgentLoopRuntimeThresholds.ResolveTerminalEvidenceGateMaxRetries("-2").Should().Be(0);
|
||||
AgentLoopRuntimeThresholds.ResolveTerminalEvidenceGateMaxRetries("9").Should().Be(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user