Compare commits

...

33 Commits

Author SHA1 Message Date
108ee40ffe Merge pull request 'v0.1.4版本' (#2) from host into master
Reviewed-on: #2
2025-06-15 14:42:58 +00:00
7dc73e66eb Merge branch 'prompt' into host 2025-06-15 22:38:59 +08:00
441d8a0759 提示词调整参数问题 2025-06-15 22:36:39 +08:00
776cebd48e 优化模型超参数 2025-06-12 11:17:09 +08:00
3f3ecadec9 1. 修复工具调用前不查知识库问题
2. 完善MCP报错机制
3. 增加ArcGIS Pro查看属性表工具
2025-06-11 00:50:46 +08:00
200db4b10a 合并后提交 2025-06-08 17:10:31 +08:00
09d1b28b5b 提示词 2025-06-08 17:08:16 +08:00
86b800c477 更新对话框版本信息 2025-06-08 16:17:20 +08:00
6a1fe1fa3f 补充程序基本信息 2025-06-08 16:16:26 +08:00
544bc8cd40 解决MCP服务出错影响主程序的问题 2025-06-08 16:13:57 +08:00
9b600174e8 修复回答状态显示异常问题 2025-06-08 15:48:41 +08:00
d0b6671cce 完善ArcGIS工具报错信息 2025-06-08 15:24:55 +08:00
660ba2ad53 增强异常捕获能力,减少闪退 2025-06-08 15:12:48 +08:00
c61938383b 修复Prompt卡片不显示问题 2025-06-08 14:27:26 +08:00
edab972b3e 增加deepseek系列更多模型及自定义模型 2025-06-08 11:53:31 +08:00
ff7e6ad732 合并后提交 2025-06-08 02:09:00 +08:00
d790defcfe prompt 2025-06-08 02:06:41 +08:00
9e17dd7bd6 提示词提及选择与导出的冲突 2025-06-08 02:02:42 +08:00
936c388a62 实现模型选择器功能 2025-06-08 02:01:58 +08:00
b88d405ab8 合并后提交 2025-06-08 00:21:39 +08:00
db488111bb 提示词 2025-06-08 00:12:10 +08:00
56d0e0083c 恢复知识库MCP 2025-06-07 23:19:36 +08:00
718718ef11 Revert "1. 解决提示词卡片显示不出的问题"
This reverts commit 54b78d0b5c.
2025-06-07 23:19:06 +08:00
54b78d0b5c 1. 解决提示词卡片显示不出的问题
2. 强制在第一次调用ArcGIS Pro工具前先查知识库
3. 解决当前回答状态显示错误的问题
2025-06-07 23:17:29 +08:00
e2fd3b376c 执行工具前有文字说明,调用prompt的意识 2025-06-05 00:04:42 +08:00
ec46fe4aad 合并后提交 2025-06-04 21:14:15 +08:00
3b9005381a 优化适配提示词卡片 2025-06-04 16:14:43 +08:00
b18c35b7f7 输出纯文本 2025-06-03 22:27:49 +08:00
c04cc42d94 提示词泛化,错误编码查询 2025-06-02 11:45:35 +08:00
f2bff487b7 Merge branch 'host' into prompt 2025-06-02 11:19:06 +08:00
691f54c73f 内嵌XML规则,明确调用动态Prompt和知识库,简要回答避免画蛇添足 2025-06-02 11:17:24 +08:00
f4262d6aef 合并后提交 2025-06-01 19:01:45 +08:00
c16557129c 修改系统提示词(没什么大改动) 2025-06-01 18:57:44 +08:00
18 changed files with 624 additions and 288 deletions

View File

@ -6,6 +6,13 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<NoWarn>CA1416</NoWarn>
<TargetFramework>net8.0-windows</TargetFramework>
<Version>0.1.3</Version>
<Title>LinkToolAddin</Title>
<Authors>LinkTool团队</Authors>
<Description>LinkTool以大模型赋能让您只需一两句话便能完成复杂的空间分析与时空大数据处理任务。</Description>
<Copyright>华南农业大学</Copyright>
<PackageReleaseNotes>校AI大赛提交版本</PackageReleaseNotes>
<Company>华南农业大学</Company>
</PropertyGroup>
<ItemGroup>
<None Remove="Config.daml" />

View File

@ -24,7 +24,7 @@ public class ArcGisPro
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")]
public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams)
{
IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams,null,null,null,GPExecuteToolFlags.InheritGPOptions|GPExecuteToolFlags.GPThread);
JsonRpcResultEntity jsonRpcResultEntity;
if (results.IsFailed)
{
@ -33,8 +33,8 @@ public class ArcGisPro
{
Error = new Error()
{
Code = results.ErrorCode,
Message = GetMessagesString(results.ErrorMessages)
Code = results.ErrorCode.ToString(),
Message = GetMessagesString(results.ErrorMessages)+"\n"+GetMessagesString(results.Messages)
}
};
}else if(results.HasWarnings)
@ -100,7 +100,7 @@ public class ArcGisPro
return result;
}
[McpServerTool, Description("列出gdb数据库中的所有数据名称")]
[McpServerTool, Description("列出gdb数据库中的所有数据名称")]
public static async Task<JsonRpcResultEntity> ListData(string gdbPath)
{
var datasets = new List<string>();
@ -137,6 +137,80 @@ public class ArcGisPro
return result;
}
[McpServerTool, Description("获取要素类的属性表内容可以查看属性表中的至多前20条记录的内容")]
public static async Task<JsonRpcResultEntity> GetFeatureDatasetAttributeTable(string datasetPath, string dataName, string rowsLimit)
{
JsonRpcResultEntity result = new JsonRpcResultEntity();
await QueuedTask.Run(async () =>
{
try
{
using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath)));
FeatureClass featureClass = gdb.OpenDataset<FeatureClass>(dataName);
List<Dictionary<string, string>> attributeTable = await GetAttributeTableAsync(featureClass,Convert.ToInt32(rowsLimit));
result = new JsonRpcSuccessEntity()
{
Id = 1,
Result = JsonConvert.SerializeObject(attributeTable)
};
return result;
}catch (Exception ex)
{
result = new JsonRpcErrorEntity()
{
Error = new Error()
{
Message = ex.Message,
Code = "500"
}
};
return result;
}
});
return result;
}
private static async Task<List<Dictionary<string, string>>> GetAttributeTableAsync(FeatureClass featureClass,int limit = 5)
{
if (limit > 20)
limit = 20;
return await QueuedTask.Run(() =>
{
var result = new List<Dictionary<string, string>>();
using (var cursor = featureClass.Search())
{
int i = 0;
while (cursor.MoveNext())
{
i++;
if (i >= limit && limit != -1)
break;
var feature = cursor.Current as Feature;
if (feature == null)
continue;
var record = new Dictionary<string, string>();
foreach (var field in featureClass.GetDefinition().GetFields())
{
var value = feature[field.Name];
// 处理 DBNull 值
if (value is DBNull)
value = null;
record[field.Name] = value.ToString();
}
result.Add(record);
}
}
return result;
});
}
private static string GetMessagesString(IEnumerable<IGPMessage> messages)
{
StringBuilder messagesStr = new StringBuilder();

View File

@ -12,10 +12,10 @@
## 提示词工程
- [ ] 将原来的单独XML规则修改为内嵌XML规则
- [ ] 系统提示词明确提示调用动态Prompt和知识库
- [ ] 错误和继续提示词预留工具消息占位符
- [ ] 错误和继续提示词明示
- [x] 将原来的单独XML规则修改为内嵌XML规则
- [x] 系统提示词明确提示调用动态Prompt和知识库
- [x] 错误和继续提示词预留工具消息占位符
- [x] 错误和继续提示词明示
- [ ] 系统提示词泛化增强
- [ ] 针对qwen3适当调整

View File

@ -12,9 +12,11 @@ using System.Windows;
using System.Windows.Documents;
using System.Xml;
using System.Xml.Linq;
using ArcGIS.Desktop.Internal.Mapping;
using ArcGIS.Desktop.Internal.Mapping.Locate;
using LinkToolAddin.client;
using LinkToolAddin.client.prompt;
using LinkToolAddin.client.tool;
using LinkToolAddin.host.llm;
using LinkToolAddin.host.llm.entity;
using LinkToolAddin.host.mcp;
@ -76,9 +78,11 @@ public class Gateway
{
Model = model,
Messages = messages,
Temperature = 0.7,
TopP = 1,
MaxTokens = 1000,
Temperature = 0.3,
TopP = 0.6,
TopK = 25,
MaxTokens = 1024,
ThinkingBudget = 1024
});
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
log.Info(reponse);
@ -239,7 +243,10 @@ public class Gateway
api_key = "sk-db177155677e438f832860e7f4da6afc"
};
List<Message> messages = new List<Message>();
string toolInfos = await GetToolInfos(new McpServerList());
string toolInfos = "";
try
{
toolInfos = await GetToolInfos(new McpServerList());
messages.Add(new Message
{
Role = "system",
@ -250,6 +257,11 @@ public class Gateway
Role = "user",
Content = message
});
}catch (Exception ex)
{
log.Error(ex);
MessageBox.Show(ex.Message,"获取MCP列表失败");
}
goOn = true;
string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>";
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>";
@ -257,10 +269,12 @@ public class Gateway
PromptServerList promptServerList = new PromptServerList();
int loop = 0;
string messageContent = ""; //一次请求下完整的response
bool queriedKnowledge = false;
bool executedTool = false;
while (goOn)
{
loop++;
if (loop > 20)
if (loop > 500)
{
MessageBox.Show("达到最大循环次数", "退出循环");
break;
@ -269,8 +283,9 @@ public class Gateway
{
Model = model,
Messages = messages,
Temperature = 0.7,
TopP = 1,
Temperature = 0.3,
TopP = 0.4,
TopK = 7,
MaxTokens = 1000,
};
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@ -286,12 +301,15 @@ public class Gateway
{
type = MessageType.END_TAG,
content = "",
id = (timestamp+3).ToString(),
id = (timestamp + 3).ToString(),
role = "assistant"
};
callback?.Invoke(endMessageListItem1);
break;
}
try
{
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
{
if (!goOn)
@ -306,7 +324,6 @@ public class Gateway
callback?.Invoke(endMessageListItem2);
break;
}
try
{
string chunk = llmStreamChat.Choices[0].Delta.Content;
@ -387,10 +404,12 @@ public class Gateway
{
callback?.Invoke(chatMessageListItem);
});
if (!executedTool)
{
MessageListItem toolMessageListItem = new ToolMessageItem()
{
content = "",
result = "",
content = toolName,
result = "工具运行中" + toolName,
toolName = toolName,
toolParams = toolParams,
role = "user",
@ -402,6 +421,7 @@ public class Gateway
{
callback?.Invoke(toolMessageListItem);
});
}
mcpToolRequests = new List<McpToolRequest>();
McpToolRequest mcpToolRequest = new McpToolRequest()
{
@ -418,6 +438,11 @@ public class Gateway
log.Error(e.Message);
}
}
}catch (Exception e)
{
log.Error(e.Message);
MessageBox.Show(e.Message, "请求大模型出错");
}
if (messageContent != "")
{
messages.Add(new Message
@ -429,6 +454,7 @@ public class Gateway
/*统一处理本次请求中的MCP工具调用需求*/
foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
{
executedTool = true;
try
{
McpServer mcpServer = mcpToolRequest.McpServer;
@ -439,7 +465,7 @@ public class Gateway
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
MessageListItem toolMessageItem1 = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
@ -459,7 +485,7 @@ public class Gateway
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
callback?.Invoke(toolMessageItem1);
});
}
else if (mcpServer is StdioMcpServer)
@ -467,7 +493,7 @@ public class Gateway
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
MessageListItem toolMessageItem = new ToolMessageItem
MessageListItem toolMessageItem1 = new ToolMessageItem
{
toolName = toolName,
toolParams = toolParams,
@ -487,11 +513,67 @@ public class Gateway
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
callback?.Invoke(toolMessageItem1);
});
}
else if (mcpServer is InnerMcpServer)
{
InnerMcpServer innerMcpServer = mcpServer as InnerMcpServer;
string mcpServerName = innerMcpServer.Name;
if (toolName == "ArcGisProTool" && queriedKnowledge == false)
{
JsonRpcResultEntity knowledgeResult = await KnowledgeBase.QueryArcgisToolDoc(toolParams["toolName"].ToString());
if (knowledgeResult is JsonRpcSuccessEntity)
{
JsonRpcSuccessEntity knowledgeSuccessResult = knowledgeResult as JsonRpcSuccessEntity;
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = "QueryArcgisToolDoc",
toolParams = new Dictionary<string, object>(){{"query",toolParams["toolName"].ToString()}},
type = MessageType.TOOL_MESSAGE,
status = "success",
content = JsonConvert.SerializeObject(knowledgeSuccessResult.Result),
result = JsonConvert.SerializeObject(knowledgeSuccessResult.Result),
id = (timestamp + 4).ToString(),
role = "user"
};
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ToolContinuePrompt(JsonConvert.SerializeObject(knowledgeSuccessResult))
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
});
queriedKnowledge = true;
}
else
{
JsonRpcErrorEntity knowledgeErrorResult = knowledgeResult as JsonRpcErrorEntity;
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = "QueryArcgisToolDoc",
toolParams = new Dictionary<string, object>(){{"query",toolParams["toolName"].ToString()}},
type = MessageType.TOOL_MESSAGE,
status = "fail",
content = JsonConvert.SerializeObject(knowledgeErrorResult.Error),
result = JsonConvert.SerializeObject(knowledgeErrorResult.Error),
id = (timestamp + 4).ToString(),
role = "user"
};
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(knowledgeErrorResult))
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
});
}
continue;
}
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();
@ -512,11 +594,17 @@ public class Gateway
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
JsonRpcResultEntity innerResult = await task;
string displayToolName = toolName;
if (displayToolName == "ArcGisProTool")
{
displayToolName = "【GP】"+toolParams["toolName"].ToString();
}
queriedKnowledge = false;
if (innerResult is JsonRpcErrorEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
MessageListItem toolMessageItem1 = new ToolMessageItem
{
toolName = toolName,
toolName = displayToolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "fail",
@ -528,18 +616,18 @@ public class Gateway
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolMessageItem))
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult))
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
callback?.Invoke(toolMessageItem1);
});
}
else if (innerResult is JsonRpcSuccessEntity)
{
MessageListItem toolMessageItem = new ToolMessageItem
MessageListItem toolMessageItem1 = new ToolMessageItem
{
toolName = toolName,
toolName = displayToolName,
toolParams = toolParams,
type = MessageType.TOOL_MESSAGE,
status = "success",
@ -551,11 +639,11 @@ public class Gateway
messages.Add(new Message
{
Role = "user",
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolMessageItem))
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
});
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageItem);
callback?.Invoke(toolMessageItem1);
});
}
}
@ -581,11 +669,13 @@ public class Gateway
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = "调用提示词",
toolParams = null,
toolParams = new Dictionary<string, object>(),
type = MessageType.TOOL_MESSAGE,
status = "success",
content = "成功调用提示词:"+promptRequest.PromptName,
id = (timestamp+1).ToString()
id = (timestamp+1).ToString(),
result = "成功调用提示词:"+promptRequest.PromptName,
role = "user"
};
Application.Current.Dispatcher.Invoke(() =>
{
@ -612,9 +702,14 @@ public class Gateway
private static async Task<string> GetToolInfos(McpServerList mcpServerList)
{
StringBuilder toolInfos = new StringBuilder();
int i = 0;
List<int> failedMcp = new List<int>();
List<string> failedMcpString = new List<string>();
foreach (McpServer mcpServer in mcpServerList.GetAllServers())
{
log.Info($"正在列出{mcpServer.Name}中的工具");
i++;
try
{
if (mcpServer is InnerMcpServer)
{
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
@ -685,15 +780,33 @@ public class Gateway
toolInfos.AppendLine();
}
}
}catch (Exception e)
{
log.Error(e.Message);
failedMcp.Add(i);
failedMcpString.Add(mcpServerList.GetAllServerNames()[i]);
}
}
List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts();
foreach (UserPrompt userPrompt in prompts)
{
try
{
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt}));
toolInfos.AppendLine(node.ToString());
toolInfos.AppendLine();
}
catch (Exception e)
{
log.Error(e.Message);
}
}
if (!failedMcp.IsNullOrEmpty())
{
MessageBox.Show($"读取失败的MCP服务有{JsonConvert.SerializeObject(failedMcpString)}","MCP读取错误");
}
return toolInfos.ToString();
}

