From d9b98df57fc07e36078228a365b35ff69b517d3f Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Mon, 19 May 2025 21:32:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=8E=A5=E5=85=A5MCP?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LinkToolAddin.csproj | 1 + Properties/launchSettings.json | 2 +- host/Gateway.cs | 53 +++++++++++++++++++--------- host/prompt/SystemPrompt.cs | 2 +- ui/dockpane/TestDockpane.xaml | 19 +++++++--- ui/dockpane/TestDockpane.xaml.cs | 18 +++++++++- ui/dockpane/TestDockpaneViewModel.cs | 2 +- 7 files changed, 72 insertions(+), 25 deletions(-) diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj index c53da32..a4143cc 100644 --- a/LinkToolAddin.csproj +++ b/LinkToolAddin.csproj @@ -98,6 +98,7 @@ + diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index e98eaad..5a2c881 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "LinkToolAddin": { "commandName": "Executable", - "executablePath": "C:\\Users\\PeterZhong\\AppData\\Local\\Programs\\ArcGIS\\Pro\\bin\\ArcGISPro.exe", + "executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe", "applicationUrl": "https://localhost:5001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/host/Gateway.cs b/host/Gateway.cs index 847fcd3..2380d4f 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -8,7 +8,9 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using System.Xml; using System.Xml.Linq; +using ArcGIS.Desktop.Framework.Dialogs; using LinkToolAddin.client; using LinkToolAddin.client.prompt; using LinkToolAddin.host.llm; @@ -23,6 +25,7 @@ using Microsoft.Extensions.AI; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Types; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; using Newtonsoft.Json.Schema.Generation; using Tool = LinkToolAddin.host.mcp.Tool; @@ -52,8 +55,8 @@ public class Gateway Content = message }); bool goOn = true; - string pattern = "[\\s\\S]*?<\\/tool_use>"; - string promptPattern = "[\\s\\S]*?<\\/prompt>"; + string pattern = "^[\\s\\S]*?<\\/tool_use>$"; + string promptPattern = "^[\\s\\S]*?<\\/prompt>$"; McpServerList mcpServerList = new McpServerList(); while (goOn) { @@ -65,6 +68,7 @@ public class Gateway TopP = 1, MaxTokens = 1000, }); + log.Info(reponse); messages.Add(new Message { Role = "assistant", @@ -213,18 +217,20 @@ 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) { - string serverName = mcpServer.Name; - if (serverName is null) - { - continue; - } - Type type = Type.GetType("LinkToolAddin.client.tool." + serverName); - Type type2 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro"); + InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; + Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name); MethodInfo[] methods = type.GetMethods(); foreach (MethodInfo method in methods) { @@ -237,12 +243,14 @@ public class Gateway { Tool = new Tool { - Name = methodName, + Name = innerMcpServer.Name + ":" + methodName, Description = methodDescription, Arguments = methodParamSchema } }; - toolInfos.AppendLine(JsonConvert.DeserializeXmlNode(JsonConvert.SerializeObject(toolDefinition)).ToString()); + XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)); + toolInfos.AppendLine(node.ToString()); + toolInfos.AppendLine(); } } } @@ -252,7 +260,7 @@ public class Gateway IList tools = await client.GetToolListAsync(); foreach (McpClientTool tool in tools) { - string toolName = tool.Name; + string toolName = (mcpServer as SseMcpServer).Name + ":" + tool.Name; string toolDescription = tool.Description; string toolParamSchema = tool.JsonSchema.ToString(); McpToolDefinition toolDefinition = new McpToolDefinition @@ -265,6 +273,7 @@ public class Gateway } }; toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString()); + toolInfos.AppendLine(); } }else if (mcpServer is StdioMcpServer) { @@ -272,7 +281,7 @@ public class Gateway IList tools = await client.GetToolListAsync(); foreach (McpClientTool tool in tools) { - string toolName = tool.Name; + string toolName = (mcpServer as StdioMcpServer).Name + ":" + tool.Name;; string toolDescription = tool.Description; string toolParamSchema = tool.JsonSchema.ToString(); McpToolDefinition toolDefinition = new McpToolDefinition @@ -281,16 +290,25 @@ public class Gateway { Name = toolName, Description = toolDescription, - Arguments = toolParamSchema + Arguments = CompressJson(toolParamSchema) } }; toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString()); + toolInfos.AppendLine(); } } } return toolInfos.ToString(); } + public static string CompressJson(string json) + { + // 解析JSON并自动去除无关空白 + var token = JToken.Parse(json); + // 序列化为无格式紧凑字符串 + return token.ToString(Newtonsoft.Json.Formatting.None); + } + private static string GenerateMethodParamSchema(MethodInfo method) { var generator = new JSchemaGenerator @@ -316,8 +334,11 @@ public class Gateway paramSchema.Properties.Add(param.Name, typeSchema); } - - return paramSchema.ToString(); + var settings = new JsonSerializerSettings { + Formatting = Newtonsoft.Json.Formatting.None, // 关键设置:禁用缩进和换行 + NullValueHandling = NullValueHandling.Ignore // 可选:忽略空值 + }; + return JsonConvert.SerializeObject(paramSchema, settings);; } public static async void TestChatMessage(string message, string model, string gdbPath, diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs index 186439b..48938d5 100644 --- a/host/prompt/SystemPrompt.cs +++ b/host/prompt/SystemPrompt.cs @@ -2,7 +2,7 @@ public class SystemPrompt { - public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。"; + public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。MCP工具调用的格式要求示例:\n search\n {\\\"query\\\": \\\"上海 人口\\\"}\n"; public static string ContinuePromptTemplate = "上一个工具执行的结果如下,请据此继续执行"; diff --git a/ui/dockpane/TestDockpane.xaml b/ui/dockpane/TestDockpane.xaml index 4782989..e550084 100644 --- a/ui/dockpane/TestDockpane.xaml +++ b/ui/dockpane/TestDockpane.xaml @@ -18,11 +18,20 @@ - - + + + - - - + + + + + + + + + + + \ No newline at end of file diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs index 802464a..d67e5c9 100644 --- a/ui/dockpane/TestDockpane.xaml.cs +++ b/ui/dockpane/TestDockpane.xaml.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Windows; using System.Windows.Controls; using LinkToolAddin.client; @@ -146,12 +147,27 @@ namespace LinkToolAddin.ui.dockpane private void TestWorkflow_OnClick(object sender, RoutedEventArgs e) { - Gateway.SendMessage("你有什么工具","qwen-max","test.gdb",ShowMessage); + // Gateway.SendMessage("你有什么工具可以调用的?","qwen-max","test.gdb",ShowMessage); + Gateway.TestWorkflow("dafdfdgdagdgui","","",AddReply); } public void ShowMessage(MessageListItem msg) { log.Info(msg.content); } + + private void PromptTestButton_OnClick(object sender, RoutedEventArgs e) + { + string userPrompt = PromptTestTextBox.Text; + Gateway.SendMessage(userPrompt,"qwen-max","C:/Project/test.gdb",AddReply); + } + + public void AddReply(MessageListItem msg) + { + string content = msg.content; + log.Info(content); + string originContent = ReplyTextBox.Text; + ReplyTextBox.Text = originContent + content; + } } } diff --git a/ui/dockpane/TestDockpaneViewModel.cs b/ui/dockpane/TestDockpaneViewModel.cs index 0661d9c..daf9d4c 100644 --- a/ui/dockpane/TestDockpaneViewModel.cs +++ b/ui/dockpane/TestDockpaneViewModel.cs @@ -41,7 +41,7 @@ namespace LinkToolAddin.ui.dockpane /// /// Text shown near the top of the DockPane. /// - private string _heading = "My DockPane"; + private string _heading = "Test Dockpane"; public string Heading { get => _heading;