Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 322437a00f | |||
| 51b1cc1b7d | |||
| 6a0a5c6084 | |||
| 178b92a714 |
@ -6,13 +6,6 @@
|
|||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<NoWarn>CA1416</NoWarn>
|
<NoWarn>CA1416</NoWarn>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<Version>0.1.3</Version>
|
|
||||||
<Title>LinkToolAddin</Title>
|
|
||||||
<Authors>LinkTool团队</Authors>
|
|
||||||
<Description>LinkTool以大模型赋能让您只需一两句话便能完成复杂的空间分析与时空大数据处理任务。</Description>
|
|
||||||
<Copyright>华南农业大学</Copyright>
|
|
||||||
<PackageReleaseNotes>校AI大赛提交版本</PackageReleaseNotes>
|
|
||||||
<Company>华南农业大学</Company>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Config.daml" />
|
<None Remove="Config.daml" />
|
||||||
@ -104,6 +97,10 @@
|
|||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="doc\" />
|
||||||
|
<Folder Include="resource\" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Esri.ArcGISPro.Extensions30" Version="3.4.1.55405" />
|
<PackageReference Include="Esri.ArcGISPro.Extensions30" Version="3.4.1.55405" />
|
||||||
<PackageReference Include="log4net" Version="3.1.0" />
|
<PackageReference Include="log4net" Version="3.1.0" />
|
||||||
@ -111,42 +108,6 @@
|
|||||||
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
|
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="4.0.2-beta2" />
|
<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">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\prompt\ErrorPrompt.txt" />
|
|
||||||
<EmbeddedResource Include="resource\prompt\ErrorPrompt.txt">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\img\linktool.png" />
|
|
||||||
<EmbeddedResource Include="resource\img\linktool.png">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\img\user.png" />
|
|
||||||
<EmbeddedResource Include="resource\img\user.png">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\img\tool.png" />
|
|
||||||
<EmbeddedResource Include="resource\img\tool.png">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\img\unfold.png" />
|
|
||||||
<EmbeddedResource Include="resource\img\unfold.png">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<None Remove="resource\img\fold.png" />
|
|
||||||
<EmbeddedResource Include="resource\img\fold.png">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
</ItemGroup>
|
</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')" />
|
<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>
|
</Project>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"LinkToolAddin": {
|
"LinkToolAddin": {
|
||||||
"commandName": "Executable",
|
"commandName": "Executable",
|
||||||
"executablePath": "C:\\Users\\86158\\AppData\\Local\\Programs\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
|
"executablePath": "C:/Program Files/ArcGIS/Pro/bin/ArcGISPro.exe",
|
||||||
"applicationUrl": "https://localhost:5001",
|
"applicationUrl": "https://localhost:5001",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
|||||||
@ -31,7 +31,6 @@ git clone xxx.git
|
|||||||
5. 部分情况下可能还需要替换LinkToolAddin.csproj中的所有相关安装路径
|
5. 部分情况下可能还需要替换LinkToolAddin.csproj中的所有相关安装路径
|
||||||
6. 点击运行进行测试,看是否能正常打开ArcGIS Pro和LinkTool插件
|
6. 点击运行进行测试,看是否能正常打开ArcGIS Pro和LinkTool插件
|
||||||
7. 确认无误建议commit到本地的master分支以备后续使用(但请勿推送至远端)
|
7. 确认无误建议commit到本地的master分支以备后续使用(但请勿推送至远端)
|
||||||
8. 修改McpServerList里面filesystem的白名单目录
|
|
||||||
|
|
||||||
#### 创建分支
|
#### 创建分支
|
||||||
|
|
||||||
|
|||||||
@ -50,20 +50,11 @@ public class StdioMcpClient : McpClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<McpClientTool>> GetToolListAsync()
|
public async Task<IList<McpClientTool>> GetToolListAsync()
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions));
|
IMcpClient client = await McpClientFactory.CreateAsync(new StdioClientTransport(transportOptions));
|
||||||
IList<McpClientTool> tools = await client.ListToolsAsync();
|
IList<McpClientTool> tools = await client.ListToolsAsync();
|
||||||
return tools;
|
return tools;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CallToolResponse> CallToolAsync(string toolName, Dictionary<string, object> parameters = null)
|
public async Task<CallToolResponse> CallToolAsync(string toolName, Dictionary<string, object> parameters = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,41 +1,29 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using LinkToolAddin.host;
|
|
||||||
using LinkToolAddin.host.prompt;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.client.prompt;
|
namespace LinkToolAddin.client.prompt;
|
||||||
|
|
||||||
public class DynamicPrompt
|
public class DynamicPrompt
|
||||||
{
|
{
|
||||||
public static string GetPrompt(string name,Dictionary<string,string> args = null)
|
public static string GetPrompt(string name,Dictionary<string,object> args = null)
|
||||||
{
|
{
|
||||||
PromptServerList promptServerList = new PromptServerList();
|
PromptTemplates promptTemplate = new PromptTemplates();
|
||||||
string template = promptServerList.GetPromptServer(name).Content;
|
string template = promptTemplate.GetPrompt(name);
|
||||||
if (args == null)
|
if (args == null)
|
||||||
{
|
{
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
foreach (KeyValuePair<string,string> pair in args)
|
foreach (KeyValuePair<string,object> pair in args)
|
||||||
{
|
{
|
||||||
string replaceKey = "{{"+pair.Key+"}}";
|
string replaceKey = "{{"+pair.Key+"}}";
|
||||||
template = template.Replace(replaceKey, pair.Value.ToString());
|
template.Replace(replaceKey, pair.Value.ToString());
|
||||||
}
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<UserPrompt> GetAllPrompts()
|
public static Dictionary<string, string> GetAllPrompts()
|
||||||
{
|
{
|
||||||
PromptServerList promptServerList = new PromptServerList();
|
PromptTemplates promptTemplate = new PromptTemplates();
|
||||||
List<UserPrompt> prompts = new List<UserPrompt>();
|
Dictionary<string, string> template = promptTemplate.GetPromptsDict();
|
||||||
Dictionary<string, PromptServer> promptDefinitions = promptServerList.GetPromptsDict();
|
return template;
|
||||||
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,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using LinkToolAddin.host.prompt;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.client.prompt;
|
namespace LinkToolAddin.client.prompt;
|
||||||
|
|
||||||
@ -9,17 +8,9 @@ public class PromptTemplates
|
|||||||
|
|
||||||
public PromptTemplates()
|
public PromptTemplates()
|
||||||
{
|
{
|
||||||
prompts.Add("plan", "请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具’,如果是ArcGIS Pro的工具,根据用户的具体需求和提供的数据类型," +
|
prompts.Add("plan", "请根据用户所提问题进行工具规划");
|
||||||
"判断并列出所有必要的分析步骤和工具,同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如:analysis.Erase)," +
|
prompts.Add("param", "根据帮助文档填写工具参数");
|
||||||
"确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
|
prompts.Add("code", "现在需要生成代码,要求语法正确");
|
||||||
"有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。");
|
|
||||||
prompts.Add("param", "根据帮助文档填写工具参数,请你根据“所需调用工具”,参照知识库“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\"");
|
|
||||||
prompts.Add("code", "根据你在多种编程语言、框架、设计模式和最佳实践方面拥有的广泛知识。现在需要根据用户需求生成高质量的代码,并确保语法正确。" +
|
|
||||||
"编写Arcpy代码时必须符合ArcGIS官方文档要求。参考官方文档的方法参数,确保编写正确。");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetPrompt(string name)
|
public string GetPrompt(string name)
|
||||||
|
|||||||
@ -1,59 +1,27 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ArcGIS.Core.Data;
|
using ArcGIS.Core.Data;
|
||||||
using ArcGIS.Core.Data.Raster;
|
using ArcGIS.Core.Data.Raster;
|
||||||
using ArcGIS.Core.Geometry;
|
using ArcGIS.Core.Geometry;
|
||||||
using ArcGIS.Desktop.Core.Geoprocessing;
|
|
||||||
using ArcGIS.Desktop.Framework.Threading.Tasks;
|
using ArcGIS.Desktop.Framework.Threading.Tasks;
|
||||||
using LinkToolAddin.server;
|
using LinkToolAddin.server;
|
||||||
using LinkToolAddin.ui.dockpane;
|
|
||||||
using log4net;
|
|
||||||
using ModelContextProtocol.Server;
|
using ModelContextProtocol.Server;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.client.tool;
|
namespace LinkToolAddin.client.tool;
|
||||||
|
|
||||||
public class ArcGisPro
|
public class ArcGisPro
|
||||||
{
|
{
|
||||||
private static ILog log = LogManager.GetLogger(typeof(ArcGisPro));
|
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能。")]
|
||||||
[McpServerTool, Description("可以通过调用ArcGIS Pro的地理处理工具实现一些数据处理功能,传入参数必须严格遵循ArcGIS Pro调用工具的标准调用名和参数要求知识库。")]
|
|
||||||
public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams)
|
public static async Task<JsonRpcResultEntity> ArcGisProTool(string toolName, List<string> toolParams)
|
||||||
{
|
{
|
||||||
IGPResult results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams,null,null,null,GPExecuteToolFlags.InheritGPOptions|GPExecuteToolFlags.GPThread);
|
// Call the ArcGIS Pro method and get the result
|
||||||
JsonRpcResultEntity jsonRpcResultEntity;
|
var result = await server.CallArcGISPro.CallArcGISProTool(toolName, toolParams);
|
||||||
if (results.IsFailed)
|
|
||||||
{
|
// Serialize the result back to a JSON string
|
||||||
log.Error(results.ErrorMessages);
|
return result;
|
||||||
jsonRpcResultEntity = new JsonRpcErrorEntity()
|
|
||||||
{
|
|
||||||
Error = new Error()
|
|
||||||
{
|
|
||||||
Code = results.ErrorCode.ToString(),
|
|
||||||
Message = GetMessagesString(results.ErrorMessages)+"\n"+GetMessagesString(results.Messages)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}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坐标,获取字段列表等")]
|
[McpServerTool, Description("查看指定数据的坐标系、范围、几何类型、是否有Z坐标和M坐标,获取字段列表等")]
|
||||||
@ -100,7 +68,7 @@ public class ArcGisPro
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpServerTool, Description("列出gdb数据库中的所有数据的名称")]
|
[McpServerTool, Description("列出gdb数据库中的所有数据名称")]
|
||||||
public static async Task<JsonRpcResultEntity> ListData(string gdbPath)
|
public static async Task<JsonRpcResultEntity> ListData(string gdbPath)
|
||||||
{
|
{
|
||||||
var datasets = new List<string>();
|
var datasets = new List<string>();
|
||||||
@ -136,88 +104,4 @@ public class ArcGisPro
|
|||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpServerTool, Description("获取要素类的属性表内容,可以查看属性表中的至多前20条记录的内容")]
|
|
||||||
public static async Task<JsonRpcResultEntity> GetFeatureDatasetAttributeTable(string datasetPath, string dataName, string rowsLimit)
|
|
||||||
{
|
|
||||||
JsonRpcResultEntity result = new JsonRpcResultEntity();
|
|
||||||
await QueuedTask.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath)));
|
|
||||||
FeatureClass featureClass = gdb.OpenDataset<FeatureClass>(dataName);
|
|
||||||
List<Dictionary<string, string>> attributeTable = await GetAttributeTableAsync(featureClass,Convert.ToInt32(rowsLimit));
|
|
||||||
result = new JsonRpcSuccessEntity()
|
|
||||||
{
|
|
||||||
Id = 1,
|
|
||||||
Result = JsonConvert.SerializeObject(attributeTable)
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}catch (Exception ex)
|
|
||||||
{
|
|
||||||
result = new JsonRpcErrorEntity()
|
|
||||||
{
|
|
||||||
Error = new Error()
|
|
||||||
{
|
|
||||||
Message = ex.Message,
|
|
||||||
Code = "500"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<List<Dictionary<string, string>>> GetAttributeTableAsync(FeatureClass featureClass,int limit = 5)
|
|
||||||
{
|
|
||||||
if (limit > 20)
|
|
||||||
limit = 20;
|
|
||||||
return await QueuedTask.Run(() =>
|
|
||||||
{
|
|
||||||
var result = new List<Dictionary<string, string>>();
|
|
||||||
|
|
||||||
using (var cursor = featureClass.Search())
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
while (cursor.MoveNext())
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
if (i >= limit && limit != -1)
|
|
||||||
break;
|
|
||||||
var feature = cursor.Current as Feature;
|
|
||||||
if (feature == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var record = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var field in featureClass.GetDefinition().GetFields())
|
|
||||||
{
|
|
||||||
var value = feature[field.Name];
|
|
||||||
|
|
||||||
// 处理 DBNull 值
|
|
||||||
if (value is DBNull)
|
|
||||||
value = null;
|
|
||||||
|
|
||||||
record[field.Name] = value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Add(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetMessagesString(IEnumerable<IGPMessage> messages)
|
|
||||||
{
|
|
||||||
StringBuilder messagesStr = new StringBuilder();
|
|
||||||
foreach (var gpMessage in messages)
|
|
||||||
{
|
|
||||||
messagesStr.AppendLine(gpMessage.Text);
|
|
||||||
}
|
|
||||||
return messagesStr.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -22,28 +22,4 @@ public class KnowledgeBase
|
|||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpServerTool, Description("查询ArcGIS Pro调用工具的标准调用名和参数要求知识库")]
|
|
||||||
public static async Task<JsonRpcResultEntity> QueryArcgisToolDoc(string query)
|
|
||||||
{
|
|
||||||
DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProToolDoc);
|
|
||||||
KnowledgeResult knowledgeResult = await docDb.Retrieve(query);
|
|
||||||
JsonRpcResultEntity result = new JsonRpcSuccessEntity()
|
|
||||||
{
|
|
||||||
Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList),
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[McpServerTool, Description("查询使用ArcGIS Pro进行任务规划和解决实际问题的案例知识库")]
|
|
||||||
public static async Task<JsonRpcResultEntity> QueryArcgisExampleDoc(string query)
|
|
||||||
{
|
|
||||||
DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProApplicantExample);
|
|
||||||
KnowledgeResult knowledgeResult = await docDb.Retrieve(query);
|
|
||||||
JsonRpcResultEntity result = new JsonRpcSuccessEntity()
|
|
||||||
{
|
|
||||||
Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList),
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ public class HttpRequest
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async IAsyncEnumerable<LlmStreamChat> PostWithStreamingResponseAsync(
|
public static async IAsyncEnumerable<string> PostWithStreamingResponseAsync(
|
||||||
string url,
|
string url,
|
||||||
string body,
|
string body,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
@ -114,7 +114,7 @@ public class HttpRequest
|
|||||||
|
|
||||||
if (dataObj is not null)
|
if (dataObj is not null)
|
||||||
{
|
{
|
||||||
yield return dataObj;
|
yield return dataObj.Choices[0].Delta.Content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.common;
|
|
||||||
|
|
||||||
public class LocalResource
|
|
||||||
{
|
|
||||||
public static string ReadFileByResource(string resourceName)
|
|
||||||
{
|
|
||||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
|
||||||
|
|
||||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
|
||||||
{
|
|
||||||
if (stream == null)
|
|
||||||
{
|
|
||||||
return($"找不到嵌入资源:{resourceName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (StreamReader reader = new StreamReader(stream))
|
|
||||||
{
|
|
||||||
return reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BitmapImage ReadImageByResource(string resourceName)
|
|
||||||
{
|
|
||||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
|
||||||
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
|
|
||||||
{
|
|
||||||
if (stream == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("资源未找到,请检查资源名称是否正确。");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是 WPF,可以转换为 BitmapImage
|
|
||||||
BitmapImage bitmap = new BitmapImage();
|
|
||||||
bitmap.BeginInit();
|
|
||||||
bitmap.StreamSource = stream;
|
|
||||||
bitmap.EndInit();
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# 待办事项
|
|
||||||
|
|
||||||
本文档用于记录和跟踪当前阶段待办事项及完成进度
|
|
||||||
|
|
||||||
## 核心程序
|
|
||||||
|
|
||||||
- [x] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述
|
|
||||||
- [x] 接入本地文件系统等基础性MCP服务
|
|
||||||
- [ ] Python代码执行的实现
|
|
||||||
- [x] 适配qwen3、deepseek等推理模型并迁移
|
|
||||||
- [x] 工具执行错误消息合并为一条
|
|
||||||
|
|
||||||
## 提示词工程
|
|
||||||
|
|
||||||
- [x] 将原来的单独XML规则修改为内嵌XML规则
|
|
||||||
- [x] 系统提示词明确提示调用动态Prompt和知识库
|
|
||||||
- [x] 错误和继续提示词预留工具消息占位符
|
|
||||||
- [x] 错误和继续提示词明示
|
|
||||||
- [ ] 系统提示词泛化增强
|
|
||||||
- [ ] 针对qwen3适当调整
|
|
||||||
|
|
||||||
## 前端交互
|
|
||||||
|
|
||||||
- [ ] 三类消息卡片
|
|
||||||
- [ ] 流式输出,根据id匹配修改或新增
|
|
||||||
- [ ] 添加工作空间、发送、复制粘贴等交互
|
|
||||||
- [ ] 独立UI线程
|
|
||||||
- [ ] 工具卡片特殊提示,弹出窗口显示内容
|
|
||||||
722
host/Gateway.cs
722
host/Gateway.cs
@ -8,15 +8,12 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using ArcGIS.Desktop.Internal.Mapping;
|
using ArcGIS.Desktop.Framework.Dialogs;
|
||||||
using ArcGIS.Desktop.Internal.Mapping.Locate;
|
using ArcGIS.Desktop.Internal.Mapping.Locate;
|
||||||
using LinkToolAddin.client;
|
using LinkToolAddin.client;
|
||||||
using LinkToolAddin.client.prompt;
|
using LinkToolAddin.client.prompt;
|
||||||
using LinkToolAddin.client.tool;
|
|
||||||
using LinkToolAddin.host.llm;
|
using LinkToolAddin.host.llm;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
using LinkToolAddin.host.mcp;
|
using LinkToolAddin.host.mcp;
|
||||||
@ -34,21 +31,12 @@ using Newtonsoft.Json.Schema;
|
|||||||
using Newtonsoft.Json.Schema.Generation;
|
using Newtonsoft.Json.Schema.Generation;
|
||||||
using Tool = LinkToolAddin.host.mcp.Tool;
|
using Tool = LinkToolAddin.host.mcp.Tool;
|
||||||
using LinkToolAddin.common;
|
using LinkToolAddin.common;
|
||||||
using LinkToolAddin.host.llm.entity.stream;
|
|
||||||
using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.host;
|
namespace LinkToolAddin.host;
|
||||||
|
|
||||||
public class Gateway
|
public class Gateway
|
||||||
{
|
{
|
||||||
private static ILog log = LogManager.GetLogger(typeof(Gateway));
|
private static ILog log = LogManager.GetLogger(typeof(Gateway));
|
||||||
private static bool goOn = true;
|
|
||||||
|
|
||||||
public static void StopConversation()
|
|
||||||
{
|
|
||||||
goOn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async void SendMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
public static async void SendMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||||
{
|
{
|
||||||
Llm bailian = new Bailian
|
Llm bailian = new Bailian
|
||||||
@ -78,11 +66,9 @@ public class Gateway
|
|||||||
{
|
{
|
||||||
Model = model,
|
Model = model,
|
||||||
Messages = messages,
|
Messages = messages,
|
||||||
Temperature = 0.3,
|
Temperature = 0.7,
|
||||||
TopP = 0.6,
|
TopP = 1,
|
||||||
TopK = 25,
|
MaxTokens = 1000,
|
||||||
MaxTokens = 1024,
|
|
||||||
ThinkingBudget = 1024
|
|
||||||
});
|
});
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
log.Info(reponse);
|
log.Info(reponse);
|
||||||
@ -118,7 +104,12 @@ public class Gateway
|
|||||||
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.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||||
|
});
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = JsonConvert.SerializeObject(toolResponse)
|
||||||
});
|
});
|
||||||
callback?.Invoke(toolMessageItem);
|
callback?.Invoke(toolMessageItem);
|
||||||
}else if (mcpServer is StdioMcpServer)
|
}else if (mcpServer is StdioMcpServer)
|
||||||
@ -138,7 +129,12 @@ public class Gateway
|
|||||||
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.ErrorPromptTemplate : SystemPrompt.ContinuePromptTemplate
|
||||||
|
});
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = JsonConvert.SerializeObject(toolResponse)
|
||||||
});
|
});
|
||||||
callback?.Invoke(toolMessageItem);
|
callback?.Invoke(toolMessageItem);
|
||||||
}else if (mcpServer is InnerMcpServer)
|
}else if (mcpServer is InnerMcpServer)
|
||||||
@ -160,7 +156,12 @@ public class Gateway
|
|||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "user",
|
Role = "user",
|
||||||
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult))
|
Content = SystemPrompt.ErrorPromptTemplate
|
||||||
|
});
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = JsonConvert.SerializeObject(innerResult)
|
||||||
});
|
});
|
||||||
callback?.Invoke(toolMessageItem);
|
callback?.Invoke(toolMessageItem);
|
||||||
}else if (innerResult is JsonRpcSuccessEntity)
|
}else if (innerResult is JsonRpcSuccessEntity)
|
||||||
@ -176,7 +177,12 @@ public class Gateway
|
|||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "user",
|
Role = "user",
|
||||||
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
|
Content = SystemPrompt.ContinuePromptTemplate
|
||||||
|
});
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = JsonConvert.SerializeObject(innerResult)
|
||||||
});
|
});
|
||||||
callback?.Invoke(toolMessageItem);
|
callback?.Invoke(toolMessageItem);
|
||||||
}
|
}
|
||||||
@ -190,7 +196,7 @@ public class Gateway
|
|||||||
Dictionary<string, object> promptParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(promptArgs);
|
Dictionary<string, object> promptParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(promptArgs);
|
||||||
string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName;
|
string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName;
|
||||||
string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName;
|
string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName;
|
||||||
string promptRes = DynamicPrompt.GetPrompt(promptName, null);
|
string promptRes = DynamicPrompt.GetPrompt(promptName, promptParams);
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "user",
|
Role = "user",
|
||||||
@ -208,34 +214,13 @@ public class Gateway
|
|||||||
};
|
};
|
||||||
callback?.Invoke(chatMessageListItem);
|
callback?.Invoke(chatMessageListItem);
|
||||||
}
|
}
|
||||||
if (reponse.EndsWith("[DONE]"))
|
if (reponse == "[DONE]")
|
||||||
{
|
{
|
||||||
goOn = false;
|
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)
|
public static async void SendMessageStream(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||||
{
|
{
|
||||||
Llm bailian = new Bailian
|
Llm bailian = new Bailian
|
||||||
@ -243,10 +228,8 @@ public class Gateway
|
|||||||
api_key = "sk-db177155677e438f832860e7f4da6afc"
|
api_key = "sk-db177155677e438f832860e7f4da6afc"
|
||||||
};
|
};
|
||||||
List<Message> messages = new List<Message>();
|
List<Message> messages = new List<Message>();
|
||||||
string toolInfos = "";
|
string toolInfos = await GetToolInfos(new McpServerList());
|
||||||
try
|
log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
|
||||||
{
|
|
||||||
toolInfos = await GetToolInfos(new McpServerList());
|
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "system",
|
Role = "system",
|
||||||
@ -257,459 +240,227 @@ public class Gateway
|
|||||||
Role = "user",
|
Role = "user",
|
||||||
Content = message
|
Content = message
|
||||||
});
|
});
|
||||||
}catch (Exception ex)
|
bool goOn = true;
|
||||||
{
|
string toolPattern = "^<tool_use>[\\s\\S]*?<\\/tool_use>$";
|
||||||
log.Error(ex);
|
string promptPattern = "^<prompt>[\\s\\S]*?<\\/prompt>$";
|
||||||
MessageBox.Show(ex.Message,"获取MCP列表失败");
|
|
||||||
}
|
|
||||||
goOn = true;
|
|
||||||
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();
|
McpServerList mcpServerList = new McpServerList();
|
||||||
PromptServerList promptServerList = new PromptServerList();
|
|
||||||
int loop = 0;
|
|
||||||
string messageContent = ""; //一次请求下完整的response
|
|
||||||
bool queriedKnowledge = false;
|
|
||||||
bool executedTool = false;
|
|
||||||
while (goOn)
|
while (goOn)
|
||||||
{
|
{
|
||||||
loop++;
|
|
||||||
if (loop > 500)
|
|
||||||
{
|
|
||||||
MessageBox.Show("达到最大循环次数", "退出循环");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
LlmJsonContent jsonContent = new LlmJsonContent()
|
LlmJsonContent jsonContent = new LlmJsonContent()
|
||||||
{
|
{
|
||||||
Model = model,
|
Model = model,
|
||||||
Messages = messages,
|
Messages = messages,
|
||||||
Temperature = 0.3,
|
Temperature = 0.7,
|
||||||
TopP = 0.4,
|
TopP = 1,
|
||||||
TopK = 7,
|
|
||||||
MaxTokens = 1000,
|
MaxTokens = 1000,
|
||||||
};
|
};
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>();
|
string messageContent = "";
|
||||||
List<PromptRequest> promptRequests = new List<PromptRequest>();
|
await foreach(var chunk in bailian.SendChatStreamAsync(jsonContent))
|
||||||
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
|
{
|
||||||
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
|
if (chunk == "[DONE]")
|
||||||
if (toolMatched == "" && promptMatched == "" && messageContent != "")
|
|
||||||
{
|
{
|
||||||
//如果本次回复不包含任何工具的调用或提示词的调用,则不再请求
|
|
||||||
goOn = false;
|
goOn = false;
|
||||||
MessageListItem endMessageListItem1 = new ChatMessageItem
|
}else if (chunk.StartsWith("<tool_use>"))
|
||||||
{
|
{
|
||||||
type = MessageType.END_TAG,
|
if (Regex.IsMatch(chunk, toolPattern))
|
||||||
content = "",
|
|
||||||
id = (timestamp + 3).ToString(),
|
|
||||||
role = "assistant"
|
|
||||||
};
|
|
||||||
callback?.Invoke(endMessageListItem1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await foreach(LlmStreamChat llmStreamChat in bailian.SendChatStreamAsync(jsonContent))
|
//返回工具卡片
|
||||||
{
|
XElement toolUse = XElement.Parse(chunk);
|
||||||
if (!goOn)
|
|
||||||
{
|
|
||||||
MessageListItem endMessageListItem2 = new ChatMessageItem
|
|
||||||
{
|
|
||||||
type = MessageType.END_TAG,
|
|
||||||
content = "",
|
|
||||||
id = (timestamp+3).ToString(),
|
|
||||||
role = "assistant"
|
|
||||||
};
|
|
||||||
callback?.Invoke(endMessageListItem2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string chunk = llmStreamChat.Choices[0].Delta.Content;
|
|
||||||
MessageListItem reasonMessageListItem = new ChatMessageItem()
|
|
||||||
{
|
|
||||||
content = llmStreamChat.Choices[0].Delta.ResoningContent,
|
|
||||||
role = "assistant",
|
|
||||||
type = MessageType.REASON_MESSAGE,
|
|
||||||
id = (timestamp+2).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 == "")
|
|
||||||
{
|
|
||||||
//普通消息文本
|
|
||||||
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
|
|
||||||
{
|
|
||||||
//包含工具调用请求的消息
|
|
||||||
XElement toolUse = XElement.Parse(matched);
|
|
||||||
string fullToolName = toolUse.Element("name")?.Value;
|
string fullToolName = toolUse.Element("name")?.Value;
|
||||||
string toolArgs = toolUse.Element("arguments")?.Value;
|
string toolArgs = toolUse.Element("arguments")?.Value;
|
||||||
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
|
Dictionary<string, object> toolParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(toolArgs);
|
||||||
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);
|
||||||
//将工具调用请求添加至列表中待一次回答完整后再统一进行调用
|
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 = 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
|
||||||
|
});
|
||||||
|
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
|
||||||
|
});
|
||||||
|
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 task = method.Invoke(null, toolParams.Values.ToArray()) 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
|
||||||
|
});
|
||||||
|
messages.Add(new Message
|
||||||
|
{
|
||||||
|
Role = "user",
|
||||||
|
Content = JsonConvert.SerializeObject(innerResult)
|
||||||
|
});
|
||||||
|
callback?.Invoke(toolMessageItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageListItem toolMessageItem = new ToolMessageItem
|
||||||
|
{
|
||||||
|
toolName = "",
|
||||||
|
toolParams = new Dictionary<string, object>(),
|
||||||
|
type = MessageType.TOOL_MESSAGE,
|
||||||
|
status = "loading",
|
||||||
|
content = "正在生成工具调用参数",
|
||||||
|
id = timestamp.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)
|
||||||
|
});
|
||||||
|
MessageListItem toolMessageItem = new ToolMessageItem
|
||||||
|
{
|
||||||
|
toolName = "调用提示词",
|
||||||
|
toolParams = null,
|
||||||
|
type = MessageType.TOOL_MESSAGE,
|
||||||
|
status = "success",
|
||||||
|
content = promptKey,
|
||||||
|
id = timestamp.ToString()
|
||||||
|
};
|
||||||
|
callback?.Invoke(toolMessageItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageListItem toolMessageItem = new ToolMessageItem
|
||||||
|
{
|
||||||
|
toolName = "调用提示词",
|
||||||
|
toolParams = null,
|
||||||
|
type = MessageType.TOOL_MESSAGE,
|
||||||
|
status = "loading",
|
||||||
|
content = "正在调用提示词",
|
||||||
|
id = timestamp.ToString()
|
||||||
|
};
|
||||||
|
callback?.Invoke(toolMessageItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//普通流式消息卡片
|
||||||
MessageListItem chatMessageListItem = new ChatMessageItem()
|
MessageListItem chatMessageListItem = new ChatMessageItem()
|
||||||
{
|
{
|
||||||
content = remaining,
|
content = chunk,
|
||||||
role = "assistant",
|
role = "assistant",
|
||||||
type = MessageType.CHAT_MESSAGE,
|
type = MessageType.CHAT_MESSAGE,
|
||||||
id = timestamp.ToString()
|
id = timestamp.ToString()
|
||||||
};
|
};
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
messageContent = chunk;
|
||||||
{
|
|
||||||
callback?.Invoke(chatMessageListItem);
|
callback?.Invoke(chatMessageListItem);
|
||||||
});
|
|
||||||
if (!executedTool)
|
|
||||||
{
|
|
||||||
MessageListItem toolMessageListItem = new ToolMessageItem()
|
|
||||||
{
|
|
||||||
content = toolName,
|
|
||||||
result = "工具运行中" + toolName,
|
|
||||||
toolName = toolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
role = "user",
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
id = (timestamp+1).ToString(),
|
|
||||||
status = "loading"
|
|
||||||
};
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageListItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mcpToolRequests = new List<McpToolRequest>();
|
|
||||||
McpToolRequest mcpToolRequest = new McpToolRequest()
|
|
||||||
{
|
|
||||||
McpServer = mcpServer,
|
|
||||||
ToolName = toolName,
|
|
||||||
ToolArgs = toolParams,
|
|
||||||
};
|
|
||||||
mcpToolRequests.Add(mcpToolRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e);
|
|
||||||
log.Error(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch (Exception e)
|
|
||||||
{
|
|
||||||
log.Error(e.Message);
|
|
||||||
MessageBox.Show(e.Message, "请求大模型出错");
|
|
||||||
}
|
|
||||||
if (messageContent != "")
|
|
||||||
{
|
|
||||||
messages.Add(new Message
|
messages.Add(new Message
|
||||||
{
|
{
|
||||||
Role = "assistant",
|
Role = "assistant",
|
||||||
Content = messageContent
|
Content = messageContent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/*统一处理本次请求中的MCP工具调用需求*/
|
|
||||||
foreach (McpToolRequest mcpToolRequest in mcpToolRequests)
|
|
||||||
{
|
|
||||||
executedTool = true;
|
|
||||||
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 toolMessageItem1 = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = toolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = toolResponse.IsError ? "fail" : "success",
|
|
||||||
content = JsonConvert.SerializeObject(toolResponse),
|
|
||||||
result = JsonConvert.SerializeObject(toolResponse),
|
|
||||||
id = (timestamp + 1).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
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(toolMessageItem1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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 toolMessageItem1 = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = toolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = toolResponse.IsError ? "fail" : "success",
|
|
||||||
content = JsonConvert.SerializeObject(toolResponse),
|
|
||||||
result = JsonConvert.SerializeObject(toolResponse),
|
|
||||||
id = (timestamp + 1).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
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(toolMessageItem1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (mcpServer is InnerMcpServer)
|
|
||||||
{
|
|
||||||
InnerMcpServer innerMcpServer = mcpServer as InnerMcpServer;
|
|
||||||
string mcpServerName = innerMcpServer.Name;
|
|
||||||
if (toolName == "ArcGisProTool" && queriedKnowledge == false)
|
|
||||||
{
|
|
||||||
JsonRpcResultEntity knowledgeResult = await KnowledgeBase.QueryArcgisToolDoc(toolParams["toolName"].ToString());
|
|
||||||
if (knowledgeResult is JsonRpcSuccessEntity)
|
|
||||||
{
|
|
||||||
JsonRpcSuccessEntity knowledgeSuccessResult = knowledgeResult as JsonRpcSuccessEntity;
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = "QueryArcgisToolDoc",
|
|
||||||
toolParams = new Dictionary<string, object>(){{"query",toolParams["toolName"].ToString()}},
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "success",
|
|
||||||
content = JsonConvert.SerializeObject(knowledgeSuccessResult.Result),
|
|
||||||
result = JsonConvert.SerializeObject(knowledgeSuccessResult.Result),
|
|
||||||
id = (timestamp + 4).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
messages.Add(new Message
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = SystemPrompt.ToolContinuePrompt(JsonConvert.SerializeObject(knowledgeSuccessResult))
|
|
||||||
});
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageItem);
|
|
||||||
});
|
|
||||||
queriedKnowledge = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonRpcErrorEntity knowledgeErrorResult = knowledgeResult as JsonRpcErrorEntity;
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = "QueryArcgisToolDoc",
|
|
||||||
toolParams = new Dictionary<string, object>(){{"query",toolParams["toolName"].ToString()}},
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "fail",
|
|
||||||
content = JsonConvert.SerializeObject(knowledgeErrorResult.Error),
|
|
||||||
result = JsonConvert.SerializeObject(knowledgeErrorResult.Error),
|
|
||||||
id = (timestamp + 4).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
messages.Add(new Message
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(knowledgeErrorResult))
|
|
||||||
});
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Type type = Type.GetType("LinkToolAddin.client.tool." + (mcpServer as InnerMcpServer).Name);
|
|
||||||
MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static);
|
|
||||||
var methodParams = toolParams.Values.ToArray();
|
|
||||||
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;
|
|
||||||
string displayToolName = toolName;
|
|
||||||
if (displayToolName == "ArcGisProTool")
|
|
||||||
{
|
|
||||||
displayToolName = "【GP】"+toolParams["toolName"].ToString();
|
|
||||||
}
|
|
||||||
queriedKnowledge = false;
|
|
||||||
if (innerResult is JsonRpcErrorEntity)
|
|
||||||
{
|
|
||||||
MessageListItem toolMessageItem1 = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = displayToolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "fail",
|
|
||||||
content = JsonConvert.SerializeObject(innerResult),
|
|
||||||
result = JsonConvert.SerializeObject(innerResult),
|
|
||||||
id = (timestamp + 1).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
messages.Add(new Message
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = SystemPrompt.ErrorPrompt(JsonConvert.SerializeObject(innerResult))
|
|
||||||
});
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageItem1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (innerResult is JsonRpcSuccessEntity)
|
|
||||||
{
|
|
||||||
MessageListItem toolMessageItem1 = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = displayToolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "success",
|
|
||||||
content = JsonConvert.SerializeObject(innerResult),
|
|
||||||
result = JsonConvert.SerializeObject(innerResult),
|
|
||||||
id = (timestamp + 1).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
messages.Add(new Message
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = SystemPrompt.ContinuePrompt(JsonConvert.SerializeObject(innerResult))
|
|
||||||
});
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageItem1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
Role = "user",
|
|
||||||
Content = promptContent
|
|
||||||
});
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = "调用提示词",
|
|
||||||
toolParams = new Dictionary<string, object>(),
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "success",
|
|
||||||
content = "成功调用提示词:"+promptRequest.PromptName,
|
|
||||||
id = (timestamp+1).ToString(),
|
|
||||||
result = "成功调用提示词:"+promptRequest.PromptName,
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
callback?.Invoke(toolMessageItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e);
|
|
||||||
log.Error(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MessageListItem endMessageListItem = new ChatMessageItem
|
|
||||||
{
|
|
||||||
type = MessageType.END_TAG,
|
|
||||||
content = "",
|
|
||||||
id = (timestamp+3).ToString(),
|
|
||||||
role = "assistant"
|
|
||||||
};
|
|
||||||
callback?.Invoke(endMessageListItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> GetToolInfos(McpServerList mcpServerList)
|
private static async Task<string> GetToolInfos(McpServerList mcpServerList)
|
||||||
{
|
{
|
||||||
|
int loop = 0;
|
||||||
StringBuilder toolInfos = new StringBuilder();
|
StringBuilder toolInfos = new StringBuilder();
|
||||||
int i = 0;
|
|
||||||
List<int> failedMcp = new List<int>();
|
|
||||||
List<string> failedMcpString = new List<string>();
|
|
||||||
foreach (McpServer mcpServer in mcpServerList.GetAllServers())
|
foreach (McpServer mcpServer in mcpServerList.GetAllServers())
|
||||||
{
|
{
|
||||||
i++;
|
loop++;
|
||||||
try
|
if (loop > 3)
|
||||||
{
|
{
|
||||||
|
MessageBox.Show("达到最大循环次数", "退出循环");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (mcpServer is InnerMcpServer)
|
if (mcpServer is InnerMcpServer)
|
||||||
{
|
{
|
||||||
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
|
InnerMcpServer innerMcpServer = (InnerMcpServer)mcpServer;
|
||||||
@ -780,33 +531,22 @@ public class Gateway
|
|||||||
toolInfos.AppendLine();
|
toolInfos.AppendLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (Exception e)
|
|
||||||
{
|
|
||||||
log.Error(e.Message);
|
|
||||||
failedMcp.Add(i);
|
|
||||||
failedMcpString.Add(mcpServerList.GetAllServerNames()[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts();
|
Dictionary<string, string> prompts = DynamicPrompt.GetAllPrompts();
|
||||||
foreach (UserPrompt userPrompt in prompts)
|
foreach (KeyValuePair<string, string> prompt in prompts)
|
||||||
{
|
{
|
||||||
try
|
McpPromptDefinition promptDefinition = new McpPromptDefinition
|
||||||
{
|
{
|
||||||
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt}));
|
Prompt = new LinkToolAddin.host.mcp.Prompt
|
||||||
|
{
|
||||||
|
Name = prompt.Key
|
||||||
|
}
|
||||||
|
};
|
||||||
|
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(promptDefinition));
|
||||||
toolInfos.AppendLine(node.ToString());
|
toolInfos.AppendLine(node.ToString());
|
||||||
toolInfos.AppendLine();
|
toolInfos.AppendLine();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
log.Error(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!failedMcp.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
MessageBox.Show($"读取失败的MCP服务有:{JsonConvert.SerializeObject(failedMcpString)}","MCP读取错误");
|
|
||||||
}
|
|
||||||
return toolInfos.ToString();
|
return toolInfos.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,37 +590,37 @@ public class Gateway
|
|||||||
return JsonConvert.SerializeObject(paramSchema, settings);;
|
return JsonConvert.SerializeObject(paramSchema, settings);;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void TestChatMessage(string message, string model, string gdbPath,
|
public static async void TestChatMessage(string message, string model, string gdbPath,//生成并返回一个聊天消息(ChatMessageItem),模拟AI助手的响应。通过回调函数 callback 将消息传递到前端显示。1
|
||||||
Action<MessageListItem> callback)
|
Action<MessageListItem> callback)
|
||||||
{
|
{
|
||||||
MessageListItem chatListItem = new ChatMessageItem
|
MessageListItem chatListItem = new ChatMessageItem//返回的东西1
|
||||||
{
|
{
|
||||||
content = message,
|
content = message,
|
||||||
role = "assistant",
|
role = "assistant",//角色,assistant是ai的回答,user用户的回答,sistant系统提示词1
|
||||||
type = MessageType.CHAT_MESSAGE,
|
type = MessageType.CHAT_MESSAGE,//根据type判断是工具卡还是聊天卡1
|
||||||
id = "testmsg12345"
|
id = "testmsg12345"//琅哥虚构的,定位可以用,引用选中凭id找1
|
||||||
};
|
};
|
||||||
callback?.Invoke(chatListItem);
|
callback?.Invoke(chatListItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void TestToolMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
public static async void TestToolMessage(string message, string model, string gdbPath, Action<MessageListItem> callback)//同上1
|
||||||
{
|
{
|
||||||
MessageListItem toolListItem = new ToolMessageItem
|
MessageListItem toolListItem = new ToolMessageItem
|
||||||
{
|
{
|
||||||
content = message,
|
content = message,//大模型生成的信息,正常来说是个x啥?1
|
||||||
type = MessageType.TOOL_MESSAGE,
|
type = MessageType.TOOL_MESSAGE,
|
||||||
toolName = "arcgis_pro.executeTool",
|
toolName = "arcgis_pro.executeTool",//要调用的工具名称,大模型工具不是arcgis工具,调⽤ArcGIS Pro.GP工具调用1
|
||||||
toolParams = new Dictionary<string, object>
|
toolParams = new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{"gp_name","analysis.Buffer"},
|
{"gp_name","analysis.Buffer"},//传参要传Arcgis的工具名
|
||||||
{"gp_params","[\"C:\\test.gdb\\river\",\"30 Meters\"]"}
|
{"gp_params","[\"C:\\test.gdb\\river\",\"30 Meters\"]"}//工具参数,数组,字符串要转成数组,前端要调用OpenTrueDialog,没听懂1
|
||||||
},
|
},
|
||||||
id = "testtool123456",
|
id = "testtool123456",
|
||||||
status = "success",
|
status = "success",//显示勾
|
||||||
role = "user",
|
role = "user",
|
||||||
result = "成功创建缓冲区"
|
result = "成功创建缓冲区"
|
||||||
};
|
};
|
||||||
callback?.Invoke(toolListItem);
|
callback?.Invoke(toolListItem);//这些调用结果显示在前端,显示点后面的工具名称,用户点的时候展开1
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void TestWorkflow(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
public static async void TestWorkflow(string message, string model, string gdbPath, Action<MessageListItem> callback)
|
||||||
|
|||||||
@ -28,61 +28,6 @@ public class McpServerList
|
|||||||
Description = "可以调用arcgis的地理处理工具或执行python代码等",
|
Description = "可以调用arcgis的地理处理工具或执行python代码等",
|
||||||
IsActive = true
|
IsActive = true
|
||||||
});
|
});
|
||||||
servers.Add("KnowledgeBase", new InnerMcpServer
|
|
||||||
{
|
|
||||||
Name = "KnowledgeBase",
|
|
||||||
Type = "inner",
|
|
||||||
Description = "可以调用进行查询知识库,获取相关参考信息。" ,
|
|
||||||
// "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息",
|
|
||||||
IsActive = true
|
|
||||||
});
|
|
||||||
//servers.Add("filesystem", new StdioMcpServer()
|
|
||||||
//{
|
|
||||||
// Name = "filesystem",
|
|
||||||
// Type = "stdio",
|
|
||||||
// Command = "npx",
|
|
||||||
// Args = new List<string>()
|
|
||||||
// {
|
|
||||||
// "-y",
|
|
||||||
// "@modelcontextprotocol/server-filesystem",
|
|
||||||
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb"
|
|
||||||
// }
|
|
||||||
//});
|
|
||||||
//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",
|
|
||||||
// "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb",
|
|
||||||
// "--python-path",
|
|
||||||
// "C:\\Program Files\\ArcGIS\\Pro\\bin\\Python\\envs\\custom\\python.exe"
|
|
||||||
// }
|
|
||||||
//});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public McpServer GetServer(string name)
|
public McpServer GetServer(string name)
|
||||||
@ -106,14 +51,4 @@ public class McpServerList
|
|||||||
}
|
}
|
||||||
return serverList;
|
return serverList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> GetAllServerNames()
|
|
||||||
{
|
|
||||||
List<string> serverList = new List<string>();
|
|
||||||
foreach (var server in servers)
|
|
||||||
{
|
|
||||||
serverList.Add(server.Key);
|
|
||||||
}
|
|
||||||
return serverList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,48 +0,0 @@
|
|||||||
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的帮助文档”。" +
|
|
||||||
// "有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"
|
|
||||||
"你对ArcGIS Pro的工具箱及其功能了如指掌,能够根据用户的具体需求和提供的数据类型,迅速判断并列出所有必要的分析步骤和工具,并随之调用知识库工具确认任务规划中的调用名是否正确,以及确认其对应参数" +
|
|
||||||
"同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如:analysis.Erase),确保工具名的准确无误。" +
|
|
||||||
"工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。" +
|
|
||||||
"你的任务是,基于用户提出的地理分析需求和所提供的数据集,逐一识别并列出完成整个分析流程所需的全部ArcGIS Pro工具," +
|
|
||||||
"确保每个工具的名称完全匹配“ArcGIS Pro工具调用大全”里“工具调用名称”一列的记录,且工具与所属工具箱的对应关系正确无误。" +
|
|
||||||
"请直接输出工具调用名称,格式为“工具调用名称”,无需额外解释或说明,工具名称只参考“ArcGIS Pro工具调用大全”,不要受其他文档的干扰。" +
|
|
||||||
|
|
||||||
"你的回复应简洁明了,仅包含所需工具的列表,输出格式为序号分隔的工具调用名以及调用知识库工具工具的XML格式,主要目的是纠正规划中错误的调用名和了解工具参数便于下一步的工具调用,因为调用名和参数对于工具执行非常重要,一旦出错则可能导致运行失败。严格遵循文档规定的格式和大小写,确保信息的准确性和专业性。"));
|
|
||||||
promptServers.Add("param", new PromptServer("param",
|
|
||||||
"填写ArcGIS Pro工具调用参数,生成规范的可执行的工具调用请求",
|
|
||||||
"根据知识库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,7 +6,6 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LinkToolAddin.common;
|
using LinkToolAddin.common;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
using LinkToolAddin.host.llm.entity.stream;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LinkToolAddin.host.llm;
|
namespace LinkToolAddin.host.llm;
|
||||||
@ -19,22 +18,17 @@ public class Bailian : Llm
|
|||||||
public string max_tokens { get; set; }
|
public string max_tokens { get; set; }
|
||||||
public string app_id { get; set; }
|
public string app_id { get; set; }
|
||||||
public string api_key { get; set; }
|
public string api_key { get; set; }
|
||||||
public async IAsyncEnumerable<LlmStreamChat> SendChatStreamAsync(LlmJsonContent jsonContent)
|
public async IAsyncEnumerable<string> SendChatStreamAsync(LlmJsonContent jsonContent)
|
||||||
{
|
{
|
||||||
jsonContent.Stream = true;
|
jsonContent.Stream = true;
|
||||||
StringBuilder contentBuilder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
StringBuilder reasonBuilder = new StringBuilder();
|
await foreach (var chunk in HttpRequest.PostWithStreamingResponseAsync(
|
||||||
await foreach (LlmStreamChat chunk in HttpRequest.PostWithStreamingResponseAsync(
|
|
||||||
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
||||||
JsonConvert.SerializeObject(jsonContent),
|
JsonConvert.SerializeObject(jsonContent),
|
||||||
api_key))
|
api_key))
|
||||||
{
|
{
|
||||||
contentBuilder.Append(chunk.Choices[0].Delta.Content);
|
builder.Append(chunk);
|
||||||
reasonBuilder.Append(chunk.Choices[0].Delta.ResoningContent);
|
yield return builder.ToString();
|
||||||
LlmStreamChat fullChunk = chunk;
|
|
||||||
fullChunk.Choices[0].Delta.Content = contentBuilder.ToString();
|
|
||||||
fullChunk.Choices[0].Delta.ResoningContent = reasonBuilder.ToString();
|
|
||||||
yield return fullChunk;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
using LinkToolAddin.host.llm.entity.stream;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.host.llm;
|
namespace LinkToolAddin.host.llm;
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ public interface Llm
|
|||||||
public string top_p { get; set; }
|
public string top_p { get; set; }
|
||||||
public string max_tokens { get; set; }
|
public string max_tokens { get; set; }
|
||||||
|
|
||||||
public IAsyncEnumerable<LlmStreamChat> SendChatStreamAsync(LlmJsonContent jsonContent);
|
public IAsyncEnumerable<string> SendChatStreamAsync(LlmJsonContent jsonContent);
|
||||||
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
|
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
|
||||||
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
|
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
|
||||||
public Task<string> SendApplicationAsync(CommonInput commonInput);
|
public Task<string> SendApplicationAsync(CommonInput commonInput);
|
||||||
|
|||||||
@ -25,15 +25,6 @@ namespace LinkToolAddin.host.llm.entity
|
|||||||
|
|
||||||
[JsonProperty("top_k")]
|
[JsonProperty("top_k")]
|
||||||
public int TopK { get; set; } = 40;
|
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
|
public partial class Message
|
||||||
|
|||||||
@ -50,7 +50,5 @@
|
|||||||
{
|
{
|
||||||
[JsonProperty("content")]
|
[JsonProperty("content")]
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
[JsonProperty("reasoning_content")]
|
|
||||||
public string ResoningContent { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.host.prompt;
|
|
||||||
|
|
||||||
public class PromptDefinition
|
|
||||||
{
|
|
||||||
[JsonProperty("prompt")]
|
|
||||||
public UserPrompt UserPrompt { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
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,59 +1,19 @@
|
|||||||
using LinkToolAddin.common;
|
namespace LinkToolAddin.host.prompt;
|
||||||
|
|
||||||
namespace LinkToolAddin.host.prompt;
|
|
||||||
|
|
||||||
public class SystemPrompt
|
public class SystemPrompt
|
||||||
{
|
{
|
||||||
// public static string SysPromptTemplate =
|
public static string SysPromptTemplate = "现在你是一个精通ArcGIS Pro的专家,请以此身份回答用户的问题。你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。MCP工具调用的格式要求示例,必须用<tool_use>标签表示工具调用:<tool_use>\n <name>search</name>\n <arguments>{\\\"query\\\": \\\"上海 人口\\\"}</arguments>\n</tool_use>。你每次调用请求都必须放在单独的一条消息中,不附带任何的文字说明,不带markdown格式。如需文字说明,请另外放在一次单独的消息中。\n当你认为已解决用户最初提出的问题时,请输出单独的一条消息,内容为[DONE],不附带任何其它文字说明,程序识别到后会退出循环。\n此外,你还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。";
|
||||||
// "现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。" +
|
|
||||||
// "指令:您可以使用一组工具来回答用户的问题。还可以通过<prompt></prompt>调用用户提示词,从而使你更好地理解和完成用户的任务。" +
|
public static string ContinuePromptTemplate = "工具执行的结果如下,根据以上结果决定继续执行工具或是根据结果回答问题。如果不再需要额外说明和额外的操作,请回答单独的一条内容为[DONE]的消息。工具已经成功调用,请勿重复执行上一个工具";
|
||||||
// "调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。您需要通过逐步使用工具来完成给定任务,每次工具调用需基于前一次的结果。成功调用工具之后应该马上调用下一个工具。你还可以通过<prompt></prompt>的方式来调用用户提示词,你能更好地理解和解决用户的问题。" +
|
|
||||||
// "工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。" +
|
public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,请根据报错信息重试";
|
||||||
// "输出风格:每次仅调用一个工具,必须基于前序工具的输出结果进行下一步操作。工具调用使用 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)
|
public static string SysPrompt(string gdbPath, string toolInfos)
|
||||||
{
|
{
|
||||||
string sysPrompt = LocalResource.ReadFileByResource("LinkToolAddin.resource.prompt.SystemPrompt.txt");;
|
string sysPrompt = SysPromptTemplate;
|
||||||
sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath);
|
sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath);
|
||||||
sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos);
|
sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos);
|
||||||
return sysPrompt;
|
return sysPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ContinuePrompt(string toolResult)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToolContinuePrompt(string toolResult)
|
|
||||||
{
|
|
||||||
string continuePrompt = "根据你需要调用的工具查询到如下帮助文档内容,请严格按照帮助文档中的参数和名称要求再次生成准确的工具调用请求以确保工具调用成功。\n{{toolResult}}";
|
|
||||||
continuePrompt = continuePrompt.Replace("{{toolResult}}", toolResult);
|
|
||||||
return continuePrompt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,13 +1,6 @@
|
|||||||
using Newtonsoft.Json;
|
namespace LinkToolAddin.host.prompt;
|
||||||
|
|
||||||
namespace LinkToolAddin.host.prompt;
|
|
||||||
|
|
||||||
public class UserPrompt
|
public class UserPrompt
|
||||||
{
|
{
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonProperty("description")]
|
|
||||||
public string Description { get; set; }
|
|
||||||
[JsonProperty("arguments")]
|
|
||||||
public string Arguments { get; set; }
|
|
||||||
}
|
}
|
||||||
@ -21,14 +21,13 @@ public class DocDb
|
|||||||
{
|
{
|
||||||
ArcGISProHelpDoc,
|
ArcGISProHelpDoc,
|
||||||
ArcGISProToolDoc,
|
ArcGISProToolDoc,
|
||||||
|
TaskPlanningDoc,
|
||||||
ArcGISProApplicantExample
|
ArcGISProApplicantExample
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<KnowledgeBase, string> knowledgeBase = new Dictionary<KnowledgeBase, string>
|
public Dictionary<KnowledgeBase, string> knowledgeBase = new Dictionary<KnowledgeBase, string>
|
||||||
{
|
{
|
||||||
{KnowledgeBase.ArcGISProHelpDoc,"6a77c5a68de64f469b79fcdcde9d5001"},
|
{KnowledgeBase.ArcGISProHelpDoc,"6a77c5a68de64f469b79fcdcde9d5001"}
|
||||||
{KnowledgeBase.ArcGISProToolDoc,"080f8925318247ea822a9e12db5cb5cd"},
|
|
||||||
{KnowledgeBase.ArcGISProApplicantExample,"eef60f7c879b4e8597138c261578d2a5"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public async Task<KnowledgeResult> Retrieve(string query)
|
public async Task<KnowledgeResult> Retrieve(string query)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 261 B |
Binary file not shown.
|
Before Width: | Height: | Size: 931 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 962 B |
Binary file not shown.
|
Before Width: | Height: | Size: 784 B |
@ -1,12 +0,0 @@
|
|||||||
这是上述工具调用的结果。
|
|
||||||
|
|
||||||
{{toolResult}}
|
|
||||||
|
|
||||||
请根据以下执行结果,清晰解释执行结果并执行下一步操作。
|
|
||||||
|
|
||||||
执行下一步工具的要求:
|
|
||||||
1. 解析工具输出结果
|
|
||||||
2. 调用下一个工具时确保参数继承前序输出,如果已经完成用户需求则不需继续执行工具。
|
|
||||||
3. 执行Arcgis Pro工具时可以调用知识库和提示词使任务完成得更出色!
|
|
||||||
|
|
||||||
请据此继续执行
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
执行上一个工具的时候出现以下错误
|
|
||||||
|
|
||||||
{{toolResult}}
|
|
||||||
|
|
||||||
需按以下流程处理:
|
|
||||||
1. 错误解析:分析错误类型及具体原因(见错误信息),根据错误信息选择修复方案。
|
|
||||||
2. 修复方案:
|
|
||||||
(1)如果是ArcGisPro工具,第一时间查询知识库的工具参数是否有误,多次执行失败极有可能是工具名或者参数错误,尤其注重调用工具或者提示词修正
|
|
||||||
(2)根据错误类型生成对应的工具重试策略。
|
|
||||||
(3)参数和调用名调整:及时调用知识库和用户提示词,确保工具名和参数的完全正确。明确需要修改的工具名、参数及修改方式。
|
|
||||||
(4)可以选择调用工具规划提示词优化工具执行流程或者调用知识库工具优化工具调用名和工具参数。
|
|
||||||
3. 工具重试:使用调整后的参数重新调用工具。请根据报错信息重试。
|
|
||||||
4.如果没有具体的错误描述信息请检查输出文件是否已经存在,若已经存在默认执行成功
|
|
||||||
5.出现错误编码号时要明晰编码在知识库中有相应解析,可以通过调用知识库解析错误原因
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
现在你是一个精通地理信息分析和ArcGIS Pro软件的专家,请以此身份回答用户的问题。
|
|
||||||
指令:您可以使用一组工具加文字说明来回答用户的问题。完成了用户的需求即可,不用猜测用户下一步还想做什么。计划使用ArcGisPro工具前,你可以通过<prompt><name></name><arguments>{}</arguments></prompt>格式调用用户提示词,或者调用知识库工具,从而使你更好地理解和完成用户的任务。除此之外,你具有搜索网页、编写代码、管理文件系统的能力,合理运用这些工具完成用户的需求。
|
|
||||||
调用工具要求:如果要调用工具,每次消息只能使用一个工具,用户的回复中将包含该工具的调用结果。你还可以通过<prompt></prompt>方式来调用用户提示词,或者调用知识库工具,你能更好地理解和解决用户的问题。您需要通过逐步使用工具或提示词来完成给定任务,每次调用需基于前一次的结果。成功调用工具或提示词之后应该马上调用下一个工具或提示词。
|
|
||||||
工具调用背景:你有以下工具可以调用{{toolInfos}},用户的数据库路径是{{gdbPath}}。
|
|
||||||
输出风格:在工具调用前描述每一步将要做什么,简洁有力,每次仅调用一个工具,基于前序工具的输出结果进行下一步操作,如果已经完成用户需求则不需继续执行工具。话语开头不要回复好的。工具调用使用 XML 风格的标签输出。
|
|
||||||
工具调用的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>
|
|
||||||
”
|
|
||||||
调用用户提示词示例:
|
|
||||||
“
|
|
||||||
先进行ArcGisPro工具任务规划
|
|
||||||
<prompt>
|
|
||||||
<name>plan</name>
|
|
||||||
<arguments>{}</arguments>
|
|
||||||
</prompt>
|
|
||||||
|
|
||||||
|
|
||||||
”
|
|
||||||
结果示例:用户将以以下格式返回工具调用结果:
|
|
||||||
<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>
|
|
||||||
|
|
||||||
你必须严格遵守以下每一条规则:
|
|
||||||
1.用户时间宝贵,一旦确认工具成功调用之后,不得重复调用上一次已成功执行的工具,除非有新的参数或上下文变化。
|
|
||||||
2.调用“ArcGisPro:ArcGisProTool”工具,name一定要严格按照知识库的调用名,例如"analysis.Union","management.Clip"等。输入与输出数据一定需要加上路径信息,输出数据到默认数据库中。`in_features`参数通过分号(`;`)拼接多个输入要素类路径,参数列表中有"{}"代表选填,其他必须要填写。
|
|
||||||
3.参数一定都为字符串类型,可以表示文件或其他输出类型。
|
|
||||||
4.一次只能调用一个工具,逐步调用!不要调用多个,一旦消息中没有工具或提示词调用信息即视为任务完成。因此工具或提示词调用必须连续完成。
|
|
||||||
5.只响应用户目前的需求即可,不要过度猜测用户的需求,如果有下一步的工具建议只输出文本即可,如果输出XML会执行大量无用的工具。
|
|
||||||
|
|
||||||
特别注意:
|
|
||||||
1.ArcGIS Pro中不能通过先SelectByAttribute选择后再执行ExportFeatures导出指定的部分,正确的做法是直接用ExportFeatures传入where_clause导出指定部分的数据。
|
|
||||||
2.可以先参考知识库中的案例辅助工具的规划
|
|
||||||
@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
|||||||
using ArcGIS.Desktop.Core.Geoprocessing;
|
using ArcGIS.Desktop.Core.Geoprocessing;
|
||||||
using ArcGIS.Desktop.Framework.Dialogs;
|
using ArcGIS.Desktop.Framework.Dialogs;
|
||||||
using ArcGIS.Desktop.Framework.Threading.Tasks;
|
using ArcGIS.Desktop.Framework.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace LinkToolAddin.server;
|
namespace LinkToolAddin.server;
|
||||||
|
|
||||||
@ -22,8 +21,8 @@ public class CallArcGISPro
|
|||||||
{
|
{
|
||||||
Error = new Error()
|
Error = new Error()
|
||||||
{
|
{
|
||||||
Code = results.ErrorCode.ToString(),
|
Code = results.ErrorCode,
|
||||||
Message = JsonConvert.SerializeObject(results.ErrorMessages)
|
Message = results.ErrorMessages.ToString()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -31,7 +30,7 @@ public class CallArcGISPro
|
|||||||
{
|
{
|
||||||
jsonRpcResultEntity = new JsonRpcSuccessEntity
|
jsonRpcResultEntity = new JsonRpcSuccessEntity
|
||||||
{
|
{
|
||||||
Result = JsonConvert.SerializeObject(results.Messages)
|
Result = results.Messages.ToString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return jsonRpcResultEntity;
|
return jsonRpcResultEntity;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace LinkToolAddin.server
|
|||||||
public partial class Error
|
public partial class Error
|
||||||
{
|
{
|
||||||
[JsonProperty("code")]
|
[JsonProperty("code")]
|
||||||
public string Code { get; set; }
|
public long Code { get; set; }
|
||||||
|
|
||||||
[JsonProperty("message")]
|
[JsonProperty("message")]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|||||||
@ -7,7 +7,7 @@ namespace LinkToolAddin.server
|
|||||||
public partial class JsonRpcResultEntity
|
public partial class JsonRpcResultEntity
|
||||||
{
|
{
|
||||||
[JsonProperty("jsonrpc")]
|
[JsonProperty("jsonrpc")]
|
||||||
public string Jsonrpc { get; set; } = "2.0";
|
public string Jsonrpc { get; set; }
|
||||||
|
|
||||||
[JsonProperty("id")]
|
[JsonProperty("id")]
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|||||||
@ -22,7 +22,7 @@ namespace LinkToolAddin
|
|||||||
{
|
{
|
||||||
internal class VersionButton : Button
|
internal class VersionButton : Button
|
||||||
{
|
{
|
||||||
private string version = "0.1.4";
|
private string version = "0.1.0";
|
||||||
protected override void OnClick()
|
protected override void OnClick()
|
||||||
{
|
{
|
||||||
MessageBox.Show($"当前LinkTool版本为{version}", "版本信息");
|
MessageBox.Show($"当前LinkTool版本为{version}", "版本信息");
|
||||||
|
|||||||
@ -6,9 +6,8 @@
|
|||||||
xmlns:ui="clr-namespace:LinkToolAddin.ui.dockpane"
|
xmlns:ui="clr-namespace:LinkToolAddin.ui.dockpane"
|
||||||
xmlns:extensions="clr-namespace:ArcGIS.Desktop.Extensions;assembly=ArcGIS.Desktop.Extensions"
|
xmlns:extensions="clr-namespace:ArcGIS.Desktop.Extensions;assembly=ArcGIS.Desktop.Extensions"
|
||||||
xmlns:controls="clr-namespace:ArcGIS.Desktop.Framework.Controls;assembly=ArcGIS.Desktop.Framework"
|
xmlns:controls="clr-namespace:ArcGIS.Desktop.Framework.Controls;assembly=ArcGIS.Desktop.Framework"
|
||||||
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="650" d:DesignWidth="300"
|
d:DesignHeight="300" d:DesignWidth="300"
|
||||||
d:DataContext="{Binding Path=ui.DialogDockpaneViewModel}">
|
d:DataContext="{Binding Path=ui.DialogDockpaneViewModel}">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
@ -17,52 +16,40 @@
|
|||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid Margin="8">
|
|
||||||
|
<Grid Margin="10" Background="White">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="24"/>
|
<RowDefinition Height="4*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="10"/>
|
||||||
<RowDefinition Height="24"/>
|
<RowDefinition Height="1*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="24"/>
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid Grid.Row="0" HorizontalAlignment="Center">
|
|
||||||
<Grid.ColumnDefinitions>
|
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Name="MessageScrollViewer">
|
||||||
<ColumnDefinition Width="36"/>
|
|
||||||
<ColumnDefinition Width="36"/>
|
|
||||||
<ColumnDefinition Width="36"/>
|
|
||||||
<ColumnDefinition Width="36"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Button Grid.Column="0" Content="清除" Name="ClearButton" Click="ClearButton_OnClick"></Button>
|
|
||||||
<Button Grid.Column="1" Content="顶部" Name="TopButton" Click="TopButton_OnClick"></Button>
|
|
||||||
<Button Grid.Column="2" Content="底部" Name="BottomButton" Click="BottomButton_OnClick"></Button>
|
|
||||||
<Button Grid.Column="3" Content="停止" Name="StopButton" Click="StopButton_OnClick"></Button>
|
|
||||||
<ComboBox Grid.Column="4" Name="ModelComboBox" SelectionChanged="ModelComboBox_OnSelectionChanged">
|
|
||||||
<ComboBoxItem Content="qwen3-235b-a22b" IsSelected="True"/>
|
|
||||||
<ComboBoxItem Content="qwen3-32b" />
|
|
||||||
<ComboBoxItem Content="qwq-32b" />
|
|
||||||
<ComboBoxItem Content="qwen-max-latest" />
|
|
||||||
<ComboBoxItem Content="deepseek-r1" />
|
|
||||||
<ComboBoxItem Content="deepseek-r1-0528" />
|
|
||||||
<ComboBoxItem Content="deepseek-r1-distill-qwen-32b" />
|
|
||||||
<ComboBoxItem Content="deepseek-r1-distill-llama-70b" />
|
|
||||||
<ComboBoxItem Content="deepseek-v3" />
|
|
||||||
<ComboBoxItem Content="自定义" />
|
|
||||||
</ComboBox>
|
|
||||||
</Grid>
|
|
||||||
<!-- <Button Grid.Row="0" Content="Test" Name="TestButton" Click="TestButton_OnClick"></Button> -->
|
|
||||||
<!-- <TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="LinkTool"/> -->
|
|
||||||
<ScrollViewer Grid.Row="1" HorizontalContentAlignment="Stretch" VerticalAlignment="Top" Name="ScrollViewer">
|
|
||||||
<StackPanel Name="ChatHistoryStackPanel"></StackPanel>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<TextBlock Grid.Row="2" VerticalAlignment="Center" Name="StatusTextBlock" Text=""/>
|
|
||||||
<Grid Grid.Row="3">
|
<DockPanel Grid.Row="2" Background="AliceBlue" Margin="0,0,0,0">
|
||||||
<Grid.ColumnDefinitions>
|
<Button
|
||||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
x:Name="SendButton"
|
||||||
<ColumnDefinition Width="48"></ColumnDefinition>
|
Content="发送"
|
||||||
</Grid.ColumnDefinitions>
|
Width="45"
|
||||||
<TextBox Grid.Column="0" Name="QuestionTextbox" Text="这是用户的问题" TextWrapping="Wrap"></TextBox>
|
Height="40"
|
||||||
<Button Grid.Column="1" Name="SendButton" Content="发送" Click="SendButton_OnClick"></Button>
|
Margin="5,0,5,0"
|
||||||
</Grid>
|
Background="#FFD0E0F0"
|
||||||
|
BorderBrush="#FFA0B0C0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Padding="0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
Click="Send_0nclick"/>
|
||||||
|
|
||||||
|
<TextBox x:Name="InputTextBox"
|
||||||
|
Height="40"
|
||||||
|
Margin="5,0,5,0"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Background="White"
|
||||||
|
BorderBrush="#FFD0D0D0"
|
||||||
|
BorderThickness="1"
|
||||||
|
Padding="5"/>
|
||||||
|
</DockPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,24 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using ArcGIS.Desktop.Core;
|
|
||||||
using ArcGIS.Desktop.Core.Geoprocessing;
|
using ArcGIS.Desktop.Core.Geoprocessing;
|
||||||
using LinkToolAddin.client;
|
using LinkToolAddin.client;
|
||||||
using LinkToolAddin.common;
|
|
||||||
using LinkToolAddin.host;
|
using LinkToolAddin.host;
|
||||||
using LinkToolAddin.host.llm;
|
using LinkToolAddin.host.llm;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
@ -29,33 +10,41 @@ using log4net;
|
|||||||
using log4net.Appender;
|
using log4net.Appender;
|
||||||
using log4net.Config;
|
using log4net.Config;
|
||||||
using log4net.Layout;
|
using log4net.Layout;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using ModelContextProtocol.Client;
|
using ModelContextProtocol.Client;
|
||||||
using ModelContextProtocol.Protocol.Types;
|
using ModelContextProtocol.Protocol.Types;
|
||||||
using ModelContextProtocol.Server;
|
using ModelContextProtocol.Server;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace LinkToolAddin.ui.dockpane
|
namespace LinkToolAddin.ui.dockpane
|
||||||
{
|
{
|
||||||
public class ItemModel
|
|
||||||
{
|
|
||||||
public string Content { get; set; }
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for DialogDockpaneView.xaml
|
/// Interaction logic for DialogDockpaneView.xaml
|
||||||
|
/// 显示消息列表、输入框和发送按钮
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class DialogDockpaneView : UserControl
|
public partial class DialogDockpaneView : UserControl
|
||||||
{
|
{
|
||||||
private static ILog log = LogManager.GetLogger(typeof(DialogDockpaneView));
|
private static ILog log = LogManager.GetLogger(typeof(DialogDockpaneView));
|
||||||
|
|
||||||
private List<string> idList = new List<string>();
|
|
||||||
private ConcurrentDictionary<string,MessageListItem> messageDict = new ConcurrentDictionary<string,MessageListItem>();
|
|
||||||
private ConcurrentDictionary<string,Border> borderItemsDict = new ConcurrentDictionary<string,Border>();
|
|
||||||
|
|
||||||
public DialogDockpaneView()
|
public DialogDockpaneView()
|
||||||
{
|
{
|
||||||
InitLogger();
|
InitLogger();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TestServer_OnClick(object sender, RoutedEventArgs e)
|
private async void TestServer_OnClick(object sender, RoutedEventArgs e)
|
||||||
@ -98,460 +87,68 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
log.Error("Error 日志(严重问题)");
|
log.Error("Error 日志(严重问题)");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendButton_OnClick(object sender, RoutedEventArgs e)
|
private void Send_0nclick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string question = QuestionTextbox.Text;
|
string userInput = InputTextBox.Text.Trim();
|
||||||
string defaultGdbPath = Project.Current.DefaultGeodatabasePath;
|
if (string.IsNullOrEmpty(userInput)) return;
|
||||||
string gdbPath = @"";
|
var userMessage = new ChatMessageItem
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
||||||
MessageListItem userMsg = new ChatMessageItem()
|
|
||||||
{
|
{
|
||||||
content = question,
|
id = Guid.NewGuid().ToString(),
|
||||||
role = "user",
|
role = "user",
|
||||||
type = MessageType.CHAT_MESSAGE,
|
content = userInput,
|
||||||
id = timestamp.ToString()
|
type = MessageType.CHAT_MESSAGE
|
||||||
};
|
};
|
||||||
Border userMsgBoder = GetUserChatBorder(userMsg);
|
// 使用统一方法显示消息
|
||||||
idList.Add(timestamp.ToString());
|
NewMessage(userMessage);
|
||||||
messageDict[timestamp.ToString()] = userMsg;
|
|
||||||
QuestionTextbox.Text = "";
|
// 清空输入框
|
||||||
borderItemsDict[timestamp.ToString()] = userMsgBoder;
|
InputTextBox.Clear();
|
||||||
ChatHistoryStackPanel.Children.Add(userMsgBoder);
|
|
||||||
string model = (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString() is null ? "qwen3-235b-a22b" : (ModelComboBox.SelectedItem as ComboBoxItem).Content.ToString();
|
Gateway.SendMessageStream(InputTextBox.Text, model: "qwen-max", gdbPath: "c: /user.gdb", NewMessage);
|
||||||
if (model == "自定义")
|
|
||||||
{
|
|
||||||
model = ShowInputBox("自定义模型", "请输入模型名称:", "");
|
|
||||||
}
|
|
||||||
ScrollViewer.ScrollToBottom();
|
|
||||||
Gateway.SendMessageStream(question,model,defaultGdbPath,NewMessage_Recall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ShowInputBox(string title, string message, string defaultValue = "")
|
public void NewMessage(MessageListItem messagelistItem)
|
||||||
{
|
{
|
||||||
// 创建一个自定义的输入对话框
|
// 创建用于显示消息内容的 TextBlock
|
||||||
var dialog = new System.Windows.Window
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
WindowStyle = WindowStyle.ToolWindow,
|
|
||||||
ResizeMode = ResizeMode.NoResize,
|
|
||||||
Topmost = true,
|
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner
|
|
||||||
};
|
|
||||||
|
|
||||||
// 设置对话框内容
|
|
||||||
var stackPanel = new StackPanel { Margin = new Thickness(10) };
|
|
||||||
stackPanel.Children.Add(new TextBlock { Text = message, Margin = new Thickness(0, 0, 0, 5) });
|
|
||||||
|
|
||||||
var textBox = new TextBox { Text = defaultValue, Margin = new Thickness(0, 0, 0, 10) };
|
|
||||||
stackPanel.Children.Add(textBox);
|
|
||||||
|
|
||||||
var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right };
|
|
||||||
|
|
||||||
var okButton = new Button { Content = "确定", Width = 75, Margin = new Thickness(0, 0, 5, 0) };
|
|
||||||
okButton.Click += (sender, e) => dialog.Close();
|
|
||||||
|
|
||||||
var cancelButton = new Button { Content = "取消", Width = 75 };
|
|
||||||
cancelButton.Click += (sender, e) => { textBox.Text = null; dialog.Close(); };
|
|
||||||
|
|
||||||
buttonPanel.Children.Add(okButton);
|
|
||||||
buttonPanel.Children.Add(cancelButton);
|
|
||||||
stackPanel.Children.Add(buttonPanel);
|
|
||||||
|
|
||||||
dialog.Content = stackPanel;
|
|
||||||
|
|
||||||
// 显示对话框并获取结果
|
|
||||||
dialog.ShowDialog();
|
|
||||||
return textBox.Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void NewMessage_Recall(MessageListItem msg)
|
|
||||||
{
|
|
||||||
string msgId = msg.id;
|
|
||||||
log.Info(msg.content);
|
|
||||||
double verticalOffset = ScrollViewer.VerticalOffset;
|
|
||||||
double viewportHeight = ScrollViewer.ViewportHeight;
|
|
||||||
double contentHeight = ChatHistoryStackPanel.ActualHeight;
|
|
||||||
double tolerance = 24;
|
|
||||||
if (!idList.Contains(msgId))
|
|
||||||
{
|
|
||||||
//不存在该消息,需添加到ListView中
|
|
||||||
if (msg.content == "")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.END_TAG)
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = "";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
idList.Add(msgId);
|
|
||||||
messageDict[msgId] = msg;
|
|
||||||
if (msg.role == "user")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.TOOL_MESSAGE)
|
|
||||||
{
|
|
||||||
Border border = GetToolChatBorder(msg);
|
|
||||||
borderItemsDict[msgId] = border;
|
|
||||||
ChatHistoryStackPanel.Children.Add(border);
|
|
||||||
StatusTextBlock.Text = "正在执行工具";
|
|
||||||
}else if (msg.type == MessageType.CHAT_MESSAGE)
|
|
||||||
{
|
|
||||||
Border border = GetUserChatBorder(msg);
|
|
||||||
borderItemsDict[msgId] = border;
|
|
||||||
ChatHistoryStackPanel.Children.Add(border);
|
|
||||||
StatusTextBlock.Text = "正在读取用户输入";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(msg.role == "assistant")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.REASON_MESSAGE)
|
|
||||||
{
|
|
||||||
Border border = GetAiReasonBorder(msg);
|
|
||||||
borderItemsDict[msgId] = border;
|
|
||||||
ChatHistoryStackPanel.Children.Add(border);
|
|
||||||
StatusTextBlock.Text = "深度思考中";
|
|
||||||
}else if (msg.type == MessageType.CHAT_MESSAGE)
|
|
||||||
{
|
|
||||||
Border border = GetAiChatBorder(msg);
|
|
||||||
borderItemsDict[msgId] = border;
|
|
||||||
ChatHistoryStackPanel.Children.Add(border);
|
|
||||||
StatusTextBlock.Text = "回答生成中";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (msg.role == "system")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.WARNING)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//已有该消息,只需要修改内容
|
|
||||||
messageDict[msgId] = msg;
|
|
||||||
if (msg.content == "")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.END_TAG)
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = "";
|
|
||||||
}
|
|
||||||
ChatHistoryStackPanel.Children.Remove(borderItemsDict[msgId]);
|
|
||||||
borderItemsDict.TryRemove(msgId, out Border border);
|
|
||||||
messageDict.TryRemove(msgId, out MessageListItem tempMsg);
|
|
||||||
idList.Remove(msgId);
|
|
||||||
}
|
|
||||||
if (msg.role == "user")
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.TOOL_MESSAGE)
|
|
||||||
{
|
|
||||||
Border borderItem = borderItemsDict[msgId];
|
|
||||||
Grid grid = borderItem.Child as Grid;
|
|
||||||
TextBlock textBlock = grid.Children[1] as TextBlock;
|
|
||||||
ToolMessageItem msgItem = msg as ToolMessageItem;
|
|
||||||
textBlock.Text = msgItem.toolName + " | " + msgItem.status;
|
|
||||||
Button resButton = grid.Children[3] as Button;
|
|
||||||
resButton.Tag = msg;
|
|
||||||
StatusTextBlock.Text = "正在执行工具";
|
|
||||||
}else if (msg.type == MessageType.CHAT_MESSAGE)
|
|
||||||
{
|
|
||||||
Border borderItem = borderItemsDict[msgId];
|
|
||||||
Grid grid = borderItem.Child as Grid;
|
|
||||||
TextBox textBox = grid.Children[1] as TextBox;
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
StatusTextBlock.Text = "正在读取用户输入";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (msg.type == MessageType.REASON_MESSAGE)
|
|
||||||
{
|
|
||||||
Border borderItem = borderItemsDict[msgId];
|
|
||||||
Grid grid = borderItem.Child as Grid;
|
|
||||||
TextBox textBox = grid.Children[0] as TextBox;
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
StatusTextBlock.Text = "深度思考中";
|
|
||||||
}else if (msg.type == MessageType.CHAT_MESSAGE)
|
|
||||||
{
|
|
||||||
Border borderItem = borderItemsDict[msgId];
|
|
||||||
Grid grid = borderItem.Child as Grid;
|
|
||||||
TextBox textBox = grid.Children[1] as TextBox;
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
StatusTextBlock.Text = "回答生成中";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Math.Abs(verticalOffset + viewportHeight - contentHeight) < tolerance)
|
|
||||||
{
|
|
||||||
ScrollViewer.ScrollToBottom();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border GetAiChatBorder(MessageListItem msg)
|
|
||||||
{
|
|
||||||
Border border = new Border();
|
|
||||||
border.Margin = new Thickness(8, 12, 8, 12);
|
|
||||||
border.BorderThickness = new Thickness(0);
|
|
||||||
// border.Background = Brushes.DarkSeaGreen;
|
|
||||||
Grid grid = new Grid();
|
|
||||||
Image icon = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.linktool.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
|
|
||||||
TextBox textBox = new TextBox();
|
|
||||||
grid.Children.Add(icon);
|
|
||||||
grid.Children.Add(textBox);
|
|
||||||
Grid.SetColumn(icon, 0);
|
|
||||||
Grid.SetColumn(textBox, 1);
|
|
||||||
textBox.IsReadOnly = true;
|
|
||||||
textBox.BorderThickness = new Thickness(0);
|
|
||||||
textBox.Background = Brushes.Transparent;
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
textBox.TextWrapping = TextWrapping.Wrap;
|
|
||||||
border.Child = grid;
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border GetAiReasonBorder(MessageListItem msg)
|
|
||||||
{
|
|
||||||
Border border = new Border();
|
|
||||||
border.Margin = new Thickness(8, 12, 8, 12);
|
|
||||||
border.BorderThickness = new Thickness(0);
|
|
||||||
// border.Background = Brushes.DarkSeaGreen;
|
|
||||||
Grid grid = new Grid();
|
|
||||||
Image icon = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.linktool.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
Image fold = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.fold.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
Image unfold = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.unfold.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(16, GridUnitType.Pixel)});
|
|
||||||
TextBox textBox = new TextBox();
|
|
||||||
// grid.Children.Add(icon);
|
|
||||||
grid.Children.Add(textBox);
|
|
||||||
// Grid.SetColumn(icon, 0);
|
|
||||||
Grid.SetColumn(textBox, 1);
|
|
||||||
textBox.IsReadOnly = true;
|
|
||||||
textBox.Foreground = Brushes.Gray;
|
|
||||||
textBox.BorderThickness = new Thickness(2,0,0,0);
|
|
||||||
textBox.BorderBrush = Brushes.Gray;
|
|
||||||
textBox.Background = Brushes.Transparent;
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
textBox.TextWrapping = TextWrapping.Wrap;
|
|
||||||
Button button = new Button();
|
|
||||||
StackPanel panel = new StackPanel();
|
|
||||||
panel.Orientation = Orientation.Horizontal;
|
|
||||||
panel.Children.Add(fold);
|
|
||||||
button.Content = panel;
|
|
||||||
button.BorderThickness = new Thickness(0);
|
|
||||||
button.Background = Brushes.Transparent;
|
|
||||||
button.Width = 16;
|
|
||||||
button.Height = 16;
|
|
||||||
button.Padding = new Thickness(0);
|
|
||||||
button.HorizontalAlignment = HorizontalAlignment.Center;
|
|
||||||
button.VerticalAlignment = VerticalAlignment.Top;
|
|
||||||
button.Tag = "fold";
|
|
||||||
button.Click += FoldButton_OnClick;
|
|
||||||
Grid.SetColumn(button, 2);
|
|
||||||
grid.Children.Add(button);
|
|
||||||
border.Child = grid;
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FoldButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Button button = sender as Button;
|
|
||||||
string tag = button.Tag.ToString();
|
|
||||||
if (tag == "fold")
|
|
||||||
{
|
|
||||||
button.Tag = "unfold";
|
|
||||||
Grid grid = button.Parent as Grid;
|
|
||||||
TextBox textBox = grid.Children[0] as TextBox;
|
|
||||||
textBox.Visibility = Visibility.Collapsed;
|
|
||||||
StackPanel stackPanel = button.Content as StackPanel;
|
|
||||||
Image unfold = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.unfold.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
stackPanel.Children.Clear();
|
|
||||||
stackPanel.Children.Add(unfold);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
button.Tag = "fold";
|
|
||||||
Image fold = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.fold.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
Grid grid = button.Parent as Grid;
|
|
||||||
TextBox textBox = grid.Children[0] as TextBox;
|
|
||||||
textBox.Visibility = Visibility.Visible;
|
|
||||||
StackPanel stackPanel = button.Content as StackPanel;
|
|
||||||
stackPanel.Children.Clear();
|
|
||||||
stackPanel.Children.Add(fold);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border GetUserChatBorder(MessageListItem msg)
|
|
||||||
{
|
|
||||||
Border border = new Border();
|
|
||||||
border.Margin = new Thickness(8, 12, 8, 12);
|
|
||||||
border.BorderThickness = new Thickness(0);
|
|
||||||
// border.Background = Brushes.DarkSeaGreen;
|
|
||||||
Grid grid = new Grid();
|
|
||||||
Image icon = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.user.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
|
||||||
};
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
|
|
||||||
TextBox textBox = new TextBox();
|
|
||||||
textBox.HorizontalAlignment = HorizontalAlignment.Right;
|
|
||||||
grid.Children.Add(icon);
|
|
||||||
grid.Children.Add(textBox);
|
|
||||||
Grid.SetColumn(icon, 1);
|
|
||||||
Grid.SetColumn(textBox, 0);
|
|
||||||
textBox.Background = Brushes.Transparent;
|
|
||||||
textBox.IsReadOnly = true;
|
|
||||||
textBox.BorderThickness = new Thickness(0);
|
|
||||||
textBox.Text = msg.content;
|
|
||||||
textBox.TextWrapping = TextWrapping.Wrap;
|
|
||||||
border.Child = grid;
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border GetToolChatBorder(MessageListItem msg)
|
|
||||||
{
|
|
||||||
Border border = new Border();
|
|
||||||
border.Margin = new Thickness(24);
|
|
||||||
border.Padding = new Thickness(8);
|
|
||||||
border.BorderThickness = new Thickness(1);
|
|
||||||
border.BorderBrush = Brushes.Gray;
|
|
||||||
border.CornerRadius = new CornerRadius(3);
|
|
||||||
// border.Background = Brushes.DarkSeaGreen;
|
|
||||||
Grid grid = new Grid();
|
|
||||||
Image icon = new Image()
|
|
||||||
{
|
|
||||||
Source = LocalResource.ReadImageByResource("LinkToolAddin.resource.img.tool.png"),
|
|
||||||
Stretch = Stretch.Fill,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center
|
|
||||||
};
|
|
||||||
icon.Margin = new Thickness(0, 0, 8, 0);
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(24, GridUnitType.Pixel)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(1, GridUnitType.Star)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(36, GridUnitType.Pixel)});
|
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition(){Width = new GridLength(36, GridUnitType.Pixel)});
|
|
||||||
TextBlock textBlock = new TextBlock();
|
TextBlock textBlock = new TextBlock();
|
||||||
ToolMessageItem toolMsg = msg as ToolMessageItem;
|
textBlock.Text = InputTextBox.Text;
|
||||||
textBlock.Text = toolMsg.toolName + " | " + toolMsg.status;
|
textBlock.TextWrapping = TextWrapping.Wrap; // 自动换行
|
||||||
textBlock.TextWrapping = TextWrapping.Wrap;
|
textBlock.VerticalAlignment = VerticalAlignment.Center;
|
||||||
grid.Children.Add(icon);
|
|
||||||
grid.Children.Add(textBlock);
|
// 创建 Border 作为消息气泡容器
|
||||||
Grid.SetColumn(icon, 0);
|
Border border = new Border();
|
||||||
Grid.SetColumn(textBlock, 1);
|
border.Child = textBlock;
|
||||||
Button argsButton = new Button();
|
border.CornerRadius = new CornerRadius(10);
|
||||||
argsButton.Content = "参数";
|
border.Padding = new Thickness(10);
|
||||||
argsButton.Tag = msg as ToolMessageItem;
|
border.Margin = new Thickness(5);
|
||||||
argsButton.Click += ToolArgsButton_OnClick;
|
|
||||||
border.Child = grid;
|
// 设置默认宽度限制
|
||||||
Button resButton = new Button();
|
border.MaxWidth = 300;
|
||||||
resButton.Content = "结果";
|
|
||||||
resButton.Tag = msg as ToolMessageItem;
|
if (messagelistItem.role == "user")
|
||||||
resButton.Click += ToolResButton_OnClick;
|
{
|
||||||
grid.Children.Add(argsButton);
|
// 浅蓝色背景,靠右
|
||||||
Grid.SetColumn(argsButton, 2);
|
border.Background = new SolidColorBrush(Color.FromRgb(212, 229, 239)); // 浅蓝
|
||||||
grid.Children.Add(resButton);
|
border.HorizontalAlignment = HorizontalAlignment.Right;
|
||||||
Grid.SetColumn(resButton, 3);
|
}
|
||||||
return border;
|
else if (messagelistItem.role == "assistant")
|
||||||
|
{
|
||||||
|
// 浅灰背景,靠左
|
||||||
|
border.Background = new SolidColorBrush(Color.FromRgb(240, 240, 240)); // 浅灰
|
||||||
|
border.HorizontalAlignment = HorizontalAlignment.Left;
|
||||||
|
|
||||||
|
if (messagelistItem.type == MessageType.TOOL_MESSAGE)
|
||||||
|
{
|
||||||
|
border.BorderBrush = new SolidColorBrush(Color.FromRgb(212, 220, 223)); // 灰蓝色边框
|
||||||
|
border.BorderThickness = new Thickness(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 把气泡添加到 MessageListView 显示
|
||||||
|
MessageListView.Items.Add(border);
|
||||||
|
|
||||||
|
// 自动滚动到底部
|
||||||
|
MessageScrollViewer.ScrollToEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToolArgsButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Button button = sender as Button;
|
|
||||||
ToolMessageItem toolItem = button.Tag as ToolMessageItem;
|
|
||||||
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(JsonConvert.SerializeObject(toolItem.toolParams),toolItem.toolName+"工具参数");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToolResButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Button button = sender as Button;
|
|
||||||
ToolMessageItem toolItem = button.Tag as ToolMessageItem;
|
|
||||||
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(toolItem.result,toolItem.toolName+"运行结果");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TestButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = "toolName",
|
|
||||||
toolParams = new Dictionary<string,object>(),
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "success",
|
|
||||||
content = "JsonConvert.SerializeObject(toolResponse)",
|
|
||||||
id = (timestamp + 1).ToString(),
|
|
||||||
role = "user"
|
|
||||||
};
|
|
||||||
NewMessage_Recall(toolMessageItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
idList.Clear();
|
|
||||||
messageDict.Clear();
|
|
||||||
borderItemsDict.Clear();
|
|
||||||
ChatHistoryStackPanel.Children.Clear();
|
|
||||||
QuestionTextbox.Clear();
|
|
||||||
StatusTextBlock.Text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TopButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ScrollViewer.ScrollToTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BottomButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ScrollViewer.ScrollToBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopButton_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Gateway.StopConversation();
|
|
||||||
StatusTextBlock.Text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ModelComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
log.Info("ModelComboBox_OnSelectionChanged");
|
|
||||||
string model = ModelComboBox.SelectedValue.ToString();
|
|
||||||
log.Info(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,6 @@ using ArcGIS.Desktop.Layouts;
|
|||||||
using ArcGIS.Desktop.Mapping;
|
using ArcGIS.Desktop.Mapping;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,6 +21,8 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Console;
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using LinkToolAddin.message;
|
||||||
|
|
||||||
namespace LinkToolAddin.ui.dockpane
|
namespace LinkToolAddin.ui.dockpane
|
||||||
{
|
{
|
||||||
@ -29,18 +30,17 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
{
|
{
|
||||||
private const string _dockPaneID = "DialogDockpane";
|
private const string _dockPaneID = "DialogDockpane";
|
||||||
|
|
||||||
public ObservableCollection<ItemModel> Items { get; set; }
|
private ObservableCollection<MessageListItem> _message = new ObservableCollection<MessageListItem>();
|
||||||
|
//ObservableCollection<T> 是 .NET 提供的一个特殊集合类,它会在集合发生变化时自动通知 UI 更新,它实现了 INotifyCollectionChanged 接口,UI 绑定后可以自动刷新。
|
||||||
protected DialogDockpaneViewModel()
|
public ObservableCollection<MessageListItem> Messages => _message;
|
||||||
{
|
//定义了一个只读属性 Messages,返回内部的 _messages 集合。
|
||||||
Items = new ObservableCollection<ItemModel>();
|
//该属性将被绑定到 UI 的 ItemsControl.ItemsSource 上,这样 UI 就能实时显示新消息。
|
||||||
Items.Add(new ItemModel(){Content = "adfdfdafdfs"});
|
protected DialogDockpaneViewModel() { }
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show the DockPane.
|
/// Show the DockPane.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static void Show()
|
internal static void Show()//显示面板
|
||||||
{
|
{
|
||||||
DockPane pane = FrameworkApplication.DockPaneManager.Find(_dockPaneID);
|
DockPane pane = FrameworkApplication.DockPaneManager.Find(_dockPaneID);
|
||||||
if (pane == null)
|
if (pane == null)
|
||||||
@ -52,18 +52,18 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text shown near the top of the DockPane.
|
/// Text shown near the top of the DockPane.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string _heading = "My DockPane";
|
private string _heading = "My DockPane";//标题栏文字
|
||||||
public string Heading
|
public string Heading
|
||||||
{
|
{
|
||||||
get => _heading;
|
get => _heading;//绑定到 UI 上的标题文字,默认是 "My DockPane"。
|
||||||
set => SetProperty(ref _heading, value);
|
set => SetProperty(ref _heading, value);//属性变更通知方法,用于支持数据绑定
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Button implementation to show the DockPane.
|
/// Button implementation to show the DockPane.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class DialogDockpane_ShowButton : Button
|
internal class DialogDockpane_ShowButton : Button//用于打开面板
|
||||||
{
|
{
|
||||||
protected override void OnClick()
|
protected override void OnClick()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -20,11 +20,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="24"/>
|
<RowDefinition Height="24"/>
|
||||||
<RowDefinition Height="24"/>
|
<RowDefinition Height="24"/>
|
||||||
<RowDefinition Height="480"/>
|
<RowDefinition Height="240"/>
|
||||||
<RowDefinition Height="24"/>
|
|
||||||
<RowDefinition Height="24"/>
|
|
||||||
<RowDefinition Height="24"/>
|
|
||||||
<RowDefinition Height="24"/>
|
|
||||||
<RowDefinition Height="24"/>
|
<RowDefinition Height="24"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button>
|
<Button Grid.Row="0" Content="Test Workflow" Name="TestServer" Click="TestWorkflow_OnClick"></Button>
|
||||||
@ -35,13 +31,9 @@
|
|||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBox Grid.Row="0" Grid.Column="0" ToolTip="输入测试提示词" Name="PromptTestTextBox"></TextBox>
|
<TextBox Grid.Row="0" Grid.Column="0" ToolTip="输入测试提示词" Name="PromptTestTextBox"></TextBox>
|
||||||
<Button Grid.Row="0" Grid.Column="1" Content="发送" Name="PromptTestButton" Click="PromptTestButton_OnClick"></Button>
|
<Button Grid.Row="0" Grid.Column="1" Content="测试" Name="PromptTestButton" Click="PromptTestButton_OnClick"></Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBox Grid.Row="2" ToolTip="大模型回复" Name="ReplyTextBox" TextWrapping="Wrap"></TextBox>
|
<TextBox Grid.Row="2" ToolTip="大模型回复" Name="ReplyTextBox" TextWrapping="Wrap"></TextBox>
|
||||||
<Button Grid.Row="3" Content="停止回答" Name="StopConversation" Click="StopConversation_OnClick"></Button>
|
<Button Grid.Row="3" Content="Test Stream" Name="TestStream" Click="TestStream_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>
|
|
||||||
<Button Grid.Row="7" Content="测试获取属性表" Name="TestAttrTable" Click="TestAttrTable_OnClick"></Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,21 +1,12 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Xml.Linq;
|
|
||||||
using LinkToolAddin.client;
|
using LinkToolAddin.client;
|
||||||
using LinkToolAddin.client.tool;
|
|
||||||
using LinkToolAddin.common;
|
|
||||||
using LinkToolAddin.host;
|
using LinkToolAddin.host;
|
||||||
using LinkToolAddin.host.llm;
|
using LinkToolAddin.host.llm;
|
||||||
using LinkToolAddin.host.llm.entity;
|
using LinkToolAddin.host.llm.entity;
|
||||||
using LinkToolAddin.host.mcp;
|
|
||||||
using LinkToolAddin.host.prompt;
|
|
||||||
using LinkToolAddin.message;
|
using LinkToolAddin.message;
|
||||||
using LinkToolAddin.resource;
|
using LinkToolAddin.resource;
|
||||||
using LinkToolAddin.server;
|
using LinkToolAddin.server;
|
||||||
@ -26,7 +17,6 @@ using log4net.Layout;
|
|||||||
using ModelContextProtocol.Client;
|
using ModelContextProtocol.Client;
|
||||||
using ModelContextProtocol.Protocol.Types;
|
using ModelContextProtocol.Protocol.Types;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using MessageBox = ArcGIS.Desktop.Framework.Dialogs.MessageBox;
|
|
||||||
|
|
||||||
|
|
||||||
namespace LinkToolAddin.ui.dockpane
|
namespace LinkToolAddin.ui.dockpane
|
||||||
@ -34,20 +24,20 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for TestDockpaneView.xaml
|
/// Interaction logic for TestDockpaneView.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class TestDockpaneView : UserControl
|
public partial class TestDockpaneView : UserControl//类1
|
||||||
{
|
{
|
||||||
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
|
private static ILog log = LogManager.GetLogger(typeof(TestDockpaneView));
|
||||||
|
|
||||||
private List<string> idList = new List<string>();
|
private List<string> idList = new List<string>();
|
||||||
private ConcurrentDictionary<string,MessageListItem> messageDict = new ConcurrentDictionary<string,MessageListItem>();
|
private Dictionary<string,MessageListItem> messageDict = new Dictionary<string,MessageListItem>();
|
||||||
|
|
||||||
public TestDockpaneView()
|
public TestDockpaneView()
|
||||||
{
|
{
|
||||||
InitLogger();
|
InitLogger();//初始化日志输出器1
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void InitLogger()
|
protected void InitLogger()//要复制代码(到哪去)1
|
||||||
{
|
{
|
||||||
// 1. 创建控制台输出器(Appender)
|
// 1. 创建控制台输出器(Appender)
|
||||||
var consoleAppender = new ConsoleAppender
|
var consoleAppender = new ConsoleAppender
|
||||||
@ -93,7 +83,7 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
ArcGISProMcpServer.TestMcpServer();
|
ArcGISProMcpServer.TestMcpServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void StdioMcp_test()
|
private async void StdioMcp_test()//标准输入输出的MCP测试,复制过去看能不能返回,有没有东西1
|
||||||
{
|
{
|
||||||
List<string> args = new List<string>();
|
List<string> args = new List<string>();
|
||||||
args.Add("mcp-server-time");
|
args.Add("mcp-server-time");
|
||||||
@ -106,7 +96,7 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
}
|
}
|
||||||
CallToolResponse response = await stdioMcpClient.CallToolAsync("get_current_time",
|
CallToolResponse response = await stdioMcpClient.CallToolAsync("get_current_time",
|
||||||
new Dictionary<string, object> { { "timezone", "America/New_York" } });
|
new Dictionary<string, object> { { "timezone", "America/New_York" } });
|
||||||
log.Info(JsonConvert.SerializeObject(response));
|
log.Info(JsonConvert.SerializeObject(response));//log看有没有东西和报错,实际调试看对应按钮控制台有没有东西1
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SseMcp_test()
|
private async void SseMcp_test()
|
||||||
@ -199,26 +189,14 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
log.Info(msg.content);
|
log.Info(msg.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
|
private void PromptTestButton_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string userPrompt = PromptTestTextBox.Text;
|
string userPrompt = PromptTestTextBox.Text;
|
||||||
// model可选值:qwen3-235b-a22b,qwen-max,deepseek-r1
|
// Gateway.SendMessage(userPrompt,"qwen-max","C:/Project/test.gdb",AddReply);
|
||||||
try
|
Gateway.SendMessageStream(userPrompt,"qwen-max","D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb",AddReplyStream);
|
||||||
{
|
|
||||||
await Task.Run(() => Gateway.SendMessageStream(userPrompt,"qwen3-235b-a22b", "F:\\secondsemester\\linktool\\test\\LinkTool0604\\LinkTool0604.gdb", AddReplyStream));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
log.Error(exception.Message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void AddReplyStream(MessageListItem msg)
|
public void AddReplyStream(MessageListItem msg)
|
||||||
{
|
|
||||||
await Task.Run(() => ProcessReplyStream(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessReplyStream(MessageListItem msg)
|
|
||||||
{
|
{
|
||||||
string id = msg.id;
|
string id = msg.id;
|
||||||
if (idList.Contains(id))
|
if (idList.Contains(id))
|
||||||
@ -228,31 +206,16 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
idList.Add(id);
|
idList.Add(id);
|
||||||
messageDict.TryAdd(msg.id, msg);
|
messageDict.Add(msg.id, msg);
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
foreach (string idStr in idList)
|
|
||||||
{
|
|
||||||
MessageListItem msgItem = messageDict[idStr];
|
|
||||||
string content = msgItem.content;
|
|
||||||
if (msgItem.type == MessageType.REASON_MESSAGE)
|
|
||||||
{
|
|
||||||
content = "<think>" + content + "</think>";
|
|
||||||
}
|
|
||||||
builder.AppendLine(content);
|
|
||||||
builder.AppendLine();
|
|
||||||
}
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
ReplyTextBox.Clear();
|
ReplyTextBox.Clear();
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
foreach (KeyValuePair<string,MessageListItem> pair in messageDict)
|
||||||
|
{
|
||||||
|
MessageListItem msgItem = pair.Value;
|
||||||
|
builder.AppendLine(msgItem.content);
|
||||||
ReplyTextBox.Text = builder.ToString();
|
ReplyTextBox.Text = builder.ToString();
|
||||||
ReplyTextBox.ScrollToEnd();
|
ReplyTextBox.ScrollToEnd();
|
||||||
});
|
|
||||||
}catch (Exception exception)
|
|
||||||
{
|
|
||||||
log.Error(exception.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,57 +231,5 @@ namespace LinkToolAddin.ui.dockpane
|
|||||||
{
|
{
|
||||||
Request_Bailian_Stream_Test();
|
Request_Bailian_Stream_Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopConversation_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Gateway.StopConversation();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
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;
|
|
||||||
McpServerList mcpServerList = new McpServerList();
|
|
||||||
McpServer mcpServer = mcpServerList.GetServer(serverName);
|
|
||||||
if (mcpServer is InnerMcpServer)
|
|
||||||
{
|
|
||||||
Type type = Type.GetType("LinkToolAddin.client.tool." + serverName);
|
|
||||||
var toolParamsValues = toolParams.Values.ToArray();
|
|
||||||
MethodInfo method = type.GetMethod(toolName, BindingFlags.Public | BindingFlags.Static);
|
|
||||||
var task = method.Invoke(null, toolParams.Values.ToArray()) as Task<JsonRpcResultEntity>;
|
|
||||||
JsonRpcResultEntity innerResult = await task;
|
|
||||||
MessageListItem toolMessageItem = new ToolMessageItem
|
|
||||||
{
|
|
||||||
toolName = toolName,
|
|
||||||
toolParams = toolParams,
|
|
||||||
type = MessageType.TOOL_MESSAGE,
|
|
||||||
status = "fail",
|
|
||||||
content = JsonConvert.SerializeObject(innerResult),
|
|
||||||
id = "1test"
|
|
||||||
};
|
|
||||||
AddReply(toolMessageItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TestResource_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
string content = LocalResource.ReadFileByResource("LinkToolAddin.resource.SystemPrompt.txt");
|
|
||||||
MessageBox.Show(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void TestAttrTable_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
JsonRpcResultEntity result = await ArcGisPro.GetFeatureDatasetAttributeTable(
|
|
||||||
"D:\\01_Project\\20250305_LinkTool\\20250420_AiDemoProject\\20250420_AiDemoProject.gdb",
|
|
||||||
"LandUse_2005_Copy", "30");
|
|
||||||
log.Info("finish");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,6 @@ public enum MessageType
|
|||||||
{
|
{
|
||||||
TOOL_MESSAGE,
|
TOOL_MESSAGE,
|
||||||
CHAT_MESSAGE,
|
CHAT_MESSAGE,
|
||||||
REASON_MESSAGE,
|
|
||||||
END_TAG,
|
|
||||||
WARNING,
|
|
||||||
ERROR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface MessageListItem
|
public interface MessageListItem
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user