修复重复调用工具的问题,优化流程结束逻辑
This commit is contained in:
parent
af43d0d774
commit
602fd94fc0
@ -274,6 +274,7 @@ public class Gateway
|
||||
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<\\/prompt>";
|
||||
McpServerList mcpServerList = new McpServerList();
|
||||
int loop = 0;
|
||||
string messageContent = ""; //一次请求下完整的response
|
||||
while (goOn)
|
||||
{
|
||||
loop++;
|
||||
@ -291,12 +292,14 @@ public class Gateway
|
||||
MaxTokens = 1000,
|
||||
};
|
||||
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 (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
|
||||
if (toolMatched == "" && promptMatched == "" && messageContent != "")
|
||||
{
|
||||
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
|
||||
goOn = false;
|
||||
break;
|
||||
}
|
||||
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
|
||||
@ -305,6 +308,7 @@ public class Gateway
|
||||
{
|
||||
break;
|
||||
}
|
||||
messageContent = chunk;
|
||||
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
|
||||
if (matched == "")
|
||||
{
|
||||
@ -319,7 +323,6 @@ public class Gateway
|
||||
type = MessageType.CHAT_MESSAGE,
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
messageContent = remainingPrompt;
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
}
|
||||
else
|
||||
@ -335,22 +338,7 @@ public class Gateway
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
XElement promptUse = XElement.Parse(matchedPrompt);
|
||||
string promptKey = promptUse.Element("name")?.Value;
|
||||
string promptContent = DynamicPrompt.GetPrompt(promptKey,null);
|
||||
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);
|
||||
promptKeys.Add(promptKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -371,7 +359,33 @@ public class Gateway
|
||||
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
|
||||
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
|
||||
McpServer mcpServer = mcpServerList.GetServer(serverName);
|
||||
if (mcpServer is SseMcpServer)
|
||||
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
|
||||
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)
|
||||
{
|
||||
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
||||
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
|
||||
@ -413,7 +427,7 @@ public class Gateway
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}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);
|
||||
var methodParams = toolParams.Values.ToArray();
|
||||
object[] args = new object[methodParams.Length];
|
||||
@ -473,7 +487,26 @@ public class Gateway
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*统一处理本次请求中的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 =
|
||||
"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
||||
"指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。整个流程结束之后单独输出一条内容为'[DONE]'的消息,前后不要有任何说明文字。" +
|
||||
"指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。" +
|
||||
"调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
||||
"工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
||||
"输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 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>" +
|
||||
"你必须严格遵守以下输出规则:" +
|
||||
"1.XML工具调用格式必须在下一条单独输出,如果前面有文字将永远无法调用工具。" +
|
||||
//"只有单独一条的调用请求文本程序才能识别并调用,从而将结果反馈给你。" +
|
||||
"2.文字说明如果紧跟着工具调用的XML将暴露程序,XML会在下一条单独输出,因此文字描述后面一定不能输出工具调用的XML格式。" +
|
||||
"3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" +
|
||||
"4.如果目前工作已经完成无法知道用户其他需求时,一定要单独输出一条内容为'[DONE]'的消息表示工具调用结束。不要在文本的末尾。" +
|
||||
"5.不得在同一消息中混合说明文字与工具调用。" +
|
||||
"结果:用户将以以下格式返回工具调用结果:<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>"+
|
||||
"特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用";
|
||||
|
||||
@ -245,6 +245,7 @@ namespace LinkToolAddin.ui.dockpane
|
||||
|
||||
private async void TestArcGisTool_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Type type1 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro");
|
||||
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>";
|
||||
XElement toolUse = XElement.Parse(xmlStr);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user