Compare commits

..

No commits in common. "master" and "ui_pz" have entirely different histories.

18 changed files with 292 additions and 628 deletions

View File

@ -6,13 +6,6 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<NoWarn>CA1416</NoWarn> <NoWarn>CA1416</NoWarn>
<TargetFramework>net8.0-windows</TargetFramework> <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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Config.daml" /> <None Remove="Config.daml" />

View File

@ -24,7 +24,7 @@ public class ArcGisPro
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")] [McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")]
public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams) public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams)
{ {
IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams,null,null,null,GPExecuteToolFlags.InheritGPOptions|GPExecuteToolFlags.GPThread); IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
JsonRpcResultEntity jsonRpcResultEntity; JsonRpcResultEntity jsonRpcResultEntity;
if (results.IsFailed) if (results.IsFailed)
{ {
@ -33,8 +33,8 @@ public class ArcGisPro
{ {
Error = new Error() Error = new Error()
{ {
Code = results.ErrorCode.ToString(), Code = results.ErrorCode,
Message = GetMessagesString(results.ErrorMessages)+"\n"+GetMessagesString(results.Messages) Message = GetMessagesString(results.ErrorMessages)
} }
}; };
}else if(results.HasWarnings) }else if(results.HasWarnings)
@ -100,7 +100,7 @@ public class ArcGisPro
return result; return result;
} }
[McpServerTool, Description("列出gdb数据库中的所有数据名称")] [McpServerTool, Description("列出gdb数据库中的所有数据名称")]
public static async Task<JsonRpcResultEntity> ListData(string gdbPath) public static async Task<JsonRpcResultEntity> ListData(string gdbPath)
{ {
var datasets = new List<string>(); var datasets = new List<string>();
@ -137,80 +137,6 @@ public class ArcGisPro
return result; 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) private static string GetMessagesString(IEnumerable<IGPMessage> messages)
{ {
StringBuilder messagesStr = new StringBuilder(); StringBuilder messagesStr = new StringBuilder();

View File

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

View File

@ -12,11 +12,9 @@ using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using ArcGIS.Desktop.Internal.Mapping;
using ArcGIS.Desktop.Internal.Mapping.Locate; using ArcGIS.Desktop.Internal.Mapping.Locate;
using LinkToolAddin.client; using LinkToolAddin.client;
using LinkToolAddin.client.prompt; using LinkToolAddin.client.prompt;
using LinkToolAddin.client.tool;
using LinkToolAddin.host.llm; using LinkToolAddin.host.llm;
using LinkToolAddin.host.llm.entity; using LinkToolAddin.host.llm.entity;
using LinkToolAddin.host.mcp; using LinkToolAddin.host.mcp;
@ -78,11 +76,9 @@ public class Gateway
{ {
Model = model, Model = model,
Messages = messages, Messages = messages,
Temperature = 0.3, Temperature = 0.7,
TopP = 0.6, TopP = 1,
TopK = 25, MaxTokens = 1000,
MaxTokens = 1024,
ThinkingBudget = 1024
}); });
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
log.Info(reponse); log.Info(reponse);
@ -243,25 +239,17 @@ public class Gateway
api_key = "sk-db177155677e438f832860e7f4da6afc" api_key = "sk-db177155677e438f832860e7f4da6afc"
}; };
List<Message> messages = new List<Message>(); List<Message> messages = new List<Message>();
string toolInfos = ""; string toolInfos = await GetToolInfos(new McpServerList());
try messages.Add(new Message
{ {
toolInfos = await GetToolInfos(new McpServerList()); Role = "system",
messages.Add(new Message Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
{ });
Role = "system", messages.Add(new Message
Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
});
messages.Add(new Message
{
Role = "user",
Content = message
});
}catch (Exception ex)
{ {
log.Error(ex); Role = "user",
MessageBox.Show(ex.Message,"获取MCP列表失败"); Content = message
} });
goOn = true; goOn = true;
string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>"; 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>"; string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>";
@ -269,12 +257,10 @@ public class Gateway
PromptServerList promptServerList = new PromptServerList(); PromptServerList promptServerList = new PromptServerList();
int loop = 0; int loop = 0;
string messageContent = ""; //一次请求下完整的response string messageContent = ""; //一次请求下完整的response
bool queriedKnowledge = false;
bool executedTool = false;
while (goOn) while (goOn)
{ {
loop++; loop++;
if (loop > 500) if (loop > 20)
{ {
MessageBox.Show("达到最大循环次数", "退出循环"); MessageBox.Show("达到最大循环次数", "退出循环");
break; break;
@ -283,9 +269,8 @@ public class Gateway
{ {
Model = model, Model = model,
Messages = messages, Messages = messages,
Temperature = 0.3, Temperature = 0.7,
TopP = 0.4, TopP = 1,
TopK = 7,
MaxTokens = 1000, MaxTokens = 1000,
}; };
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
@ -297,105 +282,56 @@ public class Gateway
{ {
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求 //如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
goOn = false; goOn = false;
MessageListItem endMessageListItem1 = new ChatMessageItem MessageListItem endMessageListItem1 = new ChatMessageItem
{ {
type = MessageType.END_TAG, type = MessageType.END_TAG,
content = "", content = "",
id = (timestamp + 3).ToString(), id = (timestamp+3).ToString(),
role = "assistant" role = "assistant"
}; };
callback?.Invoke(endMessageListItem1); callback?.Invoke(endMessageListItem1);
break; break;
} }
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
try
{ {
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent)) if (!goOn)
{ {
if (!goOn) MessageListItem endMessageListItem2 = new ChatMessageItem
{ {
MessageListItem endMessageListItem2 = new ChatMessageItem type = MessageType.END_TAG,
{ content = "",
type = MessageType.END_TAG, id = (timestamp+3).ToString(),
content = "", role = "assistant"
id = (timestamp+3).ToString(), };
role = "assistant" callback?.Invoke(endMessageListItem2);
}; break;
callback?.Invoke(endMessageListItem2); }
break;
} try
try {
string chunk = llmStreamChat.Choices[0].Delta.Content;
MessageListItem reasonMessageListItem = new ChatMessageItem()
{ {
string chunk = llmStreamChat.Choices[0].Delta.Content; content = llmStreamChat.Choices[0].Delta.ResoningContent,
MessageListItem reasonMessageListItem = new ChatMessageItem() role = "assistant",
type = MessageType.REASON_MESSAGE,
id = (timestamp+2).ToString()
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(reasonMessageListItem);
});
messageContent = chunk;
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
if (matched == "")
{
var (matchedPrompt, remainingPrompt) = ExtractMatchedPart(chunk, promptPattern);
if (matchedPrompt == "")
{ {
content = llmStreamChat.Choices[0].Delta.ResoningContent, //普通消息文本
role = "assistant",
type = MessageType.REASON_MESSAGE,
id = (timestamp+2).ToString()
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(reasonMessageListItem);
});
messageContent = chunk;
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
if (matched == "")
{
var (matchedPrompt, remainingPrompt) = ExtractMatchedPart(chunk, promptPattern);
if (matchedPrompt == "")
{
//普通消息文本
MessageListItem chatMessageListItem = new ChatMessageItem()
{
content = remainingPrompt,
role = "assistant",
type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(chatMessageListItem);
});
}
else
{
//包含Prompt调用请求的消息
MessageListItem chatMessageListItem = new ChatMessageItem()
{
content = remainingPrompt,
role = "assistant",
type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt);
string promptKey = promptUse.Element("name")?.Value;
string promptArgs = promptUse.Element("arguments")?.Value;
Dictionary<string, string> promptParams = JsonConvert.DeserializeObject<Dictionary<string, string>>(promptArgs);
promptRequests = new List<PromptRequest>();
promptRequests.Add(new PromptRequest()
{
PromptName = promptKey,
PromptArgs = promptParams,
PromptServer = promptServerList.GetPromptServer(promptKey)
});
}
}
else
{
//包含工具调用请求的消息
XElement toolUse = XElement.Parse(matched);
string fullToolName = toolUse.Element("name")?.Value;
string toolArgs = toolUse.Element("arguments")?.Value;
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
MessageListItem chatMessageListItem = new ChatMessageItem() MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
content = remaining, content = remainingPrompt,
role = "assistant", role = "assistant",
type = MessageType.CHAT_MESSAGE, type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString() id = timestamp.ToString()
@ -404,44 +340,83 @@ public class Gateway
{ {
callback?.Invoke(chatMessageListItem); callback?.Invoke(chatMessageListItem);
}); });
if (!executedTool) }
else
{
//包含Prompt调用请求的消息
MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
MessageListItem toolMessageListItem = new ToolMessageItem() content = remainingPrompt,
{ role = "assistant",
content = toolName, type = MessageType.CHAT_MESSAGE,
result = "工具运行中" + toolName, id = timestamp.ToString()
toolName = toolName,
toolParams = toolParams,
role = "user",
type = MessageType.TOOL_MESSAGE,
id = (timestamp+1).ToString(),
status = "loading"
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageListItem);
});
}
mcpToolRequests = new List<McpToolRequest>();
McpToolRequest mcpToolRequest = new McpToolRequest()
{
McpServer = mcpServer,
ToolName = toolName,
ToolArgs = toolParams,
}; };
mcpToolRequests.Add(mcpToolRequest); callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt);
string promptKey = promptUse.Element("name")?.Value;
string promptArgs = promptUse.Element("arguments")?.Value;
Dictionary<string, string> promptParams = JsonConvert.DeserializeObject<Dictionary<string, string>>(promptArgs);
promptRequests = new List<PromptRequest>();
promptRequests.Add(new PromptRequest()
{
PromptName = promptKey,
PromptArgs = promptParams,
PromptServer = promptServerList.GetPromptServer(promptKey)
});
} }
} }
catch (Exception e) else
{ {
Console.WriteLine(e); //包含工具调用请求的消息
log.Error(e.Message); XElement toolUse = XElement.Parse(matched);
string fullToolName = toolUse.Element("name")?.Value;
string toolArgs = toolUse.Element("arguments")?.Value;
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
MessageListItem chatMessageListItem = new ChatMessageItem()
{
content = remaining,
role = "assistant",
type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(chatMessageListItem);
});
MessageListItem toolMessageListItem = new ToolMessageItem()
{
content = "",
result = "",
toolName = toolName,
toolParams = toolParams,
role = "user",
type = MessageType.TOOL_MESSAGE,
id = (timestamp+1).ToString(),
status = "loading"
};
Application.Current.Dispatcher.Invoke(() =>
{
callback?.Invoke(toolMessageListItem);
});
mcpToolRequests = new List<McpToolRequest>();
McpToolRequest mcpToolRequest = new McpToolRequest()
{
McpServer = mcpServer,
ToolName = toolName,
ToolArgs = toolParams,
};
mcpToolRequests.Add(mcpToolRequest);
} }
} }
}catch (Exception e) catch (Exception e)
{ {
log.Error(e.Message); Console.WriteLine(e);
MessageBox.Show(e.Message, "请求大模型出错"); log.Error(e.Message);
}
} }
if (messageContent != "") if (messageContent != "")
{ {
@ -454,7 +429,6 @@ public class Gateway
/*统一处理本次请求中的MCP工具调用需求*/ /*统一处理本次请求中的MCP工具调用需求*/
foreach (McpToolRequest mcpToolRequest in mcpToolRequests) foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
{ {
executedTool = true;
try try
{ {
McpServer mcpServer = mcpToolRequest.McpServer; McpServer mcpServer = mcpToolRequest.McpServer;
@ -465,7 +439,7 @@ public class Gateway
SseMcpServer sseMcpServer = mcpServer as SseMcpServer; SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl); SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams); CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
MessageListItem toolMessageItem1 = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = toolName, toolName = toolName,
toolParams = toolParams, toolParams = toolParams,
@ -485,7 +459,7 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1); callback?.Invoke(toolMessageItem);
}); });
} }
else if (mcpServer is StdioMcpServer) else if (mcpServer is StdioMcpServer)
@ -493,7 +467,7 @@ public class Gateway
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer; StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args); StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams); CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
MessageListItem toolMessageItem1 = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = toolName, toolName = toolName,
toolParams = toolParams, toolParams = toolParams,
@ -513,67 +487,11 @@ public class Gateway
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1); callback?.Invoke(toolMessageItem);
}); });
} }
else if (mcpServer is InnerMcpServer) 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); Type type = Type.GetType("LinkToolAddin.client.tool." + (mcpServer as InnerMcpServer).Name);
MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static); MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static);
var methodParams = toolParams.Values.ToArray(); var methodParams = toolParams.Values.ToArray();
@ -594,17 +512,11 @@ public class Gateway
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>; var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
JsonRpcResultEntity innerResult = await task; JsonRpcResultEntity innerResult = await task;
string displayToolName = toolName;
if (displayToolName == "ArcGisProTool")
{
displayToolName = "【GP】"+toolParams["toolName"].ToString();
}
queriedKnowledge = false;
if (innerResult is JsonRpcErrorEntity) if (innerResult is JsonRpcErrorEntity)
{ {
MessageListItem toolMessageItem1 = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = displayToolName, toolName = toolName,
toolParams = toolParams, toolParams = toolParams,
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "fail", status = "fail",
@ -616,18 +528,18 @@ public class Gateway
messages.Add(new Message messages.Add(new Message
{ {
Role = "user", Role = "user",
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult)) Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolMessageItem))
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1); callback?.Invoke(toolMessageItem);
}); });
} }
else if (innerResult is JsonRpcSuccessEntity) else if (innerResult is JsonRpcSuccessEntity)
{ {
MessageListItem toolMessageItem1 = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = displayToolName, toolName = toolName,
toolParams = toolParams, toolParams = toolParams,
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "success", status = "success",
@ -639,11 +551,11 @@ public class Gateway
messages.Add(new Message messages.Add(new Message
{ {
Role = "user", Role = "user",
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult)) Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolMessageItem))
}); });
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
callback?.Invoke(toolMessageItem1); callback?.Invoke(toolMessageItem);
}); });
} }
} }
@ -669,13 +581,11 @@ public class Gateway
MessageListItem toolMessageItem = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = "调用提示词", toolName = "调用提示词",
toolParams = new Dictionary<string, object>(), toolParams = null,
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "success", status = "success",
content = "成功调用提示词:"+promptRequest.PromptName, content = "成功调用提示词:"+promptRequest.PromptName,
id = (timestamp+1).ToString(), id = (timestamp+1).ToString()
result = "成功调用提示词:"+promptRequest.PromptName,
role = "user"
}; };
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
@ -702,110 +612,87 @@ public class Gateway
private static async Task<string> GetToolInfos(McpServerList mcpServerList) private static async Task<string> GetToolInfos(McpServerList mcpServerList)
{ {
StringBuilder toolInfos = new StringBuilder(); 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()) foreach (McpServer mcpServer in mcpServerList.GetAllServers())
{ {
i++; log.Info($"正在列出{mcpServer.Name}中的工具");
try if (mcpServer is InnerMcpServer)
{ {
if (mcpServer is InnerMcpServer) InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name);
MethodInfo[] methods = type.GetMethods();
foreach (MethodInfo method in methods)
{ {
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; if (method.IsPublic && method.IsStatic)
Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name);
MethodInfo[] methods = type.GetMethods();
foreach (MethodInfo method in methods)
{ {
if (method.IsPublic && method.IsStatic) string methodName = method.Name;
string methodDescription = method.GetCustomAttribute<DescriptionAttribute>()?.Description;
string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method);
McpToolDefinition toolDefinition = new McpToolDefinition
{ {
string methodName = method.Name; Tool = new Tool
string methodDescription = method.GetCustomAttribute<DescriptionAttribute>()?.Description;
string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method);
McpToolDefinition toolDefinition = new McpToolDefinition
{ {
Tool = new Tool Name = innerMcpServer.Name + ":" + methodName,
{ Description = methodDescription,
Name = innerMcpServer.Name + ":" + methodName, Arguments = methodParamSchema
Description = methodDescription, }
Arguments = methodParamSchema };
} XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition));
}; toolInfos.AppendLine(node.ToString());
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)); toolInfos.AppendLine();
toolInfos.AppendLine(node.ToString()); }
toolInfos.AppendLine(); }
}
else if(mcpServer is SseMcpServer)
{
SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl);
IList<McpClientTool> tools = await client.GetToolListAsync();
foreach (McpClientTool tool in tools)
{
string toolName = (mcpServer as SseMcpServer).Name + ":" + tool.Name;
string toolDescription = tool.Description;
string toolParamSchema = tool.JsonSchema.ToString();
McpToolDefinition toolDefinition = new McpToolDefinition
{
Tool = new Tool
{
Name = toolName,
Description = toolDescription,
Arguments = toolParamSchema
} }
} };
toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
toolInfos.AppendLine();
} }
else if(mcpServer is SseMcpServer) }else if (mcpServer is StdioMcpServer)
{
SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl);
IList<McpClientTool> tools = await client.GetToolListAsync();
foreach (McpClientTool tool in tools)
{
string toolName = (mcpServer as SseMcpServer).Name + ":" + tool.Name;
string toolDescription = tool.Description;
string toolParamSchema = tool.JsonSchema.ToString();
McpToolDefinition toolDefinition = new McpToolDefinition
{
Tool = new Tool
{
Name = toolName,
Description = toolDescription,
Arguments = toolParamSchema
}
};
toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
toolInfos.AppendLine();
}
}else if (mcpServer is StdioMcpServer)
{
StdioMcpClient client = new StdioMcpClient((mcpServer as StdioMcpServer).Command, (mcpServer as StdioMcpServer).Args);
IList<McpClientTool> tools = await client.GetToolListAsync();
foreach (McpClientTool tool in tools)
{
string toolName = (mcpServer as StdioMcpServer).Name + ":" + tool.Name;;
string toolDescription = tool.Description;
string toolParamSchema = tool.JsonSchema.ToString();
McpToolDefinition toolDefinition = new McpToolDefinition
{
Tool = new Tool
{
Name = toolName,
Description = toolDescription,
Arguments = CompressJson(toolParamSchema)
}
};
toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
toolInfos.AppendLine();
}
}
}catch (Exception e)
{ {
log.Error(e.Message); StdioMcpClient client = new StdioMcpClient((mcpServer as StdioMcpServer).Command, (mcpServer as StdioMcpServer).Args);
failedMcp.Add(i); IList<McpClientTool> tools = await client.GetToolListAsync();
failedMcpString.Add(mcpServerList.GetAllServerNames()[i]); foreach (McpClientTool tool in tools)
{
string toolName = (mcpServer as StdioMcpServer).Name + ":" + tool.Name;;
string toolDescription = tool.Description;
string toolParamSchema = tool.JsonSchema.ToString();
McpToolDefinition toolDefinition = new McpToolDefinition
{
Tool = new Tool
{
Name = toolName,
Description = toolDescription,
Arguments = CompressJson(toolParamSchema)
}
};
toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
toolInfos.AppendLine();
}
} }
} }
List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts(); List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts();
foreach (UserPrompt userPrompt in prompts) foreach (UserPrompt userPrompt in prompts)
{ {
try XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt}));
{ toolInfos.AppendLine(node.ToString());
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt})); toolInfos.AppendLine();
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(); return toolInfos.ToString();
} }

