SQL 리뷰 계층과 AgentLoop 응답 분해 helper를 추가해 코드 탭 마감 품질을 높임
- SqlReviewService를 추가해 SQL fallback 결과에 review severity, key findings, review checklist를 붙이고 schema migration, seed/reference data, reporting query마다 다른 검토 포인트를 안내하도록 확장했습니다. - SqlAnalysisService와 CodeLanguageCatalog를 업데이트해 SQL fallback summary와 workflow summary가 rollback notes, dependency order, row-count guard 같은 리뷰 힌트를 직접 포함하도록 보강했습니다. - AgentLoopResponseClassificationService를 추가해 LLM 응답에서 text/tool_use 분리, no-tool 연속 카운트 계산, thinking summary 생성을 helper로 분리했고 AgentLoopService 본체는 해당 helper를 사용하도록 정리했습니다. - README, docs/DEVELOPMENT.md, docs/NEXT_ROADMAP.md에 2026-04-15 11:50 (KST) 기준 이력을 반영했습니다. 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_loop_sql_finalize\\ -p:IntermediateOutputPath=obj\\verify_loop_sql_finalize\\ : 경고 0 / 오류 0 - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopResponseClassificationServiceTests|AgentLoopLlmRequestPreparationServiceTests|AgentLoopIterationPreparationServiceTests|SqlAnalysisServiceTests|SqlReviewServiceTests|CodeLanguageCatalogTests|WorkspaceContextGeneratorTests" -p:OutputPath=bin\\verify_loop_sql_finalize_tests\\ -p:IntermediateOutputPath=obj\\verify_loop_sql_finalize_tests\\ : 통과 48
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
internal sealed record AgentLoopResponseClassificationResult(
|
||||
string TextResponse,
|
||||
IReadOnlyList<string> TextParts,
|
||||
List<ContentBlock> ToolCalls,
|
||||
int NextConsecutiveNoToolResponses)
|
||||
{
|
||||
public string BuildThinkingSummary(int maxLength = 150)
|
||||
{
|
||||
if (string.IsNullOrEmpty(TextResponse))
|
||||
return string.Empty;
|
||||
|
||||
return TextResponse.Length > maxLength
|
||||
? TextResponse[..maxLength] + "…"
|
||||
: TextResponse;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LLM 응답 블록을 텍스트와 tool_use로 분리하고, 무도구 응답 연속 횟수를 계산한다.
|
||||
/// </summary>
|
||||
internal static class AgentLoopResponseClassificationService
|
||||
{
|
||||
public static AgentLoopResponseClassificationResult Classify(
|
||||
IReadOnlyList<ContentBlock> blocks,
|
||||
int consecutiveNoToolResponses)
|
||||
{
|
||||
var textParts = new List<string>();
|
||||
var toolCalls = new List<ContentBlock>();
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block.Type == "text" && !string.IsNullOrWhiteSpace(block.Text))
|
||||
textParts.Add(block.Text);
|
||||
else if (block.Type == "tool_use")
|
||||
toolCalls.Add(block);
|
||||
}
|
||||
|
||||
var nextConsecutiveNoToolResponses = toolCalls.Count == 0
|
||||
? consecutiveNoToolResponses + 1
|
||||
: 0;
|
||||
|
||||
return new AgentLoopResponseClassificationResult(
|
||||
string.Join("\n", textParts),
|
||||
textParts,
|
||||
toolCalls,
|
||||
nextConsecutiveNoToolResponses);
|
||||
}
|
||||
}
|
||||
@@ -726,21 +726,12 @@ public partial class AgentLoopService
|
||||
return $"⚠ LLM 오류: {ex.Message}";
|
||||
}
|
||||
|
||||
// 응답에서 텍스트와 도구 호출 분리
|
||||
var textParts = new List<string>();
|
||||
var toolCalls = new List<ContentBlock>();
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block.Type == "text" && !string.IsNullOrWhiteSpace(block.Text))
|
||||
textParts.Add(block.Text);
|
||||
else if (block.Type == "tool_use")
|
||||
toolCalls.Add(block);
|
||||
}
|
||||
|
||||
// 텍스트 부분
|
||||
var textResponse = string.Join("\n", textParts);
|
||||
consecutiveNoToolResponses = toolCalls.Count == 0 ? consecutiveNoToolResponses + 1 : 0;
|
||||
var responseClassification = AgentLoopResponseClassificationService.Classify(
|
||||
blocks,
|
||||
consecutiveNoToolResponses);
|
||||
var textResponse = responseClassification.TextResponse;
|
||||
var toolCalls = responseClassification.ToolCalls;
|
||||
consecutiveNoToolResponses = responseClassification.NextConsecutiveNoToolResponses;
|
||||
|
||||
// 워크플로우 상세 로그: LLM 응답
|
||||
WorkflowLogService.LogLlmResponse(_conversationId, _currentRunId, iteration,
|
||||
@@ -804,9 +795,7 @@ public partial class AgentLoopService
|
||||
// Thinking UI: 텍스트 응답 중 도구 호출이 있으면 "사고 과정"으로 표시
|
||||
if (!string.IsNullOrEmpty(textResponse) && toolCalls.Count > 0)
|
||||
{
|
||||
var thinkingSummary = textResponse.Length > 150
|
||||
? textResponse[..150] + "…"
|
||||
: textResponse;
|
||||
var thinkingSummary = responseClassification.BuildThinkingSummary();
|
||||
EmitEvent(AgentEventType.Thinking, "", thinkingSummary);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user