fix: 扩展模型消息内容块数组配置
- 升级高级设置为消息级格式并兼容旧 system 配置 - 增加 VLM 图片首轮与文本追问的多轮连接验证
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
package tech.easyflow.agent.runtime;
|
||||
|
||||
import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
|
||||
import com.easyagents.agent.runtime.model.AgentMessageContentFormat;
|
||||
import com.easyagents.agent.runtime.model.AgentModelProviderType;
|
||||
import com.easyagents.agent.runtime.model.AgentModelSpec;
|
||||
import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tech.easyflow.ai.entity.Model;
|
||||
@@ -61,7 +61,7 @@ public final class AgentModelSpecMapper {
|
||||
spec.setSupportImage(Boolean.TRUE.equals(model.getSupportImage()));
|
||||
spec.setSupportImageBase64Only(Boolean.TRUE.equals(model.getSupportImageB64Only()));
|
||||
spec.setHttpVersionPolicy(parseHttpVersionPolicy(model));
|
||||
spec.setSystemContentFormat(parseSystemContentFormat(model));
|
||||
spec.setMessageContentFormat(parseMessageContentFormat(model));
|
||||
spec.getMetadata().put("modelId", model.getId());
|
||||
if (providerType != null && !providerType.isBlank()) {
|
||||
spec.getMetadata().put("sourceProviderType", providerType);
|
||||
@@ -93,25 +93,30 @@ public final class AgentModelSpecMapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模型配置中的 Agent system content 格式。
|
||||
* 解析模型配置中的 Agent 消息 content 格式。
|
||||
*
|
||||
* @param model 模型配置
|
||||
* @return system content 格式,缺失或非法时返回 STRING
|
||||
* @return 消息 content 格式,缺失或非法时返回 STANDARD
|
||||
*/
|
||||
private static AgentSystemContentFormat parseSystemContentFormat(Model model) {
|
||||
Object rawFormat = model.getOptions() == null
|
||||
? null
|
||||
: model.getOptions().get("agentSystemContentFormat");
|
||||
private static AgentMessageContentFormat parseMessageContentFormat(Model model) {
|
||||
Map<String, Object> options = model.getOptions();
|
||||
Object rawFormat = options == null ? null : options.get("agentMessageContentFormat");
|
||||
if ((rawFormat == null || String.valueOf(rawFormat).isBlank()) && options != null) {
|
||||
rawFormat = options.get("agentSystemContentFormat");
|
||||
}
|
||||
if (rawFormat == null || String.valueOf(rawFormat).isBlank()) {
|
||||
return AgentSystemContentFormat.STRING;
|
||||
return AgentMessageContentFormat.STANDARD;
|
||||
}
|
||||
String normalizedFormat = String.valueOf(rawFormat).trim().toUpperCase(Locale.ROOT);
|
||||
if ("STRING".equals(normalizedFormat)) {
|
||||
return AgentMessageContentFormat.STANDARD;
|
||||
}
|
||||
try {
|
||||
return AgentSystemContentFormat.valueOf(normalizedFormat);
|
||||
return AgentMessageContentFormat.valueOf(normalizedFormat);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
LOG.warn("Invalid Agent system content format '{}' for model {}, fallback to STRING",
|
||||
LOG.warn("Invalid Agent message content format '{}' for model {}, fallback to STANDARD",
|
||||
rawFormat, model.getId());
|
||||
return AgentSystemContentFormat.STRING;
|
||||
return AgentMessageContentFormat.STANDARD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.easyagents.agent.runtime.message.AgentMessage;
|
||||
import com.easyagents.agent.runtime.message.AgentMessageRole;
|
||||
import com.easyagents.agent.runtime.message.AgentTextBlock;
|
||||
import com.easyagents.agent.runtime.model.AgentGenerationOptions;
|
||||
import com.easyagents.agent.runtime.model.AgentMessageContentFormat;
|
||||
import com.easyagents.agent.runtime.model.AgentModelFactory;
|
||||
import com.easyagents.agent.runtime.model.AgentModelProviderType;
|
||||
import com.easyagents.agent.runtime.model.AgentModelSpec;
|
||||
@@ -108,10 +109,11 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用一次非流式 Chat 请求优先同时验证连接、视觉与工具调用能力。
|
||||
* 使用非流式 Chat 请求验证连接、视觉与工具调用能力。
|
||||
*
|
||||
* <p>当兼容接口明确拒绝工具参数时,追加一次不带工具的连接兜底请求,
|
||||
* 避免将可用的普通对话模型误判为连接失败。</p>
|
||||
* 避免将可用的普通对话模型误判为连接失败。严格内容块数组模式会先
|
||||
* 追加一次多轮上下文验证。</p>
|
||||
*
|
||||
* @param model 已补齐供应商默认配置的模型
|
||||
* @return 连接和工具能力验证结果
|
||||
@@ -125,6 +127,9 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
String nonce = nonceSupplier.get();
|
||||
|
||||
try {
|
||||
if (modelSpec.getMessageContentFormat() == AgentMessageContentFormat.TEXT_PARTS) {
|
||||
verifyMessageContentCompatibility(modelSpec);
|
||||
}
|
||||
boolean supportTool = verifyProbeRequest(modelSpec, nonce);
|
||||
return ChatModelVerificationResult.passed(effectiveHttpVersion, supportTool);
|
||||
} catch (Exception exception) {
|
||||
@@ -198,6 +203,24 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
validateTextResponse(modelSpec.isSupportImage(), aggregateText(responses));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证严格内容块数组模式可以携带完整多轮上下文。
|
||||
*
|
||||
* <p>支持图片时将图片放在首轮用户消息中,再追加助手历史和纯文本追问,
|
||||
* 覆盖 VLM 首轮成功后继续对话的请求格式。</p>
|
||||
*
|
||||
* @param modelSpec 运行时模型声明
|
||||
* @throws BusinessException 响应为空时抛出
|
||||
*/
|
||||
private void verifyMessageContentCompatibility(AgentModelSpec modelSpec) {
|
||||
List<ChatResponse> responses = request(
|
||||
modelSpec,
|
||||
buildMessageContentCompatibilityHistory(modelSpec.isSupportImage()),
|
||||
List.of(),
|
||||
null);
|
||||
validateTextResponse(false, aggregateText(responses));
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用统一低成本参数执行一次非流式模型请求。
|
||||
*
|
||||
@@ -229,6 +252,44 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
List<ToolSchema> tools,
|
||||
ToolChoice toolChoice,
|
||||
int maxTokens) {
|
||||
List<Msg> messages = List.of(
|
||||
messageAdapter.toMsg(AgentMessage.text(
|
||||
AgentMessageRole.SYSTEM, VERIFICATION_SYSTEM_PROMPT)),
|
||||
messageAdapter.toMsg(verificationMessage));
|
||||
return request(modelSpec, messages, tools, toolChoice, maxTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用统一低成本参数执行一次指定上下文的非流式模型请求。
|
||||
*
|
||||
* @param modelSpec 运行时模型声明
|
||||
* @param messages 完整上下文消息
|
||||
* @param tools 工具 Schema
|
||||
* @param toolChoice 工具选择策略
|
||||
* @return 模型响应片段
|
||||
*/
|
||||
private List<ChatResponse> request(AgentModelSpec modelSpec,
|
||||
List<Msg> messages,
|
||||
List<ToolSchema> tools,
|
||||
ToolChoice toolChoice) {
|
||||
return request(modelSpec, messages, tools, toolChoice, MAX_TOKENS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定输出预算执行一次指定上下文的非流式模型请求。
|
||||
*
|
||||
* @param modelSpec 运行时模型声明
|
||||
* @param messages 完整上下文消息
|
||||
* @param tools 工具 Schema
|
||||
* @param toolChoice 工具选择策略
|
||||
* @param maxTokens 最大输出 Token 数
|
||||
* @return 模型响应片段
|
||||
*/
|
||||
private List<ChatResponse> request(AgentModelSpec modelSpec,
|
||||
List<Msg> messages,
|
||||
List<ToolSchema> tools,
|
||||
ToolChoice toolChoice,
|
||||
int maxTokens) {
|
||||
AgentGenerationOptions generationOptions = new AgentGenerationOptions();
|
||||
generationOptions.setStream(false);
|
||||
generationOptions.setThinkingEnabled(false);
|
||||
@@ -236,10 +297,6 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
generationOptions.setMaxTokens(maxTokens);
|
||||
io.agentscope.core.model.Model agentScopeModel = modelFactory.create(modelSpec, generationOptions);
|
||||
|
||||
List<Msg> messages = List.of(
|
||||
messageAdapter.toMsg(AgentMessage.text(
|
||||
AgentMessageRole.SYSTEM, VERIFICATION_SYSTEM_PROMPT)),
|
||||
messageAdapter.toMsg(verificationMessage));
|
||||
GenerateOptions.Builder requestBuilder = GenerateOptions.builder()
|
||||
.stream(false)
|
||||
.maxTokens(maxTokens)
|
||||
@@ -257,6 +314,26 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
|
||||
.block(phaseTimeout.plusSeconds(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造包含首轮用户消息、助手历史和文本追问的验证上下文。
|
||||
*
|
||||
* @param supportImage 是否在首轮用户消息中附加验证图片
|
||||
* @return AgentScope 多轮消息
|
||||
*/
|
||||
private List<Msg> buildMessageContentCompatibilityHistory(boolean supportImage) {
|
||||
AgentMessage firstUserMessage = buildVerificationMessage(
|
||||
supportImage ? "请阅读图片,等待下一条消息。" : "这是第一轮消息。",
|
||||
supportImage);
|
||||
return List.of(
|
||||
messageAdapter.toMsg(AgentMessage.text(
|
||||
AgentMessageRole.SYSTEM, VERIFICATION_SYSTEM_PROMPT)),
|
||||
messageAdapter.toMsg(firstUserMessage),
|
||||
messageAdapter.toMsg(AgentMessage.text(
|
||||
AgentMessageRole.ASSISTANT, "已收到。")),
|
||||
messageAdapter.toMsg(AgentMessage.text(
|
||||
AgentMessageRole.USER, "请直接回复“你好”,不要补充其他内容。")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 为支持该扩展字段的 OpenAI-compatible 服务显式关闭思考。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user