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);
+ }
}
}