合并后提交
This commit is contained in:
commit
f4262d6aef
@ -97,10 +97,6 @@
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="doc\" />
|
||||
<Folder Include="resource\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Esri.ArcGISPro.Extensions30" Version="3.4.1.55405" />
|
||||
<PackageReference Include="log4net" Version="3.1.0" />
|
||||
@ -108,6 +104,16 @@
|
||||
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="4.0.2-beta2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="resource\SystemPrompt.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resource\prompt\SystemPrompt.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<None Remove="resource\prompt\ContinuePrompt.txt" />
|
||||
<EmbeddedResource Include="resource\prompt\ContinuePrompt.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets" Condition="Exists('C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets') AND !Exists('Esri.ArcGISPro.Extensions.targets')" />
|
||||
</Project>
|
||||
|
||||
@ -31,6 +31,7 @@ git clone xxx.git
|
||||
5. 部分情况下可能还需要替换LinkToolAddin.csproj中的所有相关安装路径
|
||||
6. 点击运行进行测试,看是否能正常打开ArcGIS Pro和LinkTool插件
|
||||
7. 确认无误建议commit到本地的master分支以备后续使用(但请勿推送至远端)
|
||||
8. 修改McpServerList里面filesystem的白名单目录
|
||||
|
||||
#### 创建分支
|
||||
|
||||
|
||||
@ -51,9 +51,18 @@ public class StdioMcpClient : McpClient
|
||||
|
||||
public async Task<IList<McpClientTool>> GetToolListAsync()
|
||||
{
|
||||
IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions));
|
||||
IList<McpClientTool> tools = await client.ListToolsAsync();
|
||||
return tools;
|
||||
try
|
||||
{
|
||||
IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions));
|
||||
IList<McpClientTool> tools = await client.ListToolsAsync();
|
||||
return tools;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<CallToolResponse> CallToolAsync(string toolName, Dictionary<string, object> parameters = null)
|
||||
|
||||
@ -1,29 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using LinkToolAddin.host;
|
||||
using LinkToolAddin.host.prompt;
|
||||
|
||||
namespace LinkToolAddin.client.prompt;
|
||||
|
||||
public class DynamicPrompt
|
||||
{
|
||||
public static string GetPrompt(string name,Dictionary<string,object> args = null)
|
||||
public static string GetPrompt(string name,Dictionary<string,string> args = null)
|
||||
{
|
||||
PromptTemplates promptTemplate = new PromptTemplates();
|
||||
string template = promptTemplate.GetPrompt(name);
|
||||
PromptServerList promptServerList = new PromptServerList();
|
||||
string template = promptServerList.GetPromptServer(name).Content;
|
||||
if (args == null)
|
||||
{
|
||||
return template;
|
||||
}
|
||||
foreach (KeyValuePair<string,object> pair in args)
|
||||
foreach (KeyValuePair<string,string> pair in args)
|
||||
{
|
||||
string replaceKey = "{{"+pair.Key+"}}";
|
||||
template.Replace(replaceKey, pair.Value.ToString());
|
||||
template = template.Replace(replaceKey, pair.Value.ToString());
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetAllPrompts()
|
||||
public static List<UserPrompt> GetAllPrompts()
|
||||
{
|
||||
PromptTemplates promptTemplate = new PromptTemplates();
|
||||
Dictionary<string, string> template = promptTemplate.GetPromptsDict();
|
||||
return template;
|
||||
PromptServerList promptServerList = new PromptServerList();
|
||||
List<UserPrompt> prompts = new List<UserPrompt>();
|
||||
Dictionary<string, PromptServer> promptDefinitions = promptServerList.GetPromptsDict();
|
||||
foreach (KeyValuePair<string, PromptServer> pair in promptDefinitions)
|
||||
{
|
||||
prompts.Add(new UserPrompt()
|
||||
{
|
||||
Name = pair.Value.Name,
|
||||
Description = pair.Value.Description,
|
||||
Arguments = pair.Value.Arguments
|
||||
});
|
||||
}
|
||||
return prompts;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using LinkToolAddin.host.prompt;
|
||||
|
||||
namespace LinkToolAddin.client.prompt;
|
||||
|
||||
|
||||
@ -2,12 +2,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ArcGIS.Core.Data;
|
||||
using ArcGIS.Core.Data.Raster;
|
||||
using ArcGIS.Core.Geometry;
|
||||
using ArcGIS.Desktop.Core.Geoprocessing;
|
||||
using ArcGIS.Desktop.Framework.Threading.Tasks;
|
||||
using LinkToolAddin.server;
|
||||
using LinkToolAddin.ui.dockpane;
|
||||
using log4net;
|
||||
using ModelContextProtocol.Server;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@ -16,14 +20,40 @@ namespace LinkToolAddin.client.tool;
|
||||
|
||||
public class ArcGisPro
|
||||
{
|
||||
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能。")]
|
||||
private static ILog log = LogManager.GetLogger(typeof(ArcGisPro));
|
||||
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能,传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")]
|
||||
public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams)
|
||||
{
|
||||
// Call the ArcGIS Pro method and get the result
|
||||
var result = await server.CallArcGISPro.CallArcGISProTool(toolName, toolParams);
|
||||
|
||||
// Serialize the result back to a JSON string
|
||||
return result;
|
||||
IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
|
||||
JsonRpcResultEntity jsonRpcResultEntity;
|
||||
if (results.IsFailed)
|
||||
{
|
||||
log.Error(results.ErrorMessages);
|
||||
jsonRpcResultEntity = new JsonRpcErrorEntity()
|
||||
{
|
||||
Error = new Error()
|
||||
{
|
||||
Code = results.ErrorCode,
|
||||
Message = GetMessagesString(results.ErrorMessages)
|
||||
}
|
||||
};
|
||||
}else if(results.HasWarnings)
|
||||
{
|
||||
log.Warn(results.Messages);
|
||||
jsonRpcResultEntity = new JsonRpcSuccessEntity
|
||||
{
|
||||
Result = GetMessagesString(results.Messages)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Info("success gp tool");
|
||||
jsonRpcResultEntity = new JsonRpcSuccessEntity
|
||||
{
|
||||
Result = GetMessagesString(results.Messages)
|
||||
};
|
||||
}
|
||||
return jsonRpcResultEntity;
|
||||
}
|
||||
|
||||
[McpServerTool, Description("查看指定数据的坐标系、范围、几何类型、是否有Z坐标和M坐标,获取字段列表等")]
|
||||
@ -106,4 +136,14 @@ public class ArcGisPro
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string GetMessagesString(IEnumerable<IGPMessage> messages)
|
||||
{
|
||||
StringBuilder messagesStr = new StringBuilder();
|
||||
foreach (var gpMessage in messages)
|
||||
{
|
||||
messagesStr.AppendLine(gpMessage.Text);
|
||||
}
|
||||
return messagesStr.ToString();
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,7 @@ public class HttpRequest
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async IAsyncEnumerable<string> PostWithStreamingResponseAsync(
|
||||
public static async IAsyncEnumerable<LlmStreamChat> PostWithStreamingResponseAsync(
|
||||
string url,
|
||||
string body,
|
||||
string apiKey,
|
||||
@ -114,7 +114,7 @@ public class HttpRequest
|
||||
|
||||
if (dataObj is not null)
|
||||
{
|
||||
yield return dataObj.Choices[0].Delta.Content;
|
||||
yield return dataObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
common/LocalResource.cs
Normal file
31
common/LocalResource.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LinkToolAddin.common;
|
||||
|
||||
public class LocalResource
|
||||
{
|
||||
public static string ReadFileByResource(string resourceName)
|
||||
{
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
|
||||
string[] resourceNames = assembly.GetManifestResourceNames();
|
||||
foreach (string name in resourceNames)
|
||||
{
|
||||
Console.WriteLine(name);
|
||||
}
|
||||
|
||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
return($"找不到嵌入资源:{resourceName}");
|
||||
}
|
||||
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
doc/TodoList.md
Normal file
28
doc/TodoList.md
Normal file
@ -0,0 +1,28 @@
|
||||
# 待办事项
|
||||
|
||||
本文档用于记录和跟踪当前阶段待办事项及完成进度
|
||||
|
||||
## 核心程序
|
||||
|
||||
- [x] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述
|
||||
- [x] 接入本地文件系统等基础性MCP服务
|
||||
- [ ] Python代码执行的实现
|
||||
- [x] 适配qwen3、deepseek等推理模型并迁移
|
||||
- [x] 工具执行错误消息合并为一条
|
||||
|
||||
## 提示词工程
|
||||
|
||||
- [ ] 将原来的单独XML规则修改为内嵌XML规则
|
||||
- [ ] 系统提示词明确提示调用动态Prompt和知识库
|
||||
- [ ] 错误和继续提示词预留工具消息占位符
|
||||
- [ ] 错误和继续提示词明示
|
||||
- [ ] 系统提示词泛化增强
|
||||
- [ ] 针对qwen3适当调整
|
||||
|
||||
## 前端交互
|
||||
|
||||
- [ ] 三类消息卡片
|
||||
- [ ] 流式输出,根据id匹配修改或新增
|
||||
- [ ] 添加工作空间、发送、复制粘贴等交互
|
||||
- [ ] 独立UI线程
|
||||
- [ ] 工具卡片特殊提示,弹出窗口显示内容
|
||||
503
host/Gateway.cs
503
host/Gateway.cs
@ -8,10 +8,10 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using ArcGIS.Desktop.Framework.Dialogs;
|
||||
using ArcGIS.Desktop.Internal.Mapping.Locate;
|
||||
using LinkToolAddin.client;
|
||||
using LinkToolAddin.client.prompt;
|
||||
@ -32,6 +32,8 @@ using Newtonsoft.Json.Schema;
|
||||
using Newtonsoft.Json.Schema.Generation;
|
||||
using Tool = LinkToolAddin.host.mcp.Tool;
|
||||
using LinkToolAddin.common;
|
||||
using LinkToolAddin.host.llm.entity.stream;
|
||||
using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox;
|
||||
|
||||
namespace LinkToolAddin.host;
|
||||
|
||||
@ -112,12 +114,7 @@ public class Gateway
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||
});
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(toolResponse)
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (mcpServer is StdioMcpServer)
|
||||
@ -137,12 +134,7 @@ public class Gateway
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||
});
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(toolResponse)
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse)) : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (mcpServer is InnerMcpServer)
|
||||
@ -164,12 +156,7 @@ public class Gateway
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = SystemPrompt.ErrorPromptTemplate
|
||||
});
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(innerResult)
|
||||
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult))
|
||||
});
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (innerResult is JsonRpcSuccessEntity)
|
||||
@ -185,12 +172,7 @@ public class Gateway
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = SystemPrompt.ContinuePromptTemplate
|
||||
});
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(innerResult)
|
||||
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
|
||||
});
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}
|
||||
@ -204,7 +186,7 @@ public class Gateway
|
||||
Dictionary<string, object> promptParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(promptArgs);
|
||||
string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName;
|
||||
string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName;
|
||||
string promptRes = DynamicPrompt.GetPrompt(promptName, promptParams);
|
||||
string promptRes = DynamicPrompt.GetPrompt(promptName, null);
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
@ -222,13 +204,34 @@ public class Gateway
|
||||
};
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
}
|
||||
if (reponse == "[DONE]")
|
||||
if (reponse.EndsWith("[DONE]"))
|
||||
{
|
||||
goOn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static (string Matched, string Remaining) ExtractMatchedPart(string input, string toolPattern)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(toolPattern))
|
||||
return (string.Empty, input);
|
||||
|
||||
Regex regex = new Regex(toolPattern);
|
||||
Match match = regex.Match(input);
|
||||
|
||||
if (!match.Success)
|
||||
return (string.Empty, input);
|
||||
|
||||
string matched = match.Value;
|
||||
int startIndex = match.Index;
|
||||
int length = match.Length;
|
||||
|
||||
// 构造剩余字符串
|
||||
string remaining = input.Substring(0, startIndex) + input.Substring(startIndex + length);
|
||||
|
||||
return (matched, remaining);
|
||||
}
|
||||
|
||||
public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||
{
|
||||
Llm bailian = new Bailian
|
||||
@ -237,7 +240,6 @@ public class Gateway
|
||||
};
|
||||
List<Message> messages = new List<Message>();
|
||||
string toolInfos = await GetToolInfos(new McpServerList());
|
||||
log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "system",
|
||||
@ -249,10 +251,12 @@ public class Gateway
|
||||
Content = message
|
||||
});
|
||||
goOn = true;
|
||||
string toolPattern = "^<tool_use>[\\s\\S]*?<\\/tool_use>$";
|
||||
string promptPattern = "^<prompt>[\\s\\S]*?<\\/prompt>$";
|
||||
string toolPattern = "<tool_use>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/tool_use>";
|
||||
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>";
|
||||
McpServerList mcpServerList = new McpServerList();
|
||||
PromptServerList promptServerList = new PromptServerList();
|
||||
int loop = 0;
|
||||
string messageContent = ""; //一次请求下完整的response
|
||||
while (goOn)
|
||||
{
|
||||
loop++;
|
||||
@ -270,220 +274,291 @@ public class Gateway
|
||||
MaxTokens = 1000,
|
||||
};
|
||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string messageContent = "";
|
||||
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
|
||||
List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>();
|
||||
List<PromptRequest> promptRequests = new List<PromptRequest>();
|
||||
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
|
||||
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
|
||||
if (toolMatched == "" && promptMatched == "" && messageContent != "")
|
||||
{
|
||||
if (chunk == "[DONE]")
|
||||
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
|
||||
goOn = false;
|
||||
break;
|
||||
}
|
||||
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
|
||||
{
|
||||
if (!goOn)
|
||||
{
|
||||
goOn = false;
|
||||
}else if (chunk.StartsWith("<tool_use>"))
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Regex.IsMatch(chunk, toolPattern))
|
||||
string chunk = llmStreamChat.Choices[0].Delta.Content;
|
||||
MessageListItem reasonMessageListItem = new ChatMessageItem()
|
||||
{
|
||||
//返回工具卡片
|
||||
messages.Add(new Message
|
||||
content = llmStreamChat.Choices[0].Delta.ResoningContent,
|
||||
role = "assistant",
|
||||
type = MessageType.REASON_MESSAGE,
|
||||
id = (timestamp-1).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 == "")
|
||||
{
|
||||
Role = "assistant",
|
||||
Content = chunk
|
||||
//普通消息文本
|
||||
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<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
|
||||
{
|
||||
//包含工具调用请求的消息
|
||||
MessageListItem chatMessageListItem = new ChatMessageItem()
|
||||
{
|
||||
content = remaining,
|
||||
role = "assistant",
|
||||
type = MessageType.CHAT_MESSAGE,
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
});
|
||||
XElement toolUse = XElement.Parse(chunk);
|
||||
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);
|
||||
if (mcpServer is SseMcpServer)
|
||||
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
|
||||
mcpToolRequests = new List<McpToolRequest>();
|
||||
McpToolRequest mcpToolRequest = new McpToolRequest()
|
||||
{
|
||||
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
|
||||
SseMcpClient client = new SseMcpClient(sseMcpServer.BaseUrl);
|
||||
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = toolResponse.IsError ? "fail" : "success",
|
||||
content = JsonConvert.SerializeObject(toolResponse),
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
// Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
// messages.Add(new Message
|
||||
// {
|
||||
// Role = "user",
|
||||
// Content = JsonConvert.SerializeObject(toolResponse)
|
||||
// });
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (mcpServer is StdioMcpServer)
|
||||
{
|
||||
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
|
||||
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
|
||||
CallToolResponse toolResponse = await client.CallToolAsync(toolName,toolParams);
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = toolResponse.IsError ? "fail" : "success",
|
||||
content = JsonConvert.SerializeObject(toolResponse),
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
// Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||
Content = toolResponse.IsError ? SystemPrompt.ErrorPromptTemplate : SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
// messages.Add(new Message
|
||||
// {
|
||||
// Role = "user",
|
||||
// Content = JsonConvert.SerializeObject(toolResponse)
|
||||
// });
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (mcpServer is InnerMcpServer)
|
||||
{
|
||||
Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
|
||||
MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static);
|
||||
var methodParams = toolParams.Values.ToArray();
|
||||
object[] args = new object[methodParams.Length];
|
||||
for (int i = 0; i < methodParams.Length; i++)
|
||||
{
|
||||
if (methodParams[i].GetType() == typeof(JArray))
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
list = (methodParams[i] as JArray).Select(token => token.ToString()).ToList();
|
||||
args[i] = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = methodParams[i];
|
||||
}
|
||||
}
|
||||
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
|
||||
JsonRpcResultEntity innerResult = await task;
|
||||
if (innerResult is JsonRpcErrorEntity)
|
||||
{
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "fail",
|
||||
content = JsonConvert.SerializeObject(innerResult),
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = SystemPrompt.ErrorPromptTemplate
|
||||
});
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(innerResult)
|
||||
});
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}else if (innerResult is JsonRpcSuccessEntity)
|
||||
{
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "success",
|
||||
content = JsonConvert.SerializeObject(innerResult),
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
// Content = SystemPrompt.ContinuePromptTemplate
|
||||
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
|
||||
});
|
||||
// messages.Add(new Message
|
||||
// {
|
||||
// Role = "user",
|
||||
// Content = JsonConvert.SerializeObject(innerResult)
|
||||
// });
|
||||
callback?.Invoke(toolMessageItem);
|
||||
}
|
||||
}
|
||||
McpServer = mcpServer,
|
||||
ToolName = toolName,
|
||||
ToolArgs = toolParams,
|
||||
};
|
||||
mcpToolRequests.Add(mcpToolRequest);
|
||||
}
|
||||
else
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
log.Error(e.Message);
|
||||
}
|
||||
}
|
||||
if (messageContent != "")
|
||||
{
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "assistant",
|
||||
Content = messageContent
|
||||
});
|
||||
}
|
||||
/*统一处理本次请求中的MCP工具调用需求*/
|
||||
foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = "",
|
||||
toolParams = new Dictionary<string, object>(),
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "loading",
|
||||
content = "正在生成工具调用参数",
|
||||
id = timestamp.ToString()
|
||||
status = toolResponse.IsError ? "fail" : "success",
|
||||
content = JsonConvert.SerializeObject(toolResponse),
|
||||
id = (timestamp + 1).ToString()
|
||||
};
|
||||
callback?.Invoke(toolMessageItem);
|
||||
continue;
|
||||
}
|
||||
}else if (chunk.StartsWith("<prompt>"))
|
||||
{
|
||||
if (Regex.IsMatch(chunk, promptPattern))
|
||||
{
|
||||
XElement promptUse = XElement.Parse(chunk);
|
||||
string promptKey = promptUse.Element("name")?.Value;
|
||||
string promptContent = DynamicPrompt.GetPrompt(promptKey,null);
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = JsonConvert.SerializeObject(promptContent)
|
||||
Content = toolResponse.IsError
|
||||
? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
: SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
toolName = "调用提示词",
|
||||
toolParams = null,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "success",
|
||||
content = promptKey,
|
||||
id = timestamp.ToString()
|
||||
};
|
||||
callback?.Invoke(toolMessageItem);
|
||||
callback?.Invoke(toolMessageItem);
|
||||
});
|
||||
}
|
||||
else
|
||||
else if (mcpServer is StdioMcpServer)
|
||||
{
|
||||
StdioMcpServer stdioMcpServer = mcpServer as StdioMcpServer;
|
||||
StdioMcpClient client = new StdioMcpClient(stdioMcpServer.Command, stdioMcpServer.Args);
|
||||
CallToolResponse toolResponse = await client.CallToolAsync(toolName, toolParams);
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = "调用提示词",
|
||||
toolParams = null,
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "loading",
|
||||
content = "正在调用提示词",
|
||||
id = timestamp.ToString()
|
||||
status = toolResponse.IsError ? "fail" : "success",
|
||||
content = JsonConvert.SerializeObject(toolResponse),
|
||||
id = (timestamp + 1).ToString()
|
||||
};
|
||||
callback?.Invoke(toolMessageItem);
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = toolResponse.IsError
|
||||
? SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
: SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolResponse))
|
||||
});
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(toolMessageItem);
|
||||
});
|
||||
}
|
||||
else if (mcpServer is InnerMcpServer)
|
||||
{
|
||||
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];
|
||||
for (int i = 0; i < methodParams.Length; i++)
|
||||
{
|
||||
if (methodParams[i].GetType() == typeof(JArray))
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
list = (methodParams[i] as JArray).Select(token => token.ToString()).ToList();
|
||||
args[i] = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = methodParams[i];
|
||||
}
|
||||
}
|
||||
|
||||
var task = method.Invoke(null, args) as Task<JsonRpcResultEntity>;
|
||||
JsonRpcResultEntity innerResult = await task;
|
||||
if (innerResult is JsonRpcErrorEntity)
|
||||
{
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "fail",
|
||||
content = JsonConvert.SerializeObject(innerResult),
|
||||
id = (timestamp + 1).ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(toolMessageItem))
|
||||
});
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(toolMessageItem);
|
||||
});
|
||||
}
|
||||
else if (innerResult is JsonRpcSuccessEntity)
|
||||
{
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = toolName,
|
||||
toolParams = toolParams,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "success",
|
||||
content = JsonConvert.SerializeObject(innerResult),
|
||||
id = (timestamp + 1).ToString()
|
||||
};
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "user",
|
||||
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(toolMessageItem))
|
||||
});
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(toolMessageItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
//普通流式消息卡片
|
||||
MessageListItem chatMessageListItem = new ChatMessageItem()
|
||||
Console.WriteLine(ex.Message);
|
||||
log.Error(ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
/*统一处理本次请求中的Prompt调用需求*/
|
||||
foreach (PromptRequest promptRequest in promptRequests)
|
||||
{
|
||||
try
|
||||
{
|
||||
string promptContent = DynamicPrompt.GetPrompt(promptRequest.PromptName, promptRequest.PromptArgs);
|
||||
messages.Add(new Message
|
||||
{
|
||||
content = chunk,
|
||||
role = "assistant",
|
||||
type = MessageType.CHAT_MESSAGE,
|
||||
id = timestamp.ToString()
|
||||
Role = "user",
|
||||
Content = promptContent
|
||||
});
|
||||
MessageListItem toolMessageItem = new ToolMessageItem
|
||||
{
|
||||
toolName = "调用提示词",
|
||||
toolParams = null,
|
||||
type = MessageType.TOOL_MESSAGE,
|
||||
status = "success",
|
||||
content = "成功调用提示词:"+promptRequest.PromptName,
|
||||
id = (timestamp+1).ToString()
|
||||
};
|
||||
messageContent = chunk;
|
||||
callback?.Invoke(chatMessageListItem);
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
callback?.Invoke(toolMessageItem);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
log.Error(e.Message);
|
||||
}
|
||||
}
|
||||
messages.Add(new Message
|
||||
{
|
||||
Role = "assistant",
|
||||
Content = messageContent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,6 +567,7 @@ public class Gateway
|
||||
StringBuilder toolInfos = new StringBuilder();
|
||||
foreach (McpServer mcpServer in mcpServerList.GetAllServers())
|
||||
{
|
||||
log.Info($"正在列出{mcpServer.Name}中的工具");
|
||||
if (mcpServer is InnerMcpServer)
|
||||
{
|
||||
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
|
||||
@ -564,17 +640,10 @@ public class Gateway
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, string> prompts = DynamicPrompt.GetAllPrompts();
|
||||
foreach (KeyValuePair<string, string> prompt in prompts)
|
||||
List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts();
|
||||
foreach (UserPrompt userPrompt in prompts)
|
||||
{
|
||||
McpPromptDefinition promptDefinition = new McpPromptDefinition
|
||||
{
|
||||
Prompt = new LinkToolAddin.host.mcp.Prompt
|
||||
{
|
||||
Name = prompt.Key
|
||||
}
|
||||
};
|
||||
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(promptDefinition));
|
||||
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt}));
|
||||
toolInfos.AppendLine(node.ToString());
|
||||
toolInfos.AppendLine();
|
||||
}
|
||||
|
||||
@ -35,6 +35,53 @@ public class McpServerList
|
||||
Description = "可以调用进行查询知识库,获取相关参考信息。",
|
||||
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)
|
||||
|
||||
40
host/PromptServerList.cs
Normal file
40
host/PromptServerList.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using LinkToolAddin.host.prompt;
|
||||
|
||||
namespace LinkToolAddin.host;
|
||||
|
||||
public class PromptServerList
|
||||
{
|
||||
Dictionary<string, PromptServer> promptServers = new Dictionary<string, PromptServer>();
|
||||
|
||||
public PromptServerList()
|
||||
{
|
||||
promptServers.Add("plan", new PromptServer("plan",
|
||||
"根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表",
|
||||
"请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具’,如果是ArcGIS Pro的工具,根据用户的具体需求和提供的数据类型," +
|
||||
"判断并列出所有必要的分析步骤和工具,同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如:analysis.Erase)," +
|
||||
"确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
|
||||
"有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"));
|
||||
promptServers.Add("param", new PromptServer("param",
|
||||
"填写ArcGIS Pro工具调用参数,生成规范的可执行的工具调用请求",
|
||||
"根据帮助文档填写工具参数,请你根据“所需调用工具”,参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" +
|
||||
"列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" +
|
||||
"不能更改所需调用工具的名称。例如:arcpy.analysis.Buffer,不能只写成Buffer。确保格式、参数的完整性和准确性,避免任何遗漏或错误。" +
|
||||
"特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具(Erase),将圆形要素(circle.shp)与方形要素(square.shp)进行空间叠加运算。" +
|
||||
"输出: \"in_features\":\"circle.shp\",\r\n \"erase_features\":\"sqaure.shp\",\r\n \"out_feature_class\":\"res.shp\",\r\n \"cluster_tolerance\":\"1\""));
|
||||
promptServers.Add("code", new PromptServer("code",
|
||||
"生成可运行的arcpy代码",
|
||||
"根据你在多种编程语言、框架、设计模式和最佳实践方面拥有的广泛知识。现在需要根据用户需求生成高质量的代码,并确保语法正确。" +
|
||||
"编写Arcpy代码时必须符合ArcGIS官方文档要求。参考官方文档的方法参数,确保编写正确。"));
|
||||
}
|
||||
|
||||
public Dictionary<string, PromptServer> GetPromptsDict()
|
||||
{
|
||||
return promptServers;
|
||||
}
|
||||
|
||||
public PromptServer GetPromptServer(string key)
|
||||
{
|
||||
return promptServers[key];
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using LinkToolAddin.common;
|
||||
using LinkToolAddin.host.llm.entity;
|
||||
using LinkToolAddin.host.llm.entity.stream;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LinkToolAddin.host.llm;
|
||||
@ -18,17 +19,22 @@ public class Bailian : Llm
|
||||
public string max_tokens { get; set; }
|
||||
public string app_id { get; set; }
|
||||
public string api_key { get; set; }
|
||||
public async IAsyncEnumerable<string> SendChatStreamAsync(LlmJsonContent jsonContent)
|
||||
public async IAsyncEnumerable<LlmStreamChat> SendChatStreamAsync(LlmJsonContent jsonContent)
|
||||
{
|
||||
jsonContent.Stream = true;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
await foreach (var chunk in HttpRequest.PostWithStreamingResponseAsync(
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
StringBuilder reasonBuilder = new StringBuilder();
|
||||
await foreach (LlmStreamChat chunk in HttpRequest.PostWithStreamingResponseAsync(
|
||||
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
||||
JsonConvert.SerializeObject(jsonContent),
|
||||
api_key))
|
||||
{
|
||||
builder.Append(chunk);
|
||||
yield return builder.ToString();
|
||||
contentBuilder.Append(chunk.Choices[0].Delta.Content);
|
||||
reasonBuilder.Append(chunk.Choices[0].Delta.ResoningContent);
|
||||
LlmStreamChat fullChunk = chunk;
|
||||
fullChunk.Choices[0].Delta.Content = contentBuilder.ToString();
|
||||
fullChunk.Choices[0].Delta.ResoningContent = reasonBuilder.ToString();
|
||||
yield return fullChunk;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LinkToolAddin.host.llm.entity;
|
||||
using LinkToolAddin.host.llm.entity.stream;
|
||||
|
||||
namespace LinkToolAddin.host.llm;
|
||||
|
||||
@ -11,7 +12,7 @@ public interface Llm
|
||||
public string top_p { get; set; }
|
||||
public string max_tokens { get; set; }
|
||||
|
||||
public IAsyncEnumerable<string> SendChatStreamAsync(LlmJsonContent jsonContent);
|
||||
public IAsyncEnumerable<LlmStreamChat> SendChatStreamAsync(LlmJsonContent jsonContent);
|
||||
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
|
||||
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
|
||||
public Task<string> SendApplicationAsync(CommonInput commonInput);
|
||||
|
||||
@ -25,6 +25,15 @@ namespace LinkToolAddin.host.llm.entity
|
||||
|
||||
[JsonProperty("top_k")]
|
||||
public int TopK { get; set; } = 40;
|
||||
|
||||
[JsonProperty("enable_thinking")]
|
||||
public bool EnableThinking { get; set; } = true;
|
||||
|
||||
[JsonProperty("thinking_budget")]
|
||||
public long ThinkingBudget { get; set; } = 1200;
|
||||
|
||||
[JsonProperty("incremental_output")]
|
||||
public bool IncrementalOutput { get; set; } = true;
|
||||
}
|
||||
|
||||
public partial class Message
|
||||
|
||||
@ -50,5 +50,7 @@
|
||||
{
|
||||
[JsonProperty("content")]
|
||||
public string Content { get; set; }
|
||||
[JsonProperty("reasoning_content")]
|
||||
public string ResoningContent { get; set; }
|
||||
}
|
||||
}
|
||||
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; }
|
||||
}
|
||||
9
host/prompt/PromptDefinition.cs
Normal file
9
host/prompt/PromptDefinition.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
|
||||
public class PromptDefinition
|
||||
{
|
||||
[JsonProperty("prompt")]
|
||||
public UserPrompt UserPrompt { get; set; }
|
||||
}
|
||||
10
host/prompt/PromptRequest.cs
Normal file
10
host/prompt/PromptRequest.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
|
||||
public class PromptRequest
|
||||
{
|
||||
public PromptServer PromptServer { get; set; }
|
||||
public string PromptName { get; set; }
|
||||
public Dictionary<string, string> PromptArgs { get; set; }
|
||||
}
|
||||
32
host/prompt/PromptServer.cs
Normal file
32
host/prompt/PromptServer.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
|
||||
public class PromptServer
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Arguments { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
public PromptServer(string name, string description, string content)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
Content = content;
|
||||
string pattern = "{{([\\s\\S]*?)}}";
|
||||
var matches = Regex.Matches(content, pattern);
|
||||
StringBuilder args = new StringBuilder();
|
||||
foreach (var match in matches)
|
||||
{
|
||||
string variableName = match.ToString().Replace("{{","").Replace("}}","");
|
||||
string arg = "{ \"" + variableName + "\" : \"type\" : \" string \" }";
|
||||
args.Append(arg);
|
||||
}
|
||||
if (args.ToString() != string.Empty)
|
||||
{
|
||||
Arguments = "{\"type\":\"object\",\"properties\":" + args.ToString() + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,37 +1,36 @@
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
using LinkToolAddin.common;
|
||||
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
|
||||
public class SystemPrompt
|
||||
{
|
||||
public static string SysPromptTemplate =
|
||||
"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
||||
"指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。整个流程结束之后单独输出一条内容为'[DONE]'的消息,前后不要有任何说明文字。" +
|
||||
"调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
||||
"工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
||||
"输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" +
|
||||
"如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。" +
|
||||
"工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:<tool_use>\n <name>{tool_name}</name>\n <arguments>{json_arguments}</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.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" +
|
||||
"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软件的专家,请以此身份回答用户的问题。\r\n\r\n指令:\r\n你需要使用一组工具来回答用户的问题。也可以通过 <prompt> 标签调用用户提示词,以帮助你更好地理解任务。所有操作完成后,在最后一条输出中包含 '[DONE]',但不要单独发送这条消息。\r\n\r\n调用工具要求:\r\n1. 每次只能调用一个工具,并等待用户的反馈结果。\r\n2. 所有工具调用都必须基于前一步的结果进行推理和决策。\r\n3. 工具调用必须以 XML 格式输出,并且必须作为**独立的一条消息输出**,前后不得有任何解释性文字或说明内容。\r\n4. 如果需要提供解释、说明或引导信息,请先单独输出一段普通文本,**其中不能包含任何 XML 标签或格式**。\r\n5. 不得重复调用已经成功执行过的工具,除非有新的参数或上下文需要重新调用。\r\n\r\n工具调用背景:\r\n你有以下工具可以调用:{{toolInfos}} \r\n用户的数据库路径是:{{gdbPath}}\r\n\r\n输出风格:\r\n- 所有工具调用都必须使用标准 XML 格式输出,并且每条消息只包含一次调用。\r\n- 文字说明必须单独输出,且为普通段落格式,不含任何 XML 或 Markdown 标记。\r\n- 输出顺序应为:【可选的文字说明】→【必选的工具调用】,或者仅输出工具调用。\r\n\r\n工具调用格式示例:\r\n<tool_use>\r\n <name>{tool_name}</name>\r\n <arguments>{json_arguments}</arguments>\r\n</tool_use>\r\n\r\n例如:\r\n<tool_use>\r\n <name>gaode:maps_geo</name>\r\n <arguments>{\"address\":\"广州市政府, 广州市\", \"city\":\"广州\"}</arguments>\r\n</tool_use>\r\n\r\n注意事项:\r\n1. 工具名称必须与实际工具完全一致。\r\n2. 参数必须为合法 JSON 格式,且符合工具要求。\r\n3. 用户时间宝贵,请高效调用工具,尽快完成任务。\r\n4. 最终完成时应在最后一次响应中包含 [DONE],而不是单独输出。\r\n\r\n工具调用结果返回格式:\r\n用户将以如下格式返回工具调用结果:\r\n<tool_use_result>\r\n <name>{tool_name}</name>\r\n <result>{result}</result>\r\n</tool_use_result>\r\n\r\n例如:\r\n<tool_use_result>\r\n <name>ArcGIS_Pro:GP</name>\r\n <result>{\"output_file\": \"source.shp\"}</result>\r\n</tool_use_result>\r\n\r\n请始终遵循此格式以确保工具调用被正确解析和执行。";
|
||||
|
||||
public static string ContinuePromptTemplate = "这是上述工具调用的结果。{{toolResult}}\n请根据以下执行结果,清晰解释执行结果并执行下一步操作。" +
|
||||
"执行下一步工具的要求:1. 解析工具输出结果2. 调用下一个工具时确保参数继承前序输出。请据此继续执行";
|
||||
|
||||
public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,需按以下流程处理:1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,```" +
|
||||
"2. 修复方案:(1)根据错误类型生成对应的工具重试策略(2)参数调整:明确需要修改的参数及修改方式。3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功";
|
||||
// public static string SysPromptTemplate =
|
||||
// "现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
||||
// "指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。" +
|
||||
// "调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
||||
// "工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
||||
// "输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。" +
|
||||
// "如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。" +
|
||||
// "工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:<tool_use>\n <name>{tool_name}</name>\n <arguments>{json_arguments}</arguments>\n</tool_use>。" +
|
||||
// "工具名称:需与所使用工具的精确名称一致。" +
|
||||
// "参数:应为包含工具所需参数的 JSON 对象。例如:<tool_use>\\n <name>gaode:maps_geo</name>\\n <arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>\\n</tool_use>" +
|
||||
// "你必须严格遵守以下输出规则:" +
|
||||
// "3.用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。" +
|
||||
// "结果:用户将以以下格式返回工具调用结果:<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工具前必须先查询帮助文档、标准调用名和参数后再进行调用";
|
||||
// //"现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。\r\n\r\n指令:\r\n你需要使用一组工具来回答用户的问题。也可以通过 <prompt> 标签调用用户提示词,以帮助你更好地理解任务。所有操作完成后,在最后一条输出中包含 '[DONE]',但不要单独发送这条消息。\r\n\r\n调用工具要求:\r\n1. 每次只能调用一个工具,并等待用户的反馈结果。\r\n2. 所有工具调用都必须基于前一步的结果进行推理和决策。\r\n3. 工具调用必须以 XML 格式输出,并且必须作为**独立的一条消息输出**,前后不得有任何解释性文字或说明内容。\r\n4. 如果需要提供解释、说明或引导信息,请先单独输出一段普通文本,**其中不能包含任何 XML 标签或格式**。\r\n5. 不得重复调用已经成功执行过的工具,除非有新的参数或上下文需要重新调用。\r\n\r\n工具调用背景:\r\n你有以下工具可以调用:{{toolInfos}} \r\n用户的数据库路径是:{{gdbPath}}\r\n\r\n输出风格:\r\n- 所有工具调用都必须使用标准 XML 格式输出,并且每条消息只包含一次调用。\r\n- 文字说明必须单独输出,且为普通段落格式,不含任何 XML 或 Markdown 标记。\r\n- 输出顺序应为:【可选的文字说明】→【必选的工具调用】,或者仅输出工具调用。\r\n\r\n工具调用格式示例:\r\n<tool_use>\r\n <name>{tool_name}</name>\r\n <arguments>{json_arguments}</arguments>\r\n</tool_use>\r\n\r\n例如:\r\n<tool_use>\r\n <name>gaode:maps_geo</name>\r\n <arguments>{\"address\":\"广州市政府, 广州市\", \"city\":\"广州\"}</arguments>\r\n</tool_use>\r\n\r\n注意事项:\r\n1. 工具名称必须与实际工具完全一致。\r\n2. 参数必须为合法 JSON 格式,且符合工具要求。\r\n3. 用户时间宝贵,请高效调用工具,尽快完成任务。\r\n4. 最终完成时应在最后一次响应中包含 [DONE],而不是单独输出。\r\n\r\n工具调用结果返回格式:\r\n用户将以如下格式返回工具调用结果:\r\n<tool_use_result>\r\n <name>{tool_name}</name>\r\n <result>{result}</result>\r\n</tool_use_result>\r\n\r\n例如:\r\n<tool_use_result>\r\n <name>ArcGIS_Pro:GP</name>\r\n <result>{\"output_file\": \"source.shp\"}</result>\r\n</tool_use_result>\r\n\r\n请始终遵循此格式以确保工具调用被正确解析和执行。";
|
||||
//
|
||||
// public static string ContinuePromptTemplate = "这是上述工具调用的结果。{{toolResult}}\n请根据以下执行结果,清晰解释执行结果并执行下一步操作。" +
|
||||
// "执行下一步工具的要求:1. 解析工具输出结果2. 调用下一个工具时确保参数继承前序输出。请据此继续执行";
|
||||
//
|
||||
// public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误\n{{toolResult}}\n,需按以下流程处理:1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,```" +
|
||||
// "2. 修复方案:(1)根据错误类型生成对应的工具重试策略(2)参数调整:明确需要修改的参数及修改方式。3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功"+
|
||||
// "5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误";
|
||||
|
||||
public static string SysPrompt(string gdbPath, string toolInfos)
|
||||
{
|
||||
string sysPrompt = SysPromptTemplate;
|
||||
string sysPrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.SystemPrompt.txt");;
|
||||
sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath);
|
||||
sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos);
|
||||
return sysPrompt;
|
||||
@ -39,8 +38,15 @@ public class SystemPrompt
|
||||
|
||||
public static string ContinuePrompt(string toolResult)
|
||||
{
|
||||
string continuePrompt = ContinuePromptTemplate;
|
||||
string continuePrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.ContinuePrompt.txt");
|
||||
continuePrompt = continuePrompt.Replace("{{toolResult}}", toolResult);
|
||||
return continuePrompt;
|
||||
}
|
||||
|
||||
public static string ErrorPrompt(string toolResult)
|
||||
{
|
||||
string errPrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.ErrorPrompt.txt");
|
||||
errPrompt = errPrompt.Replace("{{toolResult}}", toolResult);
|
||||
return errPrompt;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,13 @@
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LinkToolAddin.host.prompt;
|
||||
|
||||
public class UserPrompt
|
||||
{
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
[JsonProperty("arguments")]
|
||||
public string Arguments { get; set; }
|
||||
}
|
||||
11
resource/prompt/ContinuePrompt.txt
Normal file
11
resource/prompt/ContinuePrompt.txt
Normal file
@ -0,0 +1,11 @@
|
||||
这是上述工具调用的结果。
|
||||
|
||||
{{toolResult}}
|
||||
|
||||
请根据以下执行结果,清晰解释执行结果并执行下一步操作。
|
||||
|
||||
执行下一步工具的要求:
|
||||
1. 解析工具输出结果
|
||||
2. 调用下一个工具时确保参数继承前序输出。
|
||||
|
||||
请据此继续执行
|
||||
12
resource/prompt/ErrorPrompt.txt
Normal file
12
resource/prompt/ErrorPrompt.txt
Normal file
@ -0,0 +1,12 @@
|
||||
执行上一个工具的时候出现以下错误
|
||||
|
||||
{{toolResult}}
|
||||
|
||||
需按以下流程处理:
|
||||
1. 错误解析:分析错误类型及具体原因(见下方错误信息),根据错误信息选择修复方案,
|
||||
2. 修复方案:
|
||||
(1)根据错误类型生成对应的工具重试策略
|
||||
(2)参数调整:明确需要修改的参数及修改方式。
|
||||
3. 工具重试:使用调整后的参数重新调用工具。错误信息如下,请根据报错信息重试,
|
||||
4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功
|
||||
5.查询可能相关的知识库和帮助文档,纠正调用参数中存在的错误
|
||||
40
resource/prompt/SystemPrompt.txt
Normal file
40
resource/prompt/SystemPrompt.txt
Normal file
@ -0,0 +1,40 @@
|
||||
现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。
|
||||
|
||||
指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。
|
||||
|
||||
调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
|
||||
|
||||
输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 XML 风格的标签进行格式化以单独信息格式输出,前后没有文字信息。如果需要进行文字说明,请只有文字内容,以普通段落形式呈现没有其他格式。
|
||||
|
||||
工具调用格式:工具名称包含在一对标签内,每个参数也需用对应的标签包裹。结构如下:
|
||||
|
||||
<tool_use>
|
||||
<name>{tool_name}</name>
|
||||
<arguments>{json_arguments}</arguments>
|
||||
</tool_use>。
|
||||
|
||||
工具名称:需与所使用工具的精确名称一致。
|
||||
参数:应为包含工具所需参数的 JSON 对象。
|
||||
例如:
|
||||
<tool_use>
|
||||
<name>gaode:maps_geo</name>
|
||||
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
你必须严格遵守以下输出规则:用户时间宝贵,不得重复调用上一次已成功执行的工具调用,除非有新的参数或上下文变化。
|
||||
|
||||
结果:用户将以以下格式返回工具调用结果:
|
||||
<tool_use_result>
|
||||
<name>{tool_name}</name>
|
||||
<result>{result}</result>
|
||||
</tool_use_result>。
|
||||
|
||||
应为字符串类型,可以表示文件或其他输出类型。
|
||||
|
||||
工具调用示例:MCP工具调用的格式要求示例:以下是使用虚拟工具的示例:
|
||||
<tool_use>
|
||||
<name>gaode:maps_geo</name>
|
||||
<arguments>{\\\"address\\\":\\\"广州市政府, 广州市\\\", \\\"city\\\":\\\"广州\\\"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
特别地:需要调用ArcGIS Pro工具前必须先查询帮助文档、标准调用名和参数后再进行调用
|
||||
@ -7,7 +7,7 @@ namespace LinkToolAddin.server
|
||||
public partial class JsonRpcResultEntity
|
||||
{
|
||||
[JsonProperty("jsonrpc")]
|
||||
public string Jsonrpc { get; set; }
|
||||
public string Jsonrpc { get; set; } = "2.0";
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
@ -24,12 +24,13 @@
|
||||
<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">
|
||||
<Grid.RowDefinitions><RowDefinition Height="24"/></Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Row="0" Grid.Column="0" ToolTip="输入测试提示词" Name="PromptTestTextBox"></TextBox>
|
||||
@ -39,5 +40,6 @@
|
||||
<Button Grid.Row="3" Content="停止回答" Name="StopConversation" Click="StopConversation_OnClick"></Button>
|
||||
<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>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -8,6 +9,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Xml.Linq;
|
||||
using LinkToolAddin.client;
|
||||
using LinkToolAddin.common;
|
||||
using LinkToolAddin.host;
|
||||
using LinkToolAddin.host.llm;
|
||||
using LinkToolAddin.host.llm.entity;
|
||||
@ -23,6 +25,7 @@ using log4net.Layout;
|
||||
using ModelContextProtocol.Client;
|
||||
using ModelContextProtocol.Protocol.Types;
|
||||
using Newtonsoft.Json;
|
||||
using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox;
|
||||
|
||||
|
||||
namespace LinkToolAddin.ui.dockpane
|
||||
@ -35,7 +38,7 @@ namespace LinkToolAddin.ui.dockpane
|
||||
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
|
||||
|
||||
private List<string> idList = new List<string>();
|
||||
private Dictionary<string,MessageListItem> messageDict = new Dictionary<string,MessageListItem>();
|
||||
private ConcurrentDictionary<string,MessageListItem> messageDict = new ConcurrentDictionary<string,MessageListItem>();
|
||||
|
||||
public TestDockpaneView()
|
||||
{
|
||||
@ -195,14 +198,26 @@ namespace LinkToolAddin.ui.dockpane
|
||||
log.Info(msg.content);
|
||||
}
|
||||
|
||||
private void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
|
||||
private async void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string userPrompt = PromptTestTextBox.Text;
|
||||
// Gateway.SendMessage(userPrompt,"qwen-max","C:/Project/test.gdb",AddReply);
|
||||
Gateway.SendMessageStream(userPrompt,"qwen-max", "F:\\secondsemester\\linktool\\test\\linktooltest\\linktooltest.gdb", AddReplyStream);
|
||||
// model可选值:qwen3-235b-a22b,qwen-max,deepseek-r1
|
||||
try
|
||||
{
|
||||
await Task.Run(() => 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 async void AddReplyStream(MessageListItem msg)
|
||||
{
|
||||
await Task.Run(() => ProcessReplyStream(msg));
|
||||
}
|
||||
|
||||
private void ProcessReplyStream(MessageListItem msg)
|
||||
{
|
||||
string id = msg.id;
|
||||
if (idList.Contains(id))
|
||||
@ -212,16 +227,34 @@ namespace LinkToolAddin.ui.dockpane
|
||||
else
|
||||
{
|
||||
idList.Add(id);
|
||||
messageDict.Add(msg.id, msg);
|
||||
messageDict.TryAdd(msg.id, msg);
|
||||
}
|
||||
ReplyTextBox.Clear();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (KeyValuePair<string,MessageListItem> pair in messageDict)
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
MessageListItem msgItem = pair.Value;
|
||||
builder.AppendLine(msgItem.content);
|
||||
ReplyTextBox.Text = builder.ToString();
|
||||
ReplyTextBox.ScrollToEnd();
|
||||
ReplyTextBox.Clear();
|
||||
});
|
||||
try
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (KeyValuePair<string,MessageListItem> pair in messageDict)
|
||||
{
|
||||
MessageListItem msgItem = pair.Value;
|
||||
string content = msgItem.content;
|
||||
if (msgItem.type == MessageType.REASON_MESSAGE)
|
||||
{
|
||||
content = "<think>" + content + "</think>";
|
||||
}
|
||||
builder.AppendLine(content);
|
||||
builder.AppendLine();
|
||||
}
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
ReplyTextBox.Text = builder.ToString();
|
||||
ReplyTextBox.ScrollToEnd();
|
||||
});
|
||||
}catch (Exception exception)
|
||||
{
|
||||
log.Error(exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,6 +278,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);
|
||||
@ -274,5 +308,11 @@ namespace LinkToolAddin.ui.dockpane
|
||||
AddReply(toolMessageItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void TestResource_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt");
|
||||
MessageBox.Show(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ public enum MessageType
|
||||
{
|
||||
TOOL_MESSAGE,
|
||||
CHAT_MESSAGE,
|
||||
REASON_MESSAGE,
|
||||
}
|
||||
|
||||
public interface MessageListItem
|
||||
|
||||
Loading…
Reference in New Issue
Block a user