v0.1.4版本 #2
@ -274,6 +274,7 @@ public class Gateway
|
|||||||
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>";
|
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>";
|
||||||
McpServerList mcpServerList = new McpServerList();
|
McpServerList mcpServerList = new McpServerList();
|
||||||
int loop = 0;
|
int loop = 0;
|
||||||
|
string messageContent = ""; //一次请求下完整的response
|
||||||
while (goOn)
|
while (goOn)
|
||||||
{
|
{
|
||||||
loop++;
|
loop++;
|
||||||
@ -291,12 +292,14 @@ public class Gateway
|
|||||||
MaxTokens = 1000,
|
MaxTokens = 1000,
|
||||||
};
|
};
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
string messageContent = ""; //一次请求下完整的response
|
List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>();
|
||||||
|
List<string> promptKeys = new List<string>();
|
||||||
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
|
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
|
||||||
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
|
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
|
||||||
if (toolMatched == "" && promptMatched == "" && messageContent != "")
|
if (toolMatched == "" && promptMatched == "" && messageContent != "")
|
||||||
{
|
{
|
||||||
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
|
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
|
||||||
|
goOn = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
|
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
|
||||||
@ -305,6 +308,7 @@ public class Gateway
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
messageContent = chunk;
|
||||||
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
|
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
|
||||||
if (matched == "")
|
if (matched == "")
|
||||||
{
|
{
|
||||||
@ -319,7 +323,6 @@ public class Gateway
|
|||||||
type = MessageType.CHAT_MESSAGE,
|
type = MessageType.CHAT_MESSAGE,
|
||||||
id = timestamp.ToString()
|
id = timestamp.ToString()
|
||||||
};
|
};
|
||||||
messageContent = remainingPrompt;
|
|
||||||
callback?.Invoke(chatMessageListItem);
|
callback?.Invoke(chatMessageListItem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -335,22 +338,7 @@ public class Gateway
|
|||||||
callback?.Invoke(chatMessageListItem);
|
callback?.Invoke(chatMessageListItem);
|
||||||
XElement promptUse = XElement.Parse(matchedPrompt);
|
XElement promptUse = XElement.Parse(matchedPrompt);
|
||||||
string promptKey = promptUse.Element("name")?.Value;
|
string promptKey = promptUse.Element("name")?.Value;
|
||||||
string promptContent = DynamicPrompt.GetPrompt(promptKey,null);
|
promptKeys.Add(promptKey);
|
||||||
messages.Add(new Message
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = JsonConvert.SerializeObject(promptContent)
|
|
||||||
});
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = "调用提示词",
|
|
||||||
toolParams = null,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "success",
|
|
||||||
content = "成功调用提示词:"+promptKey,
|
|
||||||
id = (timestamp+1).ToString()
|
|
||||||
};
|
|
||||||
callback?.Invoke(toolMessageItem);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -371,6 +359,32 @@ public class Gateway
|
|||||||
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
|
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
|
||||||
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
|
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
|
||||||
McpServer mcpServer = mcpServerList.GetServer(serverName);
|
McpServer mcpServer = mcpServerList.GetServer(serverName);
|
||||||
|
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
|
||||||
|
mcpToolRequests = new List<McpToolRequest>();
|
||||||
|
McpToolRequest mcpToolRequest = new McpToolRequest()
|
||||||
|
{
|
||||||
|
McpServer = mcpServer,
|
||||||
|
ToolName = toolName,
|
||||||
|
ToolArgs = toolParams,
|
||||||
|
};
|
||||||
|
mcpToolRequests.Add(mcpToolRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageContent != "")
|
||||||
|
{
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "assistant",
|
||||||
|
Content = messageContent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*统一处理本次请求中的MCP工具调用需求*/
|
||||||
|
foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
|
||||||
|
{
|
||||||
|
McpServer mcpServer = mcpToolRequest.McpServer;
|
||||||
|
string toolName = mcpToolRequest.ToolName;
|
||||||
|
Dictionary<string, object> toolParams = mcpToolRequest.ToolArgs;
|
||||||
if (mcpServer is SseMcpServer)
|
if (mcpServer is SseMcpServer)
|
||||||
{
|
{
|
||||||
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
||||||
@ -413,7 +427,7 @@ public class Gateway
|
|||||||
callback?.Invoke(toolMessageItem);
|
callback?.Invoke(toolMessageItem);
|
||||||
}else if (mcpServer is InnerMcpServer)
|
}else if (mcpServer is InnerMcpServer)
|
||||||
{
|
{
|
||||||
Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
|
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();
|
||||||
object[] args = new object[methodParams.Length];
|
object[] args = new object[methodParams.Length];
|
||||||
@ -474,6 +488,25 @@ public class Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*统一处理本次请求中的Prompt调用需求*/
|
||||||
|
foreach (string promptKey in promptKeys)
|
||||||
|
{
|
||||||
|
string promptContent = DynamicPrompt.GetPrompt(promptKey);
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = promptContent
|
||||||
|
});
|
||||||
|
MessageListItem toolMessageItem = new ToolMessageItem
|
||||||
|
{
|
||||||
|
toolName = "调用提示词",
|
||||||
|
toolParams = null,
|
||||||
|
type = MessageType.TOOL_MESSAGE,
|
||||||
|
status = "success",
|
||||||
|
content = "成功调用提示词:"+promptKey,
|
||||||
|
id = (timestamp+1).ToString()
|
||||||
|
};
|
||||||
|
callback?.Invoke(toolMessageItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
host/mcp/McpToolRequest.cs
Normal file
10
host/mcp/McpToolRequest.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LinkToolAddin.host.mcp;
|
||||||
|
|
||||||
|
public class McpToolRequest
|
||||||
|
{
|
||||||
|
public McpServer McpServer { get; set; }
|
||||||
|
public string ToolName { get; set; }
|
||||||
|
public Dictionary<string, object> ToolArgs { get; set; }
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ public class SystemPrompt
|
|||||||
{
|
{
|
||||||
public static string SysPromptTemplate =
|
public static string SysPromptTemplate =
|
||||||
"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
||||||
"指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。整个流程结束之后单独输出一条内容为'[DONE]'的消息,前后不要有任何说明文字。" +
|
"指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。" +
|
||||||
"调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
"调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
||||||
"工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
"工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
||||||
"输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" +
|
"输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" +
|
||||||
@ -13,12 +13,7 @@ public class SystemPrompt
|
|||||||
"工具名称:需与所使用工具的精确名称一致。" +
|
"工具名称:需与所使用工具的精确名称一致。" +
|
||||||
"参数:应为包含工具所需参数的 JSON 对象。例如:<tool_use>\\n <name>gaode:maps_geo</name>\\n <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>\\n</tool_use>" +
|
"参数:应为包含工具所需参数的 JSON 对象。例如:<tool_use>\\n <name>gaode:maps_geo</name>\\n <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>\\n</tool_use>" +
|
||||||
"你必须严格遵守以下输出规则:" +
|
"你必须严格遵守以下输出规则:" +
|
||||||
"1.XML工具调用格式必须在下一条单独输出,如果前面有文字将永远无法调用工具。" +
|
|
||||||
//"只有单独一条的调用请求文本程序才能识别并调用,从而将结果反馈给你。" +
|
|
||||||
"2.文字说明如果紧跟着工具调用的XML将暴露程序,XML会在下一条单独输出,因此文字描述后面一定不能输出工具调用的XML格式。" +
|
|
||||||
"3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" +
|
"3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" +
|
||||||
"4.如果目前工作已经完成无法知道用户其他需求时,一定要单独输出一条内容为'[DONE]'的消息表示工具调用结束。不要在文本的末尾。" +
|
|
||||||
"5.不得在同一消息中混合说明文字与工具调用。" +
|
|
||||||
"结果:用户将以以下格式返回工具调用结果:<tool_use_result>\n <name>{tool_name}</name>\n <result>{result}</result>\n</tool_use_result>。应为字符串类型,可以表示文件或其他输出类型。" +
|
"结果:用户将以以下格式返回工具调用结果:<tool_use_result>\n <name>{tool_name}</name>\n <result>{result}</result>\n</tool_use_result>。应为字符串类型,可以表示文件或其他输出类型。" +
|
||||||
"工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:<tool_use>\\n <name>gaode:maps_geo</name>\\n <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>\\n</tool_use>"+
|
"工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:<tool_use>\\n <name>gaode:maps_geo</name>\\n <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>\\n</tool_use>"+
|
||||||
"特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用";
|
"特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用";
|
||||||
|
|||||||
@ -245,6 +245,7 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
|
|
||||||
private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e)
|
private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
Type type1 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro");
|
||||||
string xmlStr =
|
string xmlStr =
|
||||||
"<tool_use>\n<name>ArcGisPro:ArcGisProTool</name>\n<arguments>{\"toolName\": \"Buffer\", \"toolParams\": [\"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Copy\", \"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Buffer30m\", \"30 Meters\", \"NONE\", \"ROUND\", \"ALL\"]}</arguments>\n</tool_use>";
|
"<tool_use>\n<name>ArcGisPro:ArcGisProTool</name>\n<arguments>{\"toolName\": \"Buffer\", \"toolParams\": [\"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Copy\", \"D:\\\\01_Project\\\\20250305_LinkTool\\\\20250420_AiDemoProject\\\\20250420_AiDemoProject.gdb\\\\LandUse_2005_Buffer30m\", \"30 Meters\", \"NONE\", \"ROUND\", \"ALL\"]}</arguments>\n</tool_use>";
|
||||||
XElement toolUse = XElement.Parse(xmlStr);
|
XElement toolUse = XElement.Parse(xmlStr);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user