??? ???? ???? ??? ?? ???? ???? ??? ? ?????? ?? ?? ??

- WPF/MVVM, ASP.NET API, React/Vue/Next, Node, Python API, Spring, Android, Go, Rust CLI, generic solution ??? ProjectScaffold? ???? ???? ????? ???
- Code ?? empty workspace ??? ??? ????? ??? ?? ??? file_write? ?? ???? ??? ????? file_manage/file_write? ?? ?? ???? ???? ???
- AgentLoop ?? ? ProjectLayoutGate? ??? ??? ?? ??? ?? ??? ??? ????? ?? ??? ? ????? ???
- code-scaffold.skill.md? when_to_use? file_manage/file_edit ?????? ??? proactive auto-skill ???? ???
- IntentGate, scaffold profile, code quality ?? ???? ???

?? ??
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_project_scaffold_layout\\ -p:IntermediateOutputPath=obj\\verify_project_scaffold_layout\\ ?? 0 / ?? 0
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "IntentGateServiceTests|ProjectScaffoldProfileCatalogTests|SkillServiceRuntimePolicyTests|AgentLoopCodeQualityTests" -p:OutputPath=bin\\verify_project_scaffold_layout_tests\\ -p:IntermediateOutputPath=obj\\verify_project_scaffold_layout_tests\\ ?? 183
This commit is contained in:
2026-04-15 23:46:05 +09:00
parent 4980113b99
commit 6810fb1954
15 changed files with 1140 additions and 286 deletions

View File

