v0.1.4版本 #2

Merged
PeterZhong merged 70 commits from host into master 2025-06-15 14:42:58 +00:00
4 changed files with 207 additions and 106 deletions
Showing only changes of commit b06e1825df - Show all commits

View File

@ -5,11 +5,18 @@
## 核心程序 ## 核心程序
- [x] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述 - [x] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述
- [ ] 接入本地文件系统等基础性MCP服务 - [x] 接入本地文件系统等基础性MCP服务
- [ ] Python代码执行的实现 - [ ] Python代码执行的实现
- [ ] 适配qwen3、deepseek等推理模型并迁移 - [x] 适配qwen3、deepseek等推理模型并迁移
- [x] 工具执行错误消息合并为一条 - [x] 工具执行错误消息合并为一条
## 提示词工程 ## 提示词工程
- [ ] 将原来的单独XML规则修改为内嵌XML规则
- [ ] 系统提示词明确提示调用动态Prompt和知识库
- [ ] 错误和继续提示词预留工具消息占位符
- [ ] 错误和继续提示词明示
- [ ] 系统提示词泛化增强
- [ ] 针对qwen3适当调整
## 前端交互 ## 前端交互

View File

@ -259,7 +259,6 @@ public class Gateway
}; };
List<Message> messages = new List<Message>(); List<Message> messages = new List<Message>();
string toolInfos = await GetToolInfos(new McpServerList()); string toolInfos = await GetToolInfos(new McpServerList());
log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
messages.Add(new Message messages.Add(new Message
{ {
Role = "system", Role = "system",
@ -311,86 +310,94 @@ public class Gateway
break; break;
} }
string chunk = llmStreamChat.Choices[0].Delta.Content; try
MessageListItem reasonMessageListItem = new ChatMessageItem()
{ {
content = llmStreamChat.Choices[0].Delta.ResoningContent, string chunk = llmStreamChat.Choices[0].Delta.Content;
role = "assistant", MessageListItem reasonMessageListItem = new ChatMessageItem()
type = MessageType.REASON_MESSAGE,
id = (timestamp-1).ToString()
};
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,
MessageListItem chatMessageListItem = new ChatMessageItem() role = "assistant",
type = MessageType.REASON_MESSAGE,
id = (timestamp-1).ToString()
};
callback?.Invoke(reasonMessageListItem);
messageContent = chunk;
var (matched, remaining) = ExtractMatchedPart(chunk, toolPattern);
if (matched == "")
{
var (matchedPrompt, remainingPrompt) = ExtractMatchedPart(chunk, promptPattern);
if (matchedPrompt == "")
{ {
content = remainingPrompt, //普通消息文本
role = "assistant", MessageListItem chatMessageListItem = new ChatMessageItem()
type = MessageType.CHAT_MESSAGE, {
id = timestamp.ToString() content = remainingPrompt,
}; role = "assistant",
callback?.Invoke(chatMessageListItem); type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
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 else
{ {
//包含Prompt调用请求的消息 //包含工具调用请求的消息
MessageListItem chatMessageListItem = new ChatMessageItem() MessageListItem chatMessageListItem = new ChatMessageItem()
{ {
content = remainingPrompt, content = remaining,
role = "assistant", role = "assistant",
type = MessageType.CHAT_MESSAGE, type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString() id = timestamp.ToString()
}; };
callback?.Invoke(chatMessageListItem); callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt); XElement toolUse = XElement.Parse(matched);
string promptKey = promptUse.Element("name")?.Value; string fullToolName = toolUse.Element("name")?.Value;
string promptArgs = promptUse.Element("arguments")?.Value; string toolArgs = toolUse.Element("arguments")?.Value;
Dictionary<string, string> promptParams = JsonConvert.DeserializeObject<Dictionary<string, string>>(promptArgs); Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
promptRequests = new List<PromptRequest>(); string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
promptRequests.Add(new PromptRequest() string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
McpServer mcpServer = mcpServerList.GetServer(serverName);
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
mcpToolRequests = new List<McpToolRequest>();
McpToolRequest mcpToolRequest = new McpToolRequest()
{ {
PromptName = promptKey, McpServer = mcpServer,
PromptArgs = promptParams, ToolName = toolName,
PromptServer = promptServerList.GetPromptServer(promptKey) ToolArgs = toolParams,
}); };
mcpToolRequests.Add(mcpToolRequest);
} }
} }
else catch (Exception e)
{ {
//包含工具调用请求的消息 Console.WriteLine(e);
MessageListItem chatMessageListItem = new ChatMessageItem() log.Error(e.Message);
{
content = remaining,
role = "assistant",
type = MessageType.CHAT_MESSAGE,
id = timestamp.ToString()
};
callback?.Invoke(chatMessageListItem);
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);
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
mcpToolRequests = new List<McpToolRequest>();
McpToolRequest mcpToolRequest = new McpToolRequest()
{
McpServer = mcpServer,
ToolName = toolName,
ToolArgs = toolParams,
};
mcpToolRequests.Add(mcpToolRequest);
} }
}
}
if (messageContent != "") if (messageContent != "")
{ {
messages.Add(new Message messages.Add(new Message
@ -402,14 +409,16 @@ public class Gateway
/*统一处理本次请求中的MCP工具调用需求*/ /*统一处理本次请求中的MCP工具调用需求*/
foreach (McpToolRequest mcpToolRequest in mcpToolRequests) foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
{ {
McpServer mcpServer = mcpToolRequest.McpServer; try
string toolName = mcpToolRequest.ToolName; {
Dictionary<string, object> toolParams = mcpToolRequest.ToolArgs; McpServer mcpServer = mcpToolRequest.McpServer;
if (mcpServer is SseMcpServer) string toolName = mcpToolRequest.ToolName;
Dictionary<string, object> toolParams = mcpToolRequest.ToolArgs;
if (mcpServer is SseMcpServer)
{ {
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 toolMessageItem = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = toolName, toolName = toolName,
@ -417,19 +426,22 @@ public class Gateway
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success", status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse), content = JsonConvert.SerializeObject(toolResponse),
id = (timestamp+1).ToString() id = (timestamp + 1).ToString()
}; };
messages.Add(new Message messages.Add(new Message
{ {
Role = "user", Role = "user",
Content = toolResponse.IsError ? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) Content = toolResponse.IsError
? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse))
: SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
}); });
callback?.Invoke(toolMessageItem); callback?.Invoke(toolMessageItem);
}else if (mcpServer is StdioMcpServer) }
else if (mcpServer is StdioMcpServer)
{ {
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 toolMessageItem = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
toolName = toolName, toolName = toolName,
@ -437,18 +449,21 @@ public class Gateway
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = toolResponse.IsError ? "fail" : "success", status = toolResponse.IsError ? "fail" : "success",
content = JsonConvert.SerializeObject(toolResponse), content = JsonConvert.SerializeObject(toolResponse),
id = (timestamp+1).ToString() id = (timestamp + 1).ToString()
}; };
messages.Add(new Message messages.Add(new Message
{ {
Role = "user", Role = "user",
Content = toolResponse.IsError ? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse)) Content = toolResponse.IsError
? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse))
: SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
}); });
callback?.Invoke(toolMessageItem); callback?.Invoke(toolMessageItem);
}else if (mcpServer is InnerMcpServer) }
else if (mcpServer is InnerMcpServer)
{ {
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();
object[] args = new object[methodParams.Length]; object[] args = new object[methodParams.Length];
for (int i = 0; i < methodParams.Length; i++) for (int i = 0; i < methodParams.Length; i++)
@ -464,6 +479,7 @@ public class Gateway
args[i] = methodParams[i]; args[i] = methodParams[i];
} }
} }
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;
if (innerResult is JsonRpcErrorEntity) if (innerResult is JsonRpcErrorEntity)
@ -475,7 +491,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "fail", status = "fail",
content = JsonConvert.SerializeObject(innerResult), content = JsonConvert.SerializeObject(innerResult),
id = (timestamp+1).ToString() id = (timestamp + 1).ToString()
}; };
messages.Add(new Message messages.Add(new Message
{ {
@ -483,7 +499,8 @@ public class Gateway
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolMessageItem)) Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolMessageItem))
}); });
callback?.Invoke(toolMessageItem); callback?.Invoke(toolMessageItem);
}else if (innerResult is JsonRpcSuccessEntity) }
else if (innerResult is JsonRpcSuccessEntity)
{ {
MessageListItem toolMessageItem = new ToolMessageItem MessageListItem toolMessageItem = new ToolMessageItem
{ {
@ -492,7 +509,7 @@ public class Gateway
type = MessageType.TOOL_MESSAGE, type = MessageType.TOOL_MESSAGE,
status = "success", status = "success",
content = JsonConvert.SerializeObject(innerResult), content = JsonConvert.SerializeObject(innerResult),
id = (timestamp+1).ToString() id = (timestamp + 1).ToString()
}; };
messages.Add(new Message messages.Add(new Message
{ {
@ -502,26 +519,41 @@ public class Gateway
callback?.Invoke(toolMessageItem); callback?.Invoke(toolMessageItem);
} }
} }
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
log.Error(ex.Message);
}
} }
/*统一处理本次请求中的Prompt调用需求*/ /*统一处理本次请求中的Prompt调用需求*/
foreach (PromptRequest promptRequest in promptRequests) foreach (PromptRequest promptRequest in promptRequests)
{ {
string promptContent = DynamicPrompt.GetPrompt(promptRequest.PromptName, promptRequest.PromptArgs); try
messages.Add(new Message
{ {
Role = "user", string promptContent = DynamicPrompt.GetPrompt(promptRequest.PromptName, promptRequest.PromptArgs);
Content = promptContent messages.Add(new Message
}); {
MessageListItem toolMessageItem = new ToolMessageItem Role = "user",
Content = promptContent
});
MessageListItem toolMessageItem = new ToolMessageItem
{
toolName = "调用提示词",
toolParams = null,
type = MessageType.TOOL_MESSAGE,
status = "success",
content = "成功调用提示词:"+promptRequest.PromptName,
id = (timestamp+1).ToString()
};
callback?.Invoke(toolMessageItem);
}
catch (Exception e)
{ {
toolName = "调用提示词", Console.WriteLine(e);
toolParams = null, log.Error(e.Message);
type = MessageType.TOOL_MESSAGE, }
status = "success",
content = "成功调用提示词:"+promptRequest.PromptName,
id = (timestamp+1).ToString()
};
callback?.Invoke(toolMessageItem);
} }
} }
} }
@ -531,6 +563,7 @@ public class Gateway
StringBuilder toolInfos = new StringBuilder(); StringBuilder toolInfos = new StringBuilder();
foreach (McpServer mcpServer in mcpServerList.GetAllServers()) foreach (McpServer mcpServer in mcpServerList.GetAllServers())
{ {
log.Info($"正在列出{mcpServer.Name}中的工具");
if (mcpServer is InnerMcpServer) if (mcpServer is InnerMcpServer)
{ {
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer; InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;

View File

@ -35,6 +35,53 @@ public class McpServerList
Description = "可以调用进行查询知识库,获取相关参考信息。", Description = "可以调用进行查询知识库,获取相关参考信息。",
IsActive = true IsActive = true
}); });
servers.Add("filesystem", new StdioMcpServer()
{
Name = "filesystem",
Type = "stdio",
Command = "npx",
Args = new List<string>()
{
"-y",
"@modelcontextprotocol/server-filesystem",
"D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData"
}
});
servers.Add("fetch", new StdioMcpServer()
{
Name = "fetch",
Type = "stdio",
Command = "uvx",
Args = new List<string>()
{
"mcp-server-fetch"
}
});
servers.Add("bing-search", new StdioMcpServer()
{
Name = "bing-search",
Type = "stdio",
Command = "npx",
Args = new List<string>()
{
"bing-cn-mcp"
}
});
// servers.Add("mcp-python-interpreter", new StdioMcpServer()
// {
// Name = "mcp-python-interpreter",
// Type = "stdio",
// Command = "uvx",
// Args = new List<string>()
// {
// "--native-tls",
// "mcp-python-interpreter",
// "--dir",
// "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\TestData",
// "--python-path",
// "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe"
// }
// });
} }
public McpServer GetServer(string name) public McpServer GetServer(string name)

