context:通义万相文生图节点功能以及发送邮箱和HTTP请求节点调研

This commit is contained in:
zengxb
2026-02-24 10:34:26 +08:00
parent b40805cb57
commit 0a115f289e
14 changed files with 476 additions and 57 deletions

View File

@@ -0,0 +1,23 @@
package org.ruoyi.enums;
import lombok.Getter;
/**
* 文生图模型分类
*
* @author Zengxb
* @date 2026-02-14
*/
@Getter
public enum ImageModeType {
TONGYI_WANX("Tongyiwanx", "万相");
private final String code;
private final String description;
ImageModeType(String code, String description) {
this.code = code;
this.description = description;
}
}

View File

@@ -1,6 +1,5 @@
package org.ruoyi.service.chat.impl.provider;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.supervisor.SupervisorAgent;
import dev.langchain4j.agentic.supervisor.SupervisorResponseStrategy;
@@ -30,6 +29,8 @@ import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* qianWenAI服务调用
@@ -44,14 +45,20 @@ public class QianWenChatServiceImpl extends AbstractStreamingChatService {
@Autowired
private McpSseConfig mcpSseConfig;
/**
* 千问开发者默认地址
*/
private static final String QWEN_API_HOST = "https://dashscope.aliyuncs.com/api/v1";
// 添加文档解析的前缀字段
private static final String UPLOAD_FILE_API_PREFIX = "fileid";
// 缓存不同API Key和模型的MCP智能体实例
private final ConcurrentHashMap<String, SupervisorAgent> supervisorCache = new ConcurrentHashMap<>();
// 缓存不同API Key和模型的MCP客户端实例
private final ConcurrentHashMap<String, McpClient> mcpClientCache = new ConcurrentHashMap<>();
// 缓存不同API Key和模型的MCP工具提供者实例
private final ConcurrentHashMap<String, ToolProvider> toolProviderCache = new ConcurrentHashMap<>();
// 用于线程安全的锁
private final ReentrantLock cacheLock = new ReentrantLock();
@Override
protected StreamingChatModel buildStreamingChatModel(ChatModelVo chatModelVo,ChatRequest chatRequest) {
return QwenStreamingChatModel.builder()
@@ -102,17 +109,16 @@ public class QianWenChatServiceImpl extends AbstractStreamingChatService {
}
/**
* 调用MCP服务智能体
* @param userMessage 用户信息
* @param chatModelVo 模型信息
* @return 返回LLM信息
* 获取缓存键
*/
protected String doAgent(String userMessage,ChatModelVo chatModelVo) {
// 判断是否开启MCP服务
if (!mcpSseConfig.isEnabled()) {
return "";
}
private String getCacheKey(ChatModelVo chatModelVo) {
return chatModelVo.getApiKey() + ":" + chatModelVo.getModelName();
}
/**
* 初始化MCP客户端连接
*/
private McpClient initializeMcpClient() {
// 步骤1根据SSE对外暴露端点连接
McpTransport httpMcpTransport = new StreamableHttpMcpTransport.Builder().
url(mcpSseConfig.getUrl()).
@@ -120,42 +126,74 @@ public class QianWenChatServiceImpl extends AbstractStreamingChatService {
build();
// 步骤2开启客户端连接
McpClient mcpClient = new DefaultMcpClient.Builder()
return new DefaultMcpClient.Builder()
.transport(httpMcpTransport)
.build();
}
// 获取所有mcp工具
List<ToolSpecification> toolSpecifications = mcpClient.listTools();
System.out.println(toolSpecifications);
/**
* 调用MCP服务智能体
* @param userMessage 用户信息
* @param chatModelVo 模型信息
* @return 返回LLM信息
*/
protected String doAgent(String userMessage, ChatModelVo chatModelVo) {
// 判断是否开启MCP服务
if (!mcpSseConfig.isEnabled()) {
return "";
}
// 生成缓存键
String cacheKey = getCacheKey(chatModelVo);
// 尝试从缓存获取监督智能体
SupervisorAgent cachedSupervisor = supervisorCache.get(cacheKey);
if (cachedSupervisor != null) {
// 如果已存在缓存的监督智能体,直接使用
return cachedSupervisor.invoke(userMessage);
}
cacheLock.lock();
try {
// 双重检查,防止并发情况下的重复初始化
cachedSupervisor = supervisorCache.get(cacheKey);
if (cachedSupervisor != null) {
return cachedSupervisor.invoke(userMessage);
}
// 步骤3将mcp对象包装
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
// 获取或初始化MCP客户端
McpClient mcpClient = mcpClientCache.computeIfAbsent(cacheKey, k -> initializeMcpClient());
// 步骤4加载LLM模型对话
QwenChatModel qwenChatModel = QwenChatModel.builder()
.baseUrl(QWEN_API_HOST)
.apiKey(chatModelVo.getApiKey())
.modelName(chatModelVo.getModelName())
.build();
// 步骤3将mcp对象包装
ToolProvider toolProvider = toolProviderCache.computeIfAbsent(cacheKey, k -> McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build());
// 步骤5将MCP对象由智能体Agent管控
McpAgent mcpAgent = AgenticServices.agentBuilder(McpAgent.class)
.chatModel(qwenChatModel)
.toolProvider(toolProvider)
.build();
// 步骤4加载LLM模型对话
QwenChatModel qwenChatModel = QwenChatModel.builder()
.apiKey(chatModelVo.getApiKey())
.modelName(chatModelVo.getModelName())
.build();
// 步骤6:将所有MCP对象由超级智能体管控
SupervisorAgent supervisor = AgenticServices
.supervisorBuilder()
.chatModel(qwenChatModel)
.subAgents(mcpAgent)
.responseStrategy(SupervisorResponseStrategy.LAST)
.build();
// 步骤5将MCP对象由智能体Agent管控
McpAgent mcpAgent = AgenticServices.agentBuilder(McpAgent.class)
.chatModel(qwenChatModel)
.toolProvider(toolProvider)
.build();
// 步骤7调用大模型LLM
return supervisor.invoke(userMessage);
// 步骤6将所有MCP对象由超级智能体管控
SupervisorAgent supervisor = AgenticServices
.supervisorBuilder()
.chatModel(qwenChatModel)
.subAgents(mcpAgent)
.responseStrategy(SupervisorResponseStrategy.LAST)
.build();
// 缓存监督智能体
supervisorCache.put(cacheKey, supervisor);
// 步骤7调用大模型LLM
return supervisor.invoke(userMessage);
} finally {
cacheLock.unlock();
}
}
@Override

View File

@@ -0,0 +1,43 @@
package org.ruoyi.service.image;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.chat.Service.IImageGenerationService;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import org.ruoyi.common.chat.domain.entity.image.ImageContext;
import org.springframework.validation.annotation.Validated;
@Slf4j
@Validated
public abstract class AbstractImageGenerationService implements IImageGenerationService {
/**
* 根据文字生成图片
* @param imageContext 文生图上下文对象
* @return 生成的图片URL
*/
@Override
public String generateImage(ImageContext imageContext){
// 获取模型管理视图对象
ChatModelVo chatModelVo = imageContext.getChatModelVo();
// 获取提示词
String prompt = imageContext.getPrompt();
// 获取图片尺寸大小
String size = imageContext.getSize();
// 获取随机数种子
Integer seed = imageContext.getSeed();
return doGenerateImage(chatModelVo, prompt, size, seed);
}
/**
* 执行生成图片(钩子方法 - 子类必须实现)
*
* @param prompt 提示词
*/
protected abstract String doGenerateImage(ChatModelVo chatModelVo, String prompt, String size, Integer seed);
/**
* 构建具体厂商的 ImageModel原生SDK 非langchain4j-dashscope版
* 子类必须实现此方法,返回对应厂商的模型实例
*/
protected abstract Object buildImageModel(ChatModelVo chatModelVo);
}

View File

@@ -0,0 +1,73 @@
package org.ruoyi.service.image.provider;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.enums.ImageModeType;
import org.ruoyi.service.image.AbstractImageGenerationService;
import org.springframework.stereotype.Service;
/**
* 万相文生图AI调用
*
* @author Zengxb
* @date 2026/02/14
*/
@Service
@Slf4j
public class TongYiWanxImageServiceImpl extends AbstractImageGenerationService {
/**
* 默认图片数量1张
*/
private final static int IMAGE_DEFAULT_SIZE = 1;
/**
* 默认图片分辨率1280*1280
*/
private final static String IMAGE_DEFAULT_RESOLUTION = "1280*1280";
@Override
protected String doGenerateImage(ChatModelVo chatModelVo, String prompt, String size, Integer seed) {
// 构建万相模型对象
var param = (ImageSynthesisParam) buildImageModel(chatModelVo);
// 设置图片大小和提示词以及随机数种子
param.setSize(StringUtils.isEmpty(size) ? IMAGE_DEFAULT_RESOLUTION : size);
param.setPrompt(prompt);
param.setSeed(seed);
// 同步调用 AI 大模型,生成图片
var imageSynthesis = new ImageSynthesis();
ImageSynthesisResult result;
try {
log.info("同步调用通义万相文生图接口中....");
result = imageSynthesis.call(param);
} catch (ApiException | NoApiKeyException e) {
log.error("同步调用通义万相文生图接口失败", e);
return "";
}
// 直接提取图片URL
var output = result.getOutput();
var results = output.getResults();
return results.isEmpty() ? "" : results.get(0).get("url");
}
@Override
protected Object buildImageModel(ChatModelVo chatModelVo) {
return ImageSynthesisParam.builder()
.prompt("")
.apiKey(chatModelVo.getApiKey())
.model(chatModelVo.getModelName())
.n(IMAGE_DEFAULT_SIZE)
.build();
}
@Override
public String getProviderName() {
return ImageModeType.TONGYI_WANX.getCode();
}
}