feat: 增强智能体模型调用兼容能力
- 增加模型流式开关和 HTTP 传输策略配置 - 使用 AgentScope 执行基础连接、流式与 VLM 双阶段验证 - 固定多模态校验图片并统一验证状态展示
This commit is contained in:
@@ -4,8 +4,6 @@ package tech.easyflow.ai.service.impl;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alicp.jetcache.Cache;
|
||||
import com.easyagents.core.document.Document;
|
||||
import com.easyagents.core.model.chat.ChatModel;
|
||||
import com.easyagents.core.model.chat.ChatOptions;
|
||||
import com.easyagents.core.model.embedding.EmbeddingModel;
|
||||
import com.easyagents.core.model.rerank.RerankModel;
|
||||
import com.easyagents.core.store.VectorData;
|
||||
@@ -23,6 +21,7 @@ import tech.easyflow.ai.entity.ModelProvider;
|
||||
import tech.easyflow.ai.mapper.ModelMapper;
|
||||
import tech.easyflow.ai.service.ModelProviderService;
|
||||
import tech.easyflow.ai.service.ModelService;
|
||||
import tech.easyflow.ai.service.verification.ChatModelConnectivityVerifier;
|
||||
import tech.easyflow.common.tree.Tree;
|
||||
import tech.easyflow.common.util.SqlOperatorsUtil;
|
||||
import tech.easyflow.common.util.SqlUtil;
|
||||
@@ -51,6 +50,10 @@ public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements
|
||||
@Resource
|
||||
private Cache<String, Object> cache;
|
||||
|
||||
/** 与智能体运行时同链路的 Chat Model 连通性验证器。 */
|
||||
@Autowired(required = false)
|
||||
private ChatModelConnectivityVerifier chatModelConnectivityVerifier;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean addAiLlm(Model entity) {
|
||||
@@ -69,8 +72,7 @@ public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements
|
||||
Map<String, Object> resMap = new HashMap<>();
|
||||
// 走聊天验证逻辑
|
||||
if (Model.MODEL_TYPES[0].equals(modelType)) {
|
||||
verifyChatLlm(model);
|
||||
return null;
|
||||
return verifyChatLlm(model);
|
||||
}
|
||||
// 走向量化验证逻辑
|
||||
if (Model.MODEL_TYPES[1].equals(modelType)) {
|
||||
@@ -154,25 +156,17 @@ public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyChatLlm(Model llm) {
|
||||
|
||||
ChatModel chatModel = llm.toChatModel();
|
||||
if (chatModel == null) {
|
||||
throw new BusinessException("chatModel为空");
|
||||
/**
|
||||
* 使用智能体同链路验证 Chat Model 或 VLM。
|
||||
*
|
||||
* @param model 已补齐供应商默认配置的模型
|
||||
* @return 结构化双阶段验证结果
|
||||
*/
|
||||
private Map<String, Object> verifyChatLlm(Model model) {
|
||||
if (chatModelConnectivityVerifier == null) {
|
||||
throw new BusinessException("Agent 模型连通性验证组件未加载");
|
||||
}
|
||||
try {
|
||||
ChatOptions options=new ChatOptions();
|
||||
options.setThinkingEnabled(false);
|
||||
String response = chatModel.chat("我在对模型配置进行校验,你收到这条消息无需做任何思考,直接回复一个“你好”即可!",options);
|
||||
if (response == null) {
|
||||
throw new BusinessException("校验未通过,请前往后端日志查看详情!");
|
||||
}
|
||||
log.info("校验结果:{}", response);
|
||||
} catch (Exception e) {
|
||||
log.error("校验失败:{}", e.getMessage());
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
|
||||
return chatModelConnectivityVerifier.verify(model).toMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package tech.easyflow.ai.service.support;
|
||||
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* 提供模型连接验证专用的固定 PNG 图片。
|
||||
*/
|
||||
public final class VlmVerificationImage {
|
||||
|
||||
/** 模型需要识别并返回的固定数字。 */
|
||||
public static final String VERIFICATION_CODE = "5839";
|
||||
|
||||
/** 固定验证图片的 classpath 路径。 */
|
||||
private static final String RESOURCE_PATH = "/images/vlm-verification.png";
|
||||
/** PNG 文件签名字节。 */
|
||||
private static final byte[] PNG_SIGNATURE = {
|
||||
(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
|
||||
};
|
||||
/** 启动后复用的固定 PNG 字节。 */
|
||||
private static final byte[] PNG_BYTES = loadPng();
|
||||
|
||||
/**
|
||||
* 禁止实例化工具类。
|
||||
*/
|
||||
private VlmVerificationImage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回固定连接验证 PNG 的字节副本。
|
||||
*
|
||||
* @return PNG 字节副本
|
||||
*/
|
||||
public static byte[] pngBytes() {
|
||||
return PNG_BYTES.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 classpath 加载固定验证图片。
|
||||
*
|
||||
* @return PNG 文件字节
|
||||
* @throws BusinessException 资源缺失、读取失败或文件格式非法时抛出
|
||||
*/
|
||||
private static byte[] loadPng() {
|
||||
try (InputStream input = VlmVerificationImage.class.getResourceAsStream(RESOURCE_PATH)) {
|
||||
if (input == null) {
|
||||
throw new BusinessException("VLM 校验图片资源不存在:" + RESOURCE_PATH);
|
||||
}
|
||||
byte[] bytes = input.readAllBytes();
|
||||
if (!hasPngSignature(bytes)) {
|
||||
throw new BusinessException("VLM 校验图片资源不是有效的 PNG 文件");
|
||||
}
|
||||
return bytes;
|
||||
} catch (IOException error) {
|
||||
throw new BusinessException("VLM 校验图片读取失败:" + safeMessage(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否包含标准 PNG 签名。
|
||||
*
|
||||
* @param bytes 待检查文件字节
|
||||
* @return 包含完整 PNG 签名时返回 true
|
||||
*/
|
||||
private static boolean hasPngSignature(byte[] bytes) {
|
||||
if (bytes == null || bytes.length < PNG_SIGNATURE.length) {
|
||||
return false;
|
||||
}
|
||||
for (int index = 0; index < PNG_SIGNATURE.length; index++) {
|
||||
if (bytes[index] != PNG_SIGNATURE[index]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取非空异常描述。
|
||||
*
|
||||
* @param error 原始异常
|
||||
* @return 可展示的异常描述
|
||||
*/
|
||||
private static String safeMessage(Exception error) {
|
||||
return error.getMessage() == null || error.getMessage().isBlank()
|
||||
? "未知错误"
|
||||
: error.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package tech.easyflow.ai.service.verification;
|
||||
|
||||
import tech.easyflow.ai.entity.Model;
|
||||
|
||||
/**
|
||||
* Chat Model 与 VLM 的运行时同链路连通性验证器。
|
||||
*/
|
||||
public interface ChatModelConnectivityVerifier {
|
||||
|
||||
/**
|
||||
* 验证模型的非流式基础连接与流式响应能力。
|
||||
*
|
||||
* @param model 已补齐供应商默认配置的模型
|
||||
* @return 双阶段验证结果
|
||||
*/
|
||||
ChatModelVerificationResult verify(Model model);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package tech.easyflow.ai.service.verification;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Chat Model 与 VLM 的双阶段连通性验证结果。
|
||||
*/
|
||||
public final class ChatModelVerificationResult {
|
||||
|
||||
/** 整体验证状态。 */
|
||||
private final ModelVerificationStatus status;
|
||||
/** 非流式基础连接验证状态。 */
|
||||
private final ModelVerificationStatus nonStreaming;
|
||||
/** 流式响应验证状态。 */
|
||||
private final ModelVerificationStatus streaming;
|
||||
/** 实际生效的 HTTP 版本策略。 */
|
||||
private final String effectiveHttpVersion;
|
||||
/** 用户可见的简洁结果说明。 */
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* 创建验证结果。
|
||||
*
|
||||
* @param status 整体验证状态
|
||||
* @param nonStreaming 非流式验证状态
|
||||
* @param streaming 流式验证状态
|
||||
* @param effectiveHttpVersion 实际生效的 HTTP 版本策略
|
||||
* @param message 用户可见结果说明
|
||||
*/
|
||||
private ChatModelVerificationResult(ModelVerificationStatus status,
|
||||
ModelVerificationStatus nonStreaming,
|
||||
ModelVerificationStatus streaming,
|
||||
String effectiveHttpVersion,
|
||||
String message) {
|
||||
this.status = status;
|
||||
this.nonStreaming = nonStreaming;
|
||||
this.streaming = streaming;
|
||||
this.effectiveHttpVersion = effectiveHttpVersion;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建双阶段全部通过的结果。
|
||||
*
|
||||
* @param effectiveHttpVersion 实际生效的 HTTP 版本策略
|
||||
* @return 全部通过结果
|
||||
*/
|
||||
public static ChatModelVerificationResult passed(String effectiveHttpVersion) {
|
||||
return new ChatModelVerificationResult(
|
||||
ModelVerificationStatus.PASSED,
|
||||
ModelVerificationStatus.PASSED,
|
||||
ModelVerificationStatus.PASSED,
|
||||
effectiveHttpVersion,
|
||||
"验证成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建基础连接通过但流式阶段失败的结果。
|
||||
*
|
||||
* @param effectiveHttpVersion 实际生效的 HTTP 版本策略
|
||||
* @return 部分通过结果
|
||||
*/
|
||||
public static ChatModelVerificationResult streamingUnavailable(String effectiveHttpVersion) {
|
||||
return new ChatModelVerificationResult(
|
||||
ModelVerificationStatus.PARTIAL,
|
||||
ModelVerificationStatus.PASSED,
|
||||
ModelVerificationStatus.FAILED,
|
||||
effectiveHttpVersion,
|
||||
"连接成功,流式响应不可用,可关闭智能体的模型流式响应。");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整体验证状态。
|
||||
*
|
||||
* @return 整体验证状态
|
||||
*/
|
||||
public ModelVerificationStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取非流式基础连接验证状态。
|
||||
*
|
||||
* @return 非流式验证状态
|
||||
*/
|
||||
public ModelVerificationStatus getNonStreaming() {
|
||||
return nonStreaming;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流式响应验证状态。
|
||||
*
|
||||
* @return 流式验证状态
|
||||
*/
|
||||
public ModelVerificationStatus getStreaming() {
|
||||
return streaming;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际生效的 HTTP 版本策略。
|
||||
*
|
||||
* @return HTTP 版本策略名称
|
||||
*/
|
||||
public String getEffectiveHttpVersion() {
|
||||
return effectiveHttpVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可见结果说明。
|
||||
*
|
||||
* @return 结果说明
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为现有模型验证接口使用的响应结构。
|
||||
*
|
||||
* @return 有序响应字段
|
||||
*/
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("status", status.name());
|
||||
result.put("nonStreaming", nonStreaming.name());
|
||||
result.put("streaming", streaming.name());
|
||||
result.put("effectiveHttpVersion", effectiveHttpVersion);
|
||||
result.put("message", message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package tech.easyflow.ai.service.verification;
|
||||
|
||||
/**
|
||||
* 模型连通性验证状态。
|
||||
*/
|
||||
public enum ModelVerificationStatus {
|
||||
/** 所有要求的验证阶段均通过。 */
|
||||
PASSED,
|
||||
/** 基础连接通过,但增强能力验证未通过。 */
|
||||
PARTIAL,
|
||||
/** 验证失败。 */
|
||||
FAILED,
|
||||
/** 前置阶段失败,当前阶段未执行。 */
|
||||
SKIPPED
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,32 @@
|
||||
package tech.easyflow.ai.service.support;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* VLM 连接验证专用 PNG 测试。
|
||||
*/
|
||||
public class VlmVerificationImageTest {
|
||||
|
||||
/**
|
||||
* 验证固定资源图片可重复读取并能被标准 PNG 解码器解析。
|
||||
*
|
||||
* @throws Exception 图片解码失败时抛出
|
||||
*/
|
||||
@Test
|
||||
public void pngBytesShouldReturnDeterministicReadableImage() throws Exception {
|
||||
byte[] first = VlmVerificationImage.pngBytes();
|
||||
byte[] second = VlmVerificationImage.pngBytes();
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(first));
|
||||
|
||||
Assert.assertArrayEquals(first, second);
|
||||
Assert.assertNotNull(image);
|
||||
Assert.assertEquals(480, image.getWidth());
|
||||
Assert.assertEquals(180, image.getHeight());
|
||||
Assert.assertEquals("5839", VlmVerificationImage.VERIFICATION_CODE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user