210 lines
7.7 KiB
C#
210 lines
7.7 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
|
|
namespace AxCopilot.Services.Agent;
|
|
|
|
public class MemoryTool : IAgentTool
|
|
{
|
|
public string Name => "memory";
|
|
|
|
public string Description => "프로젝트 규칙, 사용자 선호도, 학습 내용을 저장하고 검색합니다.\n대화 간 지속되는 메모리로, 새 대화에서도 이전에 학습한 내용을 활용할 수 있습니다.\n- action=\"save\": 새 메모리 저장 (type, content 필수)\n- action=\"search\": 관련 메모리 검색 (query 필수)\n- action=\"list\": 현재 메모리 전체 목록\n- action=\"delete\": 메모리 삭제 (id 필수)\ntype 종류: rule(프로젝트 규칙), preference(사용자 선호), fact(사실), correction(실수 교정)";
|
|
|
|
public ToolParameterSchema Parameters
|
|
{
|
|
get
|
|
{
|
|
ToolParameterSchema obj = new ToolParameterSchema
|
|
{
|
|
Properties = new Dictionary<string, ToolProperty>
|
|
{
|
|
["action"] = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "save | search | list | delete"
|
|
},
|
|
["type"] = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "메모리 유형: rule | preference | fact | correction. save 시 필수."
|
|
},
|
|
["content"] = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "저장할 내용. save 시 필수."
|
|
},
|
|
["query"] = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "검색 쿼리. search 시 필수."
|
|
},
|
|
["id"] = new ToolProperty
|
|
{
|
|
Type = "string",
|
|
Description = "메모리 ID. delete 시 필수."
|
|
}
|
|
}
|
|
};
|
|
int num = 1;
|
|
List<string> list = new List<string>(num);
|
|
CollectionsMarshal.SetCount(list, num);
|
|
CollectionsMarshal.AsSpan(list)[0] = "action";
|
|
obj.Required = list;
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
|
|
{
|
|
App app = Application.Current as App;
|
|
if (!(app?.SettingsService?.Settings.Llm.EnableAgentMemory ?? true))
|
|
{
|
|
return Task.FromResult(ToolResult.Ok("에이전트 메모리가 비활성 상태입니다. 설정에서 활성화하세요."));
|
|
}
|
|
AgentMemoryService agentMemoryService = app?.MemoryService;
|
|
if (agentMemoryService == null)
|
|
{
|
|
return Task.FromResult(ToolResult.Fail("메모리 서비스를 사용할 수 없습니다."));
|
|
}
|
|
if (!args.TryGetProperty("action", out var value))
|
|
{
|
|
return Task.FromResult(ToolResult.Fail("action이 필요합니다."));
|
|
}
|
|
string text = value.GetString() ?? "";
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
ToolResult result = text switch
|
|
{
|
|
"save" => ExecuteSave(args, agentMemoryService, context),
|
|
"search" => ExecuteSearch(args, agentMemoryService),
|
|
"list" => ExecuteList(agentMemoryService),
|
|
"delete" => ExecuteDelete(args, agentMemoryService),
|
|
_ => ToolResult.Fail("알 수 없는 액션: " + text + ". save | search | list | delete 중 선택하세요."),
|
|
};
|
|
if (1 == 0)
|
|
{
|
|
}
|
|
return Task.FromResult(result);
|
|
}
|
|
|
|
private static ToolResult ExecuteSave(JsonElement args, AgentMemoryService svc, AgentContext context)
|
|
{
|
|
JsonElement value;
|
|
string text = (args.TryGetProperty("type", out value) ? (value.GetString() ?? "fact") : "fact");
|
|
JsonElement value2;
|
|
string text2 = (args.TryGetProperty("content", out value2) ? (value2.GetString() ?? "") : "");
|
|
if (string.IsNullOrWhiteSpace(text2))
|
|
{
|
|
return ToolResult.Fail("content가 필요합니다.");
|
|
}
|
|
string[] source = new string[4] { "rule", "preference", "fact", "correction" };
|
|
if (!source.Contains(text))
|
|
{
|
|
return ToolResult.Fail("잘못된 type: " + text + ". rule | preference | fact | correction 중 선택하세요.");
|
|
}
|
|
string workFolder = (string.IsNullOrEmpty(context.WorkFolder) ? null : context.WorkFolder);
|
|
MemoryEntry memoryEntry = svc.Add(text, text2, "agent:" + context.ActiveTab, workFolder);
|
|
return ToolResult.Ok($"메모리 저장됨 [{memoryEntry.Type}] (ID: {memoryEntry.Id}): {memoryEntry.Content}");
|
|
}
|
|
|
|
private static ToolResult ExecuteSearch(JsonElement args, AgentMemoryService svc)
|
|
{
|
|
JsonElement value;
|
|
string text = (args.TryGetProperty("query", out value) ? (value.GetString() ?? "") : "");
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return ToolResult.Fail("query가 필요합니다.");
|
|
}
|
|
List<MemoryEntry> relevant = svc.GetRelevant(text);
|
|
if (relevant.Count == 0)
|
|
{
|
|
return ToolResult.Ok("관련 메모리가 없습니다.");
|
|
}
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
StringBuilder stringBuilder2 = stringBuilder;
|
|
StringBuilder stringBuilder3 = stringBuilder2;
|
|
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
|
|
handler.AppendLiteral("관련 메모리 ");
|
|
handler.AppendFormatted(relevant.Count);
|
|
handler.AppendLiteral("개:");
|
|
stringBuilder3.AppendLine(ref handler);
|
|
foreach (MemoryEntry item in relevant)
|
|
{
|
|
stringBuilder2 = stringBuilder;
|
|
StringBuilder stringBuilder4 = stringBuilder2;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(18, 4, stringBuilder2);
|
|
handler.AppendLiteral(" [");
|
|
handler.AppendFormatted(item.Type);
|
|
handler.AppendLiteral("] ");
|
|
handler.AppendFormatted(item.Content);
|
|
handler.AppendLiteral(" (사용 ");
|
|
handler.AppendFormatted(item.UseCount);
|
|
handler.AppendLiteral("회, ID: ");
|
|
handler.AppendFormatted(item.Id);
|
|
handler.AppendLiteral(")");
|
|
stringBuilder4.AppendLine(ref handler);
|
|
}
|
|
return ToolResult.Ok(stringBuilder.ToString());
|
|
}
|
|
|
|
private static ToolResult ExecuteList(AgentMemoryService svc)
|
|
{
|
|
IReadOnlyList<MemoryEntry> all = svc.All;
|
|
if (all.Count == 0)
|
|
{
|
|
return ToolResult.Ok("저장된 메모리가 없습니다.");
|
|
}
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
StringBuilder stringBuilder2 = stringBuilder;
|
|
StringBuilder stringBuilder3 = stringBuilder2;
|
|
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
|
|
handler.AppendLiteral("전체 메모리 ");
|
|
handler.AppendFormatted(all.Count);
|
|
handler.AppendLiteral("개:");
|
|
stringBuilder3.AppendLine(ref handler);
|
|
foreach (IGrouping<string, MemoryEntry> item in from e in all
|
|
group e by e.Type)
|
|
{
|
|
stringBuilder2 = stringBuilder;
|
|
StringBuilder stringBuilder4 = stringBuilder2;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2);
|
|
handler.AppendLiteral("\n[");
|
|
handler.AppendFormatted(item.Key);
|
|
handler.AppendLiteral("]");
|
|
stringBuilder4.AppendLine(ref handler);
|
|
foreach (MemoryEntry item2 in item.OrderByDescending((MemoryEntry e) => e.UseCount))
|
|
{
|
|
stringBuilder2 = stringBuilder;
|
|
StringBuilder stringBuilder5 = stringBuilder2;
|
|
handler = new StringBuilder.AppendInterpolatedStringHandler(17, 3, stringBuilder2);
|
|
handler.AppendLiteral(" • ");
|
|
handler.AppendFormatted(item2.Content);
|
|
handler.AppendLiteral(" (사용 ");
|
|
handler.AppendFormatted(item2.UseCount);
|
|
handler.AppendLiteral("회, ID: ");
|
|
handler.AppendFormatted(item2.Id);
|
|
handler.AppendLiteral(")");
|
|
stringBuilder5.AppendLine(ref handler);
|
|
}
|
|
}
|
|
return ToolResult.Ok(stringBuilder.ToString());
|
|
}
|
|
|
|
private static ToolResult ExecuteDelete(JsonElement args, AgentMemoryService svc)
|
|
{
|
|
JsonElement value;
|
|
string text = (args.TryGetProperty("id", out value) ? (value.GetString() ?? "") : "");
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return ToolResult.Fail("id가 필요합니다.");
|
|
}
|
|
return svc.Remove(text) ? ToolResult.Ok("메모리 삭제됨 (ID: " + text + ")") : ToolResult.Fail("해당 ID의 메모리를 찾을 수 없습니다: " + text);
|
|
}
|
|
}
|