1. 修复工具调用前不查知识库问题
2. 完善MCP报错机制 3. 增加ArcGIS Pro查看属性表工具
This commit is contained in:
parent
86b800c477
commit
3f3ecadec9
@ -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();
|
||||
|
||||
124
host/Gateway.cs
124
host/Gateway.cs
@ -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;
|
||||
@ -265,6 +267,8 @@ public class Gateway
|
||||
PromptServerList promptServerList = new PromptServerList();
|
||||
int loop = 0;
|
||||
string messageContent = ""; //一次请求下完整的response
|
||||
bool queriedKnowledge = false;
|
||||
bool executedTool = false;
|
||||
while (goOn)
|
||||
{
|
||||
loop++;
|
||||
@ -398,21 +402,24 @@ public class Gateway
|
||||
{
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
});
|
||||
MessageListItem toolMessageListItem = new ToolMessageItem()
|
||||
if (!executedTool)
|
||||
{
|
||||
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);
|
||||
});
|
||||
MessageListItem toolMessageListItem = new ToolMessageItem()
|
||||
{
|
||||
content = toolName,
|
||||
result = "工具运行中" + toolName,
|
||||
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()
|
||||
{
|
||||
@ -434,7 +441,6 @@ public class Gateway
|
||||
log.Error(e.Message);
|
||||
MessageBox.Show(e.Message, "请求大模型出错");
|
||||
}
|
||||
|
||||
if (messageContent != "")
|
||||
{
|
||||
messages.Add(new Message
|
||||
@ -446,6 +452,7 @@ public class Gateway
|
||||
/*统一处理本次请求中的MCP工具调用需求*/
|
||||
foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
|
||||
{
|
||||
executedTool = true;
|
||||
try
|
||||
{
|
||||
McpServer mcpServer = mcpToolRequest.McpServer;
|
||||
@ -456,7 +463,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,
|
||||
@ -476,7 +483,7 @@ public class Gateway
|
||||
});
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(toolMessageItem);
|
||||
callback?.Invoke(toolMessageItem1);
|
||||
});
|
||||
}
|
||||
else if (mcpServer is StdioMcpServer)
|
||||
@ -484,7 +491,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,
|
||||
@ -504,11 +511,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();
|
||||
@ -534,9 +597,10 @@ public class Gateway
|
||||
{
|
||||
displayToolName = "【GP】"+toolParams["toolName"].ToString();
|
||||
}
|
||||
queriedKnowledge = false;
|
||||
if (innerResult is JsonRpcErrorEntity)
|
||||
{
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
MessageListItem toolMessageItem1 = new ToolMessageItem
|
||||
{
|
||||
toolName = displayToolName,
|
||||
toolParams = toolParams,
|
||||
@ -550,16 +614,16 @@ 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 = displayToolName,
|
||||
toolParams = toolParams,
|
||||
@ -573,11 +637,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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -603,7 +667,7 @@ public class Gateway
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = "调用提示词",
|
||||
toolParams = null,
|
||||
toolParams = new Dictionary<string, object>(),
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "success",
|
||||
content = "成功调用提示词:"+promptRequest.PromptName,
|
||||
@ -638,6 +702,7 @@ public class Gateway
|
||||
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())
|
||||
{
|
||||
i++;
|
||||
@ -717,6 +782,7 @@ public class Gateway
|
||||
{
|
||||
log.Error(e.Message);
|
||||
failedMcp.Add(i);
|
||||
failedMcpString.Add(mcpServerList.GetAllServerNames()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,7 +800,11 @@ public class Gateway
|
||||
log.Error(e.Message);
|
||||
}
|
||||
}
|
||||
MessageBox.Show($"读取失败的MCP序号:{JsonConvert.SerializeObject(failedMcp)}","MCP读取错误");
|
||||
|
||||
if (!failedMcp.IsNullOrEmpty())
|
||||
{
|
||||
MessageBox.Show($"读取失败的MCP服务有:{JsonConvert.SerializeObject(failedMcpString)}","MCP读取错误");
|
||||
}
|
||||
return toolInfos.ToString();
|
||||
}
|
||||
|
||||
|
||||
@ -105,4 +105,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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@ namespace LinkToolAddin
|
||||
{
|
||||
internal class VersionButton : Button
|
||||
{
|
||||
private string version = "0.1.3";
|
||||
private string version = "0.1.4";
|
||||
protected override void OnClick()
|
||||
{
|
||||
MessageBox.Show($"当前LinkTool版本为{version}", "版本信息");
|
||||
|
||||
@ -122,6 +122,7 @@ namespace LinkToolAddin.ui.dockpane
|
||||
{
|
||||
model = ShowInputBox("自定义模型", "请输入模型名称:", "");
|
||||
}
|
||||
ScrollViewer.ScrollToBottom();
|
||||
Gateway.SendMessageStream(question,model,defaultGdbPath,NewMessage_Recall);
|
||||
}
|
||||
|
||||
@ -218,6 +219,13 @@ namespace LinkToolAddin.ui.dockpane
|
||||
StatusTextBlock.Text = "回答生成中";
|
||||
}
|
||||
}
|
||||
else if (msg.role == "system")
|
||||
{
|
||||
if (msg.type == MessageType.WARNING)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -241,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)
|
||||
{
|
||||
|
||||
@ -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>
|
||||
@ -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;
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,9 @@ public enum MessageType
|
||||
TOOL_MESSAGE,
|
||||
CHAT_MESSAGE,
|
||||
REASON_MESSAGE,
|
||||
END_TAG
|
||||
END_TAG,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public interface MessageListItem
|
||||
|
||||
Loading…
Reference in New Issue
Block a user