View File

@ -32,57 +32,56 @@ public class McpServerList
{ {
Name = "KnowledgeBase", Name = "KnowledgeBase",
Type = "inner", Type = "inner",
Description = "可以调用进行查询知识库,获取相关参考信息。" , Description = "可以调用进行查询知识库,获取相关参考信息。",
// "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息",
IsActive = true IsActive = true
}); });
//servers.Add("filesystem", new StdioMcpServer() // servers.Add("filesystem", new StdioMcpServer()
//{ // {
// Name = "filesystem", // Name = "filesystem",
// Type = "stdio", // Type = "stdio",
// Command = "npx", // Command = "npx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "-y", // "-y",
// "@modelcontextprotocol/server-filesystem", // "@modelcontextprotocol/server-filesystem",
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb" // "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData"
// } // }
//}); // });
//servers.Add("fetch", new StdioMcpServer() // servers.Add("fetch", new StdioMcpServer()
//{ // {
// Name = "fetch", // Name = "fetch",
// Type = "stdio", // Type = "stdio",
// Command = "uvx", // Command = "uvx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "mcp-server-fetch" // "mcp-server-fetch"
// } // }
//}); // });
//servers.Add("bing-search", new StdioMcpServer() // servers.Add("bing-search", new StdioMcpServer()
//{ // {
// Name = "bing-search", // Name = "bing-search",
// Type = "stdio", // Type = "stdio",
// Command = "npx", // Command = "npx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "bing-cn-mcp" // "bing-cn-mcp"
// } // }
//}); // });
//servers.Add("mcp-python-interpreter", new StdioMcpServer() // servers.Add("mcp-python-interpreter", new StdioMcpServer()
//{ // {
// Name = "mcp-python-interpreter", // Name = "mcp-python-interpreter",
// Type = "stdio", // Type = "stdio",
// Command = "uvx", // Command = "uvx",
// Args = new List<string>() // Args = new List<string>()
// { // {
// "--native-tls", // "--native-tls",
// "mcp-python-interpreter", // "mcp-python-interpreter",
// "--dir", // "--dir",
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb", // "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData",
// "--python-path", // "--python-path",
// "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe" // "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe"
// } // }
//}); // });
} }
public McpServer GetServer(string name) public McpServer GetServer(string name)
@ -106,14 +105,4 @@ public class McpServerList
} }
return serverList; 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,21 +11,13 @@ public class PromptServerList
{ {
promptServers.Add("plan", new PromptServer("plan", promptServers.Add("plan", new PromptServer("plan",
"根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表", "根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表",
//"请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具如果是ArcGIS Pro的工具根据用户的具体需求和提供的数据类型," + "请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具如果是ArcGIS Pro的工具根据用户的具体需求和提供的数据类型," +
// "判断并列出所有必要的分析步骤和工具同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase)" + "判断并列出所有必要的分析步骤和工具同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase)" +
// "确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" + "确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
// "有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。" "有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"));
"你对ArcGIS Pro的工具箱及其功能了如指掌能够根据用户的具体需求和提供的数据类型迅速判断并列出所有必要的分析步骤和工具并随之调用知识库工具确认任务规划中的调用名是否正确以及确认其对应参数" +
"同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase),确保工具名的准确无误。" +
"工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。有一些与分析无关的数据能够进行排除在选择工具时不受其干扰。" +
"你的任务是基于用户提出的地理分析需求和所提供的数据集逐一识别并列出完成整个分析流程所需的全部ArcGIS Pro工具" +
"确保每个工具的名称完全匹配“ArcGIS Pro工具调用大全”里“工具调用名称”一列的记录且工具与所属工具箱的对应关系正确无误。" +
"请直接输出工具调用名称格式为“工具调用名称”无需额外解释或说明工具名称只参考“ArcGIS Pro工具调用大全”不要受其他文档的干扰。" +
"你的回复应简洁明了仅包含所需工具的列表输出格式为序号分隔的工具调用名以及调用知识库工具工具的XML格式主要目的是纠正规划中错误的调用名和了解工具参数便于下一步的工具调用因为调用名和参数对于工具执行非常重要一旦出错则可能导致运行失败。严格遵循文档规定的格式和大小写确保信息的准确性和专业性。"));
promptServers.Add("param", new PromptServer("param", promptServers.Add("param", new PromptServer("param",
"填写ArcGIS Pro工具调用参数生成规范的可执行的工具调用请求", "填写ArcGIS Pro工具调用参数生成规范的可执行的工具调用请求",
"根据知识库Arcgis Pro帮助文档填写工具参数请你根据“所需调用工具”参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" + "根据帮助文档填写工具参数请你根据“所需调用工具”参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" +
"列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" + "列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" +
"不能更改所需调用工具的名称。例如arcpy.analysis.Buffer不能只写成Buffer。确保格式、参数的完整性和准确性避免任何遗漏或错误。" + "不能更改所需调用工具的名称。例如arcpy.analysis.Buffer不能只写成Buffer。确保格式、参数的完整性和准确性避免任何遗漏或错误。" +
"特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具Erase将圆形要素circle.shp与方形要素square.shp进行空间叠加运算。" + "特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具Erase将圆形要素circle.shp与方形要素square.shp进行空间叠加运算。" +

View File

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

View File

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

View File

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

View File

@ -1,39 +1,35 @@
现在你是一个精通地理信息分析和ArcGIS Pro软件的专家请以此身份回答用户的问题。 现在你是一个精通地理信息分析和ArcGIS Pro软件的专家请以此身份回答用户的问题。
指令您可以使用一组工具加文字说明来回答用户的问题。完成了用户的需求即可不用猜测用户下一步还想做什么。计划使用ArcGisPro工具前你可以通过<prompt><name></name><arguments>{}</arguments></prompt>格式调用用户提示词,或者调用知识库工具,从而使你更好地理解和完成用户的任务。除此之外,你具有搜索网页、编写代码、管理文件系统的能力,合理运用这些工具完成用户的需求。
调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。你还可以通过<prompt></prompt>方式来调用用户提示词,或者调用知识库工具,你能更好地理解和解决用户的问题。您需要通过逐步使用工具或提示词来完成给定任务,每次调用需基于前一次的结果。成功调用工具或提示词之后应该马上调用下一个工具或提示词。 指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。
工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
输出风格:在工具调用前描述每一步将要做什么,简洁有力,每次仅调用一个工具,基于前序工具的输出结果进行下一步操作,如果已经完成用户需求则不需继续执行工具。话语开头不要回复好的。工具调用使用 XML 风格的标签输出。 调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
工具调用的XML格式一定一定要完全正确不能有错漏格式如下
输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。
工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:
<tool_use> <tool_use>
<name>{tool_name}</name> <name>{tool_name}</name>
<arguments>{json_arguments}</arguments> <arguments>{json_arguments}</arguments>
</tool_use> </tool_use>
工具名称:需与所使用工具的精确名称一致。 工具名称:需与所使用工具的精确名称一致。
参数:应为包含工具所需参数的 JSON 对象。 参数:应为包含工具所需参数的 JSON 对象。
例如: 例如:
调用工具示例:
“将执行高德的兴趣点工具确认广州市政府的位置
<tool_use> <tool_use>
<name>gaode:maps_geo</name> <name>gaode:maps_geo</name>
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments> <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
</tool_use> </tool_use>
调用用户提示词示例:
先进行ArcGisPro工具任务规划
<prompt>
<name>plan</name>
<arguments>{}</arguments>
</prompt>
你必须严格遵守以下输出规则:用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。
结果:用户将以以下格式返回工具调用结果:
结果示例:用户将以以下格式返回工具调用结果:
<tool_use_result> <tool_use_result>
<name>{tool_name}</name> <name>{tool_name}</name>
<result>{result}</result> <result>{result}</result>
</tool_use_result> </tool_use_result>。
应为字符串类型,可以表示文件或其他输出类型。
工具调用示例:MCP工具调用的格式要求示例以下是使用虚拟工具的示例 工具调用示例:MCP工具调用的格式要求示例以下是使用虚拟工具的示例
<tool_use> <tool_use>
@ -41,13 +37,4 @@
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments> <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
</tool_use> </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() Error = new Error()
{ {
Code = results.ErrorCode.ToString(), Code = results.ErrorCode,
Message = JsonConvert.SerializeObject(results.ErrorMessages) Message = JsonConvert.SerializeObject(results.ErrorMessages)
} }
}; };

View File

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

View File

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

View File

@ -31,24 +31,11 @@
<ColumnDefinition Width="36"/> <ColumnDefinition Width="36"/>
<ColumnDefinition Width="36"/> <ColumnDefinition Width="36"/>
<ColumnDefinition Width="36"/> <ColumnDefinition Width="36"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="清除" Name="ClearButton" Click="ClearButton_OnClick"></Button> <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="1" Content="顶部" Name="TopButton" Click="TopButton_OnClick"></Button>
<Button Grid.Column="2" Content="底部" Name="BottomButton" Click="BottomButton_OnClick"></Button> <Button Grid.Column="2" Content="底部" Name="BottomButton" Click="BottomButton_OnClick"></Button>
<Button Grid.Column="3" Content="停止" Name="StopButton" Click="StopButton_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> </Grid>
<!-- <Button Grid.Row="0" Content="Test" Name="TestButton" Click="TestButton_OnClick"></Button> --> <!-- <Button Grid.Row="0" Content="Test" Name="TestButton" Click="TestButton_OnClick"></Button> -->
<!-- <TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="LinkTool"/> --> <!-- <TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="LinkTool"/> -->

View File

@ -117,53 +117,7 @@ namespace LinkToolAddin.ui.dockpane
QuestionTextbox.Text = ""; QuestionTextbox.Text = "";
borderItemsDict[timestamp.ToString()] = userMsgBoder; borderItemsDict[timestamp.ToString()] = userMsgBoder;
ChatHistoryStackPanel.Children.Add(userMsgBoder); ChatHistoryStackPanel.Children.Add(userMsgBoder);
string model = (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString() is null ? "qwen3-235b-a22b" : (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString(); Gateway.SendMessageStream(question,"qwen3-235b-a22b",defaultGdbPath,NewMessage_Recall);
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) public void NewMessage_Recall(MessageListItem msg)
@ -179,10 +133,6 @@ namespace LinkToolAddin.ui.dockpane
//不存在该消息需添加到ListView中 //不存在该消息需添加到ListView中
if (msg.content == "") if (msg.content == "")
{ {
if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
}
return; return;
} }
idList.Add(msgId); idList.Add(msgId);
@ -194,13 +144,11 @@ namespace LinkToolAddin.ui.dockpane
Border border = GetToolChatBorder(msg); Border border = GetToolChatBorder(msg);
borderItemsDict[msgId] = border; borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border); ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "正在执行工具";
}else if (msg.type == MessageType.CHAT_MESSAGE) }else if (msg.type == MessageType.CHAT_MESSAGE)
{ {
Border border = GetUserChatBorder(msg); Border border = GetUserChatBorder(msg);
borderItemsDict[msgId] = border; borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border); ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "正在读取用户输入";
} }
} }
else if(msg.role == "assistant") else if(msg.role == "assistant")
@ -210,20 +158,11 @@ namespace LinkToolAddin.ui.dockpane
Border border = GetAiReasonBorder(msg); Border border = GetAiReasonBorder(msg);
borderItemsDict[msgId] = border; borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border); ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "深度思考中";
}else if (msg.type == MessageType.CHAT_MESSAGE) }else if (msg.type == MessageType.CHAT_MESSAGE)
{ {
Border border = GetAiChatBorder(msg); Border border = GetAiChatBorder(msg);
borderItemsDict[msgId] = border; borderItemsDict[msgId] = border;
ChatHistoryStackPanel.Children.Add(border); ChatHistoryStackPanel.Children.Add(border);
StatusTextBlock.Text = "回答生成中";
}
}
else if (msg.role == "system")
{
if (msg.type == MessageType.WARNING)
{
} }
} }
} }
@ -233,10 +172,6 @@ namespace LinkToolAddin.ui.dockpane
messageDict[msgId] = msg; messageDict[msgId] = msg;
if (msg.content == "") if (msg.content == "")
{ {
if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
}
ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]); ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]);
borderItemsDict.TryRemove(msgId, out Border border); borderItemsDict.TryRemove(msgId, out Border border);
messageDict.TryRemove(msgId, out MessageListItem tempMsg); messageDict.TryRemove(msgId, out MessageListItem tempMsg);
@ -249,10 +184,7 @@ namespace LinkToolAddin.ui.dockpane
Border borderItem = borderItemsDict[msgId]; Border borderItem = borderItemsDict[msgId];
Grid grid = borderItem.Child as Grid; Grid grid = borderItem.Child as Grid;
TextBlock textBlock = grid.Children[1] as TextBlock; TextBlock textBlock = grid.Children[1] as TextBlock;
ToolMessageItem msgItem = msg as ToolMessageItem; textBlock.Text = (msg as ToolMessageItem).toolName;
textBlock.Text = msgItem.toolName + " | " + msgItem.status;
Button resButton = grid.Children[3] as Button;
resButton.Tag = msg;
StatusTextBlock.Text = "正在执行工具"; StatusTextBlock.Text = "正在执行工具";
}else if (msg.type == MessageType.CHAT_MESSAGE) }else if (msg.type == MessageType.CHAT_MESSAGE)
{ {
@ -279,6 +211,9 @@ namespace LinkToolAddin.ui.dockpane
TextBox textBox = grid.Children[1] as TextBox; TextBox textBox = grid.Children[1] as TextBox;
textBox.Text = msg.content; textBox.Text = msg.content;
StatusTextBlock.Text = "回答生成中"; StatusTextBlock.Text = "回答生成中";
}else if (msg.type == MessageType.END_TAG)
{
StatusTextBlock.Text = "";
} }
} }
} }
@ -528,7 +463,6 @@ namespace LinkToolAddin.ui.dockpane
borderItemsDict.Clear(); borderItemsDict.Clear();
ChatHistoryStackPanel.Children.Clear(); ChatHistoryStackPanel.Children.Clear();
QuestionTextbox.Clear(); QuestionTextbox.Clear();
StatusTextBlock.Text = "";
} }
private void TopButton_OnClick(object sender, RoutedEventArgs e) private void TopButton_OnClick(object sender, RoutedEventArgs e)
@ -544,14 +478,6 @@ namespace LinkToolAddin.ui.dockpane
private void StopButton_OnClick(object sender, RoutedEventArgs e) private void StopButton_OnClick(object sender, RoutedEventArgs e)
{ {
Gateway.StopConversation(); 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,7 +25,6 @@
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button> <Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button>
<Grid Grid.Row="1"> <Grid Grid.Row="1">
@ -42,6 +41,5 @@
<Button Grid.Row="4" Content="Test Stream" Name="TestStream" Click="TestStream_OnClick"></Button> <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="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="6" Content="Test Resource" Name="TestResource" Click="TestResource_OnClick"></Button>
<Button Grid.Row="7" Content="测试获取属性表" Name="TestAttrTable" Click="TestAttrTable_OnClick"></Button>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -9,7 +9,6 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Xml.Linq; using System.Xml.Linq;
using LinkToolAddin.client; using LinkToolAddin.client;
using LinkToolAddin.client.tool;
using LinkToolAddin.common; using LinkToolAddin.common;
using LinkToolAddin.host; using LinkToolAddin.host;
using LinkToolAddin.host.llm; using LinkToolAddin.host.llm;
@ -205,7 +204,7 @@ namespace LinkToolAddin.ui.dockpane
// model可选值qwen3-235b-a22b,qwen-max,deepseek-r1 // model可选值qwen3-235b-a22b,qwen-max,deepseek-r1
try try
{ {
await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "F:\\secondsemester\\linktool\\test\\LinkTool0604\\LinkTool0604.gdb", AddReplyStream)); await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb", AddReplyStream));
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -312,13 +311,5 @@ namespace LinkToolAddin.ui.dockpane
string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt"); string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt");
MessageBox.Show(content); 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,9 +5,7 @@ public enum MessageType
TOOL_MESSAGE, TOOL_MESSAGE,
CHAT_MESSAGE, CHAT_MESSAGE,
REASON_MESSAGE, REASON_MESSAGE,
END_TAG, END_TAG
WARNING,
ERROR
} }
public interface MessageListItem public interface MessageListItem