View File

@ -32,11 +32,12 @@ public class McpServerList
{
Name = "KnowledgeBase",
Type = "inner",
Description = "可以调用进行查询知识库,获取相关参考信息。",
Description = "可以调用进行查询知识库,获取相关参考信息。" ,
// "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息",
IsActive = true
});
// servers.Add("filesystem", new StdioMcpServer()
// {
//servers.Add("filesystem", new StdioMcpServer()
//{
// Name = "filesystem",
// Type = "stdio",
// Command = "npx",
@ -44,11 +45,11 @@ public class McpServerList
// {
// "-y",
// "@modelcontextprotocol/server-filesystem",
// "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData"
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb"
// }
// });
// servers.Add("fetch", new StdioMcpServer()
// {
//});
//servers.Add("fetch", new StdioMcpServer()
//{
// Name = "fetch",
// Type = "stdio",
// Command = "uvx",
@ -56,9 +57,9 @@ public class McpServerList
// {
// "mcp-server-fetch"
// }
// });
// servers.Add("bing-search", new StdioMcpServer()
// {
//});
//servers.Add("bing-search", new StdioMcpServer()
//{
// Name = "bing-search",
// Type = "stdio",
// Command = "npx",
@ -66,9 +67,9 @@ public class McpServerList
// {
// "bing-cn-mcp"
// }
// });
// servers.Add("mcp-python-interpreter", new StdioMcpServer()
// {
//});
//servers.Add("mcp-python-interpreter", new StdioMcpServer()
//{
// Name = "mcp-python-interpreter",
// Type = "stdio",
// Command = "uvx",
@ -77,11 +78,11 @@ public class McpServerList
// "--native-tls",
// "mcp-python-interpreter",
// "--dir",
// "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData",
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb",
// "--python-path",
// "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe"
// }
// });
//});
}
public McpServer GetServer(string name)
@ -105,4 +106,14 @@ public class McpServerList
}
return serverList;
}
public List<string> GetAllServerNames()
{
List<string> serverList = new List<string>();
foreach (var server in servers)
{
serverList.Add(server.Key);
}
return serverList;
}
}

