Files

166 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
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 SkillManagerTool : IAgentTool
{
public string Name => "skill_manager";
public string Description => "마크다운 기반 스킬(워크플로우)을 관리합니다.\n- list: 사용 가능한 스킬 목록 조회\n- info: 특정 스킬의 상세 정보 확인\n- reload: 스킬 폴더를 다시 스캔하여 새 스킬 로드";
public ToolParameterSchema Parameters
{
get
{
ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
Dictionary<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
ToolProperty obj = new ToolProperty
{
Type = "string",
Description = "list (목록), info (상세정보), reload (재로드)"
};
int num = 3;
List<string> list = new List<string>(num);
CollectionsMarshal.SetCount(list, num);
Span<string> span = CollectionsMarshal.AsSpan(list);
span[0] = "list";
span[1] = "info";
span[2] = "reload";
obj.Enum = list;
dictionary["action"] = obj;
dictionary["skill_name"] = new ToolProperty
{
Type = "string",
Description = "스킬 이름 (info 액션에서 사용)"
};
toolParameterSchema.Properties = dictionary;
num = 1;
List<string> list2 = new List<string>(num);
CollectionsMarshal.SetCount(list2, num);
CollectionsMarshal.AsSpan(list2)[0] = "action";
toolParameterSchema.Required = list2;
return toolParameterSchema;
}
}
public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
{
App app = Application.Current as App;
if (!(app?.SettingsService?.Settings.Llm.EnableSkillSystem ?? true))
{
return ToolResult.Ok("스킬 시스템이 비활성 상태입니다. 설정 → AX Agent → 공통에서 활성화하세요.");
}
JsonElement a;
string action = (args.TryGetProperty("action", out a) ? (a.GetString() ?? "") : "");
JsonElement s;
string skillName = (args.TryGetProperty("skill_name", out s) ? (s.GetString() ?? "") : "");
if (1 == 0)
{
}
ToolResult result = action switch
{
"list" => ListSkills(),
"info" => InfoSkill(skillName),
"reload" => ReloadSkills(app),
_ => ToolResult.Fail("지원하지 않는 action: " + action + ". list, info, reload 중 선택하세요."),
};
if (1 == 0)
{
}
return result;
}
private static ToolResult ListSkills()
{
IReadOnlyList<SkillDefinition> skills = SkillService.Skills;
if (skills.Count == 0)
{
return ToolResult.Ok("로드된 스킬이 없습니다. %APPDATA%\\AxCopilot\\skills\\에 *.skill.md 파일을 추가하세요.");
}
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2);
handler.AppendLiteral("사용 가능한 스킬 (");
handler.AppendFormatted(skills.Count);
handler.AppendLiteral("개):\n");
stringBuilder3.AppendLine(ref handler);
foreach (SkillDefinition item in skills)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(6, 2, stringBuilder2);
handler.AppendLiteral(" /");
handler.AppendFormatted(item.Name);
handler.AppendLiteral(" — ");
handler.AppendFormatted(item.Label);
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2);
handler.AppendLiteral(" ");
handler.AppendFormatted(item.Description);
stringBuilder5.AppendLine(ref handler);
stringBuilder.AppendLine();
}
stringBuilder.AppendLine("슬래시 명령어(/{name})로 호출하거나, 대화에서 해당 워크플로우를 요청할 수 있습니다.");
return ToolResult.Ok(stringBuilder.ToString());
}
private static ToolResult InfoSkill(string name)
{
if (string.IsNullOrEmpty(name))
{
return ToolResult.Fail("skill_name이 필요합니다.");
}
SkillDefinition skillDefinition = SkillService.Find(name);
if (skillDefinition == null)
{
return ToolResult.Fail("'" + name + "' 스킬을 찾을 수 없습니다. skill_manager(action: list)로 목록을 확인하세요.");
}
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(11, 2, stringBuilder2);
handler.AppendLiteral("스킬 상세: ");
handler.AppendFormatted(skillDefinition.Label);
handler.AppendLiteral(" (/");
handler.AppendFormatted(skillDefinition.Name);
handler.AppendLiteral(")");
stringBuilder3.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2);
handler.AppendLiteral("설명: ");
handler.AppendFormatted(skillDefinition.Description);
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2);
handler.AppendLiteral("파일: ");
handler.AppendFormatted(skillDefinition.FilePath);
stringBuilder5.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(18, 1, stringBuilder2);
handler.AppendLiteral("\n--- 시스템 프롬프트 ---\n");
handler.AppendFormatted(skillDefinition.SystemPrompt);
stringBuilder6.AppendLine(ref handler);
return ToolResult.Ok(stringBuilder.ToString());
}
private static ToolResult ReloadSkills(App? app)
{
string customFolder = app?.SettingsService?.Settings.Llm.SkillsFolderPath ?? "";
SkillService.LoadSkills(customFolder);
return ToolResult.Ok($"스킬 재로드 완료. {SkillService.Skills.Count}개 로드됨.");
}
}