重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
agentscope-java by agentscope-ai/agentscope-java
npx skills add https://github.com/agentscope-ai/agentscope-java --skill agentscope-java当用户要求你编写 AgentScope Java 代码时,请仔细遵循这些说明。
🚫 绝对禁止:
.block() - 这是头号错误。仅在 main() 方法或显式创建可运行示例的测试代码中使用 .block()。Thread.sleep() - 请改用 Mono.delay()。ThreadLocal - 使用 Reactor Context 配合 Mono.deferContextual()。System.getenv()。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
io.agentscope.core.model.* 中,不是 io.agentscope.model.*。✅ 始终要做到:
Mono 和 Flux。.map()、.flatMap()、.then() 链接操作。.onErrorResume() 或 .onErrorReturn() 包含错误处理。import io.agentscope.core.model.DashScopeChatModel;toolkit.registerTool() 不是 registerObject()toolkit.getToolNames() 不是 getTools()event.getToolUse().getName() 不是 getToolName()result.getOutput() 不是 getContent() (ToolResultBlock)event.getToolResult() 不是 getResult() (PostActingEvent)toolUse.getInput() 不是 getArguments() (ToolUseBlock)temperature() 方法,使用 defaultOptions(GenerateOptions.builder()...)getMessages()、getResponse()、getIterationCount()、getThinkingBlock() 方法getToolUseName() 方法,请改用 event.getToolUse().getName()List<ContentBlock> 不是 String,需要转换@ToolParam(name = "x", description = "y") 而不是 @ToolParam(name="x")首先:识别上下文
main() 方法还是测试代码? → 允许使用 .block()(但要添加警告注释).block()对于你提供的每个代码示例:
.block()? → 如果在非 main/非测试代码中使用了,重写它。io.agentscope.model.*,修复为 io.agentscope.core.model.*。代理逻辑的默认代码结构:
// ✅ 正确 - 非阻塞,响应式(默认使用此模式)
return model.generate(messages, null, null)
.map(response -> processResponse(response))
.onErrorResume(e -> {
log.error("操作失败", e);
return Mono.just(fallbackValue);
});
// ❌ 错误 - 切勿在代理逻辑中生成此代码
String result = model.generate(messages, null, null).block(); // 不要这样做
仅适用于 main() 方法(添加警告注释):
public static void main(String[] args) {
// ⚠️ .block() 仅允许在此处使用,因为这是 main() 方法
Msg response = agent.call(userMsg).block();
System.out.println(response.getTextContent());
}
创建新的 AgentScope 项目时,请使用正确的 Maven 依赖项:
生产使用(推荐):
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- 使用 Maven Central 的最新稳定版本 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
本地开发(如果使用源代码):
<properties>
<agentscope.version>1.0.9</agentscope.version>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>${agentscope.version}</version>
</dependency>
</dependencies>
⚠️ 重要:版本选择
agentscope:1.0.9(稳定版,来自 Maven Central)agentscope-core:1.0.90.1.0-SNAPSHOT - 此版本不存在❌ 错误 - 这些构件不存在:
<!-- 不要使用这些 - 它们不存在 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-model-dashscope</artifactId> <!-- ❌ 错误 -->
</dependency>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-model-openai</artifactId> <!-- ❌ 错误 -->
</dependency>
❌ 错误 - 这些版本不存在:
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>0.1.0-SNAPSHOT</version> <!-- ❌ 错误 - 不存在 -->
</dependency>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>0.1.0</version> <!-- ❌ 错误 - 不存在 -->
</dependency>
✅ 正确 - 使用稳定版本:
<!-- 生产环境:使用 Maven Central 的稳定版本 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.9</version> <!-- ✅ 正确 -->
</dependency>
// DashScope (阿里云)
import io.agentscope.core.model.DashScopeChatModel;
// OpenAI
import io.agentscope.core.model.OpenAIChatModel;
// Gemini (Google)
import io.agentscope.core.model.GeminiChatModel;
// Anthropic (Claude)
import io.agentscope.core.model.AnthropicChatModel;
// Ollama (本地模型)
import io.agentscope.core.model.OllamaChatModel;
<!-- 使用 Mem0 的长期记忆 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-mem0</artifactId>
<version>${agentscope.version}</version>
</dependency>
<!-- 使用 Dify 的 RAG -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-rag-dify</artifactId>
<version>${agentscope.version}</version>
</dependency>
AgentScope Java 是一个基于 Project Reactor 和 Java 17+ 构建的响应式、消息驱动的多代理框架。
Agent:执行的基本单位。大多数代理继承 AgentBase。Msg:代理之间交换的消息对象。Memory:存储对话历史(InMemoryMemory、LongTermMemory)。Toolkit 和 AgentTool:定义代理可以使用的功能。Model:与 LLM 交互(OpenAI、DashScope、Gemini、Anthropic 等)。Hook:在代理执行的不同生命周期点拦截和修改。Pipeline:以顺序或并行模式编排多个代理。几乎所有操作(代理调用、模型推理、工具执行)都返回 Mono<T> 或 Flux<T>。
Msg 对象进行通信以 Java 17 (LTS) 为目标,以获得最大兼容性:
var、密封类)@Data、@Builder)⚠️ 关键:避免预览特性
// ❌ 错误 - 需要 Java 21 并启用 --enable-preview
return switch (event) {
case PreReasoningEvent e -> Mono.just(e); // switch 中的模式匹配
default -> Mono.just(event);
};
// ✅ 正确 - 兼容 Java 17
if (event instanceof PreReasoningEvent e) { // instanceof 模式匹配 (Java 17)
return Mono.just(event);
} else {
return Mono.just(event);
}
⚠️ 切勿在代理逻辑中阻塞
阻塞操作会破坏响应式链并导致性能问题。
规则:
.block()(仅在 main 方法或测试中使用)Mono(例如 agent.call())Flux(例如 model.stream()).map()、.flatMap()、.then() 链接操作Mono.defer() 进行惰性求值Mono.deferContextual() 访问响应式上下文示例:
// ❌ 错误 - 阻塞
public Mono<String> processData(String input) {
String result = externalService.call(input).block(); // 不要这样做
return Mono.just(result);
}
// ✅ 正确 - 非阻塞
public Mono<String> processData(String input) {
return externalService.call(input)
.map(this::transform)
.flatMap(this::validate);
}
Msg)使用构建器模式创建消息:
Msg userMsg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.name("user")
.build();
内容块:
TextBlock:用于文本内容ThinkingBlock:用于思维链 (CoT) 推理ToolUseBlock:用于工具调用ToolResultBlock:用于工具输出辅助方法:
// 优先使用安全的辅助方法
String text = msg.getTextContent(); // 安全,如果未找到则返回 null
// 避免直接访问
String text = msg.getContent().get(0).getText(); // 可能抛出 NPE
继承 AgentBase 并实现 doCall(List<Msg> msgs):
public class MyAgent extends AgentBase {
private final Model model;
private final Memory memory;
public MyAgent(String name, Model model) {
super(name, "一个自定义代理", true, List.of());
this.model = model;
this.memory = new InMemoryMemory();
}
@Override
protected Mono<Msg> doCall(List<Msg> msgs) {
// 1. 处理输入
if (msgs != null) {
msgs.forEach(memory::addMessage);
}
// 2. 调用模型或逻辑
return model.generate(memory.getMessages(), null, null)
.map(response -> Msg.builder()
.name(getName())
.role(MsgRole.ASSISTANT)
.content(TextBlock.builder().text(response.getText()).build())
.build());
}
}
使用 @Tool 注解定义基于函数的工具。工具可以返回:
String(同步)Mono<String>(异步)Mono<ToolResultBlock>(用于复杂结果)⚠️ 关键:@ToolParam 格式
@ToolParam(name = "city", description = "城市名称")@ToolParam(name="city", description="...")(= 周围没有空格)@ToolParam("city")(缺少 name= 和 description=)同步工具示例:
public class WeatherTools {
@Tool(description = "获取城市的当前天气。返回温度和状况。")
public String getWeather(
@ToolParam(name = "city", description = "城市名称,例如 'San Francisco'")
String city) {
// 实现
return "Sunny, 25°C";
}
}
异步工具示例:
public class AsyncTools {
private final WebClient webClient;
@Tool(description = "从受信任的 API 端点获取数据")
public Mono<String> fetchData(
@ToolParam(name = "url", description = "API 端点 URL(必须以 https://api.myservice.com 开头)")
String url) {
// 安全性:验证 URL 以防止 SSRF
if (!url.startsWith("https://api.myservice.com")) {
return Mono.just("错误:不允许的 URL。必须以 https://api.myservice.com 开头");
}
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(10))
.onErrorResume(e -> Mono.just("错误:" + e.getMessage()));
}
}
使用 Toolkit 注册:
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());
toolkit.registerTool(new AsyncTools());
钩子允许你在代理执行的不同生命周期点拦截和修改。
public interface Hook {
<T extends HookEvent> Mono<T> onEvent(T event);
default int priority() { return 100; } // 值越低,优先级越高
}
PreReasoningEvent:LLM 推理之前(可修改)PostReasoningEvent:LLM 推理之后(可修改)ReasoningChunkEvent:流式推理块(通知)PreActingEvent:工具执行之前(可修改)PostActingEvent:工具执行之后(可修改)ActingChunkEvent:流式工具执行(通知)兼容 Java 17+(推荐):
Hook loggingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
// 使用 if-instanceof 而不是 switch 模式(兼容 Java 17)
if (event instanceof PreReasoningEvent e) {
log.info("使用模型进行推理:{}", e.getModelName());
return Mono.just(event);
} else if (event instanceof PreActingEvent e) {
log.info("调用工具:{}", e.getToolUse().getName());
return Mono.just(event);
} else if (event instanceof PostActingEvent e) {
log.info("工具 {} 已完成", e.getToolUse().getName());
return Mono.just(event);
} else {
return Mono.just(event);
}
}
@Override
public int priority() {
return 500; // 低优先级(日志记录)
}
};
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.hook(loggingHook)
.build();
替代方案:传统的 if-else(Java 17):
Hook loggingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof PreReasoningEvent) {
PreReasoningEvent e = (PreReasoningEvent) event;
log.info("使用模型进行推理:{}", e.getModelName());
} else if (event instanceof PreActingEvent) {
PreActingEvent e = (PreActingEvent) event;
log.info("调用工具:{}", e.getToolUse().getName());
} else if (event instanceof PostActingEvent) {
PostActingEvent e = (PostActingEvent) event;
log.info("工具 {} 已完成", e.getToolUse().getName());
}
return Mono.just(event);
}
@Override
public int priority() {
return 500;
}
};
优先级指南:
管道以结构化工作流编排多个代理。
按顺序执行代理(一个的输出成为下一个的输入):
SequentialPipeline pipeline = SequentialPipeline.builder()
.addAgent(researchAgent)
.addAgent(summaryAgent)
.addAgent(reviewAgent)
.build();
Msg result = pipeline.execute(userInput).block();
并行执行代理并聚合结果:
FanoutPipeline pipeline = FanoutPipeline.builder()
.addAgent(agent1)
.addAgent(agent2)
.addAgent(agent3)
.build();
Msg result = pipeline.execute(userInput).block();
何时使用:
Memory memory = new InMemoryMemory();
// 配置长期记忆
LongTermMemory longTermMemory = Mem0LongTermMemory.builder()
.apiKey(System.getenv("MEM0_API_KEY"))
.userId("user_123")
.build();
// 与代理一起使用
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.longTermMemory(longTermMemory)
.longTermMemoryMode(LongTermMemoryMode.BOTH) // STATIC_CONTROL, AGENTIC, 或 BOTH
.build();
内存模式:
STATIC_CONTROL:框架自动管理内存(通过钩子)AGENTIC:代理决定何时使用内存(通过工具)BOTH:结合两种方法AgentScope 支持 MCP 以集成外部工具和资源。
// 创建 MCP 客户端
// 安全性:在生产环境中,使用特定版本或本地二进制文件以防止供应链攻击
McpClientWrapper mcpClient = McpClientBuilder.stdio()
.command("npx")
.args("-y", "@modelcontextprotocol/server-filesystem@0.6.2", "/path/to/files") // 始终固定版本
.build();
// 使用 toolkit 注册
Toolkit toolkit = new Toolkit();
toolkit.registration()
.mcpClient(mcpClient)
.enableTools(List.of("read_file", "write_file"))
.apply();
// 与代理一起使用
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.toolkit(toolkit)
.build();
@Test
void testAgentCall() {
Msg input = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.build();
StepVerifier.create(agent.call(input))
.assertNext(response -> {
assertEquals(MsgRole.ASSISTANT, response.getRole());
assertNotNull(response.getTextContent());
})
.verifyComplete();
}
@Test
void testWithMockModel() {
Model mockModel = mock(Model.class);
when(mockModel.generate(any(), any(), any()))
.thenReturn(Mono.just(ChatResponse.builder()
.text("模拟响应")
.build()));
ReActAgent agent = ReActAgent.builder()
.name("TestAgent")
.model(mockModel)
.build();
// 测试代理行为
}
测试最佳实践:
StepVerifier 测试响应式链private static final Logger log = LoggerFactory.getLogger(MyClass.class);
// 使用参数化日志记录
log.info("处理来自用户的消息:{}", userId);
log.error("调用模型失败:{}", modelName, exception);
// 优先使用具体的错误消息
return Mono.error(new IllegalArgumentException(
"无效的模型名称:" + modelName + "。预期为其中之一:" + VALID_MODELS));
// 使用 onErrorResume 进行优雅降级
return model.generate(msgs, null, null)
.onErrorResume(e -> {
log.error("模型调用失败,使用回退", e);
return Mono.just(fallbackResponse);
});
// 对可空返回值使用 Optional
public Optional<AgentTool> findTool(String name) {
return Optional.ofNullable(tools.get(name));
}
// 使用 Objects.requireNonNull 进行验证
public MyAgent(Model model) {
this.model = Objects.requireNonNull(model, "模型不能为空");
}
// 对公共 API 使用 Javadoc
/**
* 使用指定配置创建新代理。
*
* @param name 代理名称(必须唯一)
* @param model 要使用的 LLM 模型
* @return 配置好的代理实例
* @throws IllegalArgumentException 如果名称为空或为空字符串
*/
public static ReActAgent create(String name, Model model) {
// 实现
}
// 谨慎使用内联注释,仅用于复杂逻辑
// 计算指数退避:2^attempt * baseDelay
Duration delay = Duration.ofMillis((long) Math.pow(2, attempt) * baseDelayMs);
Mono、Fluxprivate static final Logger log = LoggerFactory.getLogger(MyClass.class);)在响应式链中阻塞
// ❌ 错误
return someMonoOperation().block();
使用 Thread.sleep() 或阻塞 I/O
// ❌ 错误
Thread.sleep(1000);
// ✅ 正确
return Mono.delay(Duration.ofSeconds(1));
在没有同步的情况下修改共享状态
// ❌ 错误
private List<Msg> messages = new ArrayList<>();
public void addMessage(Msg msg) {
messages.add(msg); // 非线程安全
}
静默忽略错误
// ❌ 错误
.onErrorResume(e -> Mono.empty())
// ✅ 正确
.onErrorResume(e -> {
log.error("操作失败", e);
return Mono.just(fallbackValue);
})
在响应式代码中使用 ThreadLocal
// ❌ 错误
ThreadLocal<String> context = new ThreadLocal<>();
// ✅ 正确
return Mono.deferContextual(ctx -> {
String value = ctx.get("key");
// 使用值
});
创建没有适当资源管理的代理
// ❌ 错误 - 没有清理
public void processRequests() {
for (int i = 0; i < 1000; i++) {
ReActAgent agent = createAgent();
agent.call(msg).block();
}
}
硬编码 API 密钥或密钥
// ❌ 错误
String apiKey = "sk-1234567890";
// ✅ 正确
String apiKey = System.getenv("OPENAI_API_KEY");
使用 Java 预览特性(需要 --enable-preview)
// ❌ 错误 - 需要 Java 21 并启用 --enable-preview
return switch (event) {
case PreReasoningEvent e -> handleReasoning(e);
case PostActingEvent e -> handleActing(e);
default -> Mono.just(event);
};
// ✅ 正确 - 兼容 Java 17
if (event instanceof PreReasoningEvent e) {
return handleReasoning(e);
} else if (event instanceof PostActingEvent e) {
return handleActing(e);
} else {
return Mono.just(event);
}
// 错误
Msg response = agent.call(msg).block(); // 不要在代理逻辑中阻塞
// 正确
return agent.call(msg)
.flatMap(response -> processResponse(response));
// 错误
String text = msg.getContent().get(0).getText(); // 可能抛出 NPE
// 正确
String text = msg.getTextContent(); // 安全的辅助方法
// 或者
String text = msg.getContentBlocks(TextBlock.class).stream()
.findFirst()
.map(TextBlock::getText)
.orElse("");
// 错误
ThreadLocal<String> context = new ThreadLocal<>(); // 在响应式流中可能无效
// 正确
return Mono.deferContextual(ctx -> {
String value = ctx.get("key");
// 使用值
});
// 错误
.onErrorResume(e -> Mono.empty()) // 静默失败
// 正确
.onErrorResume(e -> {
log.error("处理失败:{}", input, e);
return Mono.just(createErrorResponse(e));
})
package com.example.agentscope;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.ReasoningChunkEvent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.Model;
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.core.model.DashScopeChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 展示 AgentScope 最佳实践的完整示例。
*/
public class CompleteExample {
private static final Logger log = LoggerFactory.getLogger(CompleteExample.class);
public static void main(String[] args) {
// 1. 创建模型(没有 .temperature() 方法,使用 defaultOptions)
Model model = DashScopeChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.stream(true)
.build();
// 2. 创建包含工具的工具包
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());
toolkit.registerTool(new TimeTools());
// 3. 为流式输出创建钩子
Hook streamingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof ReasoningChunkEvent e) {
String text = e.getIncrementalChunk().getTextContent();
if (text != null) {
System.out.print(text);
}
}
return Mono.just(event);
}
@Override
public int priority() {
return 500; // 低优先级
}
};
// 4. 构建代理
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.sysPrompt("你是一个乐于助人的助手。在适当的时候使用工具。")
.model(model)
.toolkit(toolkit)
.memory(new InMemoryMemory())
.hook(streamingHook)
.maxIters(10)
.build();
// 5. 使用代理
Msg userMsg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder()
.text("旧金山的天气如何?现在几点了?")
.build())
.build();
try {
System.out.println("用户:" + userMsg.getTextContent());
System.out.print("助手:");
// ⚠️ 重要:.block() 仅允许在 main() 方法中用于演示目的
// 切勿在代理逻辑、服务方法或库代码中使用 .block()
Msg response = agent.call(userMsg).block();
System.out.println("\n\n--- 响应详情 ---");
System.out.println("角色:" + response.getRole());
System.out.println("内容:" + response.getTextContent());
} catch (Exception e) {
log.error("代理执行期间出错", e);
System.err.println("错误:" + e.getMessage());
}
}
/**
* 天气信息的示例工具类。
*/
public static class WeatherTools {
@Tool(description = "获取城市的当前天气。返回温度和状况。")
public String getWeather(
@ToolParam(name = "city", description = "城市名称,例如 'San Francisco'")
String city) {
log.info("获取城市天气:{}", city);
// 模拟 API 调用
return String.format("%s 的天气:晴朗,22°C,微风", city);
}
}
/**
* 时间信息的示例工具类。
*/
public static class TimeTools {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Tool(description = "获取当前日期和时间")
public String getCurrentTime() {
LocalDateTime now = LocalDateTime.now();
String formatted = now.format(FORMATTER);
log.info("返回当前时间:{}", formatted);
return "当前时间:" + formatted;
}
}
}
ReActAgent agent = ReActAgent.builder()
.name("AgentName")
.sysPrompt("系统提示")
.model(model)
.toolkit(toolkit)
.memory(memory)
.hooks(hooks)
.maxIters(10)
.build();
Msg msg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.build();
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new MyTools());
Hook hook = new Hook() {
public <T extends HookEvent> Mono<T> onEvent(T event) {
// 处理事件
return Mono.just(event);
}
};
SequentialPipeline pipeline = SequentialPipeline.builder()
.addAgent(agent1)
.addAgent(agent2)
.build();
每周安装次数
39
仓库
GitHub 星标数
1.6K
首次出现
2026年1月21日
安全审计
安装于
codex30
gemini-cli29
opencode29
claude-code29
cursor24
github-copilot22
When the user asks you to write AgentScope Java code, follow these instructions carefully.
🚫 ABSOLUTELY FORBIDDEN:
.block() in example code - This is the #1 mistake. Only use .block() in main() methods or test code when explicitly creating a runnable example.Thread.sleep() - Use Mono.delay() instead.ThreadLocal - Use Reactor Context with Mono.deferContextual().System.getenv().io.agentscope.core.model.*, NOT io.agentscope.model.*.✅ ALWAYS DO:
Mono and Flux for all asynchronous operations..map(), .flatMap(), .then()..onErrorResume() or .onErrorReturn().import io.agentscope.core.model.DashScopeChatModel;FIRST: Identify the context
main() method or test code? → .block() is allowed (but add a warning comment).block() is FORBIDDENFor every code example you provide:
.block()? → If yes in non-main/non-test code, REWRITE IT.io.agentscope.model.*, FIX TO io.agentscope.core.model.*.Default code structure for agent logic:
// ✅ CORRECT - Non-blocking, reactive (use this pattern by default)
return model.generate(messages, null, null)
.map(response -> processResponse(response))
.onErrorResume(e -> {
log.error("Operation failed", e);
return Mono.just(fallbackValue);
});
// ❌ WRONG - Never generate this in agent logic
String result = model.generate(messages, null, null).block(); // DON'T DO THIS
Only for main() methods (add warning comment):
public static void main(String[] args) {
// ⚠️ .block() is ONLY allowed here because this is a main() method
Msg response = agent.call(userMsg).block();
System.out.println(response.getTextContent());
}
When creating a new AgentScope project, use the correct Maven dependencies:
For production use (recommended):
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Use the latest stable release from Maven Central -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
For local development (if working with source code):
<properties>
<agentscope.version>1.0.9</agentscope.version>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>${agentscope.version}</version>
</dependency>
</dependencies>
⚠️ IMPORTANT: Version Selection
agentscope:1.0.9 for production (stable, from Maven Central)agentscope-core:1.0.9 only if you're developing AgentScope itself0.1.0-SNAPSHOT - this version doesn't exist❌ WRONG - These artifacts don't exist:
<!-- DON'T use these - they don't exist -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-model-dashscope</artifactId> <!-- ❌ WRONG -->
</dependency>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-model-openai</artifactId> <!-- ❌ WRONG -->
</dependency>
❌ WRONG - These versions don't exist:
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>0.1.0-SNAPSHOT</version> <!-- ❌ WRONG - doesn't exist -->
</dependency>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>0.1.0</version> <!-- ❌ WRONG - doesn't exist -->
</dependency>
✅ CORRECT - Use the stable release:
<!-- For production: use the stable release from Maven Central -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.9</version> <!-- ✅ CORRECT -->
</dependency>
// DashScope (Alibaba Cloud)
import io.agentscope.core.model.DashScopeChatModel;
// OpenAI
import io.agentscope.core.model.OpenAIChatModel;
// Gemini (Google)
import io.agentscope.core.model.GeminiChatModel;
// Anthropic (Claude)
import io.agentscope.core.model.AnthropicChatModel;
// Ollama (Local models)
import io.agentscope.core.model.OllamaChatModel;
<!-- Long-term memory with Mem0 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-mem0</artifactId>
<version>${agentscope.version}</version>
</dependency>
<!-- RAG with Dify -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-rag-dify</artifactId>
<version>${agentscope.version}</version>
</dependency>
AgentScope Java is a reactive, message-driven multi-agent framework built on Project Reactor and Java 17+.
Agent : The fundamental unit of execution. Most agents extend AgentBase.Msg : The message object exchanged between agents.Memory : Stores conversation history (InMemoryMemory, LongTermMemory).Toolkit & AgentTool: Defines capabilities the agent can use.Model : Interfaces with LLMs (OpenAI, DashScope, Gemini, Anthropic, etc.).Almost all operations (agent calls, model inference, tool execution) return Mono<T> or Flux<T>.
Msg objectsTarget Java 17 (LTS) for maximum compatibility:
var, Sealed classes)@Data, @Builder for DTOs/Messages)⚠️ CRITICAL: Avoid Preview Features
// ❌ WRONG - Requires Java 21 with --enable-preview
return switch (event) {
case PreReasoningEvent e -> Mono.just(e); // Pattern matching in switch
default -> Mono.just(event);
};
// ✅ CORRECT - Java 17 compatible
if (event instanceof PreReasoningEvent e) { // Pattern matching for instanceof (Java 17)
return Mono.just(event);
} else {
return Mono.just(event);
}
⚠️ NEVER BLOCK IN AGENT LOGIC
Blocking operations will break the reactive chain and cause performance issues.
Rules:
.block() in agent logic (only in main methods or tests)Mono for single results (e.g., agent.call())Flux for streaming responses (e.g., model.stream()).map(), .flatMap(), .then()Mono.defer() for lazy evaluationExample:
// ❌ WRONG - Blocking
public Mono<String> processData(String input) {
String result = externalService.call(input).block(); // DON'T DO THIS
return Mono.just(result);
}
// ✅ CORRECT - Non-blocking
public Mono<String> processData(String input) {
return externalService.call(input)
.map(this::transform)
.flatMap(this::validate);
}
Msg)Create messages using the Builder pattern:
Msg userMsg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.name("user")
.build();
Content Blocks:
TextBlock : For text contentThinkingBlock : For Chain of Thought (CoT) reasoningToolUseBlock : For tool callsToolResultBlock : For tool outputsHelper Methods:
// Prefer safe helper methods
String text = msg.getTextContent(); // Safe, returns null if not found
// Avoid direct access
String text = msg.getContent().get(0).getText(); // May throw NPE
Extend AgentBase and implement doCall(List<Msg> msgs):
public class MyAgent extends AgentBase {
private final Model model;
private final Memory memory;
public MyAgent(String name, Model model) {
super(name, "A custom agent", true, List.of());
this.model = model;
this.memory = new InMemoryMemory();
}
@Override
protected Mono<Msg> doCall(List<Msg> msgs) {
// 1. Process inputs
if (msgs != null) {
msgs.forEach(memory::addMessage);
}
// 2. Call model or logic
return model.generate(memory.getMessages(), null, null)
.map(response -> Msg.builder()
.name(getName())
.role(MsgRole.ASSISTANT)
.content(TextBlock.builder().text(response.getText()).build())
.build());
}
}
Use @Tool annotation for function-based tools. Tools can return:
String (synchronous)Mono<String> (asynchronous)Mono<ToolResultBlock> (for complex results)⚠️ CRITICAL: @ToolParam Format
@ToolParam(name = "city", description = "City name")@ToolParam(name="city", description="...") (no spaces around =)@ToolParam("city") (missing name= and description=)Synchronous Tool Example:
public class WeatherTools {
@Tool(description = "Get current weather for a city. Returns temperature and conditions.")
public String getWeather(
@ToolParam(name = "city", description = "City name, e.g., 'San Francisco'")
String city) {
// Implementation
return "Sunny, 25°C";
}
}
Asynchronous Tool Example:
public class AsyncTools {
private final WebClient webClient;
@Tool(description = "Fetch data from trusted API endpoint")
public Mono<String> fetchData(
@ToolParam(name = "url", description = "API endpoint URL (must start with https://api.myservice.com)")
String url) {
// SECURITY: Validate URL to prevent SSRF
if (!url.startsWith("https://api.myservice.com")) {
return Mono.just("Error: URL not allowed. Must start with https://api.myservice.com");
}
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(10))
.onErrorResume(e -> Mono.just("Error: " + e.getMessage()));
}
}
Register with Toolkit:
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());
toolkit.registerTool(new AsyncTools());
Hooks allow you to intercept and modify agent execution at various lifecycle points.
public interface Hook {
<T extends HookEvent> Mono<T> onEvent(T event);
default int priority() { return 100; } // Lower = higher priority
}
PreReasoningEvent : Before LLM reasoning (modifiable)PostReasoningEvent : After LLM reasoning (modifiable)ReasoningChunkEvent : Streaming reasoning chunks (notification)PreActingEvent : Before tool execution (modifiable)PostActingEvent : After tool execution (modifiable)ActingChunkEvent : Streaming tool execution (notification)Java 17+ compatible (recommended):
Hook loggingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
// Use if-instanceof instead of switch patterns (Java 17 compatible)
if (event instanceof PreReasoningEvent e) {
log.info("Reasoning with model: {}", e.getModelName());
return Mono.just(event);
} else if (event instanceof PreActingEvent e) {
log.info("Calling tool: {}", e.getToolUse().getName());
return Mono.just(event);
} else if (event instanceof PostActingEvent e) {
log.info("Tool {} completed", e.getToolUse().getName());
return Mono.just(event);
} else {
return Mono.just(event);
}
}
@Override
public int priority() {
return 500; // Low priority (logging)
}
};
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.hook(loggingHook)
.build();
Alternative: Traditional if-else (Java 17):
Hook loggingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof PreReasoningEvent) {
PreReasoningEvent e = (PreReasoningEvent) event;
log.info("Reasoning with model: {}", e.getModelName());
} else if (event instanceof PreActingEvent) {
PreActingEvent e = (PreActingEvent) event;
log.info("Calling tool: {}", e.getToolUse().getName());
} else if (event instanceof PostActingEvent) {
PostActingEvent e = (PostActingEvent) event;
log.info("Tool {} completed", e.getToolUse().getName());
}
return Mono.just(event);
}
@Override
public int priority() {
return 500;
}
};
Priority Guidelines:
Pipelines orchestrate multiple agents in structured workflows.
Executes agents in sequence (output of one becomes input of next):
SequentialPipeline pipeline = SequentialPipeline.builder()
.addAgent(researchAgent)
.addAgent(summaryAgent)
.addAgent(reviewAgent)
.build();
Msg result = pipeline.execute(userInput).block();
Executes agents in parallel and aggregates results:
FanoutPipeline pipeline = FanoutPipeline.builder()
.addAgent(agent1)
.addAgent(agent2)
.addAgent(agent3)
.build();
Msg result = pipeline.execute(userInput).block();
When to Use:
Memory memory = new InMemoryMemory();
// Configure long-term memory
LongTermMemory longTermMemory = Mem0LongTermMemory.builder()
.apiKey(System.getenv("MEM0_API_KEY"))
.userId("user_123")
.build();
// Use with agent
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.longTermMemory(longTermMemory)
.longTermMemoryMode(LongTermMemoryMode.BOTH) // STATIC_CONTROL, AGENTIC, or BOTH
.build();
Memory Modes:
STATIC_CONTROL : Framework automatically manages memory (via hooks)AGENTIC : Agent decides when to use memory (via tools)BOTH : Combines both approachesAgentScope supports MCP for integrating external tools and resources.
// Create MCP client
// SECURITY: In production, use a specific version or a local binary to prevent supply chain attacks
McpClientWrapper mcpClient = McpClientBuilder.stdio()
.command("npx")
.args("-y", "@modelcontextprotocol/server-filesystem@0.6.2", "/path/to/files") // Always pin versions
.build();
// Register with toolkit
Toolkit toolkit = new Toolkit();
toolkit.registration()
.mcpClient(mcpClient)
.enableTools(List.of("read_file", "write_file"))
.apply();
// Use with agent
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.toolkit(toolkit)
.build();
@Test
void testAgentCall() {
Msg input = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.build();
StepVerifier.create(agent.call(input))
.assertNext(response -> {
assertEquals(MsgRole.ASSISTANT, response.getRole());
assertNotNull(response.getTextContent());
})
.verifyComplete();
}
@Test
void testWithMockModel() {
Model mockModel = mock(Model.class);
when(mockModel.generate(any(), any(), any()))
.thenReturn(Mono.just(ChatResponse.builder()
.text("Mocked response")
.build()));
ReActAgent agent = ReActAgent.builder()
.name("TestAgent")
.model(mockModel)
.build();
// Test agent behavior
}
Testing Best Practices:
StepVerifierprivate static final Logger log = LoggerFactory.getLogger(MyClass.class);
// Use parameterized logging
log.info("Processing message from user: {}", userId);
log.error("Failed to call model: {}", modelName, exception);
// Prefer specific error messages
return Mono.error(new IllegalArgumentException(
"Invalid model name: " + modelName + ". Expected one of: " + VALID_MODELS));
// Use onErrorResume for graceful degradation
return model.generate(msgs, null, null)
.onErrorResume(e -> {
log.error("Model call failed, using fallback", e);
return Mono.just(fallbackResponse);
});
// Use Optional for nullable returns
public Optional<AgentTool> findTool(String name) {
return Optional.ofNullable(tools.get(name));
}
// Use Objects.requireNonNull for validation
public MyAgent(Model model) {
this.model = Objects.requireNonNull(model, "Model cannot be null");
}
// Use Javadoc for public APIs
/**
* Creates a new agent with the specified configuration.
*
* @param name The agent name (must be unique)
* @param model The LLM model to use
* @return Configured agent instance
* @throws IllegalArgumentException if name is null or empty
*/
public static ReActAgent create(String name, Model model) {
// Implementation
}
// Use inline comments sparingly, only for complex logic
// Calculate exponential backoff: 2^attempt * baseDelay
Duration delay = Duration.ofMillis((long) Math.pow(2, attempt) * baseDelayMs);
Mono, Flux for reactive programmingprivate static final Logger log = LoggerFactory.getLogger(MyClass.class);)Block in reactive chains
// ❌ WRONG
return someMonoOperation().block();
Use Thread.sleep() or blocking I/O
// ❌ WRONG
Thread.sleep(1000);
// ✅ CORRECT
return Mono.delay(Duration.ofSeconds(1));
Mutate shared state without synchronization
// ❌ WRONG
private List<Msg> messages = new ArrayList<>();
public void addMessage(Msg msg) {
messages.add(msg); // Not thread-safe
}
Ignore errors silently
// ❌ WRONG
.onErrorResume(e -> Mono.empty())
// ✅ CORRECT
.onErrorResume(e -> {
log.error("Operation failed", e);
return Mono.just(fallbackValue);
})
Use ThreadLocal in reactive code
// ❌ WRONG
ThreadLocal<String> context = new ThreadLocal<>();
// ✅ CORRECT
return Mono.deferContextual(ctx -> {
String value = ctx.get("key");
// Use value
});
// WRONG
Msg response = agent.call(msg).block(); // Don't block in agent logic
// CORRECT
return agent.call(msg)
.flatMap(response -> processResponse(response));
// WRONG
String text = msg.getContent().get(0).getText(); // May throw NPE
// CORRECT
String text = msg.getTextContent(); // Safe helper method
// OR
String text = msg.getContentBlocks(TextBlock.class).stream()
.findFirst()
.map(TextBlock::getText)
.orElse("");
// WRONG
ThreadLocal<String> context = new ThreadLocal<>(); // May not work in reactive streams
// CORRECT
return Mono.deferContextual(ctx -> {
String value = ctx.get("key");
// Use value
});
// WRONG
.onErrorResume(e -> Mono.empty()) // Silent failure
// CORRECT
.onErrorResume(e -> {
log.error("Failed to process: {}", input, e);
return Mono.just(createErrorResponse(e));
})
package com.example.agentscope;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.ReasoningChunkEvent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.Model;
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.core.model.DashScopeChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* Complete example demonstrating AgentScope best practices.
*/
public class CompleteExample {
private static final Logger log = LoggerFactory.getLogger(CompleteExample.class);
public static void main(String[] args) {
// 1. Create model (no .temperature() method, use defaultOptions)
Model model = DashScopeChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.stream(true)
.build();
// 2. Create toolkit with tools
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());
toolkit.registerTool(new TimeTools());
// 3. Create hook for streaming output
Hook streamingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof ReasoningChunkEvent e) {
String text = e.getIncrementalChunk().getTextContent();
if (text != null) {
System.out.print(text);
}
}
return Mono.just(event);
}
@Override
public int priority() {
return 500; // Low priority
}
};
// 4. Build agent
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.sysPrompt("You are a helpful assistant. Use tools when appropriate.")
.model(model)
.toolkit(toolkit)
.memory(new InMemoryMemory())
.hook(streamingHook)
.maxIters(10)
.build();
// 5. Use agent
Msg userMsg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder()
.text("What's the weather in San Francisco and what time is it?")
.build())
.build();
try {
System.out.println("User: " + userMsg.getTextContent());
System.out.print("Assistant: ");
// ⚠️ IMPORTANT: .block() is ONLY allowed in main() methods for demo purposes
// NEVER use .block() in agent logic, service methods, or library code
Msg response = agent.call(userMsg).block();
System.out.println("\n\n--- Response Details ---");
System.out.println("Role: " + response.getRole());
System.out.println("Content: " + response.getTextContent());
} catch (Exception e) {
log.error("Error during agent execution", e);
System.err.println("Error: " + e.getMessage());
}
}
/**
* Example tool class for weather information.
*/
public static class WeatherTools {
@Tool(description = "Get current weather for a city. Returns temperature and conditions.")
public String getWeather(
@ToolParam(name = "city", description = "City name, e.g., 'San Francisco'")
String city) {
log.info("Getting weather for city: {}", city);
// Simulate API call
return String.format("Weather in %s: Sunny, 22°C, Light breeze", city);
}
}
/**
* Example tool class for time information.
*/
public static class TimeTools {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Tool(description = "Get current date and time")
public String getCurrentTime() {
LocalDateTime now = LocalDateTime.now();
String formatted = now.format(FORMATTER);
log.info("Returning current time: {}", formatted);
return "Current time: " + formatted;
}
}
}
ReActAgent agent = ReActAgent.builder()
.name("AgentName")
.sysPrompt("System prompt")
.model(model)
.toolkit(toolkit)
.memory(memory)
.hooks(hooks)
.maxIters(10)
.build();
Msg msg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.build();
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new MyTools());
Hook hook = new Hook() {
public <T extends HookEvent> Mono<T> onEvent(T event) {
// Handle event
return Mono.just(event);
}
};
SequentialPipeline pipeline = SequentialPipeline.builder()
.addAgent(agent1)
.addAgent(agent2)
.build();
Weekly Installs
39
Repository
GitHub Stars
1.6K
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubFailSocketWarnSnykWarn
Installed on
codex30
gemini-cli29
opencode29
claude-code29
cursor24
github-copilot22
AI界面设计评审工具 - 全面评估UI/UX设计质量、检测AI生成痕迹与优化用户体验
58,500 周安装
Cairo/StarkNet 智能合约漏洞扫描器 | 6大安全模式检测与静态分析
1,500 周安装
如何创建智能体技能:模块化扩展AI能力,包含工作流程、工具集成与专业知识
1,500 周安装
marimo Python笔记本教程:交互式数据科学工具,替代Jupyter的响应式开发
1,500 周安装
OSS-Fuzz 开源模糊测试工具:Google 免费分布式安全测试平台使用指南
1,600 周安装
libFuzzer模糊测试工具:LLVM进程内覆盖率引导的C/C++代码安全测试
1,600 周安装
Culture Index 解读指南:行为特质分析、团队构成评估与倦怠风险检测
1,600 周安装
toolkit.registerTool() NOT registerObject()toolkit.getToolNames() NOT getTools()event.getToolUse().getName() NOT getToolName()result.getOutput() NOT getContent() (ToolResultBlock)event.getToolResult() NOT getResult() (PostActingEvent)toolUse.getInput() NOT getArguments() (ToolUseBlock)temperature() method, use defaultOptions(GenerateOptions.builder()...)getMessages(), getResponse(), getIterationCount(), getThinkingBlock() methodsgetToolUseName() method, use event.getToolUse().getName() insteadList<ContentBlock> NOT String, need to convert@ToolParam(name = "x", description = "y") NOT @ToolParam(name="x")Hook : Intercepts and modifies agent execution at various lifecycle points.Pipeline : Orchestrates multiple agents in sequential or parallel patterns.Mono.deferContextual()Create agents without proper resource management
// ❌ WRONG - No cleanup
public void processRequests() {
for (int i = 0; i < 1000; i++) {
ReActAgent agent = createAgent();
agent.call(msg).block();
}
}
Hardcode API keys or secrets
// ❌ WRONG
String apiKey = "sk-1234567890";
// ✅ CORRECT
String apiKey = System.getenv("OPENAI_API_KEY");
Use Java preview features (requires --enable-preview)
// ❌ WRONG - Requires Java 21 with --enable-preview
return switch (event) {
case PreReasoningEvent e -> handleReasoning(e);
case PostActingEvent e -> handleActing(e);
default -> Mono.just(event);
};
// ✅ CORRECT - Java 17 compatible
if (event instanceof PreReasoningEvent e) {
return handleReasoning(e);
} else if (event instanceof PostActingEvent e) {
return handleActing(e);
} else {
return Mono.just(event);
}