View File

@ -11,13 +11,21 @@ public class PromptServerList
{
promptServers.Add("plan", new PromptServer("plan",
"根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表",
"请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具如果是ArcGIS Pro的工具根据用户的具体需求和提供的数据类型," +
"判断并列出所有必要的分析步骤和工具同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase)" +
"确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
"有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"));
//"请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具如果是ArcGIS Pro的工具根据用户的具体需求和提供的数据类型," +
// "判断并列出所有必要的分析步骤和工具同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase)" +
// "确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
// "有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"
"你对ArcGIS Pro的工具箱及其功能了如指掌能够根据用户的具体需求和提供的数据类型迅速判断并列出所有必要的分析步骤和工具并随之调用知识库工具确认任务规划中的调用名是否正确以及确认其对应参数" +
"同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase),确保工具名的准确无误。" +
"工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。有一些与分析无关的数据能够进行排除在选择工具时不受其干扰。" +
"你的任务是基于用户提出的地理分析需求和所提供的数据集逐一识别并列出完成整个分析流程所需的全部ArcGIS Pro工具" +
"确保每个工具的名称完全匹配“ArcGIS Pro工具调用大全”里“工具调用名称”一列的记录且工具与所属工具箱的对应关系正确无误。" +
"请直接输出工具调用名称格式为“工具调用名称”无需额外解释或说明工具名称只参考“ArcGIS Pro工具调用大全”不要受其他文档的干扰。" +
"你的回复应简洁明了仅包含所需工具的列表输出格式为序号分隔的工具调用名以及调用知识库工具工具的XML格式主要目的是纠正规划中错误的调用名和了解工具参数便于下一步的工具调用因为调用名和参数对于工具执行非常重要一旦出错则可能导致运行失败。严格遵循文档规定的格式和大小写确保信息的准确性和专业性。"));
promptServers.Add("param", new PromptServer("param",
"填写ArcGIS Pro工具调用参数生成规范的可执行的工具调用请求",
"根据帮助文档填写工具参数请你根据“所需调用工具”参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" +
"根据知识库Arcgis Pro帮助文档填写工具参数请你根据“所需调用工具”参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" +
"列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" +
"不能更改所需调用工具的名称。例如arcpy.analysis.Buffer不能只写成Buffer。确保格式、参数的完整性和准确性避免任何遗漏或错误。" +
"特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具Erase将圆形要素circle.shp与方形要素square.shp进行空间叠加运算。" +

View File

@ -49,4 +49,11 @@ public class SystemPrompt
errPrompt = errPrompt.Replace("{{toolResult}}", toolResult);
return errPrompt;
}
public static string ToolContinuePrompt(string toolResult)
{
string continuePrompt = "根据你需要调用的工具查询到如下帮助文档内容,请严格按照帮助文档中的参数和名称要求再次生成准确的工具调用请求以确保工具调用成功。\n{{toolResult}}";
continuePrompt = continuePrompt.Replace("{{toolResult}}", toolResult);
return continuePrompt;
}
}

