에이전트 루프와 코드 언어 지원, PPT 생성 품질을 함께 고도화
- AgentCommandQueue를 도입해 실행 중 추가 입력을 우선순위와 인터럽트 여부까지 포함해 처리하도록 정리함 - AgentToolResultBudget와 AgentQueryContextBuilder에 tool result preview 캐시를 연결해 긴 세션에서 축약 결과 재사용을 안정화함 - CodeLanguageCatalog를 추가해 코드 탭의 내장 언어 지원, 인덱싱 확장자, 시스템 프롬프트 언어 가이드, LSP 언어 판정을 한 카탈로그로 통합함 - 설정의 코드 탭에 지원 언어(LSP)와 코드 탭 기본 지원 언어를 명시적으로 표시하도록 보강함 - DocumentPlannerTool의 presentation 구조를 컨설팅형 스토리라인으로 정리하고, PptxSkill에 executive_summary/recommendation/roadmap/comparison/kpi_dashboard 레이아웃을 추가함 - pptx-creator 스킬을 AX native pptx_create 중심으로 재작성하고, 관련 회귀 테스트를 추가했으며 WorkspaceContextGeneratorTests의 nullable 경고도 정리함 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_impl\\ -p:IntermediateOutputPath=obj\\verify_impl\\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "CodeLanguageCatalogTests|AgentCommandQueueTests|AgentToolResultBudgetTests|DocumentPlannerPresentationTests|PptxSkillConsultingDeckTests" -p:OutputPath=bin\\verify_impl_tests\\ -p:IntermediateOutputPath=obj\\verify_impl_tests\\
This commit is contained in:
@@ -36,14 +36,64 @@ public partial class AgentLoopService
|
||||
};
|
||||
private readonly ConcurrentDictionary<string, PermissionPromptPreview> _pendingPermissionPreviews = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>실행 중 사용자 메시지 주입 큐 (Claude Code 스타일 mid-execution steering).</summary>
|
||||
private readonly ConcurrentQueue<string> _pendingUserMessages = new();
|
||||
/// <summary>실행 중 추가 입력/알림 큐. 우선순위와 종류를 함께 보존합니다.</summary>
|
||||
private readonly AgentCommandQueue _pendingCommands = new();
|
||||
|
||||
/// <summary>실행 중인 에이전트 루프에 사용자 메시지를 주입합니다. 다음 LLM 호출 전에 반영됩니다.</summary>
|
||||
public void InjectUserMessage(string message)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
_pendingUserMessages.Enqueue(message);
|
||||
_pendingCommands.EnqueuePrompt(
|
||||
message,
|
||||
IsRunning ? "now" : "next",
|
||||
requestInterrupt: IsRunning);
|
||||
}
|
||||
|
||||
private void DrainPendingCommands(List<ChatMessage> messages)
|
||||
{
|
||||
var drained = _pendingCommands.DrainAll();
|
||||
if (drained.Count == 0)
|
||||
return;
|
||||
|
||||
var interruptingPrompts = drained.Count(x => x.Kind == AgentCommandKind.Prompt && x.RequestInterrupt);
|
||||
if (interruptingPrompts > 0)
|
||||
{
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "system",
|
||||
MetaKind = "queued_input_interrupt",
|
||||
Content = $"[queued input] {interruptingPrompts} new prompt(s) arrived during execution. Prioritize the newest user direction before continuing.",
|
||||
Timestamp = DateTime.Now,
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var item in drained)
|
||||
{
|
||||
switch (item.Kind)
|
||||
{
|
||||
case AgentCommandKind.Notification:
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "system",
|
||||
MetaKind = "queue_notification",
|
||||
Content = item.Content,
|
||||
Timestamp = item.CreatedAt,
|
||||
});
|
||||
EmitEvent(AgentEventType.Thinking, "", item.Content);
|
||||
break;
|
||||
|
||||
default:
|
||||
messages.Add(new ChatMessage
|
||||
{
|
||||
Role = "user",
|
||||
MetaKind = item.RequestInterrupt ? "queued_prompt_interrupt" : "queued_prompt",
|
||||
Content = item.Content,
|
||||
Timestamp = item.CreatedAt,
|
||||
});
|
||||
EmitEvent(AgentEventType.UserMessage, "", item.Content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에이전트 이벤트 스트림 (UI 바인딩용).</summary>
|
||||
@@ -179,7 +229,7 @@ public partial class AgentLoopService
|
||||
_currentRunId = Guid.NewGuid().ToString("N");
|
||||
_docFallbackAttempted = false;
|
||||
_documentPlanApproved = false;
|
||||
while (_pendingUserMessages.TryDequeue(out _)) { } // 이전 실행의 잔여 메시지 제거
|
||||
_pendingCommands.Clear(); // 이전 실행의 잔여 큐 제거
|
||||
var llm = _settings.Settings.Llm;
|
||||
var baseMax = llm.MaxAgentIterations > 0 ? llm.MaxAgentIterations : 25;
|
||||
var maxIterations = baseMax; // 동적 조정 가능
|
||||
@@ -440,11 +490,7 @@ public partial class AgentLoopService
|
||||
}
|
||||
|
||||
// ── 실행 중 사용자 메시지 주입 (Claude Code 스타일 steering) ──
|
||||
while (_pendingUserMessages.TryDequeue(out var injectedMsg))
|
||||
{
|
||||
messages.Add(new ChatMessage { Role = "user", Content = injectedMsg });
|
||||
EmitEvent(AgentEventType.UserMessage, "", injectedMsg);
|
||||
}
|
||||
DrainPendingCommands(messages);
|
||||
|
||||
var queryView = AgentQueryContextBuilder.Build(messages);
|
||||
var queryMessages = queryView.Messages;
|
||||
@@ -455,6 +501,7 @@ public partial class AgentLoopService
|
||||
$"start={queryView.WindowStartIndex}, " +
|
||||
$"pairs={queryView.PreservedToolPairCount}, " +
|
||||
$"tool_result_budget={queryView.TruncatedToolResultCount}, " +
|
||||
$"tool_result_preview_reuse={queryView.ReusedToolResultPreviewCount}, " +
|
||||
$"tokens {queryView.TokensBeforeBudget}->{queryView.TokensAfterBudget}";
|
||||
WorkflowLogService.LogTransition(
|
||||
_conversationId,
|
||||
|
||||
Reference in New Issue
Block a user