动态提示词支持参数,格式与Tool对齐

This commit is contained in:
PeterZhong 2025-05-31 20:51:13 +08:00
parent 6f384920a3
commit d2b18958c1
10 changed files with 156 additions and 29 deletions

View File

@ -98,7 +98,6 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Folder Include="doc\" />
<Folder Include="resource\" />
</ItemGroup>
<ItemGroup>

View File

@ -1,29 +1,41 @@
using System.Collections.Generic;
using LinkToolAddin.host;
using LinkToolAddin.host.prompt;
namespace LinkToolAddin.client.prompt;
public class DynamicPrompt
{
public static string GetPrompt(string name,Dictionary<string,object> args = null)
public static string GetPrompt(string name,Dictionary<string,string> args = null)
{
PromptTemplates promptTemplate = new PromptTemplates();
string template = promptTemplate.GetPrompt(name);
PromptServerList promptServerList = new PromptServerList();
string template = promptServerList.GetPromptServer(name).Content;
if (args == null)
{
return template;
}
foreach (KeyValuePair<string,object> pair in args)
foreach (KeyValuePair<string,string> pair in args)
{
string replaceKey = "{{"+pair.Key+"}}";
template.Replace(replaceKey, pair.Value.ToString());
template = template.Replace(replaceKey, pair.Value.ToString());
}
return template;
}
public static Dictionary<string, string> GetAllPrompts()
public static List<UserPrompt> GetAllPrompts()
{
PromptTemplates promptTemplate = new PromptTemplates();
Dictionary<string, string> template = promptTemplate.GetPromptsDict();
return template;
PromptServerList promptServerList = new PromptServerList();
List<UserPrompt> prompts = new List<UserPrompt>();
Dictionary<string, PromptServer> promptDefinitions = promptServerList.GetPromptsDict();
foreach (KeyValuePair<string, PromptServer> pair in promptDefinitions)
{
prompts.Add(new UserPrompt()
{
Name = pair.Value.Name,
Description = pair.Value.Description,
Arguments = pair.Value.Arguments
});
}
return prompts;
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using LinkToolAddin.host.prompt;
namespace LinkToolAddin.client.prompt;

15
doc/TodoList.md Normal file
View File

@ -0,0 +1,15 @@
# 待办事项
本文档用于记录和跟踪当前阶段待办事项及完成进度
## 核心程序
- [ ] 提示词调用完整实现:以面向对象形式取代字典形式,加入参数和描述
- [ ] 接入本地文件系统等基础性MCP服务
- [ ] Python代码执行的实现
- [ ] 适配qwen3、deepseek等推理模型并迁移
- [ ] 工具执行错误消息合并为一条
## 提示词工程
## 前端交互

View File

@ -204,7 +204,7 @@ public class Gateway
Dictionary<string, object> promptParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(promptArgs);
string serverName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[0] : fullPromptName;
string promptName = fullPromptName.Contains(":") ? fullPromptName.Split(':')[1] : fullPromptName;
string promptRes = DynamicPrompt.GetPrompt(promptName, promptParams);
string promptRes = DynamicPrompt.GetPrompt(promptName, null);
messages.Add(new Message
{
Role = "user",
@ -271,8 +271,9 @@ public class Gateway
});
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]*?)<\\/prompt>";
string promptPattern = "<prompt>([\\s\\S]*?)<name>([\\s\\S]*?)<\\/name>([\\s\\S]*?)<arguments>([\\s\\S]*?)<\\/arguments>([\\s\\S]*?)<\\/prompt>";
McpServerList mcpServerList = new McpServerList();
PromptServerList promptServerList = new PromptServerList();
int loop = 0;
string messageContent = ""; //一次请求下完整的response
while (goOn)
@ -293,7 +294,7 @@ public class Gateway
};
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
List<McpToolRequest> mcpToolRequests = new List<McpToolRequest>();
List<string> promptKeys = new List<string>();
List<PromptRequest> promptRequests = new List<PromptRequest>();
var (toolMatched, toolRemaining) = ExtractMatchedPart(messageContent, toolPattern);
var (promptMatched, promptRemaining) = ExtractMatchedPart(messageContent, promptPattern);
if (toolMatched == "" && promptMatched == "" && messageContent != "")
@ -338,7 +339,15 @@ public class Gateway
callback?.Invoke(chatMessageListItem);
XElement promptUse = XElement.Parse(matchedPrompt);
string promptKey = promptUse.Element("name")?.Value;
promptKeys.Add(promptKey);
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
@ -489,9 +498,9 @@ public class Gateway
}
}
/*统一处理本次请求中的Prompt调用需求*/
foreach (string promptKey in promptKeys)
foreach (PromptRequest promptRequest in promptRequests)
{
string promptContent = DynamicPrompt.GetPrompt(promptKey);
string promptContent = DynamicPrompt.GetPrompt(promptRequest.PromptName, promptRequest.PromptArgs);
messages.Add(new Message
{
Role = "user",
@ -503,7 +512,7 @@ public class Gateway
toolParams = null,
type = MessageType.TOOL_MESSAGE,
status = "success",
content = "成功调用提示词:"+promptKey,
content = "成功调用提示词:"+promptRequest.PromptName,
id = (timestamp+1).ToString()
};
callback?.Invoke(toolMessageItem);
@ -588,17 +597,10 @@ public class Gateway
}
}
Dictionary<string, string> prompts = DynamicPrompt.GetAllPrompts();
foreach (KeyValuePair<string, string> prompt in prompts)
List<UserPrompt> prompts = DynamicPrompt.GetAllPrompts();
foreach (UserPrompt userPrompt in prompts)
{
McpPromptDefinition promptDefinition = new McpPromptDefinition
{
Prompt = new LinkToolAddin.host.mcp.Prompt
{
Name = prompt.Key
}
};
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(promptDefinition));
XNode node = JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(new PromptDefinition(){UserPrompt = userPrompt}));
toolInfos.AppendLine(node.ToString());
toolInfos.AppendLine();
}

