diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj index bece90c..26846fa 100644 --- a/LinkToolAddin.csproj +++ b/LinkToolAddin.csproj @@ -6,6 +6,13 @@ true CA1416 net8.0-windows + 0.1.3 + LinkToolAddin + LinkTool团队 + LinkTool以大模型赋能让您只需一两句话便能完成复杂的空间分析与时空大数据处理任务。 + 华南农业大学 + 校AI大赛提交版本 + 华南农业大学 diff --git a/client/tool/ArcGisPro.cs b/client/tool/ArcGisPro.cs index 52994b0..6c1adcf 100644 --- a/client/tool/ArcGisPro.cs +++ b/client/tool/ArcGisPro.cs @@ -24,7 +24,7 @@ public class ArcGisPro [McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能,传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")] public static async Task ArcGisProTool(string toolName, List 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) diff --git a/host/Gateway.cs b/host/Gateway.cs index 78889e8..8ce17fb 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -239,17 +239,25 @@ public class Gateway api_key = "sk-db177155677e438f832860e7f4da6afc" }; List messages = new List(); - string toolInfos = await GetToolInfos(new McpServerList()); - messages.Add(new Message + string toolInfos = ""; + try { - Role = "system", - Content = SystemPrompt.SysPrompt(gdbPath, toolInfos) - }); - messages.Add(new Message + toolInfos = await GetToolInfos(new McpServerList()); + messages.Add(new Message + { + Role = "system", + Content = SystemPrompt.SysPrompt(gdbPath, toolInfos) + }); + messages.Add(new Message + { + Role = "user", + Content = message + }); + }catch (Exception ex) { - Role = "user", - Content = message - }); + log.Error(ex); + MessageBox.Show(ex.Message,"获取MCP列表失败"); + } goOn = true; string toolPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>"; string promptPattern = "([\\s\\S]*?)([\\s\\S]*?)<\\/name>([\\s\\S]*?)([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>"; @@ -260,7 +268,7 @@ public class Gateway while (goOn) { loop++; - if (loop > 20) + if (loop > 500) { MessageBox.Show("达到最大循环次数", "退出循环"); break; @@ -269,8 +277,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(); @@ -282,56 +291,105 @@ public class Gateway { //如果本次回复不包含任何工具的调用或提示词的调用,则不再请求 goOn = false; - MessageListItem endMessageListItem1 = new ChatMessageItem + MessageListItem endMessageListItem1 = new ChatMessageItem { type = MessageType.END_TAG, content = "", - id = (timestamp+3).ToString(), + id = (timestamp + 3).ToString(), role = "assistant" }; callback?.Invoke(endMessageListItem1); break; } - await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent)) - { - if (!goOn) - { - MessageListItem endMessageListItem2 = new ChatMessageItem - { - type = MessageType.END_TAG, - content = "", - id = (timestamp+3).ToString(), - role = "assistant" - }; - callback?.Invoke(endMessageListItem2); - break; - } - try + try + { + await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent)) { - string chunk = llmStreamChat.Choices[0].Delta.Content; - MessageListItem reasonMessageListItem = new ChatMessageItem() + if (!goOn) { - 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 endMessageListItem2 = new ChatMessageItem { - //普通消息文本 + type = MessageType.END_TAG, + content = "", + id = (timestamp+3).ToString(), + role = "assistant" + }; + callback?.Invoke(endMessageListItem2); + break; + } + try + { + string chunk = llmStreamChat.Choices[0].Delta.Content; + MessageListItem reasonMessageListItem = new ChatMessageItem() + { + 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 promptParams = JsonConvert.DeserializeObject>(promptArgs); + promptRequests = new List(); + 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 toolParams = JsonConvert.DeserializeObject>(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 = remainingPrompt, + content = remaining, role = "assistant", type = MessageType.CHAT_MESSAGE, id = timestamp.ToString() @@ -340,84 +398,43 @@ public class Gateway { callback?.Invoke(chatMessageListItem); }); - } - else - { - //包含Prompt调用请求的消息 - MessageListItem chatMessageListItem = new ChatMessageItem() + MessageListItem toolMessageListItem = new ToolMessageItem() { - content = remainingPrompt, - role = "assistant", - type = MessageType.CHAT_MESSAGE, - id = timestamp.ToString() + content = "", + result = "", + toolName = toolName, + toolParams = toolParams, + role = "user", + type = MessageType.TOOL_MESSAGE, + id = (timestamp+1).ToString(), + status = "loading" }; - callback?.Invoke(chatMessageListItem); - XElement promptUse = XElement.Parse(matchedPrompt); - string promptKey = promptUse.Element("name")?.Value; - string promptArgs = promptUse.Element("arguments")?.Value; - Dictionary promptParams = JsonConvert.DeserializeObject>(promptArgs); - promptRequests = new List(); - promptRequests.Add(new PromptRequest() + Application.Current.Dispatcher.Invoke(() => { - PromptName = promptKey, - PromptArgs = promptParams, - PromptServer = promptServerList.GetPromptServer(promptKey) + callback?.Invoke(toolMessageListItem); }); + mcpToolRequests = new List(); + McpToolRequest mcpToolRequest = new McpToolRequest() + { + McpServer = mcpServer, + ToolName = toolName, + ToolArgs = toolParams, + }; + mcpToolRequests.Add(mcpToolRequest); } } - else + catch (Exception e) { - //包含工具调用请求的消息 - XElement toolUse = XElement.Parse(matched); - string fullToolName = toolUse.Element("name")?.Value; - string toolArgs = toolUse.Element("arguments")?.Value; - Dictionary toolParams = JsonConvert.DeserializeObject>(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 = new McpToolRequest() - { - McpServer = mcpServer, - ToolName = toolName, - ToolArgs = toolParams, - }; - mcpToolRequests.Add(mcpToolRequest); + Console.WriteLine(e); + log.Error(e.Message); } } - catch (Exception e) - { - Console.WriteLine(e); - log.Error(e.Message); - } + }catch (Exception e) + { + log.Error(e.Message); + MessageBox.Show(e.Message, "请求大模型出错"); } + if (messageContent != "") { messages.Add(new Message @@ -512,11 +529,16 @@ public class Gateway var task = method.Invoke(null, args) as Task; JsonRpcResultEntity innerResult = await task; + string displayToolName = toolName; + if (displayToolName == "ArcGisProTool") + { + displayToolName = "【GP】"+toolParams["toolName"].ToString(); + } if (innerResult is JsonRpcErrorEntity) { MessageListItem toolMessageItem = new ToolMessageItem { - toolName = toolName, + toolName = displayToolName, toolParams = toolParams, type = MessageType.TOOL_MESSAGE, status = "fail", @@ -539,7 +561,7 @@ public class Gateway { MessageListItem toolMessageItem = new ToolMessageItem { - toolName = toolName, + toolName = displayToolName, toolParams = toolParams, type = MessageType.TOOL_MESSAGE, status = "success", @@ -586,7 +608,8 @@ public class Gateway status = "success", content = "成功调用提示词:"+promptRequest.PromptName, id = (timestamp+1).ToString(), - result = "成功调用提示词:"+promptRequest.PromptName + result = "成功调用提示词:"+promptRequest.PromptName, + role = "user" }; Application.Current.Dispatcher.Invoke(() => { @@ -613,88 +636,105 @@ public class Gateway private static async Task GetToolInfos(McpServerList mcpServerList) { StringBuilder toolInfos = new StringBuilder(); + int i = 0; + List failedMcp = new List(); foreach (McpServer mcpServer in mcpServerList.GetAllServers()) { - log.Info($"正在列出{mcpServer.Name}中的工具"); - if (mcpServer is InnerMcpServer) + i++; + try { - InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; - Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name); - MethodInfo[] methods = type.GetMethods(); - foreach (MethodInfo method in methods) + if (mcpServer is InnerMcpServer) { - if (method.IsPublic && method.IsStatic) + InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; + Type type = Type.GetType("LinkToolAddin.client.tool." + innerMcpServer.Name); + MethodInfo[] methods = type.GetMethods(); + foreach (MethodInfo method in methods) { - string methodName = method.Name; - string methodDescription = method.GetCustomAttribute()?.Description; - string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method); + if (method.IsPublic && method.IsStatic) + { + string methodName = method.Name; + string methodDescription = method.GetCustomAttribute()?.Description; + string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method); + McpToolDefinition toolDefinition = new McpToolDefinition + { + Tool = new Tool + { + Name = innerMcpServer.Name + ":" + methodName, + Description = methodDescription, + Arguments = methodParamSchema + } + }; + XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)); + toolInfos.AppendLine(node.ToString()); + toolInfos.AppendLine(); + } + } + } + else if(mcpServer is SseMcpServer) + { + SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl); + IList 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 = innerMcpServer.Name + ":" + methodName, - Description = methodDescription, - Arguments = methodParamSchema + Name = toolName, + Description = toolDescription, + Arguments = toolParamSchema } }; - XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)); - toolInfos.AppendLine(node.ToString()); + 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 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(); } } - } - else if(mcpServer is SseMcpServer) + }catch (Exception e) { - SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl); - IList 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 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(); - } + log.Error(e.Message); + failedMcp.Add(i); } } List prompts = DynamicPrompt.GetAllPrompts(); foreach (UserPrompt userPrompt in prompts) { - XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt})); - toolInfos.AppendLine(node.ToString()); - toolInfos.AppendLine(); + try + { + XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt})); + toolInfos.AppendLine(node.ToString()); + toolInfos.AppendLine(); + } + catch (Exception e) + { + log.Error(e.Message); + } } + MessageBox.Show($"读取失败的MCP序号:{JsonConvert.SerializeObject(failedMcp)}","MCP读取错误"); return toolInfos.ToString(); } diff --git a/server/CallArcGISPro.cs b/server/CallArcGISPro.cs index 4d5c611..016b399 100644 --- a/server/CallArcGISPro.cs +++ b/server/CallArcGISPro.cs @@ -22,7 +22,7 @@ public class CallArcGISPro { Error = new Error() { - Code = results.ErrorCode, + Code = results.ErrorCode.ToString(), Message = JsonConvert.SerializeObject(results.ErrorMessages) } }; diff --git a/server/JsonRpcErrorEntity.cs b/server/JsonRpcErrorEntity.cs index ad0d795..ca57640 100644 --- a/server/JsonRpcErrorEntity.cs +++ b/server/JsonRpcErrorEntity.cs @@ -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; } diff --git a/ui/VersionButton.cs b/ui/VersionButton.cs index bc4df57..be464f7 100644 --- a/ui/VersionButton.cs +++ b/ui/VersionButton.cs @@ -22,7 +22,7 @@ namespace LinkToolAddin { internal class VersionButton : Button { - private string version = "0.1.1"; + private string version = "0.1.3"; protected override void OnClick() { MessageBox.Show($"当前LinkTool版本为{version}", "版本信息"); diff --git a/ui/dockpane/DialogDockpane.xaml b/ui/dockpane/DialogDockpane.xaml index 31542f5..b62fab7 100644 --- a/ui/dockpane/DialogDockpane.xaml +++ b/ui/dockpane/DialogDockpane.xaml @@ -43,6 +43,11 @@ + + + + + diff --git a/ui/dockpane/DialogDockpane.xaml.cs b/ui/dockpane/DialogDockpane.xaml.cs index 278944c..907dd2f 100644 --- a/ui/dockpane/DialogDockpane.xaml.cs +++ b/ui/dockpane/DialogDockpane.xaml.cs @@ -118,8 +118,52 @@ namespace LinkToolAddin.ui.dockpane borderItemsDict[timestamp.ToString()] = userMsgBoder; ChatHistoryStackPanel.Children.Add(userMsgBoder); string model = (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString() is null ? "qwen3-235b-a22b" : (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString(); + if (model == "自定义") + { + model = ShowInputBox("自定义模型", "请输入模型名称:", ""); + } 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) { @@ -134,6 +178,10 @@ namespace LinkToolAddin.ui.dockpane //不存在该消息,需添加到ListView中 if (msg.content == "") { + if (msg.type == MessageType.END_TAG) + { + StatusTextBlock.Text = ""; + } return; } idList.Add(msgId); @@ -168,9 +216,6 @@ namespace LinkToolAddin.ui.dockpane borderItemsDict[msgId] = border; ChatHistoryStackPanel.Children.Add(border); StatusTextBlock.Text = "回答生成中"; - }else if (msg.type == MessageType.END_TAG) - { - StatusTextBlock.Text = ""; } } } @@ -180,6 +225,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); @@ -219,9 +268,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 = ""; } } }