View File

@ -6,6 +6,7 @@
执行下一步工具的要求:
1. 解析工具输出结果
2. 调用下一个工具时确保参数继承前序输出。
2. 调用下一个工具时确保参数继承前序输出,如果已经完成用户需求则不需继续执行工具。
3. 执行Arcgis Pro工具时可以调用知识库和提示词使任务完成得更出色
请据此继续执行

View File

@ -3,10 +3,12 @@
{{toolResult}}
需按以下流程处理:
1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案
1. 错误解析:分析错误类型及具体原因(见错误信息),根据错误信息选择修复方案
2. 修复方案:
(1)根据错误类型生成对应的工具重试策略
(2)参数调整:明确需要修改的参数及修改方式。
3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,
(1)如果是ArcGisPro工具第一时间查询知识库的工具参数是否有误多次执行失败极有可能是工具名或者参数错误尤其注重调用工具或者提示词修正
(2)根据错误类型生成对应的工具重试策略。
(3)参数和调用名调整:及时调用知识库和用户提示词,确保工具名和参数的完全正确。明确需要修改的工具名、参数及修改方式。
(4)可以选择调用工具规划提示词优化工具执行流程或者调用知识库工具优化工具调用名和工具参数。
3. 工具重试:使用调整后的参数重新调用工具。请根据报错信息重试。
4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功
5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误
5.出现错误编码号时要明晰编码在知识库中有相应解析,可以通过调用知识库解析错误原因