@@ -1,4 +1,4 @@
using AxCopilot.Models;
using AxCopilot.Models;
namespace AxCopilot.Services.Agent;
@@ -28,8 +28,8 @@ public partial class AgentLoopService
taskPolicy)
});
EmitEvent(AgentEventType.Thinking, "", highImpactCodeChange
? "고영향 코드 변경으로 분류돼 참조 검증과 build/test 검증을 더 엄격하게 이어갑니다."
: "코드 변경 후 build/test/diff 검증을 이어갑니다.");
? "怨좎쁺??肄붾뱶 蹂€寃쎌쑝濡?遺꾨쪟??李몄“ 寃€利앷낵 build/test 寃€利앹쓣 ???꾧꺽?섍쾶 ?댁뼱媛묐땲??"
: "肄붾뱶 蹂€寃???build/test/diff 寃€利앹쓣 ?댁뼱媛묐땲??");
}
else if (HasCodeVerificationEvidenceAfterLastModification(messages, requireHighImpactCodeVerification))
{
@@ -43,12 +43,24 @@ public partial class AgentLoopService
TaskTypePolicy taskPolicy,
bool requireHighImpactCodeVerification,
int totalToolCalls,
AgentContext context,
ExplorationTrackingState explorationState,
bool workspaceWasInitiallyEmpty,
RunState runState,
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy)
{
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase) || totalToolCalls <= 0)
return false;
if (TryApplyProjectLayoutGateTransition(
messages,
textResponse,
context,
explorationState,
workspaceWasInitiallyEmpty,
runState))
return true;
var hasCodeVerificationEvidence = HasCodeVerificationEvidenceAfterLastModification(
messages,
requireHighImpactCodeVerification);
@@ -69,12 +81,12 @@ public partial class AgentLoopService
{
Role = "user",
Content = requireHighImpactCodeVerification
? "[System:CodeQualityGate] 공용/핵심 코드 변경 이후 검증 근거가 부족합니다. 종료하지 말고 file_read, grep/glob, git diff, build/test까지 확인한 뒤에만 마무리하세요."
: "[System:CodeQualityGate] 마지막 코드 수정 이후 build/test/file_read/diff 근거가 부족합니다. 종료하지 말고 검증 근거를 보강한 뒤에만 마무리하세요."
? "[System:CodeQualityGate] 怨듭슜/?듭떖 肄붾뱶 蹂€寃??댄썑 寃€利?洹쇨굅媛€ 遺€議깊빀?덈떎. 醫낅즺?섏? 留먭퀬 file_read, grep/glob, git diff, build/test源뚯? ?뺤씤???ㅼ뿉留?留덈Т由ы븯?몄슂."
: "[System:CodeQualityGate] 留덉?留?肄붾뱶 ?섏젙 ?댄썑 build/test/file_read/diff 洹쇨굅媛€ 遺€議깊빀?덈떎. 醫낅즺?섏? 留먭퀬 寃€利?洹쇨굅瑜?蹂닿컯???ㅼ뿉留?留덈Т由ы븯?몄슂."
});
EmitEvent(AgentEventType.Thinking, "", requireHighImpactCodeVerification
? "핵심 코드 변경의 검증 근거가 부족해 추가 검증을 진행합니다..."
: "코드 결과 검증 근거가 부족해 추가 검증을 진행합니다...");
? "?듭떖 肄붾뱶 蹂€寃쎌쓽 寃€利?洹쇨굅媛€ 遺€議깊빐 異붽? 寃€利앹쓣 吏꾪뻾?⑸땲??.."
: "肄붾뱶 寃곌낵 寃€利?洹쇨굅媛€ 遺€議깊빐 異붽? 寃€利앹쓣 吏꾪뻾?⑸땲??..");
return true;
}
@@ -89,9 +101,9 @@ public partial class AgentLoopService
messages.Add(new ChatMessage
{
Role = "user",
Content = "[System:HighImpactBuildTestGate] 핵심 코드 변경입니다. 종료하지 말고 build_runtest_loop를 모두 실행해 성공 근거를 확보한 뒤에만 마무리하세요."
Content = "[System:HighImpactBuildTestGate] ?듭떖 肄붾뱶 蹂€寃쎌엯?덈떎. 醫낅즺?섏? 留먭퀬 build_run怨?test_loop瑜?紐⑤몢 ?ㅽ뻾???깃났 洹쇨굅瑜??뺣낫???ㅼ뿉留?留덈Т由ы븯?몄슂."
});
EmitEvent(AgentEventType.Thinking, "", "핵심 변경이라 build+test 성공 근거를 모두 확보할 때까지 진행합니다...");
EmitEvent(AgentEventType.Thinking, "", "?듭떖 蹂€寃쎌씠??build+test ?깃났 洹쇨굅瑜?紐⑤몢 ?뺣낫???뚭퉴吏€ 吏꾪뻾?⑸땲??..");
return true;
}
@@ -115,13 +127,92 @@ public partial class AgentLoopService
Role = "user",
Content = BuildFinalReportQualityPrompt(taskPolicy, requireHighImpactCodeVerification)
});
EmitEvent(AgentEventType.Thinking, "", "최종 보고에 변경·검증·리스크 요약이 부족해 한 번 더 정리합니다...");
EmitEvent(AgentEventType.Thinking, "", "理쒖쥌 蹂닿퀬??蹂€寃승룰?利씲룸━?ㅽ겕 ?붿빟??遺€議깊빐 ??踰????뺣━?⑸땲??..");
return true;
}
return false;
}
private bool TryApplyProjectLayoutGateTransition(
List<ChatMessage> messages,
string? textResponse,
AgentContext context,
ExplorationTrackingState explorationState,
bool workspaceWasInitiallyEmpty,
RunState runState)
{
if (!string.Equals(ActiveTab, "Code", StringComparison.OrdinalIgnoreCase))
return false;
var scaffoldProfile = explorationState.ScaffoldProfile;
if (!workspaceWasInitiallyEmpty || scaffoldProfile == null)
return false;
if (runState.ProjectLayoutGateRetry >= 1)
return false;
if (!HasProjectScaffoldModificationEvidence(messages))
return false;
var assessment = ProjectScaffoldProfileCatalog.AssessLayout(scaffoldProfile, context.WorkFolder);
if (assessment.IsSatisfied)
return false;
runState.ProjectLayoutGateRetry++;
if (!string.IsNullOrEmpty(textResponse))
messages.Add(new ChatMessage { Role = "assistant", Content = textResponse });
messages.Add(new ChatMessage
{
Role = "user",
Content = BuildProjectLayoutGatePrompt(scaffoldProfile, assessment)
});
EmitEvent(
AgentEventType.Thinking,
"",
$"?꾨줈?앺듃 援ъ“媛€ ?됰㈃?곸쑝濡??앹꽦???대뜑 ?덉씠?꾩썐??癒쇱? ?뺣━?⑸땲??({runState.ProjectLayoutGateRetry}/1)");
return true;
}
private static bool HasProjectScaffoldModificationEvidence(List<ChatMessage> messages)
{
foreach (var message in messages)
{
if (!TryGetToolResultToolName(message, out var toolName))
continue;
if (toolName is "file_write" or "file_edit" or "file_manage" or "script_create")
return true;
}
return false;
}
private static string BuildProjectLayoutGatePrompt(
ProjectScaffoldProfile profile,
ProjectScaffoldLayoutAssessment assessment)
{
var missingDirectories = assessment.MissingDirectories.Count == 0
? "none"
: string.Join(", ", assessment.MissingDirectories.Take(6));
var suspiciousRootFiles = assessment.SuspiciousRootFiles.Count == 0
? "none"
: string.Join(", ", assessment.SuspiciousRootFiles.Take(8));
var allowedRootFiles = profile.AllowedRootFiles.Count == 0
? "only manifest and entry files"
: string.Join(", ", profile.AllowedRootFiles);
return "[System:ProjectLayoutGate] This looks like a structured scaffold request for "
+ $"{profile.Label}, but the current workspace layout is still too flat. "
+ $"Create or complete folders such as {ProjectScaffoldProfileCatalog.BuildDirectoryPreview(profile)}, "
+ $"especially the missing ones: {missingDirectories}. "
+ $"Move implementation files that are still sitting in the workspace root into the proper folders: {suspiciousRootFiles}. "
+ $"Keep only appropriate root files such as {allowedRootFiles}. "
+ "Use file_manage(mkdir/move) plus file_edit/file_write to reorganize the scaffold before finishing, "
+ "then rerun build/test if relevant and only after that summarize the result.";
}
private bool TryApplyCodeDiffEvidenceGateTransition(
List<ChatMessage> messages,
string? textResponse,
@@ -147,9 +238,9 @@ public partial class AgentLoopService
messages.Add(new ChatMessage
{
Role = "user",
Content = "[System:CodeDiffGate] 코드 변경 이후 diff 근거가 부족합니다. git_tool 도구로 변경 파일과 핵심 diff를 먼저 확인하고 요약하세요. 지금 즉시 git_tool 도구를 호출하세요."
Content = "[System:CodeDiffGate] 肄붾뱶 蹂€寃??댄썑 diff 洹쇨굅媛€ 遺€議깊빀?덈떎. git_tool ?꾧뎄濡?蹂€寃??뚯씪怨??듭떖 diff瑜?癒쇱? ?뺤씤?섍퀬 ?붿빟?섏꽭?? 吏€湲?利됱떆 git_tool ?꾧뎄瑜??몄텧?섏꽭??"
});
EmitEvent(AgentEventType.Thinking, "", "코드 diff 근거가 부족해 git diff 검증을 추가합니다...");
EmitEvent(AgentEventType.Thinking, "", "肄붾뱶 diff 洹쇨굅媛€ 遺€議깊빐 git diff 寃€利앹쓣 異붽??⑸땲??..");
return true;
}
@@ -186,7 +277,7 @@ public partial class AgentLoopService
Role = "user",
Content = BuildRecentExecutionEvidencePrompt(taskPolicy)
});
EmitEvent(AgentEventType.Thinking, "", "최근 수정 이후 실행 근거가 부족해 build/test 재검증을 수행합니다...");
EmitEvent(AgentEventType.Thinking, "", "理쒓렐 ?섏젙 ?댄썑 ?ㅽ뻾 洹쇨굅媛€ 遺€議깊빐 build/test ?ш?利앹쓣 ?섑뻾?⑸땲??..");
return true;
}
@@ -223,7 +314,7 @@ public partial class AgentLoopService
Role = "user",
Content = BuildExecutionSuccessGatePrompt(taskPolicy)
});
EmitEvent(AgentEventType.Thinking, "", "실패한 실행 근거만 있어 build/test 성공 결과를 다시 검증합니다...");
EmitEvent(AgentEventType.Thinking, "", "?ㅽ뙣???ㅽ뻾 洹쇨굅留??덉뼱 build/test ?깃났 寃곌낵瑜??ㅼ떆 寃€利앺빀?덈떎...");
return true;
}
@@ -263,7 +354,7 @@ public partial class AgentLoopService
Role = "user",
Content = BuildTerminalEvidenceGatePrompt(taskPolicy, lastArtifactFilePath)
});
EmitEvent(AgentEventType.Thinking, "", $"종료 전 실행 증거가 부족해 보강 단계를 진행합니다 ({runState.TerminalEvidenceGateRetry}/{retryMax})");
EmitEvent(AgentEventType.Thinking, "", $"醫낅즺 ???ㅽ뻾 利앷굅媛€ 遺€議깊빐 蹂닿컯 ?④퀎瑜?吏꾪뻾?⑸땲??({runState.TerminalEvidenceGateRetry}/{retryMax})");
return true;
}
}