AX Agent loop 정책을 분리하고 transcript 가상화·성능 계측 구조를 강화한다
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- AgentLoop 검증/문서/compact 정책 메서드를 partial 파일로 분리해 loop 본문의 책임을 줄임 - transcript 렌더에 cache pruning과 deferred scrolling을 적용해 긴 세션의 UI 부담을 낮춤 - AgentPerformanceLogService를 추가해 transcript 렌더와 agent loop 실행 요약을 perf 로그로 남김 - README와 DEVELOPMENT 문서에 2026-04-09 10: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:
156
src/AxCopilot/Services/Agent/AgentLoopTransitions.Documents.cs
Normal file
156
src/AxCopilot/Services/Agent/AgentLoopTransitions.Documents.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using AxCopilot.Models;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
public partial class AgentLoopService
|
||||
{
|
||||
private void ApplyDocumentPlanSuccessTransitions(
|
||||
LlmService.ContentBlock call,
|
||||
ToolResult result,
|
||||
List<ChatMessage> messages,
|
||||
ref bool documentPlanCalled,
|
||||
ref string? documentPlanPath,
|
||||
ref string? documentPlanTitle,
|
||||
ref string? documentPlanScaffold)
|
||||
{
|
||||
if (!string.Equals(call.ToolName, "document_plan", StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
|
||||
documentPlanCalled = true;
|
||||
var po = result.Output ?? string.Empty;
|
||||
var pm = System.Text.RegularExpressions.Regex.Match(po, @"path:\s*""([^""]+)""");
|
||||
if (pm.Success) documentPlanPath = pm.Groups[1].Value;
|
||||
var tm = System.Text.RegularExpressions.Regex.Match(po, @"title:\s*""([^""]+)""");
|
||||
if (tm.Success) documentPlanTitle = tm.Groups[1].Value;
|
||||
documentPlanScaffold = ExtractDocumentPlanScaffold(po);
|
||||
|
||||
if (!ContainsDocumentPlanFollowUpInstruction(po))
|
||||
return;
|
||||
|
||||
var toolHint = ResolveDocumentPlanFollowUpTool(po);
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
Content =
|
||||
"document_plan이 완료되었습니다. " +
|
||||
"방금 생성된 골격의 [내용...] 자리와 각 섹션 내용을 실제 상세 본문으로 모두 채운 뒤 " +
|
||||
$"{toolHint} 도구를 지금 즉시 호출하세요. " +
|
||||
"설명만 하지 말고 실제 문서 생성 도구 호출로 바로 이어가세요."
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", $"문서 개요 완료 · {toolHint} 실행 유도");
|
||||
}
|
||||
|
||||
private static string? ExtractDocumentPlanScaffold(string output)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(output))
|
||||
return null;
|
||||
|
||||
var markers = new (string Start, string End)[]
|
||||
{
|
||||
("--- body 시작 ---", "--- body 끝 ---"),
|
||||
("--- body start ---", "--- body end ---"),
|
||||
("<!-- body start marker -->", "<!-- body end marker -->"),
|
||||
};
|
||||
|
||||
foreach (var (startMarker, endMarker) in markers)
|
||||
{
|
||||
var start = output.IndexOf(startMarker, StringComparison.OrdinalIgnoreCase);
|
||||
if (start < 0)
|
||||
continue;
|
||||
|
||||
var contentStart = start + startMarker.Length;
|
||||
var end = output.IndexOf(endMarker, contentStart, StringComparison.OrdinalIgnoreCase);
|
||||
if (end <= contentStart)
|
||||
continue;
|
||||
|
||||
var scaffold = output[contentStart..end].Trim();
|
||||
if (!string.IsNullOrWhiteSpace(scaffold))
|
||||
return scaffold;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool ContainsDocumentPlanFollowUpInstruction(string output)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(output))
|
||||
return false;
|
||||
|
||||
return output.Contains("즉시 실행", StringComparison.OrdinalIgnoreCase)
|
||||
|| output.Contains("immediate next step", StringComparison.OrdinalIgnoreCase)
|
||||
|| output.Contains("call html_create", StringComparison.OrdinalIgnoreCase)
|
||||
|| output.Contains("call document_assemble", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string ResolveDocumentPlanFollowUpTool(string output)
|
||||
{
|
||||
if (output.Contains("document_assemble", StringComparison.OrdinalIgnoreCase))
|
||||
return "document_assemble";
|
||||
if (output.Contains("docx_create", StringComparison.OrdinalIgnoreCase))
|
||||
return "docx_create";
|
||||
if (output.Contains("markdown_create", StringComparison.OrdinalIgnoreCase))
|
||||
return "markdown_create";
|
||||
if (output.Contains("file_write", StringComparison.OrdinalIgnoreCase))
|
||||
return "file_write";
|
||||
return "html_create";
|
||||
}
|
||||
|
||||
private async Task<(bool Completed, bool ConsumedExtraIteration)> TryHandleTerminalDocumentCompletionTransitionAsync(
|
||||
LlmService.ContentBlock call,
|
||||
ToolResult result,
|
||||
List<LlmService.ContentBlock> toolCalls,
|
||||
List<ChatMessage> messages,
|
||||
Models.LlmSettings llm,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy,
|
||||
AgentContext context,
|
||||
CancellationToken ct)
|
||||
{
|
||||
if (!result.Success || !IsTerminalDocumentTool(call.ToolName) || toolCalls.Count != 1)
|
||||
return (false, false);
|
||||
|
||||
var verificationEnabled = executionPolicy.EnablePostToolVerification
|
||||
&& AgentTabSettingsResolver.IsPostToolVerificationEnabled(ActiveTab, llm);
|
||||
var shouldVerify = ShouldRunPostToolVerification(
|
||||
ActiveTab,
|
||||
call.ToolName,
|
||||
result.Success,
|
||||
verificationEnabled,
|
||||
verificationEnabled);
|
||||
var consumedExtraIteration = false;
|
||||
if (shouldVerify)
|
||||
{
|
||||
await RunPostToolVerificationAsync(messages, call.ToolName, result, context, ct);
|
||||
consumedExtraIteration = true;
|
||||
}
|
||||
|
||||
EmitEvent(AgentEventType.Complete, "", "에이전트 작업 완료");
|
||||
return (true, consumedExtraIteration);
|
||||
}
|
||||
|
||||
private async Task<bool> TryApplyPostToolVerificationTransitionAsync(
|
||||
LlmService.ContentBlock call,
|
||||
ToolResult result,
|
||||
List<ChatMessage> messages,
|
||||
Models.LlmSettings llm,
|
||||
ModelExecutionProfileCatalog.ExecutionPolicy executionPolicy,
|
||||
AgentContext context,
|
||||
CancellationToken ct)
|
||||
{
|
||||
if (!result.Success)
|
||||
return false;
|
||||
|
||||
var verificationEnabled = executionPolicy.EnablePostToolVerification
|
||||
&& AgentTabSettingsResolver.IsPostToolVerificationEnabled(ActiveTab, llm);
|
||||
var shouldVerify = ShouldRunPostToolVerification(
|
||||
ActiveTab,
|
||||
call.ToolName,
|
||||
result.Success,
|
||||
verificationEnabled,
|
||||
verificationEnabled);
|
||||
if (!shouldVerify)
|
||||
return false;
|
||||
|
||||
await RunPostToolVerificationAsync(messages, call.ToolName, result, context, ct);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user