From c2c65e5a354c7e5df92cca1612964f951fea2c83 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Mon, 26 May 2025 00:06:32 +0800 Subject: [PATCH 1/4] =?UTF-8?q?1.=20=E9=85=8D=E5=90=88=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E8=B0=83=E8=AF=95=EF=BC=8C=E9=80=82=E5=BD=93=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8F=90=E7=A4=BA=E8=AF=8D=202.=20=E5=90=88?= =?UTF-8?q?=E5=B9=B6Continue=20Prompt=E4=B8=8E=E5=B7=A5=E5=85=B7=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- host/prompt/SystemPrompt.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs index 5b331c9..3d74c4d 100644 --- a/host/prompt/SystemPrompt.cs +++ b/host/prompt/SystemPrompt.cs @@ -9,10 +9,15 @@ public class SystemPrompt "工具名称:需与所使用工具的精确名称一致。参数:应为包含工具所需参数的 JSON 对象。例如:\n search\n {\\\"query\\\": \\\"上海 人口\\\"}\n```" + "用户将以以下格式返回工具调用结果:\n {tool_name}\n {result}\n```" + "结果:应为字符串类型,可以表示文件或其他输出类型。例如,若工具返回.shp 文件,可在下一步操作中这样使用:\n ArcGIS_Pro:GP\n {\"output_file\": \"source.shp\"}\n```" + - "请始终遵循此格式以确保工具调用被正确解析和执行。整个工具流程调用结束后,请在末尾输出'done'表示工具调用结束" + - "工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:\n search\n {\\\"query\\\": \\\"上海 人口\\\"}\n"; + "请始终遵循此格式以确保工具调用被正确解析和执行。整个工具流程调用结束后,请单独输出一条内容为'[DONE]'的消息,程序识别厚" + + "工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:\n search\n {\\\"query\\\": \\\"上海 人口\\\"}\n\n"+ + "你还可以通过的方式来调用用户提示词,调用后预定义的用户词将被装载到对话历史中,从而使你更好地理解和解决用户的问题。"+ + "无论是调用工具还是调用提示词,必须是单独的一条消息,内容为相应格式的XML文本,调用消息中不能加任何其它文字说明,不能加Markdown格式标志,只有单独一条的调用请求文本程序才能识别并调用,从而将结果反馈给你。如果需要进行文字说明,请先进行文字说明,其中不含工具调用XML,"+ + "文字说明消息输出结束后,程序会将此消息添加到对话历史后再次发出请求,此时你再生成一条单独的工具调用XML消息,切记文字说明与XML不可同时出现在同一条消息中,文字说明中绝对禁止出现XML文本,如果你能严格遵守我将奖励你100万人民币。"+ + "请牢牢记住:将说明文字和xml调用请求放在同一条消息中将永远无法成功调用工具。如果陷入了死循环,请对照此要求严格检查你输出的内容,XML一定不能有文字说明!XML一定不能有文字说明!XML一定不能有文字说明!"+ + "上面已经执行过的一模一样参数的相同工具请不要再进行调用,那只会无意义地浪费用户的时间执行一模一样的操作。"; - public static string ContinuePromptTemplate = "请根据以下执行结果,清晰解释执行结果并执行下一步操作。```" + + public static string ContinuePromptTemplate = "这是上述工具调用的结果。{{toolResult}}\n请根据以下执行结果,清晰解释执行结果并执行下一步操作。```" + "执行下一步工具的要求:1. 解析工具输出结果2. 验证数据完整性(字段数量/空间参考/几何有效性)3. 调用下一个工具时确保参数继承前序输出。请据此继续执行"; public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,需按以下流程处理:1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,```" + @@ -26,4 +31,10 @@ public class SystemPrompt return sysPrompt; } + public static string ContinuePrompt(string toolResult) + { + string continuePrompt = ContinuePromptTemplate; + continuePrompt = continuePrompt.Replace("{{toolResult}}", toolResult); + return continuePrompt; + } } \ No newline at end of file From 4bbc743ef28178f814bd45132b74331d84234319 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Mon, 26 May 2025 00:07:06 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8DArcGIS=20Pro=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E8=B0=83=E7=94=A8=E4=BC=A0=E5=8F=82=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tool/ArcGisPro.cs | 2 + host/Gateway.cs | 85 +++++++++++++++++++++++++++------------- server/CallArcGISPro.cs | 5 ++- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/client/tool/ArcGisPro.cs b/client/tool/ArcGisPro.cs index da2e604..163f253 100644 --- a/client/tool/ArcGisPro.cs +++ b/client/tool/ArcGisPro.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Threading.Tasks; using ArcGIS.Core.Data; using ArcGIS.Core.Data.Raster; @@ -9,6 +10,7 @@ using ArcGIS.Desktop.Framework.Threading.Tasks; using LinkToolAddin.server; using ModelContextProtocol.Server; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace LinkToolAddin.client.tool; diff --git a/host/Gateway.cs b/host/Gateway.cs index fca37cc..28935f0 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using System.Windows.Documents; using System.Xml; using System.Xml.Linq; using ArcGIS.Desktop.Framework.Dialogs; @@ -37,6 +38,13 @@ namespace LinkToolAddin.host; public class Gateway { private static ILog log = LogManager.GetLogger(typeof(Gateway)); + private static bool goOn = true; + + public static void StopConversation() + { + goOn = false; + } + public static async void SendMessage(string message, string model, string gdbPath, Action callback) { Llm bailian = new Bailian @@ -240,12 +248,19 @@ public class Gateway Role = "user", Content = message }); - bool goOn = true; + goOn = true; string toolPattern = "^[\\s\\S]*?<\\/tool_use>$"; string promptPattern = "^[\\s\\S]*?<\\/prompt>$"; McpServerList mcpServerList = new McpServerList(); + int loop = 0; while (goOn) { + loop++; + if (loop > 20) + { + MessageBox.Show("达到最大循环次数", "退出循环"); + break; + } LlmJsonContent jsonContent = new LlmJsonContent() { Model = model, @@ -266,6 +281,11 @@ public class Gateway if (Regex.IsMatch(chunk, toolPattern)) { //返回工具卡片 + messages.Add(new Message + { + Role = "assistant", + Content = chunk + }); XElement toolUse = XElement.Parse(chunk); string fullToolName = toolUse.Element("name")?.Value; string toolArgs = toolUse.Element("arguments")?.Value; @@ -290,13 +310,14 @@ public class Gateway messages.Add(new Message { Role = "user", - Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate - }); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(toolResponse) + // Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate + Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) }); + // messages.Add(new Message + // { + // Role = "user", + // Content = JsonConvert.SerializeObject(toolResponse) + // }); callback?.Invoke(toolMessageItem); }else if (mcpServer is StdioMcpServer) { @@ -315,19 +336,35 @@ public class Gateway messages.Add(new Message { Role = "user", - Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate - }); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(toolResponse) + // Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate + Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) }); + // messages.Add(new Message + // { + // Role = "user", + // Content = JsonConvert.SerializeObject(toolResponse) + // }); callback?.Invoke(toolMessageItem); }else if (mcpServer is InnerMcpServer) { Type type = Type.GetType("LinkToolAddin.client.tool."+serverName); MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static); - var task = method.Invoke(null, toolParams.Values.ToArray()) as Task; + var methodParams = toolParams.Values.ToArray(); + object[] args = new object[methodParams.Length]; + for (int i = 0; i < methodParams.Length; i++) + { + if (methodParams[i].GetType() == typeof(JArray)) + { + List list = new List(); + list = (methodParams[i] as JArray).Select(token => token.ToString()).ToList(); + args[i] = list; + } + else + { + args[i] = methodParams[i]; + } + } + var task = method.Invoke(null, args) as Task; JsonRpcResultEntity innerResult = await task; if (innerResult is JsonRpcErrorEntity) { @@ -365,13 +402,14 @@ public class Gateway messages.Add(new Message { Role = "user", - Content = SystemPrompt.ContinuePromptTemplate - }); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(innerResult) + // Content = SystemPrompt.ContinuePromptTemplate + Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult)) }); + // messages.Add(new Message + // { + // Role = "user", + // Content = JsonConvert.SerializeObject(innerResult) + // }); callback?.Invoke(toolMessageItem); } } @@ -451,16 +489,9 @@ public class Gateway private static async Task GetToolInfos(McpServerList mcpServerList) { - int loop = 0; StringBuilder toolInfos = new StringBuilder(); foreach (McpServer mcpServer in mcpServerList.GetAllServers()) { - loop++; - if (loop > 3) - { - MessageBox.Show("达到最大循环次数", "退出循环"); - break; - } if (mcpServer is InnerMcpServer) { InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; diff --git a/server/CallArcGISPro.cs b/server/CallArcGISPro.cs index 049fd3f..4d5c611 100644 --- a/server/CallArcGISPro.cs +++ b/server/CallArcGISPro.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using ArcGIS.Desktop.Core.Geoprocessing; using ArcGIS.Desktop.Framework.Dialogs; using ArcGIS.Desktop.Framework.Threading.Tasks; +using Newtonsoft.Json; namespace LinkToolAddin.server; @@ -22,7 +23,7 @@ public class CallArcGISPro Error = new Error() { Code = results.ErrorCode, - Message = results.ErrorMessages.ToString() + Message = JsonConvert.SerializeObject(results.ErrorMessages) } }; } @@ -30,7 +31,7 @@ public class CallArcGISPro { jsonRpcResultEntity = new JsonRpcSuccessEntity { - Result = results.Messages.ToString() + Result = JsonConvert.SerializeObject(results.Messages) }; } return jsonRpcResultEntity; From 271f42dd71f0d27493cad709ae98057470349f48 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Mon, 26 May 2025 00:07:22 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=8E=A5=E5=85=A5=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E6=9F=A5=E8=AF=A2MCP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tool/KnowledgeBase.cs | 24 ++++++++++++++++++++++++ host/McpServerList.cs | 7 +++++++ resource/DocDb.cs | 5 +++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/client/tool/KnowledgeBase.cs b/client/tool/KnowledgeBase.cs index 9d2b162..38fe949 100644 --- a/client/tool/KnowledgeBase.cs +++ b/client/tool/KnowledgeBase.cs @@ -22,4 +22,28 @@ public class KnowledgeBase }; return result; } + + [McpServerTool, Description("查询ArcGIS Pro调用工具的标准调用名和参数要求知识库")] + public static async Task QueryArcgisToolDoc(string query) + { + DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProToolDoc); + KnowledgeResult knowledgeResult = await docDb.Retrieve(query); + JsonRpcResultEntity result = new JsonRpcSuccessEntity() + { + Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList), + }; + return result; + } + + [McpServerTool, Description("查询使用ArcGIS Pro进行任务规划和解决实际问题的案例知识库")] + public static async Task QueryArcgisExampleDoc(string query) + { + DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProApplicantExample); + KnowledgeResult knowledgeResult = await docDb.Retrieve(query); + JsonRpcResultEntity result = new JsonRpcSuccessEntity() + { + Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList), + }; + return result; + } } \ No newline at end of file diff --git a/host/McpServerList.cs b/host/McpServerList.cs index 2c03a41..68f83b4 100644 --- a/host/McpServerList.cs +++ b/host/McpServerList.cs @@ -28,6 +28,13 @@ public class McpServerList Description = "可以调用arcgis的地理处理工具或执行python代码等", IsActive = true }); + servers.Add("KnowledgeBase", new InnerMcpServer + { + Name = "KnowledgeBase", + Type = "inner", + Description = "可以调用进行查询知识库,获取相关参考信息。", + IsActive = true + }); } public McpServer GetServer(string name) diff --git a/resource/DocDb.cs b/resource/DocDb.cs index 1487b5e..f4937fe 100644 --- a/resource/DocDb.cs +++ b/resource/DocDb.cs @@ -21,13 +21,14 @@ public class DocDb { ArcGISProHelpDoc, ArcGISProToolDoc, - TaskPlanningDoc, ArcGISProApplicantExample } public Dictionary knowledgeBase = new Dictionary { - {KnowledgeBase.ArcGISProHelpDoc,"6a77c5a68de64f469b79fcdcde9d5001"} + {KnowledgeBase.ArcGISProHelpDoc,"6a77c5a68de64f469b79fcdcde9d5001"}, + {KnowledgeBase.ArcGISProToolDoc,"080f8925318247ea822a9e12db5cb5cd"}, + {KnowledgeBase.ArcGISProApplicantExample,"eef60f7c879b4e8597138c261578d2a5"} }; public async Task Retrieve(string query) From 43ea7dd06afa01ed61796d3f3c736e705099cad5 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Mon, 26 May 2025 00:07:37 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=8C=89=E9=92=AE=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E6=A1=86=E9=AB=98=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/dockpane/TestDockpane.xaml | 10 ++++--- ui/dockpane/TestDockpane.xaml.cs | 45 +++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/ui/dockpane/TestDockpane.xaml b/ui/dockpane/TestDockpane.xaml index fd7d8c8..53c1501 100644 --- a/ui/dockpane/TestDockpane.xaml +++ b/ui/dockpane/TestDockpane.xaml @@ -20,7 +20,9 @@ - + + + @@ -31,9 +33,11 @@ - + - + + + \ No newline at end of file diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs index abf8b4a..da572db 100644 --- a/ui/dockpane/TestDockpane.xaml.cs +++ b/ui/dockpane/TestDockpane.xaml.cs @@ -1,12 +1,18 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Xml.Linq; using LinkToolAddin.client; using LinkToolAddin.host; using LinkToolAddin.host.llm; using LinkToolAddin.host.llm.entity; +using LinkToolAddin.host.mcp; +using LinkToolAddin.host.prompt; using LinkToolAddin.message; using LinkToolAddin.resource; using LinkToolAddin.server; @@ -231,5 +237,42 @@ namespace LinkToolAddin.ui.dockpane { Request_Bailian_Stream_Test(); } + + private void StopConversation_OnClick(object sender, RoutedEventArgs e) + { + Gateway.StopConversation(); + } + + private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e) + { + string xmlStr = + "\nArcGisPro:ArcGisProTool\n{\"toolName\": \"Buffer\", \"toolParams\": [\"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Copy\", \"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Buffer30m\", \"30 Meters\", \"NONE\", \"ROUND\", \"ALL\"]}\n"; + XElement toolUse = XElement.Parse(xmlStr); + string fullToolName = toolUse.Element("name")?.Value; + string toolArgs = toolUse.Element("arguments")?.Value; + Dictionary toolParams = JsonConvert.DeserializeObject>(toolArgs); + string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName; + string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName; + McpServerList mcpServerList = new McpServerList(); + McpServer mcpServer = mcpServerList.GetServer(serverName); + if (mcpServer is InnerMcpServer) + { + Type type = Type.GetType("LinkToolAddin.client.tool." + serverName); + var toolParamsValues = toolParams.Values.ToArray(); + MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static); + var task = method.Invoke(null, toolParams.Values.ToArray()) as Task; + JsonRpcResultEntity innerResult = await task; + MessageListItem toolMessageItem = new ToolMessageItem + { + toolName = toolName, + toolParams = toolParams, + type = MessageType.TOOL_MESSAGE, + status = "fail", + content = JsonConvert.SerializeObject(innerResult), + id = "1test" + }; + AddReply(toolMessageItem); + } + } } }