From d2b18958c1a03fee539f93cd117733924416ef33 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Sat, 31 May 2025 20:51:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=8F=90=E7=A4=BA=E8=AF=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=82=E6=95=B0=EF=BC=8C=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=B8=8ETool=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LinkToolAddin.csproj | 1 - client/prompt/DynamicPrompt.cs | 30 +++++++++++++++++------- client/prompt/PromptTemplates.cs | 1 + doc/TodoList.md | 15 ++++++++++++ host/Gateway.cs | 36 ++++++++++++++-------------- host/PromptServerList.cs | 40 ++++++++++++++++++++++++++++++++ host/prompt/PromptDefinition.cs | 9 +++++++ host/prompt/PromptRequest.cs | 10 ++++++++ host/prompt/PromptServer.cs | 32 +++++++++++++++++++++++++ host/prompt/UserPrompt.cs | 11 +++++++-- 10 files changed, 156 insertions(+), 29 deletions(-) create mode 100644 doc/TodoList.md create mode 100644 host/PromptServerList.cs create mode 100644 host/prompt/PromptDefinition.cs create mode 100644 host/prompt/PromptRequest.cs create mode 100644 host/prompt/PromptServer.cs diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj index a4143cc..c53da32 100644 --- a/LinkToolAddin.csproj +++ b/LinkToolAddin.csproj @@ -98,7 +98,6 @@ - diff --git a/client/prompt/DynamicPrompt.cs b/client/prompt/DynamicPrompt.cs index 52d695c..c63c9fa 100644 --- a/client/prompt/DynamicPrompt.cs +++ b/client/prompt/DynamicPrompt.cs @@ -1,29 +1,41 @@ using System.Collections.Generic; +using LinkToolAddin.host; +using LinkToolAddin.host.prompt; namespace LinkToolAddin.client.prompt; public class DynamicPrompt { - public static string GetPrompt(string name,Dictionary args = null) + public static string GetPrompt(string name,Dictionary args = null) { - PromptTemplates promptTemplate = new PromptTemplates(); - string template = promptTemplate.GetPrompt(name); + PromptServerList promptServerList = new PromptServerList(); + string template = promptServerList.GetPromptServer(name).Content; if (args == null) { return template; } - foreach (KeyValuePair pair in args) + foreach (KeyValuePair pair in args) { string replaceKey = "{{"+pair.Key+"}}"; - template.Replace(replaceKey, pair.Value.ToString()); + template = template.Replace(replaceKey, pair.Value.ToString()); } return template; } - public static Dictionary GetAllPrompts() + public static List GetAllPrompts() { - PromptTemplates promptTemplate = new PromptTemplates(); - Dictionary template = promptTemplate.GetPromptsDict(); - return template; + PromptServerList promptServerList = new PromptServerList(); + List prompts = new List(); + Dictionary promptDefinitions = promptServerList.GetPromptsDict(); + foreach (KeyValuePair pair in promptDefinitions) + { + prompts.Add(new UserPrompt() + { + Name = pair.Value.Name, + Description = pair.Value.Description, + Arguments = pair.Value.Arguments + }); + } + return prompts; } } \ No newline at end of file diff --git a/client/prompt/PromptTemplates.cs b/client/prompt/PromptTemplates.cs index 7e0e18f..6f9eaa1 100644 --- a/client/prompt/PromptTemplates.cs +++ b/client/prompt/PromptTemplates.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using LinkToolAddin.host.prompt; namespace LinkToolAddin.client.prompt; diff --git a/doc/TodoList.md b/doc/TodoList.md new file mode 100644 index 0000000..fd0ae5a --- /dev/null +++ b/doc/TodoList.md @@ -0,0 +1,15 @@ +# 待办事项 + +本文档用于记录和跟踪当前阶段待办事项及完成进度 + +## 核心程序 + +- [ ] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述 +- [ ] 接入本地文件系统等基础性MCP服务 +- [ ] Python代码执行的实现 +- [ ] 适配qwen3、deepseek等推理模型并迁移 +- [ ] 工具执行错误消息合并为一条 + +## 提示词工程 + +## 前端交互 \ No newline at end of file diff --git a/host/Gateway.cs b/host/Gateway.cs index f59cea8..8eb71ad 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -204,7 +204,7 @@ public class Gateway Dictionary promptParams = JsonConvert.DeserializeObject>(promptArgs); string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName; string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName; - string promptRes = DynamicPrompt.GetPrompt(promptName, promptParams); + string promptRes = DynamicPrompt.GetPrompt(promptName, null); messages.Add(new Message { Role = "user", @@ -271,8 +271,9 @@ public class Gateway }); goOn = true; string toolPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>"; - string promptPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>"; + string promptPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>"; McpServerList mcpServerList = new McpServerList(); + PromptServerList promptServerList = new PromptServerList(); int loop = 0; string messageContent = ""; //一次请求下完整的response while (goOn) @@ -293,7 +294,7 @@ public class Gateway }; long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); List mcpToolRequests = new List(); - List promptKeys = new List(); + List promptRequests = new List(); var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern); var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern); if (toolMatched == "" && promptMatched == "" && messageContent != "") @@ -338,7 +339,15 @@ public class Gateway callback?.Invoke(chatMessageListItem); XElement promptUse = XElement.Parse(matchedPrompt); string promptKey = promptUse.Element("name")?.Value; - promptKeys.Add(promptKey); + string promptArgs = promptUse.Element("arguments")?.Value; + Dictionary promptParams = JsonConvert.DeserializeObject>(promptArgs); + promptRequests = new List(); + promptRequests.Add(new PromptRequest() + { + PromptName = promptKey, + PromptArgs = promptParams, + PromptServer = promptServerList.GetPromptServer(promptKey) + }); } } else @@ -489,9 +498,9 @@ public class Gateway } } /*统一处理本次请求中的Prompt调用需求*/ - foreach (string promptKey in promptKeys) + foreach (PromptRequest promptRequest in promptRequests) { - string promptContent = DynamicPrompt.GetPrompt(promptKey); + string promptContent = DynamicPrompt.GetPrompt(promptRequest.PromptName, promptRequest.PromptArgs); messages.Add(new Message { Role = "user", @@ -503,7 +512,7 @@ public class Gateway toolParams = null, type = MessageType.TOOL_MESSAGE, status = "success", - content = "成功调用提示词:"+promptKey, + content = "成功调用提示词:"+promptRequest.PromptName, id = (timestamp+1).ToString() }; callback?.Invoke(toolMessageItem); @@ -588,17 +597,10 @@ public class Gateway } } - Dictionary prompts = DynamicPrompt.GetAllPrompts(); - foreach (KeyValuePair prompt in prompts) + List prompts = DynamicPrompt.GetAllPrompts(); + foreach (UserPrompt userPrompt in prompts) { - McpPromptDefinition promptDefinition = new McpPromptDefinition - { - Prompt = new LinkToolAddin.host.mcp.Prompt - { - Name = prompt.Key - } - }; - XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(promptDefinition)); + XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt})); toolInfos.AppendLine(node.ToString()); toolInfos.AppendLine(); } diff --git a/host/PromptServerList.cs b/host/PromptServerList.cs new file mode 100644 index 0000000..e4a5cd5 --- /dev/null +++ b/host/PromptServerList.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using LinkToolAddin.host.prompt; + +namespace LinkToolAddin.host; + +public class PromptServerList +{ + Dictionary promptServers = new Dictionary(); + + public PromptServerList() + { + promptServers.Add("plan", new PromptServer("plan", + "根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表", + "请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具’,如果是ArcGIS Pro的工具,根据用户的具体需求和提供的数据类型," + + "判断并列出所有必要的分析步骤和工具,同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如:analysis.Erase)," + + "确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" + + "有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。")); + promptServers.Add("param", new PromptServer("param", + "填写ArcGIS Pro工具调用参数,生成规范的可执行的工具调用请求", + "根据帮助文档填写工具参数,请你根据“所需调用工具”,参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" + + "列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" + + "不能更改所需调用工具的名称。例如:arcpy.analysis.Buffer,不能只写成Buffer。确保格式、参数的完整性和准确性,避免任何遗漏或错误。" + + "特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具(Erase),将圆形要素(circle.shp)与方形要素(square.shp)进行空间叠加运算。" + + "输出: \"in_features\":\"circle.shp\",\r\n \"erase_features\":\"sqaure.shp\",\r\n \"out_feature_class\":\"res.shp\",\r\n \"cluster_tolerance\":\"1\"")); + promptServers.Add("code", new PromptServer("code", + "生成可运行的arcpy代码", + "根据你在多种编程语言、框架、设计模式和最佳实践方面拥有的广泛知识。现在需要根据用户需求生成高质量的代码,并确保语法正确。" + + "编写Arcpy代码时必须符合ArcGIS官方文档要求。参考官方文档的方法参数,确保编写正确。")); + } + + public Dictionary GetPromptsDict() + { + return promptServers; + } + + public PromptServer GetPromptServer(string key) + { + return promptServers[key]; + } +} \ No newline at end of file diff --git a/host/prompt/PromptDefinition.cs b/host/prompt/PromptDefinition.cs new file mode 100644 index 0000000..59f93fd --- /dev/null +++ b/host/prompt/PromptDefinition.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace LinkToolAddin.host.prompt; + +public class PromptDefinition +{ + [JsonProperty("prompt")] + public UserPrompt UserPrompt { get; set; } +} \ No newline at end of file diff --git a/host/prompt/PromptRequest.cs b/host/prompt/PromptRequest.cs new file mode 100644 index 0000000..ffbb966 --- /dev/null +++ b/host/prompt/PromptRequest.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LinkToolAddin.host.prompt; + +public class PromptRequest +{ + public PromptServer PromptServer { get; set; } + public string PromptName { get; set; } + public Dictionary PromptArgs { get; set; } +} \ No newline at end of file diff --git a/host/prompt/PromptServer.cs b/host/prompt/PromptServer.cs new file mode 100644 index 0000000..709c4a7 --- /dev/null +++ b/host/prompt/PromptServer.cs @@ -0,0 +1,32 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace LinkToolAddin.host.prompt; + +public class PromptServer +{ + public string Name { get; set; } + public string Description { get; set; } + public string Arguments { get; set; } + public string Content { get; set; } + + public PromptServer(string name, string description, string content) + { + Name = name; + Description = description; + Content = content; + string pattern = "{{([\\s\\S]*?)}}"; + var matches = Regex.Matches(content, pattern); + StringBuilder args = new StringBuilder(); + foreach (var match in matches) + { + string variableName = match.ToString().Replace("{{","").Replace("}}",""); + string arg = "{ \"" + variableName + "\" : \"type\" : \" string \" }"; + args.Append(arg); + } + if (args.ToString() != string.Empty) + { + Arguments = "{\"type\":\"object\",\"properties\":" + args.ToString() + "}"; + } + } +} \ No newline at end of file diff --git a/host/prompt/UserPrompt.cs b/host/prompt/UserPrompt.cs index 128de5b..00c1cd8 100644 --- a/host/prompt/UserPrompt.cs +++ b/host/prompt/UserPrompt.cs @@ -1,6 +1,13 @@ -namespace LinkToolAddin.host.prompt; +using Newtonsoft.Json; + +namespace LinkToolAddin.host.prompt; public class UserPrompt { - + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("description")] + public string Description { get; set; } + [JsonProperty("arguments")] + public string Arguments { get; set; } } \ No newline at end of file