AX Agent 도구·스킬 정합성 재구성 및 실행 품질 보강

변경 목적:
- AX Agent의 도구 이름, 내부 설정, 스킬 정책, 실행 루프 사이의 불일치를 줄이고 전체 동작 품질을 높인다.
- claw-code 수준의 일관된 동작 품질을 참고하되 AX 구조에 맞는 고유한 카탈로그·정규화 레이어로 재구성한다.

핵심 수정사항:
- 도구 canonical id, legacy alias, 탭 노출, 설정 카테고리, read-only 분류를 중앙 카탈로그로 통합했다.
- ToolRegistry, AgentLoopService, 병렬 실행 분류, 권한 처리, 훅 처리, 스킬 allowed-tools 해석이 같은 이름 체계를 사용하도록 정리했다.
- Agent 설정/일반 설정/도움말의 도구 카드와 훅 편집기, 스킬 설명을 현재 런타임 구조에 맞게 갱신했다.
- 컨텍스트 압축, intent gate, spawn agents, session learning, model prompt adapter, workspace context 관련 변경과 테스트 추가를 함께 반영했다.
- 문서 이력과 비교/로드맵 문서를 최신 상태로 갱신했다.

검증 결과:
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_toolcat\ -p:IntermediateOutputPath=obj\verify_toolcat\ : 경고 0 / 오류 0
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentToolCatalogTests -p:OutputPath=bin\verify_toolcat_tests\ -p:IntermediateOutputPath=obj\verify_toolcat_tests\ : 통과 8
This commit is contained in:
2026-04-14 17:52:46 +09:00
parent fa33b98f7e
commit 8cb08576d5
200 changed files with 13522 additions and 5764 deletions

View File

