에이전트 선택적 탐색 구조 개선과 경고 정리 반영
Some checks failed
Release Gate / gate (push) Has been cancelled

- claude-code 선택적 탐색 흐름을 참고해 Cowork/Code 시스템 프롬프트에서 folder_map 상시 선행 지시를 완화하고 glob/grep 기반 좁은 탐색을 우선하도록 조정함

- FolderMapTool 기본 depth를 2로, include_files 기본값을 false로 낮추고 MultiReadTool 최대 파일 수를 8개로 줄여 초기 과탐색 폭을 보수적으로 조정함

- AgentLoopExplorationPolicy partial을 추가해 탐색 범위 분류, broad-scan corrective hint, exploration_breadth 성능 로그를 연결함

- AgentLoopService에 탐색 범위 가이드 주입과 실행 중 탐색 폭 추적을 추가하고, 좁은 질문에서 반복적인 folder_map/대량 multi_read를 교정하도록 정리함

- DocxToHtmlConverter nullable 경고를 수정해 Release 빌드 경고 0 / 오류 0 기준을 다시 충족함

- README와 docs/DEVELOPMENT.md에 2026-04-09 10:36 (KST) 기준 개발 이력을 반영함
This commit is contained in:
2026-04-09 14:27:59 +09:00
parent 7931566212
commit 33c1db4dae
119 changed files with 4453 additions and 6943 deletions

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Text;
using System.Text.Json;
using AxCopilot.Models;
using AxCopilot.Services.Agent;
namespace AxCopilot.Services;
@@ -114,34 +115,34 @@ public class McpClientService : IDisposable
try
{
if (result.Value.TryGetProperty("tools", out var toolsArr))
if (result.Value.SafeTryGetProperty("tools", out var toolsArr))
{
foreach (var tool in toolsArr.EnumerateArray())
{
var def = new McpToolDefinition
{
Name = tool.GetProperty("name").GetString() ?? "",
Description = tool.TryGetProperty("description", out var desc) ? desc.GetString() ?? "" : "",
Name = tool.GetProperty("name").SafeGetString() ?? "",
Description = tool.SafeTryGetProperty("description", out var desc) ? desc.SafeGetString() ?? "" : "",
ServerName = _config.Name,
};
if (tool.TryGetProperty("inputSchema", out var schema) &&
schema.TryGetProperty("properties", out var props))
if (tool.SafeTryGetProperty("inputSchema", out var schema) &&
schema.SafeTryGetProperty("properties", out var props))
{
foreach (var prop in props.EnumerateObject())
{
def.Parameters[prop.Name] = new McpParameterDef
{
Type = prop.Value.TryGetProperty("type", out var t) ? t.GetString() ?? "string" : "string",
Description = prop.Value.TryGetProperty("description", out var d) ? d.GetString() ?? "" : "",
Type = prop.Value.SafeTryGetProperty("type", out var t) ? t.SafeGetString() ?? "string" : "string",
Description = prop.Value.SafeTryGetProperty("description", out var d) ? d.SafeGetString() ?? "" : "",
};
}
if (schema.TryGetProperty("required", out var reqArr))
if (schema.SafeTryGetProperty("required", out var reqArr))
{
foreach (var req in reqArr.EnumerateArray())
{
var reqName = req.GetString();
var reqName = req.SafeGetString();
if (reqName != null && def.Parameters.TryGetValue(reqName, out var p))
p.Required = true;
}
@@ -167,16 +168,16 @@ public class McpClientService : IDisposable
try
{
if (result.Value.TryGetProperty("resources", out var resourcesArr))
if (result.Value.SafeTryGetProperty("resources", out var resourcesArr))
{
foreach (var resource in resourcesArr.EnumerateArray())
{
_resources.Add(new McpResourceDefinition
{
Uri = resource.TryGetProperty("uri", out var uri) ? uri.GetString() ?? "" : "",
Name = resource.TryGetProperty("name", out var name) ? name.GetString() ?? "" : "",
Description = resource.TryGetProperty("description", out var desc) ? desc.GetString() ?? "" : "",
MimeType = resource.TryGetProperty("mimeType", out var mime) ? mime.GetString() ?? "" : "",
Uri = resource.SafeTryGetProperty("uri", out var uri) ? uri.SafeGetString() ?? "" : "",
Name = resource.SafeTryGetProperty("name", out var name) ? name.SafeGetString() ?? "" : "",
Description = resource.SafeTryGetProperty("description", out var desc) ? desc.SafeGetString() ?? "" : "",
MimeType = resource.SafeTryGetProperty("mimeType", out var mime) ? mime.SafeGetString() ?? "" : "",
ServerName = _config.Name,
});
}
@@ -201,18 +202,18 @@ public class McpClientService : IDisposable
try
{
if (result.Value.TryGetProperty("content", out var contentArr))
if (result.Value.SafeTryGetProperty("content", out var contentArr))
{
var sb = new StringBuilder();
foreach (var item in contentArr.EnumerateArray())
{
if (item.TryGetProperty("text", out var text))
sb.AppendLine(text.GetString());
if (item.SafeTryGetProperty("text", out var text))
sb.AppendLine(text.SafeGetString());
}
return sb.ToString().TrimEnd();
}
if (result.Value.TryGetProperty("isError", out var isErr) && isErr.GetBoolean())
if (result.Value.SafeTryGetProperty("isError", out var isErr) && isErr.GetBoolean())
{
return $"[MCP 오류] {result}";
}
@@ -235,15 +236,15 @@ public class McpClientService : IDisposable
try
{
if (result.Value.TryGetProperty("contents", out var contentsArr))
if (result.Value.SafeTryGetProperty("contents", out var contentsArr))
{
var sb = new StringBuilder();
foreach (var item in contentsArr.EnumerateArray())
{
if (item.TryGetProperty("text", out var text))
sb.AppendLine(text.GetString());
else if (item.TryGetProperty("uri", out var itemUri))
sb.AppendLine($"uri: {itemUri.GetString()}");
if (item.SafeTryGetProperty("text", out var text))
sb.AppendLine(text.SafeGetString());
else if (item.SafeTryGetProperty("uri", out var itemUri))
sb.AppendLine($"uri: {itemUri.SafeGetString()}");
}
return sb.ToString().TrimEnd();
}
@@ -285,14 +286,14 @@ public class McpClientService : IDisposable
var root = doc.RootElement;
// notification은 건너뛰기
if (!root.TryGetProperty("id", out _)) continue;
if (!root.SafeTryGetProperty("id", out _)) continue;
if (root.TryGetProperty("result", out var result))
if (root.SafeTryGetProperty("result", out var result))
return result;
if (root.TryGetProperty("error", out var error))
if (root.SafeTryGetProperty("error", out var error))
{
var msg = error.TryGetProperty("message", out var m) ? m.GetString() : "Unknown error";
var msg = error.SafeTryGetProperty("message", out var m) ? m.SafeGetString() : "Unknown error";
LogService.Warn($"MCP '{_config.Name}' RPC 오류: {msg}");
return null;
}