View File

@ -199,7 +199,14 @@ namespace LinkToolAddin.ui.dockpane
{ {
string userPrompt = PromptTestTextBox.Text; string userPrompt = PromptTestTextBox.Text;
// model可选值qwen3-235b-a22b,qwen-max,deepseek-r1 // model可选值qwen3-235b-a22b,qwen-max,deepseek-r1
Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb", AddReplyStream); try
{
Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb", AddReplyStream);
}
catch (Exception exception)
{
log.Error(exception.Message);
}
} }
public void AddReplyStream(MessageListItem msg) public void AddReplyStream(MessageListItem msg)
@ -215,20 +222,27 @@ namespace LinkToolAddin.ui.dockpane
messageDict.Add(msg.id, msg); messageDict.Add(msg.id, msg);
} }
ReplyTextBox.Clear(); ReplyTextBox.Clear();
StringBuilder builder = new StringBuilder(); try
foreach (KeyValuePair<string,MessageListItem> pair in messageDict)
{ {
MessageListItem msgItem = pair.Value; StringBuilder builder = new StringBuilder();
string content = msgItem.content; foreach (KeyValuePair<string,MessageListItem> pair in messageDict)
if (msgItem.type == MessageType.REASON_MESSAGE)
{ {
content = "<think>" + content + "</think>"; MessageListItem msgItem = pair.Value;
string content = msgItem.content;
if (msgItem.type == MessageType.REASON_MESSAGE)
{
content = "<think>" + content + "</think>";
}
builder.AppendLine(content);
builder.AppendLine();
ReplyTextBox.Text = builder.ToString();
ReplyTextBox.ScrollToEnd();
} }
builder.AppendLine(content); }catch (Exception exception)
builder.AppendLine(); {
ReplyTextBox.Text = builder.ToString(); log.Error(exception.Message);
ReplyTextBox.ScrollToEnd();
} }
} }
public void AddReply(MessageListItem msg) public void AddReply(MessageListItem msg)