初始化

This commit is contained in:
2026-02-22 18:55:40 +08:00
commit 8392cdd861
496 changed files with 45020 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.easyagents</groupId>
<artifactId>easy-agents-chat</artifactId>
<version>${revision}</version>
</parent>
<name>easy-agents-chat-openai</name>
<artifactId>easy-agents-chat-openai</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.easyagents</groupId>
<artifactId>easy-agents-core</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) 2023-2026, Easy-Agents (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.easyagents.llm.openai;
import com.easyagents.core.model.chat.ChatConfig;
import com.easyagents.core.model.chat.ChatInterceptor;
import com.easyagents.core.util.StringUtil;
import java.util.List;
import java.util.Map;
/**
* OpenAI 聊天模型的配置类,支持通过 Builder 模式创建配置或直接构建 {@link OpenAIChatModel}。
* <p>
* 默认值:
* <ul>
* <li>provider: {@code "openai"}</li>
* <li>model: {@code "gpt-3.5-turbo"}</li>
* <li>endpoint: {@code "https://api.openai.com"}</li>
* <li>requestPath: {@code "/v1/chat/completions"}</li>
* </ul>
* <p>
* 该配置类专为 OpenAI 兼容 API 设计,适用于 OpenAI 官方、Azure OpenAI 或其他兼容服务。
*/
public class OpenAIChatConfig extends ChatConfig {
private static final String DEFAULT_PROVIDER = "openai";
private static final String DEFAULT_MODEL = "gpt-3.5-turbo";
private static final String DEFAULT_ENDPOINT = "https://api.openai.com";
private static final String DEFAULT_REQUEST_PATH = "/v1/chat/completions";
public OpenAIChatConfig() {
setProvider(DEFAULT_PROVIDER);
setEndpoint(DEFAULT_ENDPOINT);
setRequestPath(DEFAULT_REQUEST_PATH);
setModel(DEFAULT_MODEL);
}
/**
* 创建一个 {@link OpenAIChatModel} 实例,使用当前配置。
*
* @return 新的 {@link OpenAIChatModel} 实例
*/
public final OpenAIChatModel toChatModel() {
return new OpenAIChatModel(this);
}
/**
* 创建一个 {@link OpenAIChatModel} 实例,使用当前配置和指定的实例级拦截器。
*
* @param interceptors 实例级拦截器列表,可为 {@code null} 或空列表
* @return 新的 {@link OpenAIChatModel} 实例
*/
public final OpenAIChatModel toChatModel(List<ChatInterceptor> interceptors) {
return new OpenAIChatModel(this, interceptors);
}
/**
* 构建器类,用于流畅地创建 {@link OpenAIChatConfig} 或直接构建 {@link OpenAIChatModel}。
*/
public static class Builder {
private final OpenAIChatConfig config = new OpenAIChatConfig();
// --- BaseModelConfig fields ---
public Builder apiKey(String apiKey) {
config.setApiKey(apiKey);
return this;
}
public Builder provider(String provider) {
config.setProvider(provider);
return this;
}
public Builder endpoint(String endpoint) {
config.setEndpoint(endpoint);
return this;
}
public Builder requestPath(String requestPath) {
config.setRequestPath(requestPath);
return this;
}
public Builder model(String model) {
config.setModel(model);
return this;
}
/**
* 添加单个自定义属性(会进行深拷贝,不会持有外部引用)。
*/
public Builder customProperty(String key, Object value) {
config.putCustomProperty(key, value);
return this;
}
/**
* 设置自定义属性映射(会进行深拷贝,不会持有外部 map 引用)。
*/
public Builder customProperties(Map<String, Object> customProperties) {
config.setCustomProperties(customProperties);
return this;
}
// --- ChatConfig fields ---
public Builder supportImage(Boolean supportImage) {
config.setSupportImage(supportImage);
return this;
}
public Builder supportImageBase64Only(Boolean supportImageBase64Only) {
config.setSupportImageBase64Only(supportImageBase64Only);
return this;
}
public Builder supportAudio(Boolean supportAudio) {
config.setSupportAudio(supportAudio);
return this;
}
public Builder supportVideo(Boolean supportVideo) {
config.setSupportVideo(supportVideo);
return this;
}
public Builder supportTool(Boolean supportTool) {
config.setSupportTool(supportTool);
return this;
}
public Builder supportThinking(Boolean supportThinking) {
config.setSupportThinking(supportThinking);
return this;
}
public Builder thinkingEnabled(boolean thinkingEnabled) {
config.setThinkingEnabled(thinkingEnabled);
return this;
}
public Builder observabilityEnabled(boolean observabilityEnabled) {
config.setObservabilityEnabled(observabilityEnabled);
return this;
}
public Builder logEnabled(boolean logEnabled) {
config.setLogEnabled(logEnabled);
return this;
}
/**
* 构建 {@link OpenAIChatConfig} 配置对象。
* <p>
* 该方法会校验必要字段(如 {@code apiKey}),若缺失将抛出异常。
*
* @return 构建完成的配置对象
* @throws IllegalStateException 如果 {@code apiKey} 未设置或为空
*/
public OpenAIChatConfig build() {
if (StringUtil.noText(config.getApiKey())) {
throw new IllegalStateException("apiKey must be set for OpenAIChatConfig");
}
return config;
}
/**
* 直接构建 {@link OpenAIChatModel} 实例,使用默认(全局)拦截器。
*
* @return 新的聊天模型实例
* @throws IllegalStateException 如果 {@code apiKey} 未设置或为空
*/
public OpenAIChatModel buildModel() {
return new OpenAIChatModel(build());
}
/**
* 直接构建 {@link OpenAIChatModel} 实例,并指定实例级拦截器。
*
* @param interceptors 实例级拦截器列表,可为 {@code null} 或空
* @return 新的聊天模型实例
* @throws IllegalStateException 如果 {@code apiKey} 未设置或为空
*/
public OpenAIChatModel buildModel(List<ChatInterceptor> interceptors) {
return new OpenAIChatModel(build(), interceptors);
}
}
/**
* 获取一个新的构建器实例,用于链式配置。
*
* @return {@link Builder} 实例
*/
public static Builder builder() {
return new Builder();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023-2026, Easy-Agents (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.easyagents.llm.openai;
import com.easyagents.core.model.chat.BaseChatModel;
import com.easyagents.core.model.chat.OpenAICompatibleChatModel;
import com.easyagents.core.model.chat.ChatInterceptor;
import com.easyagents.core.model.chat.GlobalChatInterceptors;
import java.util.List;
/**
* OpenAI 聊天模型实现。
* <p>
* 该类封装了 OpenAI API 的具体调用细节,包括:
* <ul>
* <li>请求体构建(支持同步/流式)</li>
* <li>HTTP 客户端管理</li>
* <li>解析器配置(同步/流式使用不同解析器)</li>
* </ul>
* <p>
* 所有横切逻辑(监控、日志、拦截)由 {@link BaseChatModel} 的责任链处理,
* 本类只关注 OpenAI 协议特有的实现细节。
*/
public class OpenAIChatModel extends OpenAICompatibleChatModel<OpenAIChatConfig> {
/**
* 构造一个聊天模型实例,不使用实例级拦截器。
*
* @param config 聊天模型配置
*/
public OpenAIChatModel(OpenAIChatConfig config) {
super(config);
}
/**
* 构造一个聊天模型实例,并指定实例级拦截器。
* <p>
* 实例级拦截器会与全局拦截器(通过 {@link GlobalChatInterceptors} 注册)合并,
* 执行顺序为:可观测性拦截器 → 全局拦截器 → 实例拦截器。
*
* @param config 聊天模型配置
* @param userInterceptors 实例级拦截器列表
*/
public OpenAIChatModel(OpenAIChatConfig config, List<ChatInterceptor> userInterceptors) {
super(config, userInterceptors);
}
}

View File

@@ -0,0 +1,86 @@
package com.easyagents.llm.openai;
import com.easyagents.core.model.chat.ChatModel;
import com.easyagents.core.model.chat.ChatOptions;
import com.easyagents.core.model.chat.StreamResponseListener;
import com.easyagents.core.model.chat.response.AiMessageResponse;
import com.easyagents.core.model.client.StreamContext;
import com.easyagents.core.prompt.Prompt;
import com.easyagents.core.prompt.SimplePrompt;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ChatModelTestUtils {
public static void waitForStream(
ChatModel model,
String prompt,
StreamResponseListener listener) {
waitForStream(model, new SimplePrompt(prompt), listener, Integer.MAX_VALUE, null);
}
public static void waitForStream(
ChatModel model,
String prompt,
StreamResponseListener listener,
ChatOptions options) {
waitForStream(model, new SimplePrompt(prompt), listener, Integer.MAX_VALUE, options);
}
public static void waitForStream(
ChatModel model,
Prompt prompt,
StreamResponseListener listener) {
waitForStream(model, prompt, listener, Integer.MAX_VALUE, null);
}
public static void waitForStream(
ChatModel model,
Prompt prompt,
StreamResponseListener listener,
ChatOptions options) {
waitForStream(model, prompt, listener, Integer.MAX_VALUE, options);
}
public static void waitForStream(
ChatModel model,
Prompt prompt,
StreamResponseListener listener,
long timeoutSeconds, ChatOptions options) {
CountDownLatch latch = new CountDownLatch(1);
StreamResponseListener wrapped = new StreamResponseListener() {
@Override
public void onStart(StreamContext context) {
listener.onStart(context);
}
@Override
public void onMessage(StreamContext ctx, AiMessageResponse resp) {
listener.onMessage(ctx, resp);
}
@Override
public void onStop(StreamContext ctx) {
listener.onStop(ctx);
latch.countDown();
}
@Override
public void onFailure(StreamContext context, Throwable throwable) {
listener.onFailure(context, throwable);
}
};
model.chatStream(prompt, wrapped, options);
try {
if (!latch.await(timeoutSeconds, TimeUnit.SECONDS)) {
throw new RuntimeException("Stream did not complete within " + timeoutSeconds + "s");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,53 @@
package com.easyagents.llm.openai;
import com.easyagents.core.model.chat.ChatModel;
import com.easyagents.core.model.chat.response.AiMessageResponse;
import com.easyagents.core.prompt.SimplePrompt;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
public class GiteeAiImageTest {
@NotNull
private static OpenAIChatConfig getOpenAIChatConfig() {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("PXW1GXE******L7D12");
// config.setModel("InternVL3-78B");
config.setModel("Qwen3-32B");
config.setEndpoint("https://ai.gitee.com");
config.setLogEnabled(true);
return config;
}
@Test
public void testImage() {
OpenAIChatConfig config = getOpenAIChatConfig();
ChatModel chatModel = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("请识别并输入 markdown请用中文输出");
prompt.addImageUrl("http://www.codeformat.cn/static/images/logo.png");
AiMessageResponse response = chatModel.chat(prompt);
if (!response.isError()) {
System.out.println(response.getMessage().getContent());
}
}
@Test
public void testChat() {
OpenAIChatConfig config = getOpenAIChatConfig();
config.setSupportImage(false);
ChatModel chatModel = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("你叫什么名字");
prompt.addImageUrl("http://www.codeformat.cn/static/images/logo.png");
AiMessageResponse response = chatModel.chat(prompt);
if (!response.isError()) {
System.out.println(response.getMessage().getContent());
}
}
}

View File

@@ -0,0 +1,500 @@
package com.easyagents.llm.openai;
import com.easyagents.core.agent.react.ReActAgent;
import com.easyagents.core.agent.react.ReActAgentListener;
import com.easyagents.core.agent.react.ReActAgentState;
import com.easyagents.core.agent.react.ReActStep;
import com.easyagents.core.memory.ChatMemory;
import com.easyagents.core.message.ToolCall;
import com.easyagents.core.message.ToolMessage;
import com.easyagents.core.message.UserMessage;
import com.easyagents.core.model.chat.ChatModel;
import com.easyagents.core.model.chat.ChatOptions;
import com.easyagents.core.model.chat.StreamResponseListener;
import com.easyagents.core.model.chat.response.AiMessageResponse;
import com.easyagents.core.model.chat.tool.Tool;
import com.easyagents.core.model.chat.tool.ToolScanner;
import com.easyagents.core.model.client.StreamContext;
import com.easyagents.core.model.exception.ModelException;
import com.easyagents.core.prompt.SimplePrompt;
import org.junit.Test;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class OpenAIChatModelTest {
@Test(expected = ModelException.class)
public void testChat() {
String output = OpenAIChatConfig.builder()
.endpoint("https://ai.gitee.com")
.provider("GiteeAI")
.model("Qwen3-32B")
.apiKey("PXW1****D12")
.buildModel()
.chat("你叫什么名字");
System.out.println(output);
}
@Test()
public void testChatStream() {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("PXW1GXE***");
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen3-32B");
config.setLogEnabled(true);
ChatOptions options = ChatOptions.builder().thinkingEnabled(false).build();
ChatModel chatModel = new OpenAIChatModel(config);
ChatModelTestUtils.waitForStream(chatModel, "你叫什么名字", new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
System.out.println(response.getMessage().getContent());
}
@Override
public void onFailure(StreamContext context, Throwable throwable) {
System.out.println("onFailure>>>>" + throwable);
}
@Override
public void onStop(StreamContext context) {
System.out.println("stop!!!!");
}
}, options);
}
@Test()
public void testChatStreamBailian() {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("sk-32ab******57502");
config.setEndpoint("https://dashscope.aliyuncs.com");
config.setRequestPath("/compatible-mode/v1/chat/completions");
config.setModel("qwen3-max");
ChatModel chatModel = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("北京的天气如何?");
prompt.addToolsFromClass(WeatherFunctions.class);
ChatModelTestUtils.waitForStream(chatModel, prompt, new StreamResponseListener() {
@Override
public void onFailure(StreamContext context, Throwable throwable) {
System.out.println("onFailure>>>>" + throwable);
}
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
if (response.getMessage().getContent() == null) {
System.out.println(response.getMessage());
}
if (response.getMessage().isFinalDelta()) {
List<ToolCall> toolCalls = response.getMessage().getToolCalls();
System.out.println(toolCalls);
}
System.out.println("onMessage >>>>>" + response.getMessage().getContent());
}
@Override
public void onStop(StreamContext context) {
System.out.println("stop!!!!");
}
});
}
@Test
public void testChatOllama() {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setEndpoint("http://localhost:11434");
config.setModel("llama3");
config.setLogEnabled(true);
ChatModel chatModel = new OpenAIChatModel(config);
chatModel.chatStream("who are you", new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
System.out.println(response.getMessage().getContent());
}
@Override
public void onStop(StreamContext context) {
System.out.println("stop!!!!");
}
});
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
}
@Test()
public void testChatWithImage() {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("sk-5gqOcl*****");
config.setModel("gpt-4-turbo");
ChatModel chatModel = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("What's in this image?");
prompt.addImageUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg");
AiMessageResponse response = chatModel.chat(prompt);
System.out.println(response);
}
@Test()
public void testFunctionCalling1() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("sk-rts5NF6n*******");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("今天北京的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
AiMessageResponse response = llm.chat(prompt);
System.out.println(response.executeToolCallsAndGetResults());
// 阴转多云
}
@Test()
public void testFunctionCalling2() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setApiKey("sk-rts5NF6n*******");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("今天北京的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
AiMessageResponse response = llm.chat(prompt);
if (response.hasToolCalls()) {
prompt.setToolMessages(response.executeToolCallsAndGetToolMessages());
AiMessageResponse response1 = llm.chat(prompt);
System.out.println(response1.getMessage().getContent());
} else {
System.out.println(response);
}
}
@Test()
public void testFunctionCalling3() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ark.cn-beijing.volces.com");
config.setRequestPath("/api/v3/chat/completions");
config.setModel("deepseek-v3-250324");
config.setApiKey("2d57a");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("今天北京的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
AiMessageResponse response = llm.chat(prompt);
if (response.hasToolCalls()) {
prompt.setToolMessages(response.executeToolCallsAndGetToolMessages());
AiMessageResponse response1 = llm.chat(prompt);
System.out.println(response1.getMessage().getContent());
} else {
System.out.println(response);
}
}
@Test()
public void testFunctionCalling4() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ark.cn-beijing.volces.com");
config.setRequestPath("/api/v3/chat/completions");
config.setModel("deepseek-v3-250324");
config.setApiKey("2d57aa75");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("今天北京的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
System.out.println(" onMessage >>>>>" + response.hasToolCalls());
}
});
TimeUnit.SECONDS.sleep(5);
}
@Test()
public void testFunctionCalling44() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ark.cn-beijing.volces.com");
config.setRequestPath("/api/v3/chat/completions");
config.setModel("deepseek-v3-250324");
config.setApiKey("2d5");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("今天北京的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
System.out.println(" onMessage >>>>>" + response.hasToolCalls());
}
});
TimeUnit.SECONDS.sleep(5);
}
@Test()
public void testFunctionCalling444() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ai.gitee.com");
// config.setRequestPath("/api/v3/chat/completions");
// config.setModel("Qwen3-32B");
config.setModel("DeepSeek-V3.2");
config.setApiKey("PXW1G***L7D12");
// config.setLogEnabled(false);
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("北京和上海的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
// System.out.println("onMessage11 >>>>>" + response);
if (response.getMessage().isFinalDelta() && response.hasToolCalls()) {
System.out.println(":::::::: start....");
List<ToolMessage> toolMessages = response.executeToolCallsAndGetToolMessages();
prompt.setAiMessage(response.getMessage());
prompt.setToolMessages(toolMessages);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
String msg = response.getMessage().getContent() != null ? response.getMessage().getContent() : response.getMessage().getReasoningContent();
System.out.println(":::22" + msg);
}
@Override
public void onStop(StreamContext context) {
System.out.println("onStop >>>>>");
}
});
} else {
String msg = response.getMessage().getContent() != null ? response.getMessage().getContent() : response.getMessage().getReasoningContent();
System.out.println(">>>" + msg);
}
}
});
TimeUnit.SECONDS.sleep(25);
}
@Test()
public void testFunctionCalling5() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen3-32B");
config.setApiKey("PXW1G*********D12");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("北京和上海的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
// System.out.println("onMessage >>>>>" + response);
}
});
TimeUnit.SECONDS.sleep(25);
}
@Test()
public void testFunctionCalling55() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen3-32B");
// config.setModel("DeepSeek-V3");
// config.setSupportToolMessage(false);
config.setApiKey("PXW1");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("/no_think 北京和上海的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
if (response.getMessage().isFinalDelta() && response.hasToolCalls()) {
System.out.println(":::::::: start....");
prompt.setAiMessage(response.getMessage());
prompt.setToolMessages(response.executeToolCallsAndGetToolMessages());
llm.chatStream(prompt, new StreamResponseListener() {
@Override
public void onMessage(StreamContext context, AiMessageResponse response) {
String msg = response.getMessage().getContent() != null ? response.getMessage().getContent() : response.getMessage().getReasoningContent();
System.out.println(":::" + msg);
}
});
} else {
String msg = response.getMessage().getContent() != null ? response.getMessage().getContent() : response.getMessage().getReasoningContent();
System.out.println(">>>" + msg);
}
}
});
TimeUnit.SECONDS.sleep(25);
}
@Test()
public void testFunctionCalling6() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
config.setLogEnabled(true);
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen3-32B");
config.setApiKey("PXW1");
OpenAIChatModel llm = new OpenAIChatModel(config);
SimplePrompt prompt = new SimplePrompt("/no_think 北京和上海的天气怎么样");
prompt.addToolsFromClass(WeatherFunctions.class);
AiMessageResponse response = llm.chat(prompt);
prompt.setToolMessages(response.executeToolCallsAndGetToolMessages());
System.out.println(llm.chat(prompt));
}
@Test()
public void testReAct1() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
// config.setDebug(true);
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen3-32B");
config.setApiKey("****");
OpenAIChatModel llm = new OpenAIChatModel(config);
List<Tool> tools = ToolScanner.scan(WeatherFunctions.class);
// ReActAgent reActAgent = new ReActAgent(llm, functions, "北京和上海的天气怎么样?");
ReActAgent reActAgent = new ReActAgent(llm, tools, "介绍一下北京");
reActAgent.addListener(new ReActAgentListener() {
@Override
public void onActionStart(ReActStep step) {
System.out.println(">>>>>>" + step.getThought());
System.out.println("正在调用工具 >>>>> " + step.getAction() + ":" + step.getActionInput());
}
@Override
public void onActionEnd(ReActStep step, Object result) {
System.out.println("工具调用结束 >>>>> " + step.getAction() + ":" + step.getActionInput() + ">>>>结果:" + result);
}
@Override
public void onFinalAnswer(String finalAnswer) {
System.out.println("onFinalAnswer >>>>>" + finalAnswer);
}
@Override
public void onNonActionResponse(AiMessageResponse response) {
System.out.println("onNonActionResponse >>>>>" + response.getMessage().getContent());
}
});
reActAgent.execute();
}
@Test()
public void testReAct2() throws InterruptedException {
OpenAIChatConfig config = new OpenAIChatConfig();
// config.setDebug(true);
config.setEndpoint("https://ai.gitee.com");
config.setModel("Qwen2-72B-Instruct");
config.setApiKey("*****");
OpenAIChatModel llm = new OpenAIChatModel(config);
List<Tool> tools = ToolScanner.scan(WeatherFunctions.class);
ReActAgent reActAgent = new ReActAgent(llm, tools, "今天的天气怎么样?");
// reActAgent.setStreamable(true);
reActAgent.addListener(new ReActAgentListener() {
@Override
public void onChatResponseStream(StreamContext context, AiMessageResponse response) {
// System.out.print(response.getMessage().getContent());
}
@Override
public void onRequestUserInput(String question) {
System.out.println("onRequestUserInput>>>" + question);
ReActAgentState state = reActAgent.getState();
state.addMessage(new UserMessage("我在北京市"));
ReActAgent newAgent = new ReActAgent(llm, tools, state);
newAgent.addListener(this);
newAgent.execute();
}
@Override
public void onActionStart(ReActStep step) {
System.out.println(">>>>>>" + step.getThought());
System.out.println("正在调用工具 >>>>> " + step.getAction() + ":" + step.getActionInput());
}
@Override
public void onActionEnd(ReActStep step, Object result) {
System.out.println("工具调用结束 >>>>> " + step.getAction() + ":" + step.getActionInput() + ">>>>结果:" + result);
}
@Override
public void onFinalAnswer(String finalAnswer) {
System.out.println("onFinalAnswer >>>>>" + finalAnswer);
ChatMemory memory = reActAgent.getMemoryPrompt().getMemory();
System.out.println(memory);
}
@Override
public void onNonActionResponseStream(StreamContext context) {
System.out.println("onNonActionResponseStream >>>>>" + context);
}
});
reActAgent.execute();
TimeUnit.SECONDS.sleep(20);
}
}

View File

@@ -0,0 +1,23 @@
package com.easyagents.llm.openai;
import com.easyagents.core.model.chat.tool.annotation.ToolDef;
import com.easyagents.core.model.chat.tool.annotation.ToolParam;
import java.util.concurrent.ThreadLocalRandom;
public class WeatherFunctions {
private static final String[] weathers = {
"", "多云", "", "小雨", "中雨", "大雨", "暴雨", "雷阵雨",
"小雪", "中雪", "大雪", "暴雪", "雨夹雪", "", "", "沙尘暴",
"冰雹", "阵雨", "冻雨", "晴间多云", "局部多云", "强对流"
};
@ToolDef(name = "get_the_weather_info", description = "get the weather info")
public static String getWeatherInfo(@ToolParam(name = "city", description = "the city name") String name) {
String weather = weathers[ThreadLocalRandom.current().nextInt(weathers.length)];
System.out.println(">>>>>>>>>>>>>>!!!!!!" + name + ":" + weather);
return weather;
}
}