Merge pull request #1787 from xinnan-tech/system-prompt

强化小智提示词
This commit is contained in:
hrz
2025-07-08 17:00:46 +08:00
committed by GitHub
34 changed files with 1200 additions and 272 deletions
@@ -237,7 +237,7 @@ public interface Constant {
/**
* 版本号
*/
public static final String VERSION = "0.6.2";
public static final String VERSION = "0.6.3";
/**
* 无效固件URL
@@ -61,14 +61,15 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
wsUrl = wsUrl.replace("/mcp/", "/call/");
try {
// 创建 WebSocket 连接
// 创建 WebSocket 连接,增加超时时间到15秒
try (WebSocketClientManager client = WebSocketClientManager.build(
new WebSocketClientManager.Builder()
.uri(wsUrl)
.connectTimeout(5, TimeUnit.SECONDS)
.maxSessionDuration(9, TimeUnit.SECONDS))) {
.connectTimeout(8, TimeUnit.SECONDS)
.maxSessionDuration(10, TimeUnit.SECONDS))) {
// 发送初始化消息
// 步骤1: 发送初始化消息并等待响应
log.info("发送MCP初始化消息,智能体ID: {}", id);
McpJsonRpcRequest initializeRequest = new McpJsonRpcRequest("initialize",
Map.of(
"protocolVersion", "2024-11-05",
@@ -81,37 +82,70 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
1);
client.sendJson(initializeRequest);
// 等待初始化响应
Thread.sleep(200);
// 发送初始化完成通知
// 对于通知类型的消息,手动构建JSON以避免包含null字段
String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
client.sendText(notificationJson);
// 等待 0.2 秒
Thread.sleep(200);
// 发送工具列表请求
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
client.sendJson(toolsRequest);
// 监听响应,直到收到包含 id=2 的响应(tools/list响应)
List<String> responses = client.listener(response -> {
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
List<String> initResponses = client.listenerWithoutClose(response -> {
try {
// 先尝试解析为通用JSON对象来获取id
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
// 检查是否有result字段,表示初始化成功
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
}
return false;
} catch (Exception e) {
log.warn("解析响应失败: {}", response, e);
log.warn("解析初始化响应失败: {}", response, e);
return false;
}
});
// 处理响应
for (String response : responses) {
// 验证初始化响应
boolean initSucceeded = false;
for (String response : initResponses) {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
if (jsonMap.containsKey("result")) {
log.info("MCP初始化成功,智能体ID: {}", id);
initSucceeded = true;
break;
} else if (jsonMap.containsKey("error")) {
log.error("MCP初始化失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
log.warn("处理初始化响应失败: {}", response, e);
}
}
if (!initSucceeded) {
log.error("未收到有效的MCP初始化响应,智能体ID: {}", id);
return List.of();
}
// 步骤2: 发送初始化完成通知 - 只有在收到initialize响应后才发送
log.info("发送MCP初始化完成通知,智能体ID: {}", id);
String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
client.sendText(notificationJson);
// 步骤3: 发送工具列表请求 - 立即发送,无需额外延迟
log.info("发送MCP工具列表请求,智能体ID: {}", id);
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
client.sendJson(toolsRequest);
// 等待工具列表响应 (id=2)
List<String> toolsResponses = client.listener(response -> {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
} catch (Exception e) {
log.warn("解析工具列表响应失败: {}", response, e);
return false;
}
});
// 处理工具列表响应
for (String response : toolsResponses) {
try {
// 先解析为通用JSON对象
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
// 检查是否有result字段
@@ -122,11 +156,16 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
if (toolsObj instanceof List) {
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
// 提取工具名称列表
return toolsList.stream()
List<String> result = toolsList.stream()
.map(tool -> (String) tool.get("name"))
.filter(name -> name != null)
.collect(Collectors.toList());
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
return result;
}
} else if (jsonMap.containsKey("error")) {
log.error("获取工具列表失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
@@ -134,7 +173,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
}
}
log.warn("未找到有效的工具列表响应");
log.warn("未找到有效的工具列表响应,智能体ID: {}", id);
return List.of();
}
@@ -204,4 +243,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 加密后成token值
return AESUtils.encrypt(key, json);
}
}
}
@@ -258,17 +258,17 @@ public class ConfigServiceImpl implements ConfigService {
/**
* 构建模块配置
*
* @param prompt 提示词
* @param voice 音色
* @param referenceAudio 参考音频路径
* @param referenceText 参考文本
* @param vadModelId VAD模型ID
* @param asrModelId ASR模型ID
* @param llmModelId LLM模型ID
* @param ttsModelId TTS模型ID
* @param memModelId 记忆模型ID
* @param intentModelId 意图模型ID
* @param result 结果Map
* @param prompt 提示词
* @param voice 音色
* @param referenceAudio 参考音频路径
* @param referenceText 参考文本
* @param vadModelId VAD模型ID
* @param asrModelId ASR模型ID
* @param llmModelId LLM模型ID
* @param ttsModelId TTS模型ID
* @param memModelId 记忆模型ID
* @param intentModelId 意图模型ID
* @param result 结果Map
*/
private void buildModuleConfig(
String assistantName,
@@ -298,14 +298,20 @@ public class ConfigServiceImpl implements ConfigService {
continue;
}
ModelConfigEntity model = modelConfigService.getModelById(modelIds[i], isCache);
if (model == null) {
continue;
}
Map<String, Object> typeConfig = new HashMap<>();
if (model.getConfigJson() != null) {
typeConfig.put(model.getId(), model.getConfigJson());
// 如果是TTS类型,添加private_voice属性
if ("TTS".equals(modelTypes[i])){
if (voice != null) ((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
if (referenceAudio != null) ((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
if (referenceText != null) ((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
if ("TTS".equals(modelTypes[i])) {
if (voice != null)
((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
if (referenceAudio != null)
((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
if (referenceText != null)
((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
}
// 如果是Intent类型,且type=intent_llm,则给他添加附加模型
if ("Intent".equals(modelTypes[i])) {
@@ -51,13 +51,12 @@ public class OTAController {
if (StringUtils.isBlank(clientId)) {
clientId = deviceId;
}
String macAddress = deviceReportReqDTO.getMacAddress();
boolean macAddressValid = isMacAddressValid(macAddress);
boolean macAddressValid = isMacAddressValid(deviceId);
// 设备Id和Mac地址应是一致的, 并且必须需要application字段
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
if (!macAddressValid) {
return createResponse(DeviceReportRespDTO.createError("Invalid device ID"));
}
return createResponse(deviceService.checkDeviceActive(macAddress, clientId, deviceReportReqDTO));
return createResponse(deviceService.checkDeviceActive(deviceId, clientId, deviceReportReqDTO));
}
@Operation(summary = "设备快速检查激活状态")
@@ -222,7 +222,7 @@ public class SysParamsController {
if (response.getStatusCode() != HttpStatus.OK) {
throw new RenException("MCP接口访问失败,状态码:" + response.getStatusCode());
}
// 检查响应内容是否包含OTA相关信息
// 检查响应内容是否包含mcp相关信息
String body = response.getBody();
if (body == null || !body.contains("success")) {
throw new RenException("MCP接口返回内容格式不正确,可能不是一个真实的MCP接口");
@@ -137,6 +137,37 @@ public class WebSocketClientManager implements Closeable {
return collected;
}
private <T> List<T> listenerCustomWithoutClose(
BlockingQueue<T> queue,
Predicate<T> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
List<T> collected = new ArrayList<>();
long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
while (true) {
if (errorFuture.isDone()) {
errorFuture.get();
}
long remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
throw new TimeoutException("等待批量消息超时");
}
T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
if (msg == null) {
throw new TimeoutException("等待批量消息超时");
}
collected.add(msg);
if (predicate.test(msg)) {
break;
}
}
// 不调用 close(),保持连接开放
return collected;
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
*
@@ -147,6 +178,17 @@ public class WebSocketClientManager implements Closeable {
return listenerCustom(textMessageQueue, predicate);
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
* 不自动关闭连接,适用于需要在同一连接上发送多个消息的场景
*
* @return 返回监听期间的所有消息列表
*/
public List<String> listenerWithoutClose(Predicate<String> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustomWithoutClose(textMessageQueue, predicate);
}
public List<byte[]> listenerBinary(Predicate<byte[]> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustom(binaryMessageQueue, predicate);