AX Agent 메모리 구조 2차 강화: 계층형 메모리 관리 도구 확장
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- memory 도구에 save_scope, delete_scope 액션을 추가해 managed/user/project/local 메모리 파일을 직접 저장 및 삭제할 수 있게 확장함 - search, list 액션이 학습 메모리뿐 아니라 계층형 메모리 문서도 함께 보여주도록 개선함 - AgentMemoryService에 계층형 메모리 파일 쓰기/삭제 경로와 append/remove 로직을 추가해 메모리 계층을 실제로 관리 가능한 상태로 전환함 - 검증: 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:
@@ -14,18 +14,22 @@ public class MemoryTool : IAgentTool
|
||||
public string Description =>
|
||||
"프로젝트 규칙, 사용자 선호도, 학습 내용을 저장하고 검색합니다.\n" +
|
||||
"대화 간 지속되는 메모리로, 새 대화에서도 이전에 학습한 내용을 활용할 수 있습니다.\n" +
|
||||
"- action=\"save\": 새 메모리 저장 (type, content 필수)\n" +
|
||||
"- action=\"save\": 학습 메모리 저장 (type, content 필수)\n" +
|
||||
"- action=\"save_scope\": 계층형 메모리 파일에 저장 (scope, content 필수)\n" +
|
||||
"- action=\"search\": 관련 메모리 검색 (query 필수)\n" +
|
||||
"- action=\"list\": 현재 메모리 전체 목록\n" +
|
||||
"- action=\"delete\": 메모리 삭제 (id 필수)\n" +
|
||||
"- action=\"list\": 현재 메모리 전체 목록 + 계층형 메모리 파일 목록\n" +
|
||||
"- action=\"delete\": 학습 메모리 삭제 (id 필수)\n" +
|
||||
"- action=\"delete_scope\": 계층형 메모리 파일에서 삭제 (scope, query 필수)\n" +
|
||||
"scope 종류: managed | user | project | local\n" +
|
||||
"type 종류: rule(프로젝트 규칙), preference(사용자 선호), fact(사실), correction(실수 교정)";
|
||||
|
||||
public ToolParameterSchema Parameters => new()
|
||||
{
|
||||
Properties = new()
|
||||
{
|
||||
["action"] = new() { Type = "string", Description = "save | search | list | delete" },
|
||||
["action"] = new() { Type = "string", Description = "save | save_scope | search | list | delete | delete_scope" },
|
||||
["type"] = new() { Type = "string", Description = "메모리 유형: rule | preference | fact | correction. save 시 필수." },
|
||||
["scope"] = new() { Type = "string", Description = "계층형 메모리 대상: managed | user | project | local. save_scope/delete_scope 시 필수." },
|
||||
["content"] = new() { Type = "string", Description = "저장할 내용. save 시 필수." },
|
||||
["query"] = new() { Type = "string", Description = "검색 쿼리. search 시 필수." },
|
||||
["id"] = new() { Type = "string", Description = "메모리 ID. delete 시 필수." },
|
||||
@@ -44,6 +48,8 @@ public class MemoryTool : IAgentTool
|
||||
if (memoryService == null)
|
||||
return Task.FromResult(ToolResult.Fail("메모리 서비스를 사용할 수 없습니다."));
|
||||
|
||||
memoryService.Load(context.WorkFolder);
|
||||
|
||||
if (!args.TryGetProperty("action", out var actionEl))
|
||||
return Task.FromResult(ToolResult.Fail("action이 필요합니다."));
|
||||
var action = actionEl.GetString() ?? "";
|
||||
@@ -51,10 +57,12 @@ public class MemoryTool : IAgentTool
|
||||
return Task.FromResult(action switch
|
||||
{
|
||||
"save" => ExecuteSave(args, memoryService, context),
|
||||
"save_scope" => ExecuteSaveScope(args, memoryService, context),
|
||||
"search" => ExecuteSearch(args, memoryService),
|
||||
"list" => ExecuteList(memoryService),
|
||||
"delete" => ExecuteDelete(args, memoryService),
|
||||
_ => ToolResult.Fail($"알 수 없는 액션: {action}. save | search | list | delete 중 선택하세요."),
|
||||
"delete_scope" => ExecuteDeleteScope(args, memoryService, context),
|
||||
_ => ToolResult.Fail($"알 수 없는 액션: {action}. save | save_scope | search | list | delete | delete_scope 중 선택하세요."),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,29 +90,58 @@ public class MemoryTool : IAgentTool
|
||||
return ToolResult.Fail("query가 필요합니다.");
|
||||
|
||||
var results = svc.GetRelevant(query, 10);
|
||||
if (results.Count == 0)
|
||||
var docs = svc.InstructionDocuments
|
||||
.Where(d => d.Content.Contains(query, StringComparison.OrdinalIgnoreCase)
|
||||
|| d.Path.Contains(query, StringComparison.OrdinalIgnoreCase))
|
||||
.Take(8)
|
||||
.ToList();
|
||||
|
||||
if (results.Count == 0 && docs.Count == 0)
|
||||
return ToolResult.Ok("관련 메모리가 없습니다.");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"관련 메모리 {results.Count}개:");
|
||||
foreach (var e in results)
|
||||
sb.AppendLine($" [{e.Type}] {e.Content} (사용 {e.UseCount}회, ID: {e.Id})");
|
||||
if (docs.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"계층형 메모리 {docs.Count}개:");
|
||||
foreach (var doc in docs)
|
||||
sb.AppendLine($" [{doc.Label}] {doc.Path}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (results.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"학습 메모리 {results.Count}개:");
|
||||
foreach (var e in results)
|
||||
sb.AppendLine($" [{e.Type}] {e.Content} (사용 {e.UseCount}회, ID: {e.Id})");
|
||||
}
|
||||
return ToolResult.Ok(sb.ToString());
|
||||
}
|
||||
|
||||
private static ToolResult ExecuteList(AgentMemoryService svc)
|
||||
{
|
||||
var all = svc.All;
|
||||
if (all.Count == 0)
|
||||
var docs = svc.InstructionDocuments;
|
||||
if (all.Count == 0 && docs.Count == 0)
|
||||
return ToolResult.Ok("저장된 메모리가 없습니다.");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"전체 메모리 {all.Count}개:");
|
||||
foreach (var group in all.GroupBy(e => e.Type))
|
||||
if (docs.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"\n[{group.Key}]");
|
||||
foreach (var e in group.OrderByDescending(e => e.UseCount))
|
||||
sb.AppendLine($" • {e.Content} (사용 {e.UseCount}회, ID: {e.Id})");
|
||||
sb.AppendLine($"계층형 메모리 파일 {docs.Count}개:");
|
||||
foreach (var doc in docs)
|
||||
sb.AppendLine($" • [{doc.Label}] {doc.Path}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (all.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"학습 메모리 {all.Count}개:");
|
||||
foreach (var group in all.GroupBy(e => e.Type))
|
||||
{
|
||||
sb.AppendLine($"\n[{group.Key}]");
|
||||
foreach (var e in group.OrderByDescending(e => e.UseCount))
|
||||
sb.AppendLine($" • {e.Content} (사용 {e.UseCount}회, ID: {e.Id})");
|
||||
}
|
||||
}
|
||||
return ToolResult.Ok(sb.ToString());
|
||||
}
|
||||
@@ -119,4 +156,34 @@ public class MemoryTool : IAgentTool
|
||||
? ToolResult.Ok($"메모리 삭제됨 (ID: {id})")
|
||||
: ToolResult.Fail($"해당 ID의 메모리를 찾을 수 없습니다: {id}");
|
||||
}
|
||||
|
||||
private static ToolResult ExecuteSaveScope(JsonElement args, AgentMemoryService svc, AgentContext context)
|
||||
{
|
||||
var scope = args.TryGetProperty("scope", out var s) ? s.GetString() ?? "" : "";
|
||||
var content = args.TryGetProperty("content", out var c) ? c.GetString() ?? "" : "";
|
||||
if (string.IsNullOrWhiteSpace(scope))
|
||||
return ToolResult.Fail("scope가 필요합니다. managed | user | project | local 중 선택하세요.");
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
return ToolResult.Fail("content가 필요합니다.");
|
||||
|
||||
var result = svc.SaveInstruction(scope, content, context.WorkFolder);
|
||||
return result.Path.Length == 0
|
||||
? ToolResult.Fail(result.Message)
|
||||
: ToolResult.Ok($"{result.Message}\n경로: {result.Path}");
|
||||
}
|
||||
|
||||
private static ToolResult ExecuteDeleteScope(JsonElement args, AgentMemoryService svc, AgentContext context)
|
||||
{
|
||||
var scope = args.TryGetProperty("scope", out var s) ? s.GetString() ?? "" : "";
|
||||
var query = args.TryGetProperty("query", out var q) ? q.GetString() ?? "" : "";
|
||||
if (string.IsNullOrWhiteSpace(scope))
|
||||
return ToolResult.Fail("scope가 필요합니다. managed | user | project | local 중 선택하세요.");
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return ToolResult.Fail("query가 필요합니다.");
|
||||
|
||||
var result = svc.DeleteInstruction(scope, query, context.WorkFolder);
|
||||
return result.Changed
|
||||
? ToolResult.Ok($"{result.Message}\n경로: {result.Path}")
|
||||
: ToolResult.Fail(result.Message);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user