feat: 增加 system 消息格式配置

- 模型管理支持选择字符串或内容块数组格式

- 验证连接携带 system 消息并复用正式运行时模型配置

- 补充配置映射与验证消息角色测试
This commit is contained in:
2026-07-27 15:47:58 +08:00
parent ba4253e13e
commit dc7e46260b
5 changed files with 109 additions and 3 deletions

View File

@@ -3,6 +3,7 @@ package tech.easyflow.agent.runtime;
import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
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;
@@ -60,6 +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.getMetadata().put("modelId", model.getId());
if (providerType != null && !providerType.isBlank()) {
spec.getMetadata().put("sourceProviderType", providerType);
@@ -90,6 +92,29 @@ public final class AgentModelSpecMapper {
}
}
/**
* 解析模型配置中的 Agent system content 格式。
*
* @param model 模型配置
* @return system content 格式,缺失或非法时返回 STRING
*/
private static AgentSystemContentFormat parseSystemContentFormat(Model model) {
Object rawFormat = model.getOptions() == null
? null
: model.getOptions().get("agentSystemContentFormat");
if (rawFormat == null || String.valueOf(rawFormat).isBlank()) {
return AgentSystemContentFormat.STRING;
}
String normalizedFormat = String.valueOf(rawFormat).trim().toUpperCase(Locale.ROOT);
try {
return AgentSystemContentFormat.valueOf(normalizedFormat);
} catch (IllegalArgumentException exception) {
LOG.warn("Invalid Agent system content format '{}' for model {}, fallback to STRING",
rawFormat, model.getId());
return AgentSystemContentFormat.STRING;
}
}
/**
* 解析 AgentScope 支持的模型供应商类型。
*

View File

@@ -43,6 +43,8 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
/** 为未识别关闭思考扩展参数的推理模型保留足够的小额输出预算。 */
private static final int MAX_TOKENS = 256;
private static final Duration DEFAULT_PHASE_TIMEOUT = Duration.ofSeconds(60);
private static final String VERIFICATION_SYSTEM_PROMPT =
"You are verifying model connectivity. Follow the user's instruction exactly.";
/** AgentScope 模型工厂。 */
private final AgentModelFactory<io.agentscope.core.model.Model> modelFactory;
@@ -128,7 +130,11 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
generationOptions.setMaxTokens(MAX_TOKENS);
io.agentscope.core.model.Model agentScopeModel = modelFactory.create(modelSpec, generationOptions);
Msg message = messageAdapter.toMsg(verificationMessage);
List<Msg> messages = List.of(
messageAdapter.toMsg(AgentMessage.text(
AgentMessageRole.SYSTEM,
VERIFICATION_SYSTEM_PROMPT)),
messageAdapter.toMsg(verificationMessage));
GenerateOptions requestOptions = GenerateOptions.builder()
.stream(stream)
.maxTokens(MAX_TOKENS)
@@ -138,7 +144,7 @@ public class AgentScopeChatModelConnectivityVerifier implements ChatModelConnect
.build())
.build();
List<ChatResponse> responses = agentScopeModel
.stream(List.of(message), List.of(), requestOptions)
.stream(messages, List.of(), requestOptions)
.timeout(phaseTimeout)
.collectList()
.block(phaseTimeout.plusSeconds(1));

View File

@@ -4,6 +4,7 @@ import com.easyagents.agent.runtime.memory.AgentMemoryPolicy;
import com.easyagents.agent.runtime.model.AgentGenerationOptions;
import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
import com.easyagents.agent.runtime.model.AgentModelSpec;
import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.agent.entity.Agent;
@@ -66,6 +67,36 @@ public class AgentRuntimeCompilerModelConfigTest {
Assert.assertEquals(AgentHttpVersionPolicy.AUTO, spec.getHttpVersionPolicy());
}
/**
* 验证模型 options 中的 system content 格式会编译到中立模型声明。
*
* @throws Exception 反射调用失败时抛出
*/
@Test
public void modelSystemContentFormatShouldCompileFromOptions() throws Exception {
Model model = model(Map.of("agentSystemContentFormat", "TEXT_PARTS"));
AgentRuntimeCompiler compiler = compiler(model);
AgentModelSpec spec = invokeModelSpec(compiler);
Assert.assertEquals(AgentSystemContentFormat.TEXT_PARTS, spec.getSystemContentFormat());
}
/**
* 验证未知 system content 格式安全回退到字符串。
*
* @throws Exception 反射调用失败时抛出
*/
@Test
public void unknownSystemContentFormatShouldFallbackToString() throws Exception {
Model model = model(Map.of("agentSystemContentFormat", "PARTS"));
AgentRuntimeCompiler compiler = compiler(model);
AgentModelSpec spec = invokeModelSpec(compiler);
Assert.assertEquals(AgentSystemContentFormat.STRING, spec.getSystemContentFormat());
}
/**
* 验证 EasyFlow 忽略新旧消息数阈值,仅保留 Token 压缩配置。
*

View File

@@ -7,6 +7,7 @@ import com.easyagents.agent.runtime.model.AgentModelSpec;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.ImageBlock;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.model.GenerateOptions;
@@ -48,6 +49,7 @@ public class AgentScopeChatModelConnectivityVerifierTest {
Assert.assertEquals("HTTP_1_1", result.getEffectiveHttpVersion());
Assert.assertEquals(List.of(false, true), factory.getFactoryStreams());
Assert.assertEquals(List.of(false, true), factory.getRequestStreams());
Assert.assertEquals(List.of(MsgRole.SYSTEM, MsgRole.USER), factory.getMessageRoles().get(0));
Assert.assertEquals(List.of(false, false), factory.getEnableThinkingValues());
Assert.assertEquals(List.of(false, false), factory.getChatTemplateThinkingValues());
}
@@ -190,6 +192,8 @@ public class AgentScopeChatModelConnectivityVerifierTest {
private final List<Boolean> requestStreams = new ArrayList<>();
/** 模型请求收到的消息。 */
private final List<Msg> messages = new ArrayList<>();
/** 各阶段请求消息的角色顺序。 */
private final List<List<MsgRole>> messageRoles = new ArrayList<>();
/** OpenAI-compatible 请求中的思考开关。 */
private final List<Object> enableThinkingValues = new ArrayList<>();
/** GPUStack 模板参数中的思考开关。 */
@@ -241,7 +245,8 @@ public class AgentScopeChatModelConnectivityVerifierTest {
List<ToolSchema> tools,
GenerateOptions options) {
requestStreams.add(Boolean.TRUE.equals(options.getStream()));
messages.add(inputMessages.get(0));
messages.add(inputMessages.get(inputMessages.size() - 1));
messageRoles.add(inputMessages.stream().map(Msg::getRole).toList());
return responses;
}
@@ -284,6 +289,15 @@ public class AgentScopeChatModelConnectivityVerifierTest {
return messages;
}
/**
* 获取各阶段请求消息的角色顺序。
*
* @return 消息角色顺序
*/
private List<List<MsgRole>> getMessageRoles() {
return messageRoles;
}
/**
* 获取 OpenAI-compatible 请求中的思考开关。
*