diff --git a/host/Gateway.cs b/host/Gateway.cs index 3984613..915d588 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -274,6 +274,7 @@ public class Gateway string promptPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>"; McpServerList mcpServerList = new McpServerList(); int loop = 0; + string messageContent = ""; //一次请求下完整的response while (goOn) { loop++; @@ -291,12 +292,14 @@ public class Gateway MaxTokens = 1000, }; long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - string messageContent = ""; //一次请求下完整的response + List mcpToolRequests = new List(); + List promptKeys = new List(); var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern); var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern); if (toolMatched == "" && promptMatched == "" && messageContent != "") { //如果本次回复不包含任何工具的调用或提示词的调用,则不再请求 + goOn = false; break; } await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent)) @@ -305,6 +308,7 @@ public class Gateway { break; } + messageContent = chunk; var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern); if (matched == "") { @@ -319,7 +323,6 @@ public class Gateway type = MessageType.CHAT_MESSAGE, id = timestamp.ToString() }; - messageContent = remainingPrompt; callback?.Invoke(chatMessageListItem); } else @@ -335,22 +338,7 @@ public class Gateway callback?.Invoke(chatMessageListItem); XElement promptUse = XElement.Parse(matchedPrompt); string promptKey = promptUse.Element("name")?.Value; - string promptContent = DynamicPrompt.GetPrompt(promptKey,null); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(promptContent) - }); - MessageListItem toolMessageItem = new ToolMessageItem - { - toolName = "调用提示词", - toolParams = null, - type = MessageType.TOOL_MESSAGE, - status = "success", - content = "成功调用提示词:"+promptKey, - id = (timestamp+1).ToString() - }; - callback?.Invoke(toolMessageItem); + promptKeys.Add(promptKey); } } else @@ -371,7 +359,33 @@ public class Gateway string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName; string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName; McpServer mcpServer = mcpServerList.GetServer(serverName); - if (mcpServer is SseMcpServer) + //将工具调用请求添加至列表中待一次回答完整后再统一进行调用 + mcpToolRequests = new List(); + McpToolRequest mcpToolRequest = new McpToolRequest() + { + McpServer = mcpServer, + ToolName = toolName, + ToolArgs = toolParams, + }; + mcpToolRequests.Add(mcpToolRequest); + } + } + + if (messageContent != "") + { + messages.Add(new Message + { + Role = "assistant", + Content = messageContent + }); + } + /*统一处理本次请求中的MCP工具调用需求*/ + foreach (McpToolRequest mcpToolRequest in mcpToolRequests) + { + McpServer mcpServer = mcpToolRequest.McpServer; + string toolName = mcpToolRequest.ToolName; + Dictionary toolParams = mcpToolRequest.ToolArgs; + if (mcpServer is SseMcpServer) { SseMcpServer sseMcpServer = mcpServer as SseMcpServer; SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl); @@ -413,7 +427,7 @@ public class Gateway callback?.Invoke(toolMessageItem); }else if (mcpServer is InnerMcpServer) { - Type type = Type.GetType("LinkToolAddin.client.tool."+serverName); + Type type = Type.GetType("LinkToolAddin.client.tool."+(mcpServer as InnerMcpServer).Name); MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static); var methodParams = toolParams.Values.ToArray(); object[] args = new object[methodParams.Length]; @@ -473,7 +487,26 @@ public class Gateway callback?.Invoke(toolMessageItem); } } - } + } + /*统一处理本次请求中的Prompt调用需求*/ + foreach (string promptKey in promptKeys) + { + string promptContent = DynamicPrompt.GetPrompt(promptKey); + messages.Add(new Message + { + Role = "user", + Content = promptContent + }); + MessageListItem toolMessageItem = new ToolMessageItem + { + toolName = "调用提示词", + toolParams = null, + type = MessageType.TOOL_MESSAGE, + status = "success", + content = "成功调用提示词:"+promptKey, + id = (timestamp+1).ToString() + }; + callback?.Invoke(toolMessageItem); } } } diff --git a/host/mcp/McpToolRequest.cs b/host/mcp/McpToolRequest.cs new file mode 100644 index 0000000..cdbfab0 --- /dev/null +++ b/host/mcp/McpToolRequest.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LinkToolAddin.host.mcp; + +public class McpToolRequest +{ + public McpServer McpServer { get; set; } + public string ToolName { get; set; } + public Dictionary ToolArgs { get; set; } +} \ No newline at end of file diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs index 67ea12c..4046bf2 100644 --- a/host/prompt/SystemPrompt.cs +++ b/host/prompt/SystemPrompt.cs @@ -4,7 +4,7 @@ public class SystemPrompt { public static string SysPromptTemplate = "现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" + - "指令:您可以使用一组工具来回答用户的问题。还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。整个流程结束之后单独输出一条内容为'[DONE]'的消息,前后不要有任何说明文字。" + + "指令:您可以使用一组工具来回答用户的问题。还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。" + "调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过的方式来调用用户提示词,你能更好地理解和解决用户的问题。" + "工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" + "输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" + @@ -13,12 +13,7 @@ public class SystemPrompt "工具名称:需与所使用工具的精确名称一致。" + "参数:应为包含工具所需参数的 JSON 对象。例如:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n" + "你必须严格遵守以下输出规则:" + - "1.XML工具调用格式必须在下一条单独输出,如果前面有文字将永远无法调用工具。" + - //"只有单独一条的调用请求文本程序才能识别并调用,从而将结果反馈给你。" + - "2.文字说明如果紧跟着工具调用的XML将暴露程序,XML会在下一条单独输出,因此文字描述后面一定不能输出工具调用的XML格式。" + "3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" + - "4.如果目前工作已经完成无法知道用户其他需求时,一定要单独输出一条内容为'[DONE]'的消息表示工具调用结束。不要在文本的末尾。" + - "5.不得在同一消息中混合说明文字与工具调用。" + "结果:用户将以以下格式返回工具调用结果:\n {tool_name}\n {result}\n。应为字符串类型,可以表示文件或其他输出类型。" + "工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n"+ "特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用"; diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs index 1628085..08e3f67 100644 --- a/ui/dockpane/TestDockpane.xaml.cs +++ b/ui/dockpane/TestDockpane.xaml.cs @@ -245,6 +245,7 @@ namespace LinkToolAddin.ui.dockpane private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e) { + Type type1 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro"); 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);