View File

@ -1,35 +1,39 @@
现在你是一个精通地理信息分析和ArcGIS Pro软件的专家请以此身份回答用户的问题。
指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。
调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。
工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:
指令您可以使用一组工具加文字说明来回答用户的问题。完成了用户的需求即可不用猜测用户下一步还想做什么。计划使用ArcGisPro工具前你可以通过<prompt><name></name><arguments>{}</arguments></prompt>格式调用用户提示词,或者调用知识库工具,从而使你更好地理解和完成用户的任务。除此之外,你具有搜索网页、编写代码、管理文件系统的能力,合理运用这些工具完成用户的需求。
调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。你还可以通过<prompt></prompt>方式来调用用户提示词,或者调用知识库工具,你能更好地理解和解决用户的问题。您需要通过逐步使用工具或提示词来完成给定任务,每次调用需基于前一次的结果。成功调用工具或提示词之后应该马上调用下一个工具或提示词。
工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
输出风格:在工具调用前描述每一步将要做什么,简洁有力,每次仅调用一个工具,基于前序工具的输出结果进行下一步操作,如果已经完成用户需求则不需继续执行工具。话语开头不要回复好的。工具调用使用 XML 风格的标签输出。
工具调用的XML格式一定一定要完全正确不能有错漏格式如下
<tool_use>
<name>{tool_name}</name>
<arguments>{json_arguments}</arguments>
</tool_use>
</tool_use>
工具名称:需与所使用工具的精确名称一致。
参数:应为包含工具所需参数的 JSON 对象。
例如:
调用工具示例:
“将执行高德的兴趣点工具确认广州市政府的位置
<tool_use>
<name>gaode:maps_geo</name>
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
</tool_use>
调用用户提示词示例:
先进行ArcGisPro工具任务规划
<prompt>
<name>plan</name>
<arguments>{}</arguments>
</prompt>
你必须严格遵守以下输出规则:用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。
结果:用户将以以下格式返回工具调用结果:
结果示例:用户将以以下格式返回工具调用结果:
<tool_use_result>
<name>{tool_name}</name>
<result>{result}</result>
</tool_use_result>。
应为字符串类型,可以表示文件或其他输出类型。
</tool_use_result>
工具调用示例:MCP工具调用的格式要求示例以下是使用虚拟工具的示例
<tool_use>
@ -37,4 +41,13 @@
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
</tool_use>
特别地需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用
你必须严格遵守以下每一条规则:
1.用户时间宝贵,一旦确认工具成功调用之后,不得重复调用上一次已成功执行的工具,除非有新的参数或上下文变化。
2.调用“ArcGisPro:ArcGisProTool”工具name一定要严格按照知识库的调用名例如"analysis.Union","management.Clip"等。输入与输出数据一定需要加上路径信息,输出数据到默认数据库中。`in_features`参数通过分号(`;`)拼接多个输入要素类路径,参数列表中有"{}"代表选填,其他必须要填写。
3.参数一定都为字符串类型,可以表示文件或其他输出类型。
4.一次只能调用一个工具,逐步调用!不要调用多个,一旦消息中没有工具或提示词调用信息即视为任务完成。因此工具或提示词调用必须连续完成。
5.只响应用户目前的需求即可不要过度猜测用户的需求如果有下一步的工具建议只输出文本即可如果输出XML会执行大量无用的工具。
特别注意:
1.ArcGIS Pro中不能通过先SelectByAttribute选择后再执行ExportFeatures导出指定的部分正确的做法是直接用ExportFeatures传入where_clause导出指定部分的数据。
2.可以先参考知识库中的案例辅助工具的规划