40
host/PromptServerList.cs Normal file
View File

@ -0,0 +1,40 @@
using System.Collections.Generic;
using LinkToolAddin.host.prompt;
namespace LinkToolAddin.host;
public class PromptServerList
{
Dictionary<string, PromptServer> promptServers = new Dictionary<string, PromptServer>();
public PromptServerList()
{
promptServers.Add("plan", new PromptServer("plan",
"根据用户描述的问题推断出需要使用的ArcGIS Pro工具调用名称列表",
"请根据用户所提问题进行工具规划,输出格式为'1.工具2.工具如果是ArcGIS Pro的工具根据用户的具体需求和提供的数据类型," +
"判断并列出所有必要的分析步骤和工具同时严格遵守知识库中“ArcGIS Pro工具调用大全”里“工具调用名称”一列的工具命名规则(例如analysis.Erase)" +
"确保工具名的准确无误,工具与所属工具箱的对应关系正确无误,严格遵循文档规定的格式和大小写。工具的组合顺序优先参考知识库中“ArcGIS Pro的帮助文档”。" +
"有一些与分析无关的数据能够进行排除,在选择工具时不受其干扰。"));
promptServers.Add("param", new PromptServer("param",
"填写ArcGIS Pro工具调用参数生成规范的可执行的工具调用请求",
"根据帮助文档填写工具参数请你根据“所需调用工具”参照知识库“ArcGIS Pro工具调用大全”里工具所需的参数顺序进行陈列。" +
"列出所需调用工具的名称及其按照“ArcGIS Pro工具调用大全”里“的该工具所需的参数顺序陈列对应的参数。如果跳过了可选参数需要用空字符表示。" +
"不能更改所需调用工具的名称。例如arcpy.analysis.Buffer不能只写成Buffer。确保格式、参数的完整性和准确性避免任何遗漏或错误。" +
"特别注意,所有参数均应视为字符串类型,即使它们可能代表数字或文件路径。例如问题为:使用地理处理中的\"擦除分析\"工具Erase将圆形要素circle.shp与方形要素square.shp进行空间叠加运算。" +
"输出: \"in_features\":\"circle.shp\",\r\n \"erase_features\":\"sqaure.shp\",\r\n \"out_feature_class\":\"res.shp\",\r\n \"cluster_tolerance\":\"1\""));
promptServers.Add("code", new PromptServer("code",
"生成可运行的arcpy代码",
"根据你在多种编程语言、框架、设计模式和最佳实践方面拥有的广泛知识。现在需要根据用户需求生成高质量的代码,并确保语法正确。" +
"编写Arcpy代码时必须符合ArcGIS官方文档要求。参考官方文档的方法参数,确保编写正确。"));
}
public Dictionary<string, PromptServer> GetPromptsDict()
{
return promptServers;
}
public PromptServer GetPromptServer(string key)
{
return promptServers[key];
}
}

View File

@ -0,0 +1,9 @@
using Newtonsoft.Json;
namespace LinkToolAddin.host.prompt;
public class PromptDefinition
{
[JsonProperty("prompt")]
public UserPrompt UserPrompt { get; set; }
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace LinkToolAddin.host.prompt;
public class PromptRequest
{
public PromptServer PromptServer { get; set; }
public string PromptName { get; set; }
public Dictionary<string, string> PromptArgs { get; set; }
}

View File

@ -0,0 +1,32 @@
using System.Text;
using System.Text.RegularExpressions;
namespace LinkToolAddin.host.prompt;
public class PromptServer
{
public string Name { get; set; }
public string Description { get; set; }
public string Arguments { get; set; }
public string Content { get; set; }
public PromptServer(string name, string description, string content)
{
Name = name;
Description = description;
Content = content;
string pattern = "{{([\\s\\S]*?)}}";
var matches = Regex.Matches(content, pattern);
StringBuilder args = new StringBuilder();
foreach (var match in matches)
{
string variableName = match.ToString().Replace("{{","").Replace("}}","");
string arg = "{ \"" + variableName + "\" : \"type\" : \" string \" }";
args.Append(arg);
}
if (args.ToString() != string.Empty)
{
Arguments = "{\"type\":\"object\",\"properties\":" + args.ToString() + "}";
}
}
}

View File

@ -1,6 +1,13 @@
namespace LinkToolAddin.host.prompt;
using Newtonsoft.Json;
namespace LinkToolAddin.host.prompt;
public class UserPrompt
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("arguments")]
public string Arguments { get; set; }
}