에이전트 큐 우선순위 소비와 tool_result preview 재사용 안정화
목적: - claude-code 대비 남아 있던 에이전틱 루프/큐/컨텍스트 격차를 줄이기 위한 1차 배치를 반영한다. - 낮은 우선순위 알림이 같은 턴에 섞여 들어오던 흐름과 tool_result preview가 MsgId에만 묶여 재사용되던 한계를 개선한다. 핵심 수정: - AgentCommandQueue를 snapshot/peek/dequeue/dequeueAllMatching/dequeuePriorityBatch를 지원하는 우선순위 큐로 재구성했다. - AgentLoopService가 전체 큐를 한 번에 비우지 않고 같은 우선순위 배치만 소비하도록 조정했다. - AgentToolResultBudget가 QueryPreviewContent를 tool_use_id 단위로 재사용하도록 확장해 재구성된 tool_result 메시지에서도 동일 preview를 유지한다. - AgentCommandQueueTests, AgentToolResultBudgetTests를 확장해 priority batch dequeue, predicate matching, tool_use_id preview reuse를 회귀 검증한다. - README와 DEVELOPMENT 문서에 2026-04-15 07:00 (KST) 기준 이력과 다음 통합 고도화 계획을 반영했다. 검증: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_queue_preview\ -p:IntermediateOutputPath=obj\verify_queue_preview\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentCommandQueueTests^|AgentToolResultBudgetTests -p:OutputPath=bin\verify_queue_preview_tests\ -p:IntermediateOutputPath=obj\verify_queue_preview_tests"
This commit is contained in:
@@ -52,4 +52,39 @@ public class AgentCommandQueueTests
|
||||
drained[1].RequestInterrupt.Should().BeTrue();
|
||||
drained[2].RequestInterrupt.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DequeuePriorityBatch_ShouldLeaveLowerPriorityItemsQueued()
|
||||
{
|
||||
var queue = new AgentCommandQueue();
|
||||
queue.EnqueueNotification("later-note");
|
||||
queue.EnqueuePrompt("next-prompt");
|
||||
queue.EnqueueSteering("now-steer");
|
||||
|
||||
var firstBatch = queue.DequeuePriorityBatch();
|
||||
var secondBatch = queue.DequeuePriorityBatch();
|
||||
var thirdBatch = queue.DequeuePriorityBatch();
|
||||
|
||||
firstBatch.Select(x => x.Content).Should().Equal("now-steer");
|
||||
secondBatch.Select(x => x.Content).Should().Equal("next-prompt");
|
||||
thirdBatch.Select(x => x.Content).Should().Equal("later-note");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PeekAndDequeueAllMatching_ShouldHonorPredicate()
|
||||
{
|
||||
var queue = new AgentCommandQueue();
|
||||
queue.EnqueueNotification("later-note");
|
||||
queue.EnqueuePrompt("next-prompt");
|
||||
queue.EnqueueSteering("now-steer");
|
||||
|
||||
var peeked = queue.Peek(x => x.Kind == AgentCommandKind.Notification);
|
||||
var removed = queue.DequeueAllMatching(x => x.Kind == AgentCommandKind.Notification);
|
||||
var remaining = queue.DrainAll();
|
||||
|
||||
peeked.Should().NotBeNull();
|
||||
peeked!.Content.Should().Be("later-note");
|
||||
removed.Select(x => x.Content).Should().Equal("later-note");
|
||||
remaining.Select(x => x.Content).Should().Equal("now-steer", "next-prompt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,47 @@ public class AgentToolResultBudgetTests
|
||||
second.ReusedPreviewCount.Should().Be(1);
|
||||
secondWindow[0].Content.Should().Be(sourceMessages[0].QueryPreviewContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_ShouldReusePreviewByToolUseIdAcrossClonedMessages()
|
||||
{
|
||||
var longContent = new string('B', 1500);
|
||||
var sourceMessages = new List<ChatMessage>
|
||||
{
|
||||
new()
|
||||
{
|
||||
MsgId = "source-tool",
|
||||
Role = "user",
|
||||
Content = $$"""{"type":"tool_result","tool_use_id":"call-shared","tool_name":"file_read","content":"{{longContent}}"}""",
|
||||
QueryPreviewContent = """{"type":"tool_result","tool_use_id":"call-shared","tool_name":"file_read","content":"preview"}"""
|
||||
},
|
||||
new()
|
||||
{
|
||||
MsgId = "tail-1",
|
||||
Role = "assistant",
|
||||
Content = "recent tail"
|
||||
}
|
||||
};
|
||||
|
||||
var queryView = new List<ChatMessage>
|
||||
{
|
||||
new()
|
||||
{
|
||||
MsgId = "rebuilt-tool",
|
||||
Role = "user",
|
||||
Content = $$"""{"type":"tool_result","tool_use_id":"call-shared","tool_name":"file_read","content":"{{longContent}}"}"""
|
||||
},
|
||||
new()
|
||||
{
|
||||
MsgId = "tail-2",
|
||||
Role = "assistant",
|
||||
Content = "recent tail"
|
||||
}
|
||||
};
|
||||
|
||||
var result = AgentToolResultBudget.Apply(queryView, protectedRecentNonSystemMessages: 1, sourceMessages: sourceMessages);
|
||||
|
||||
result.ReusedPreviewCount.Should().Be(1);
|
||||
queryView[0].Content.Should().Be(sourceMessages[0].QueryPreviewContent);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user