From 7a3516f85527c78c34773334b9efb5f752d1ec44 Mon Sep 17 00:00:00 2001 From: PeterZhong Date: Sun, 1 Jun 2025 15:37:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E7=B3=BB=E7=BB=9F=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=8D=E7=8B=AC=E7=AB=8B=E4=B8=BA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LinkToolAddin.csproj | 13 +++++-- client/StdioMcpClient.cs | 15 ++++++-- common/LocalResource.cs | 31 +++++++++++++++++ host/Gateway.cs | 29 +++------------- host/prompt/SystemPrompt.cs | 56 ++++++++++++++++-------------- resource/prompt/ContinuePrompt.txt | 11 ++++++ resource/prompt/ErrorPrompt.txt | 12 +++++++ resource/prompt/SystemPrompt.txt | 40 +++++++++++++++++++++ ui/dockpane/TestDockpane.xaml | 2 ++ ui/dockpane/TestDockpane.xaml.cs | 8 +++++ 10 files changed, 159 insertions(+), 58 deletions(-) create mode 100644 common/LocalResource.cs create mode 100644 resource/prompt/ContinuePrompt.txt create mode 100644 resource/prompt/ErrorPrompt.txt create mode 100644 resource/prompt/SystemPrompt.txt diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj index c53da32..35cdbec 100644 --- a/LinkToolAddin.csproj +++ b/LinkToolAddin.csproj @@ -97,9 +97,6 @@ False - - - @@ -107,6 +104,16 @@ + + + + + + + Always + + + diff --git a/client/StdioMcpClient.cs b/client/StdioMcpClient.cs index caad4b7..64933ad 100644 --- a/client/StdioMcpClient.cs +++ b/client/StdioMcpClient.cs @@ -51,9 +51,18 @@ public class StdioMcpClient : McpClient public async Task> GetToolListAsync() { - IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions)); - IList tools = await client.ListToolsAsync(); - return tools; + try + { + IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions)); + IList tools = await client.ListToolsAsync(); + return tools; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return null; + } + } public async Task CallToolAsync(string toolName, Dictionary parameters = null) diff --git a/common/LocalResource.cs b/common/LocalResource.cs new file mode 100644 index 0000000..fc1e719 --- /dev/null +++ b/common/LocalResource.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace LinkToolAddin.common; + +public class LocalResource +{ + public static string ReadFileByResource(string resourceName) + { + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + + string[] resourceNames = assembly.GetManifestResourceNames(); + foreach (string name in resourceNames) + { + Console.WriteLine(name); + } + + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream == null) + { + return($"找不到嵌入资源:{resourceName}"); + } + + using (StreamReader reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + } +} \ No newline at end of file diff --git a/host/Gateway.cs b/host/Gateway.cs index 406d789..cb9a0fb 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -113,12 +113,7 @@ 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.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) }); callback?.Invoke(toolMessageItem); }else if (mcpServer is StdioMcpServer) @@ -138,12 +133,7 @@ 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.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) }); callback?.Invoke(toolMessageItem); }else if (mcpServer is InnerMcpServer) @@ -165,12 +155,7 @@ public class Gateway messages.Add(new Message { Role = "user", - Content = SystemPrompt.ErrorPromptTemplate - }); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(innerResult) + Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult)) }); callback?.Invoke(toolMessageItem); }else if (innerResult is JsonRpcSuccessEntity) @@ -186,12 +171,7 @@ public class Gateway messages.Add(new Message { Role = "user", - Content = SystemPrompt.ContinuePromptTemplate - }); - messages.Add(new Message - { - Role = "user", - Content = JsonConvert.SerializeObject(innerResult) + Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult)) }); callback?.Invoke(toolMessageItem); } @@ -396,7 +376,6 @@ public class Gateway Console.WriteLine(e); log.Error(e.Message); } - } if (messageContent != "") { diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs index 105fb80..2295d68 100644 --- a/host/prompt/SystemPrompt.cs +++ b/host/prompt/SystemPrompt.cs @@ -1,34 +1,36 @@ -namespace LinkToolAddin.host.prompt; +using LinkToolAddin.common; + +namespace LinkToolAddin.host.prompt; public class SystemPrompt { - public static string SysPromptTemplate = - "现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" + - "指令:您可以使用一组工具来回答用户的问题。还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。" + - "调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过的方式来调用用户提示词,你能更好地理解和解决用户的问题。" + - "工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" + - "输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" + - "如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。" + - "工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:\n {tool_name}\n {json_arguments}\n。" + - "工具名称:需与所使用工具的精确名称一致。" + - "参数:应为包含工具所需参数的 JSON 对象。例如:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n" + - "你必须严格遵守以下输出规则:" + - "3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" + - "结果:用户将以以下格式返回工具调用结果:\n {tool_name}\n {result}\n。应为字符串类型,可以表示文件或其他输出类型。" + - "工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n"+ - "特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用"; - //"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。\r\n\r\n指令:\r\n你需要使用一组工具来回答用户的问题。也可以通过 标签调用用户提示词,以帮助你更好地理解任务。所有操作完成后,在最后一条输出中包含 '[DONE]',但不要单独发送这条消息。\r\n\r\n调用工具要求:\r\n1. 每次只能调用一个工具,并等待用户的反馈结果。\r\n2. 所有工具调用都必须基于前一步的结果进行推理和决策。\r\n3. 工具调用必须以 XML 格式输出,并且必须作为**独立的一条消息输出**,前后不得有任何解释性文字或说明内容。\r\n4. 如果需要提供解释、说明或引导信息,请先单独输出一段普通文本,**其中不能包含任何 XML 标签或格式**。\r\n5. 不得重复调用已经成功执行过的工具,除非有新的参数或上下文需要重新调用。\r\n\r\n工具调用背景:\r\n你有以下工具可以调用:{{toolInfos}} \r\n用户的数据库路径是:{{gdbPath}}\r\n\r\n输出风格:\r\n- 所有工具调用都必须使用标准 XML 格式输出,并且每条消息只包含一次调用。\r\n- 文字说明必须单独输出,且为普通段落格式,不含任何 XML 或 Markdown 标记。\r\n- 输出顺序应为:【可选的文字说明】→【必选的工具调用】,或者仅输出工具调用。\r\n\r\n工具调用格式示例:\r\n\r\n {tool_name}\r\n {json_arguments}\r\n\r\n\r\n例如:\r\n\r\n gaode:maps_geo\r\n {\"address\":\"广州市政府, 广州市\", \"city\":\"广州\"}\r\n\r\n\r\n注意事项:\r\n1. 工具名称必须与实际工具完全一致。\r\n2. 参数必须为合法 JSON 格式,且符合工具要求。\r\n3. 用户时间宝贵,请高效调用工具,尽快完成任务。\r\n4. 最终完成时应在最后一次响应中包含 [DONE],而不是单独输出。\r\n\r\n工具调用结果返回格式:\r\n用户将以如下格式返回工具调用结果:\r\n\r\n {tool_name}\r\n {result}\r\n\r\n\r\n例如:\r\n\r\n ArcGIS_Pro:GP\r\n {\"output_file\": \"source.shp\"}\r\n\r\n\r\n请始终遵循此格式以确保工具调用被正确解析和执行。"; - - public static string ContinuePromptTemplate = "这是上述工具调用的结果。{{toolResult}}\n请根据以下执行结果,清晰解释执行结果并执行下一步操作。" + - "执行下一步工具的要求:1. 解析工具输出结果2. 调用下一个工具时确保参数继承前序输出。请据此继续执行"; - - public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误\n{{toolResult}}\n,需按以下流程处理:1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,```" + - "2. 修复方案:(1)根据错误类型生成对应的工具重试策略(2)参数调整:明确需要修改的参数及修改方式。3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功"+ - "5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误"; + // public static string SysPromptTemplate = + // "现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" + + // "指令:您可以使用一组工具来回答用户的问题。还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。" + + // "调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过的方式来调用用户提示词,你能更好地理解和解决用户的问题。" + + // "工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" + + // "输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" + + // "如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。" + + // "工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:\n {tool_name}\n {json_arguments}\n。" + + // "工具名称:需与所使用工具的精确名称一致。" + + // "参数:应为包含工具所需参数的 JSON 对象。例如:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n" + + // "你必须严格遵守以下输出规则:" + + // "3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" + + // "结果:用户将以以下格式返回工具调用结果:\n {tool_name}\n {result}\n。应为字符串类型,可以表示文件或其他输出类型。" + + // "工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:\\n gaode:maps_geo\\n {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}\\n"+ + // "特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用"; + // //"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。\r\n\r\n指令:\r\n你需要使用一组工具来回答用户的问题。也可以通过 标签调用用户提示词,以帮助你更好地理解任务。所有操作完成后,在最后一条输出中包含 '[DONE]',但不要单独发送这条消息。\r\n\r\n调用工具要求:\r\n1. 每次只能调用一个工具,并等待用户的反馈结果。\r\n2. 所有工具调用都必须基于前一步的结果进行推理和决策。\r\n3. 工具调用必须以 XML 格式输出,并且必须作为**独立的一条消息输出**,前后不得有任何解释性文字或说明内容。\r\n4. 如果需要提供解释、说明或引导信息,请先单独输出一段普通文本,**其中不能包含任何 XML 标签或格式**。\r\n5. 不得重复调用已经成功执行过的工具,除非有新的参数或上下文需要重新调用。\r\n\r\n工具调用背景:\r\n你有以下工具可以调用:{{toolInfos}} \r\n用户的数据库路径是:{{gdbPath}}\r\n\r\n输出风格:\r\n- 所有工具调用都必须使用标准 XML 格式输出,并且每条消息只包含一次调用。\r\n- 文字说明必须单独输出,且为普通段落格式,不含任何 XML 或 Markdown 标记。\r\n- 输出顺序应为:【可选的文字说明】→【必选的工具调用】,或者仅输出工具调用。\r\n\r\n工具调用格式示例:\r\n\r\n {tool_name}\r\n {json_arguments}\r\n\r\n\r\n例如:\r\n\r\n gaode:maps_geo\r\n {\"address\":\"广州市政府, 广州市\", \"city\":\"广州\"}\r\n\r\n\r\n注意事项:\r\n1. 工具名称必须与实际工具完全一致。\r\n2. 参数必须为合法 JSON 格式,且符合工具要求。\r\n3. 用户时间宝贵,请高效调用工具,尽快完成任务。\r\n4. 最终完成时应在最后一次响应中包含 [DONE],而不是单独输出。\r\n\r\n工具调用结果返回格式:\r\n用户将以如下格式返回工具调用结果:\r\n\r\n {tool_name}\r\n {result}\r\n\r\n\r\n例如:\r\n\r\n ArcGIS_Pro:GP\r\n {\"output_file\": \"source.shp\"}\r\n\r\n\r\n请始终遵循此格式以确保工具调用被正确解析和执行。"; + // + // public static string ContinuePromptTemplate = "这是上述工具调用的结果。{{toolResult}}\n请根据以下执行结果,清晰解释执行结果并执行下一步操作。" + + // "执行下一步工具的要求:1. 解析工具输出结果2. 调用下一个工具时确保参数继承前序输出。请据此继续执行"; + // + // public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误\n{{toolResult}}\n,需按以下流程处理:1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,```" + + // "2. 修复方案:(1)根据错误类型生成对应的工具重试策略(2)参数调整:明确需要修改的参数及修改方式。3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功"+ + // "5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误"; public static string SysPrompt(string gdbPath, string toolInfos) { - string sysPrompt = SysPromptTemplate; + string sysPrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.SystemPrompt.txt");; sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath); sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos); return sysPrompt; @@ -36,14 +38,14 @@ public class SystemPrompt public static string ContinuePrompt(string toolResult) { - string continuePrompt = ContinuePromptTemplate; + string continuePrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.ContinuePrompt.txt"); continuePrompt = continuePrompt.Replace("{{toolResult}}", toolResult); return continuePrompt; } public static string ErrorPrompt(string toolResult) { - string errPrompt = ErrorPromptTemplate; + string errPrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.ErrorPrompt.txt"); errPrompt = errPrompt.Replace("{{toolResult}}", toolResult); return errPrompt; } diff --git a/resource/prompt/ContinuePrompt.txt b/resource/prompt/ContinuePrompt.txt new file mode 100644 index 0000000..deb62c7 --- /dev/null +++ b/resource/prompt/ContinuePrompt.txt @@ -0,0 +1,11 @@ +这是上述工具调用的结果。 + +{{toolResult}} + +请根据以下执行结果,清晰解释执行结果并执行下一步操作。 + +执行下一步工具的要求: +1. 解析工具输出结果 +2. 调用下一个工具时确保参数继承前序输出。 + +请据此继续执行 \ No newline at end of file diff --git a/resource/prompt/ErrorPrompt.txt b/resource/prompt/ErrorPrompt.txt new file mode 100644 index 0000000..3257463 --- /dev/null +++ b/resource/prompt/ErrorPrompt.txt @@ -0,0 +1,12 @@ +执行上一个工具的时候出现以下错误 + +{{toolResult}} + +需按以下流程处理: +1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案, +2. 修复方案: +(1)根据错误类型生成对应的工具重试策略 +(2)参数调整:明确需要修改的参数及修改方式。 +3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试, +4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功 +5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误 \ No newline at end of file diff --git a/resource/prompt/SystemPrompt.txt b/resource/prompt/SystemPrompt.txt new file mode 100644 index 0000000..a248f9f --- /dev/null +++ b/resource/prompt/SystemPrompt.txt @@ -0,0 +1,40 @@ +现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。 + +指令:您可以使用一组工具来回答用户的问题。还可以通过调用用户提示词,从而使你更好地理解和完成用户的任务。 + +调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过的方式来调用用户提示词,你能更好地理解和解决用户的问题。工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。 + +输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。 + +工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下: + + + {tool_name} + {json_arguments} +。 + +工具名称:需与所使用工具的精确名称一致。 +参数:应为包含工具所需参数的 JSON 对象。 +例如: + + gaode:maps_geo + {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"} + + +你必须严格遵守以下输出规则:用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。 + +结果:用户将以以下格式返回工具调用结果: + + {tool_name} + {result} +。 + +应为字符串类型,可以表示文件或其他输出类型。 + +工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例: + + gaode:maps_geo + {\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"} + + +特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用 \ No newline at end of file diff --git a/ui/dockpane/TestDockpane.xaml b/ui/dockpane/TestDockpane.xaml index d7b5e09..6d4ae00 100644 --- a/ui/dockpane/TestDockpane.xaml +++ b/ui/dockpane/TestDockpane.xaml @@ -24,6 +24,7 @@ + @@ -39,5 +40,6 @@ + \ No newline at end of file diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs index 435ec72..374e8c3 100644 --- a/ui/dockpane/TestDockpane.xaml.cs +++ b/ui/dockpane/TestDockpane.xaml.cs @@ -8,6 +8,7 @@ using System.Windows; using System.Windows.Controls; using System.Xml.Linq; using LinkToolAddin.client; +using LinkToolAddin.common; using LinkToolAddin.host; using LinkToolAddin.host.llm; using LinkToolAddin.host.llm.entity; @@ -23,6 +24,7 @@ using log4net.Layout; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Types; using Newtonsoft.Json; +using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox; namespace LinkToolAddin.ui.dockpane @@ -295,5 +297,11 @@ namespace LinkToolAddin.ui.dockpane AddReply(toolMessageItem); } } + + private void TestResource_OnClick(object sender, RoutedEventArgs e) + { + string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt"); + MessageBox.Show(content); + } } }