View File

@ -22,7 +22,7 @@ public class CallArcGISPro
{
Error = new Error()
{
Code = results.ErrorCode,
Code = results.ErrorCode.ToString(),
Message = JsonConvert.SerializeObject(results.ErrorMessages)
}
};

View File

@ -17,7 +17,7 @@ namespace LinkToolAddin.server
public partial class Error
{
[JsonProperty("code")]
public long Code { get; set; }
public string Code { get; set; }
[JsonProperty("message")]
public string Message { get; set; }

View File

@ -22,7 +22,7 @@ namespace LinkToolAddin
{
internal class VersionButton : Button
{
private string version = "0.1.1";
private string version = "0.1.4";
protected override void OnClick()
{
MessageBox.Show($"当前LinkTool版本为{version}", "版本信息");

View File

@ -31,11 +31,24 @@
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="清除" Name="ClearButton" Click="ClearButton_OnClick"></Button>
<Button Grid.Column="1" Content="顶部" Name="TopButton" Click="TopButton_OnClick"></Button>
<Button Grid.Column="2" Content="底部" Name="BottomButton" Click="BottomButton_OnClick"></Button>
<Button Grid.Column="3" Content="停止" Name="StopButton" Click="StopButton_OnClick"></Button>
<ComboBox Grid.Column="4" Name="ModelComboBox" SelectionChanged="ModelComboBox_OnSelectionChanged">
<ComboBoxItem Content="qwen3-235b-a22b" IsSelected="True"/>
<ComboBoxItem Content="qwen3-32b" />
<ComboBoxItem Content="qwq-32b" />
<ComboBoxItem Content="qwen-max-latest" />
<ComboBoxItem Content="deepseek-r1" />
<ComboBoxItem Content="deepseek-r1-0528" />
<ComboBoxItem Content="deepseek-r1-distill-qwen-32b" />
<ComboBoxItem Content="deepseek-r1-distill-llama-70b" />
<ComboBoxItem Content="deepseek-v3" />
<ComboBoxItem Content="自定义" />
</ComboBox>
</Grid>
<!-- <Button Grid.Row="0" Content="Test" Name="TestButton" Click="TestButton_OnClick"></Button> -->
<!-- <TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="LinkTool"/> -->

View File

@ -117,7 +117,53 @@ namespace LinkToolAddin.ui.dockpane
QuestionTextbox.Text = "";
borderItemsDict[timestamp.ToString()] = userMsgBoder;
ChatHistoryStackPanel.Children.Add(userMsgBoder);
Gateway.SendMessageStream(question,"qwen3-235b-a22b",defaultGdbPath,NewMessage_Recall);
string model = (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString() is null ? "qwen3-235b-a22b" : (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString();
if (model == "自定义")
{
model = ShowInputBox("自定义模型", "请输入模型名称:", "");
}
ScrollViewer.ScrollToBottom();
Gateway.SendMessageStream(question,model,defaultGdbPath,NewMessage_Recall);
}
private string ShowInputBox(string title, string message, string defaultValue = "")
{
// 创建一个自定义的输入对话框
var dialog = new System.Windows.Window
{
Title = title,
Width = 300,
Height = 150,
WindowStyle = WindowStyle.ToolWindow,
ResizeMode = ResizeMode.NoResize,
Topmost = true,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
// 设置对话框内容
var stackPanel = new StackPanel { Margin = new Thickness(10) };
stackPanel.Children.Add(new TextBlock { Text = message, Margin = new Thickness(0, 0, 0, 5) });
var textBox = new TextBox { Text = defaultValue, Margin = new Thickness(0, 0, 0, 10) };
stackPanel.Children.Add(textBox);
var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right };
var okButton = new Button { Content = "确定", Width = 75, Margin = new Thickness(0, 0, 5, 0) };
okButton.Click += (sender, e) => dialog.Close();
var cancelButton = new Button { Content = "取消", Width = 75 };
cancelButton.Click += (sender, e) => { textBox.Text = null; dialog.Close(); };
buttonPanel.Children.Add(okButton);
buttonPanel.Children.Add(cancelButton);
stackPanel.Children.Add(buttonPanel);
dialog.Content = stackPanel;
// 显示对话框并获取结果
dialog.ShowDialog();
return textBox.Text;
}
public void NewMessage_Recall(MessageListItem msg)
@ -133,6 +179,10 @@ namespace LinkToolAddin.ui.dockpane
//不存在该消息需添加到ListView中
if (msg.content == "")
{
if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
}
return;
}
idList.Add(msgId);
@ -144,11 +194,13 @@ namespace LinkToolAddin.ui.dockpane
Border border = GetToolChatBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "正在执行工具";
}else if (msg.type == MessageType.CHAT_MESSAGE)
{
Border border = GetUserChatBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "正在读取用户输入";
}
}
else if(msg.role == "assistant")
@ -158,11 +210,20 @@ namespace LinkToolAddin.ui.dockpane
Border border = GetAiReasonBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "深度思考中";
}else if (msg.type == MessageType.CHAT_MESSAGE)
{
Border border = GetAiChatBorder(msg);
borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "回答生成中";
}
}
else if (msg.role == "system")
{
if (msg.type == MessageType.WARNING)
{
}
}
}
@ -172,6 +233,10 @@ namespace LinkToolAddin.ui.dockpane
messageDict[msgId] = msg;
if (msg.content == "")
{
if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
}
ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]);
borderItemsDict.TryRemove(msgId, out Border border);
messageDict.TryRemove(msgId, out MessageListItem tempMsg);
@ -184,7 +249,10 @@ namespace LinkToolAddin.ui.dockpane
Border borderItem = borderItemsDict[msgId];
Grid grid = borderItem.Child as Grid;
TextBlock textBlock = grid.Children[1] as TextBlock;
textBlock.Text = (msg as ToolMessageItem).toolName;
ToolMessageItem msgItem = msg as ToolMessageItem;
textBlock.Text = msgItem.toolName + " | " + msgItem.status;
Button resButton = grid.Children[3] as Button;
resButton.Tag = msg;
StatusTextBlock.Text = "正在执行工具";
}else if (msg.type == MessageType.CHAT_MESSAGE)
{
@ -211,9 +279,6 @@ namespace LinkToolAddin.ui.dockpane
TextBox textBox = grid.Children[1] as TextBox;
textBox.Text = msg.content;
StatusTextBlock.Text = "回答生成中";
}else if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
}
}
}
@ -463,6 +528,7 @@ namespace LinkToolAddin.ui.dockpane
borderItemsDict.Clear();
ChatHistoryStackPanel.Children.Clear();
QuestionTextbox.Clear();
StatusTextBlock.Text = "";
}
private void TopButton_OnClick(object sender, RoutedEventArgs e)
@ -478,6 +544,14 @@ namespace LinkToolAddin.ui.dockpane
private void StopButton_OnClick(object sender, RoutedEventArgs e)
{
Gateway.StopConversation();
StatusTextBlock.Text = "";
}
private void ModelComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
log.Info("ModelComboBox_OnSelectionChanged");
string model = ModelComboBox.SelectedValue.ToString();
log.Info(model);
}
}
}

