From 6fa93bd6718c84c7d721f51f71c9a5468ee2a5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com> Date: Mon, 27 Jul 2026 15:41:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE=20?= =?UTF-8?q?system=20=E6=B6=88=E6=81=AF=E5=86=85=E5=AE=B9=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 OpenAI 兼容模型增加字符串与文本数组两种 system content 策略 - 在 AgentScope 模型工厂统一安装兼容 formatter 并补充测试 --- .../agentscope/AgentOpenAIChatFormatter.java | 31 ++++++++++ .../agentscope/AgentScopeModelFactory.java | 4 ++ .../agent/runtime/model/AgentModelSpec.java | 21 +++++++ .../model/AgentSystemContentFormat.java | 13 ++++ .../AgentOpenAIChatFormatterTest.java | 62 +++++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatter.java create mode 100644 easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentSystemContentFormat.java create mode 100644 easy-agents-agent-runtime/src/test/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatterTest.java diff --git a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatter.java b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatter.java new file mode 100644 index 0000000..8f56602 --- /dev/null +++ b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatter.java @@ -0,0 +1,31 @@ +package com.easyagents.agent.runtime.agentscope; + +import io.agentscope.core.formatter.openai.OpenAIChatFormatter; +import io.agentscope.core.formatter.openai.dto.OpenAIContentPart; +import io.agentscope.core.formatter.openai.dto.OpenAIMessage; +import io.agentscope.core.message.Msg; + +import java.util.List; + +/** + * 为 OpenAI-compatible 模型补充 system content 内容块数组兼容能力。 + */ +public final class AgentOpenAIChatFormatter extends OpenAIChatFormatter { + + /** + * 将 AgentScope 消息转换为 OpenAI 消息,并规范 system 文本的 content 格式。 + * + * @param messages AgentScope 消息 + * @return OpenAI 请求消息 + */ + @Override + protected List doFormat(List messages) { + List formattedMessages = super.doFormat(messages); + for (OpenAIMessage message : formattedMessages) { + if ("system".equals(message.getRole()) && message.getContent() instanceof String text) { + message.setContent(List.of(OpenAIContentPart.text(text))); + } + } + return formattedMessages; + } +} diff --git a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentScopeModelFactory.java b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentScopeModelFactory.java index 15437d0..2f5ccdb 100644 --- a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentScopeModelFactory.java +++ b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/agentscope/AgentScopeModelFactory.java @@ -6,6 +6,7 @@ import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy; import com.easyagents.agent.runtime.model.AgentModelFactory; import com.easyagents.agent.runtime.model.AgentModelProviderType; import com.easyagents.agent.runtime.model.AgentModelSpec; +import com.easyagents.agent.runtime.model.AgentSystemContentFormat; import io.agentscope.core.formatter.openai.DeepSeekFormatter; import io.agentscope.core.formatter.openai.GLMFormatter; import io.agentscope.core.model.*; @@ -153,6 +154,9 @@ public class AgentScopeModelFactory implements AgentModelFactory { .stream(Boolean.TRUE.equals(options.getStream())) .httpTransport(httpTransportProvider.getTransport(modelSpec.getHttpVersionPolicy(), baseUrl)) .generateOptions(options); + if (modelSpec.getSystemContentFormat() == AgentSystemContentFormat.TEXT_PARTS) { + builder.formatter(new AgentOpenAIChatFormatter()); + } return builder.build(); } diff --git a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentModelSpec.java b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentModelSpec.java index 59bc84a..ac33ff1 100644 --- a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentModelSpec.java +++ b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentModelSpec.java @@ -16,6 +16,7 @@ public class AgentModelSpec { private boolean supportImage; private boolean supportImageBase64Only; private AgentHttpVersionPolicy httpVersionPolicy = AgentHttpVersionPolicy.AUTO; + private AgentSystemContentFormat systemContentFormat = AgentSystemContentFormat.STRING; private Map metadata = new LinkedHashMap<>(); /** @@ -162,6 +163,26 @@ public class AgentModelSpec { this.httpVersionPolicy = httpVersionPolicy == null ? AgentHttpVersionPolicy.AUTO : httpVersionPolicy; } + /** + * 获取 OpenAI-compatible 请求中的 system content 格式。 + * + * @return system content 格式 + */ + public AgentSystemContentFormat getSystemContentFormat() { + return systemContentFormat; + } + + /** + * 设置 OpenAI-compatible 请求中的 system content 格式。 + * + * @param systemContentFormat system content 格式 + */ + public void setSystemContentFormat(AgentSystemContentFormat systemContentFormat) { + this.systemContentFormat = systemContentFormat == null + ? AgentSystemContentFormat.STRING + : systemContentFormat; + } + /** * 获取元数据。 * diff --git a/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentSystemContentFormat.java b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentSystemContentFormat.java new file mode 100644 index 0000000..f06fd45 --- /dev/null +++ b/easy-agents-agent-runtime/src/main/java/com/easyagents/agent/runtime/model/AgentSystemContentFormat.java @@ -0,0 +1,13 @@ +package com.easyagents.agent.runtime.model; + +/** + * OpenAI-compatible 请求中 system 消息的 content 格式。 + */ +public enum AgentSystemContentFormat { + + /** 使用字符串 content,保持 OpenAI Chat Completions 的常规格式。 */ + STRING, + + /** 使用仅包含 text 内容块的数组 content。 */ + TEXT_PARTS +} diff --git a/easy-agents-agent-runtime/src/test/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatterTest.java b/easy-agents-agent-runtime/src/test/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatterTest.java new file mode 100644 index 0000000..3e4816e --- /dev/null +++ b/easy-agents-agent-runtime/src/test/java/com/easyagents/agent/runtime/agentscope/AgentOpenAIChatFormatterTest.java @@ -0,0 +1,62 @@ +package com.easyagents.agent.runtime.agentscope; + +import com.easyagents.agent.runtime.model.AgentGenerationOptions; +import com.easyagents.agent.runtime.model.AgentModelProviderType; +import com.easyagents.agent.runtime.model.AgentModelSpec; +import com.easyagents.agent.runtime.model.AgentSystemContentFormat; +import io.agentscope.core.formatter.openai.dto.OpenAIContentPart; +import io.agentscope.core.formatter.openai.dto.OpenAIMessage; +import io.agentscope.core.message.Msg; +import io.agentscope.core.message.MsgRole; +import io.agentscope.core.model.OpenAIChatModel; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +/** + * Agent OpenAI Chat 消息格式兼容测试。 + */ +public class AgentOpenAIChatFormatterTest { + + /** + * 验证 system 文本转换为 text 内容块数组,普通 user 文本保持字符串。 + */ + @Test + public void shouldConvertOnlySystemTextToContentParts() { + AgentOpenAIChatFormatter formatter = new AgentOpenAIChatFormatter(); + List messages = formatter.format(List.of( + Msg.builder().role(MsgRole.SYSTEM).textContent("system prompt").build(), + Msg.builder().role(MsgRole.USER).textContent("hello").build())); + + Assert.assertTrue(messages.get(0).getContent() instanceof List); + List contentParts = messages.get(0).getContentAsList(); + Assert.assertEquals(1, contentParts.size()); + Assert.assertEquals("text", contentParts.get(0).getType()); + Assert.assertEquals("system prompt", contentParts.get(0).getText()); + Assert.assertEquals("hello", messages.get(1).getContent()); + } + + /** + * 验证内容块数组策略会安装 EasyAgents 的 OpenAI Formatter。 + * + * @throws Exception 反射读取 Formatter 失败时抛出 + */ + @Test + public void textPartsPolicyShouldInstallAgentOpenAIFormatter() throws Exception { + AgentModelSpec spec = new AgentModelSpec(); + spec.setProviderType(AgentModelProviderType.OPENAI_COMPATIBLE); + spec.setModelName("vlm-test"); + spec.setBaseUrl("http://model.example.com/v1"); + spec.setApiKey("test-key"); + spec.setSystemContentFormat(AgentSystemContentFormat.TEXT_PARTS); + + OpenAIChatModel model = (OpenAIChatModel) new AgentScopeModelFactory() + .create(spec, new AgentGenerationOptions()); + Field formatterField = OpenAIChatModel.class.getDeclaredField("formatter"); + formatterField.setAccessible(true); + + Assert.assertTrue(formatterField.get(model) instanceof AgentOpenAIChatFormatter); + } +}