@@ -185,14 +185,46 @@ public partial class ChatWindow
Margin = new Thickness(0, 6, 0, 0),
};
var detailStack = new StackPanel();
var codeBg = TryFindResource("HintBackground") as Brush ?? Brushes.DarkGray;
// 도구 입력 파라미터 (ToolInput이 있으면 표시)
var toolInput = toolCall.ToolInput;
if (!string.IsNullOrWhiteSpace(toolInput))
{
detailStack.Children.Add(new TextBlock
{
Text = "입력",
FontSize = 10,
FontWeight = FontWeights.SemiBold,
Foreground = secondaryText,
Opacity = 0.7,
Margin = new Thickness(0, 0, 0, 3),
});
detailStack.Children.Add(MarkdownRenderer.Render(
$"```json\n{TruncateForDisplay(toolInput, 1200)}\n```",
primaryText, secondaryText, accentBrush, codeBg));
}
// 도구 결과 상세 내용
var resultSummary = toolResult.Summary;
if (!string.IsNullOrWhiteSpace(resultSummary) && resultSummary.Length > 80)
if (!string.IsNullOrWhiteSpace(resultSummary))
{
var codeBg = TryFindResource("HintBackground") as Brush ?? Brushes.DarkGray;
// 입력이 이미 있으면 "결과" 라벨 추가
if (detailStack.Children.Count > 0)
{
detailStack.Children.Add(new TextBlock
{
Text = "결과",
FontSize = 10,
FontWeight = FontWeights.SemiBold,
Foreground = secondaryText,
Opacity = 0.7,
Margin = new Thickness(0, 6, 0, 3),
});
}
detailStack.Children.Add(MarkdownRenderer.Render(
$"```\n{resultSummary}\n```", primaryText, secondaryText, accentBrush, codeBg));
$"```\n{TruncateForDisplay(resultSummary, 2000)}\n```",
primaryText, secondaryText, accentBrush, codeBg));
}
// 토큰 메타 정보
@@ -215,19 +247,22 @@ public partial class ChatWindow
}
detailBorder.Child = detailStack;
// 상세 내용이 없으면 화살표도 숨김
var hasDetail = detailStack.Children.Count > 0;
cardStack.Children.Add(detailBorder);
// 펼치기 토글 화살표
// 펼치기 토글 화살표 (상세 내용이 있을 때만 표시)
var arrowTb = new TextBlock
{
Text = "\uE76C", // 아래 화살표
FontFamily = s_segoeIconFont,
FontSize = 9,
Foreground = secondaryText,
Opacity = 0.5,
Opacity = 0.7,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 3, 0, 0),
Cursor = Cursors.Hand,
Visibility = hasDetail ? Visibility.Visible : Visibility.Collapsed,
};
cardStack.Children.Add(arrowTb);
@@ -243,11 +278,20 @@ public partial class ChatWindow
arrowTb.Text = isExpanded ? "\uE76C" : "\uE76B"; // 아래↔위
};
// 호버 효과
var normalBg = hintBg;
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? hintBg;
card.MouseEnter += (_, _) => card.Background = hoverBg;
card.MouseLeave += (_, _) => card.Background = normalBg;
// 호버 효과 (부드러운 전환)
card.MouseEnter += (_, _) =>
{
card.BeginAnimation(UIElement.OpacityProperty,
new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(120)));
card.Background = TryFindResource("ItemHoverBackground") as Brush ?? hintBg;
};
card.MouseLeave += (_, _) =>
{
card.BeginAnimation(UIElement.OpacityProperty,
new DoubleAnimation(0.92, TimeSpan.FromMilliseconds(200)));
card.Background = hintBg;
};
card.Opacity = 0.92;
return outerGrid;
}
@@ -283,7 +327,7 @@ public partial class ChatWindow
var thinkLine = new Border
{
Width = 2,
Background = new SolidColorBrush(Color.FromArgb(0x40, 0x59, 0xA5, 0xF5)),
Background = new SolidColorBrush(Color.FromArgb(0x60, 0x59, 0xA5, 0xF5)),
CornerRadius = new CornerRadius(1),
Margin = new Thickness(12, 0, 8, 0),
};
@@ -292,8 +336,8 @@ public partial class ChatWindow
var thinkCard = new Border
{
Background = new SolidColorBrush(Color.FromArgb(0x0A, 0x59, 0xA5, 0xF5)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x30, 0x59, 0xA5, 0xF5)),
Background = new SolidColorBrush(Color.FromArgb(0x1C, 0x59, 0xA5, 0xF5)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x40, 0x59, 0xA5, 0xF5)),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(10, 6, 10, 6),
@@ -316,7 +360,7 @@ public partial class ChatWindow
FontSize = 11,
FontStyle = FontStyles.Italic,
Foreground = secondaryText,
Opacity = 0.75,
Opacity = 0.88,
TextWrapping = TextWrapping.Wrap,
MaxWidth = msgMaxWidth - 60,
});
@@ -428,12 +472,12 @@ public partial class ChatWindow
var banner = new Border
{
Background = new SolidColorBrush(Color.FromArgb(0x18, 0x66, 0xBB, 0x6A)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x40, 0x66, 0xBB, 0x6A)),
Background = new SolidColorBrush(Color.FromArgb(0x24, 0x66, 0xBB, 0x6A)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x50, 0x66, 0xBB, 0x6A)),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(12, 6, 12, 6),
Margin = new Thickness(0, 4, 0, 4),
Padding = new Thickness(14, 8, 14, 8),
Margin = new Thickness(0, 6, 0, 4),
HorizontalAlignment = HorizontalAlignment.Center,
MaxWidth = msgMaxWidth,
};
@@ -465,12 +509,12 @@ public partial class ChatWindow
var msgMaxWidth = GetMessageMaxWidth();
var banner = new Border
{
Background = new SolidColorBrush(Color.FromArgb(0x18, 0xEF, 0x53, 0x50)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x40, 0xEF, 0x53, 0x50)),
Background = new SolidColorBrush(Color.FromArgb(0x24, 0xEF, 0x53, 0x50)),
BorderBrush = new SolidColorBrush(Color.FromArgb(0x50, 0xEF, 0x53, 0x50)),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(12, 6, 12, 6),
Margin = new Thickness(0, 4, 0, 4),
Padding = new Thickness(14, 8, 14, 8),
Margin = new Thickness(0, 6, 0, 4),
HorizontalAlignment = HorizontalAlignment.Center,
MaxWidth = msgMaxWidth,
};
@@ -598,4 +642,12 @@ public partial class ChatWindow
return path[..remaining] + ".../" + fileName;
}
/// <summary>긴 텍스트를 지정 길이로 잘라서 표시용으로 반환합니다.</summary>
private static string TruncateForDisplay(string text, int maxLength)
{
if (string.IsNullOrEmpty(text) || text.Length <= maxLength)
return text;
return text[..maxLength] + "\n… (truncated)";
}
}