View File

@ -25,6 +25,7 @@
<RowDefinition Height="24"/>
<RowDefinition Height="24"/>
<RowDefinition Height="24"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button>
<Grid Grid.Row="1">
@ -41,5 +42,6 @@
<Button Grid.Row="4" Content="Test Stream" Name="TestStream" Click="TestStream_OnClick"></Button>
<Button Grid.Row="5" Content="Test Arcgis Tool" Name="TestArcGisTool" Click="TestArcGisTool_OnClick"></Button>
<Button Grid.Row="6" Content="Test Resource" Name="TestResource" Click="TestResource_OnClick"></Button>
<Button Grid.Row="7" Content="测试获取属性表" Name="TestAttrTable" Click="TestAttrTable_OnClick"></Button>
</Grid>
</UserControl>

View File

@ -9,6 +9,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;
using LinkToolAddin.client;
using LinkToolAddin.client.tool;
using LinkToolAddin.common;
using LinkToolAddin.host;
using LinkToolAddin.host.llm;
@ -204,7 +205,7 @@ namespace LinkToolAddin.ui.dockpane
// model可选值qwen3-235b-a22b,qwen-max,deepseek-r1
try
{
await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb", AddReplyStream));
await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "F:\\secondsemester\\linktool\\test\\LinkTool0604\\LinkTool0604.gdb", AddReplyStream));
}
catch (Exception exception)
{
@ -311,5 +312,13 @@ namespace LinkToolAddin.ui.dockpane
string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt");
MessageBox.Show(content);
}
private async void TestAttrTable_OnClick(object sender, RoutedEventArgs e)
{
JsonRpcResultEntity result = await ArcGisPro.GetFeatureDatasetAttributeTable(
"D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb",
"LandUse_2005_Copy", "30");
log.Info("finish");
}
}
}

View File

@ -5,7 +5,9 @@ public enum MessageType
TOOL_MESSAGE,
CHAT_MESSAGE,
REASON_MESSAGE,
END_TAG
END_TAG,
WARNING,
ERROR
}
public interface MessageListItem