Learn-skills.dev agentscope-java
Expert Java developer skill for AgentScope Java framework - a reactive, message-driven multi-agent system built on Project Reactor. Use when working with reactive programming, LLM integration, agent orchestration, multi-agent systems, or when the user mentions AgentScope, ReActAgent, Mono/Flux, Project Reactor, or Java agent development. Specializes in non-blocking code, tool integration, hooks, pipelines, and production-ready agent applications.
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agentscope-ai/agentscope-java/agentscope-java" ~/.claude/skills/neversight-learn-skills-dev-agentscope-java && rm -rf "$T"
data/skills-md/agentscope-ai/agentscope-java/agentscope-java/SKILL.mdWhen the user asks you to write AgentScope Java code, follow these instructions carefully.
CRITICAL RULES - NEVER VIOLATE THESE
🚫 ABSOLUTELY FORBIDDEN:
- NEVER use
in example code - This is the #1 mistake. Only use.block()
in.block()
methods or test code when explicitly creating a runnable example.main() - NEVER use
- UseThread.sleep()
instead.Mono.delay() - NEVER use
- Use Reactor Context withThreadLocal
.Mono.deferContextual() - NEVER hardcode API keys - Always use
.System.getenv() - NEVER ignore errors silently - Always log errors and provide fallback values.
- NEVER use wrong import paths - All models are in
, NOTio.agentscope.core.model.*
.io.agentscope.model.*
✅ ALWAYS DO:
- Use
andMono
for all asynchronous operations.Flux - Chain operations with
,.map()
,.flatMap()
..then() - Use Builder pattern for creating agents, models, and messages.
- Include error handling with
or.onErrorResume()
..onErrorReturn() - Add logging with SLF4J for important operations.
- Use correct imports:
import io.agentscope.core.model.DashScopeChatModel; - Use correct APIs (many methods don't exist or have changed):
NOTtoolkit.registerTool()registerObject()
NOTtoolkit.getToolNames()getTools()
NOTevent.getToolUse().getName()getToolName()
NOTresult.getOutput()
(ToolResultBlock)getContent()
NOTevent.getToolResult()
(PostActingEvent)getResult()
NOTtoolUse.getInput()
(ToolUseBlock)getArguments()- Model builder: NO
method, usetemperature()defaultOptions(GenerateOptions.builder()...) - Hook events: NO
,getMessages()
,getResponse()
,getIterationCount()
methodsgetThinkingBlock() - ToolResultBlock: NO
method, usegetToolUseName()
insteadevent.getToolUse().getName() - ToolResultBlock.getOutput() returns
NOTList<ContentBlock>
, need to convertString - @ToolParam format: MUST use
NOT@ToolParam(name = "x", description = "y")@ToolParam(name="x")
WHEN GENERATING CODE
FIRST: Identify the context
- Is this a
method or test code? →main()
is allowed (but add a warning comment).block() - Is this agent logic, service method, or library code? →
is FORBIDDEN.block()
For every code example you provide:
- Check: Does it use
? → If yes in non-main/non-test code, REWRITE IT..block() - Check: Are all operations non-blocking? → If no, FIX IT.
- Check: Does it have error handling? → If no, ADD IT.
- Check: Are API keys from environment? → If no, CHANGE IT.
- Check: Are imports correct? → If using
, FIX TOio.agentscope.model.*
.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()); }
PROJECT SETUP
When creating a new AgentScope project, use the correct Maven dependencies:
Maven Configuration (pom.xml)
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.8</version> </dependency> </dependencies>
For local development (if working with source code):
<properties> <agentscope.version>1.0.8</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
- Use
for production (stable, from Maven Central)agentscope:1.0.8 - Use
only if you're developing AgentScope itselfagentscope-core:1.0.8 - NEVER use version
- this version doesn't exist0.1.0-SNAPSHOT
⚠️ CRITICAL: Common Dependency Mistakes
❌ 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.8</version> <!-- ✅ CORRECT --> </dependency>
Available Model Classes (all in agentscope-core)
// 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;
Optional Extensions
<!-- 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>
PROJECT OVERVIEW & ARCHITECTURE
AgentScope Java is a reactive, message-driven multi-agent framework built on Project Reactor and Java 17+.
Core Abstractions
: The fundamental unit of execution. Most agents extendAgent
.AgentBase
: The message object exchanged between agents.Msg
: Stores conversation history (Memory
,InMemoryMemory
).LongTermMemory
&Toolkit
: Defines capabilities the agent can use.AgentTool
: Interfaces with LLMs (OpenAI, DashScope, Gemini, Anthropic, etc.).Model
: Intercepts and modifies agent execution at various lifecycle points.Hook
: Orchestrates multiple agents in sequential or parallel patterns.Pipeline
Reactive Nature
Almost all operations (agent calls, model inference, tool execution) return
Mono<T> or Flux<T>.
Key Design Principles
- Non-blocking: All I/O operations are asynchronous
- Message-driven: Agents communicate via immutable
objectsMsg - Composable: Agents and pipelines can be nested and combined
- Extensible: Hooks and custom tools allow deep customization
CODING STANDARDS & BEST PRACTICES
2.1 Java Version & Style
Target Java 17 (LTS) for maximum compatibility:
- Use Java 17 features (Records, Switch expressions, Pattern Matching for instanceof,
, Sealed classes)var - AVOID Java 21+ preview features (pattern matching in switch, record patterns)
- Follow standard Java conventions (PascalCase for classes, camelCase for methods/variables)
- Use Lombok where appropriate (
,@Data
for DTOs/Messages)@Builder - Prefer immutability for data classes
- Use meaningful names that reflect domain concepts
⚠️ 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); }
2.2 Reactive Programming (Critical)
⚠️ NEVER BLOCK IN AGENT LOGIC
Blocking operations will break the reactive chain and cause performance issues.
Rules:
- ❌ Never use
in agent logic (only in.block()
methods or tests)main - ✅ Use
for single results (e.g.,Mono
)agent.call() - ✅ Use
for streaming responses (e.g.,Flux
)model.stream() - ✅ Chain operations using
,.map()
,.flatMap().then() - ✅ Use
for lazy evaluationMono.defer() - ✅ Use
for reactive context accessMono.deferContextual()
Example:
// ❌ 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); }
2.3 Message Handling (Msg
)
MsgCreate messages using the Builder pattern:
Msg userMsg = Msg.builder() .role(MsgRole.USER) .content(TextBlock.builder().text("Hello").build()) .name("user") .build();
Content Blocks:
: For text contentTextBlock
: For Chain of Thought (CoT) reasoningThinkingBlock
: For tool callsToolUseBlock
: For tool outputsToolResultBlock
Helper 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
2.4 Implementing Agents
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()); } }
2.5 Tool Definition
Use
@Tool annotation for function-based tools. Tools can return:
(synchronous)String
(asynchronous)Mono<String>
(for complex results)Mono<ToolResultBlock>
⚠️ CRITICAL: @ToolParam Format
- ✅ CORRECT:
@ToolParam(name = "city", description = "City name") - ❌ WRONG:
(no spaces around@ToolParam(name="city", description="...")
)= - ❌ WRONG:
(missing name= and description=)@ToolParam("city")
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());
HOOK SYSTEM
Hooks allow you to intercept and modify agent execution at various lifecycle points.
Hook Interface
public interface Hook { <T extends HookEvent> Mono<T> onEvent(T event); default int priority() { return 100; } // Lower = higher priority }
Common Hook Events
: Before LLM reasoning (modifiable)PreReasoningEvent
: After LLM reasoning (modifiable)PostReasoningEvent
: Streaming reasoning chunks (notification)ReasoningChunkEvent
: Before tool execution (modifiable)PreActingEvent
: After tool execution (modifiable)PostActingEvent
: Streaming tool execution (notification)ActingChunkEvent
Hook Example
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:
- 0-50: Critical system hooks (auth, security)
- 51-100: High priority hooks (validation, preprocessing)
- 101-500: Normal priority hooks (business logic)
- 501-1000: Low priority hooks (logging, metrics)
PIPELINE PATTERNS
Pipelines orchestrate multiple agents in structured workflows.
Sequential Pipeline
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();
Fanout Pipeline
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:
- Sequential: When each agent depends on the previous agent's output
- Fanout: When agents can work independently and results need aggregation
MEMORY MANAGEMENT
In-Memory (Short-term)
Memory memory = new InMemoryMemory();
Long-Term Memory
// 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:
: Framework automatically manages memory (via hooks)STATIC_CONTROL
: Agent decides when to use memory (via tools)AGENTIC
: Combines both approachesBOTH
MCP (MODEL CONTEXT PROTOCOL) INTEGRATION
AgentScope 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();
TESTING
Unit Testing with StepVerifier
@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(); }
Mocking External Dependencies
@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:
- Always test reactive chains with
StepVerifier - Mock external dependencies (models, APIs)
- Test error cases and edge conditions
- Verify that hooks are called correctly
- Test timeout and cancellation scenarios
CODE STYLE GUIDE
Logging
private 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);
Error Handling
// 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); });
Null Safety
// 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"); }
Comments
// 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);
KEY LIBRARIES
- Reactor Core:
,Mono
for reactive programmingFlux - Jackson: JSON serialization/deserialization
- SLF4J: Logging (
)private static final Logger log = LoggerFactory.getLogger(MyClass.class); - OkHttp: HTTP client for model APIs
- MCP SDK: Model Context Protocol integration
- JUnit 5: Testing framework
- Mockito: Mocking framework
- Lombok: Boilerplate reduction
PROHIBITED PRACTICES
❌ NEVER Do These
-
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 }); -
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); }
COMMON PITFALLS & SOLUTIONS
❌ Blocking Operations
// WRONG Msg response = agent.call(msg).block(); // Don't block in agent logic
// CORRECT return agent.call(msg) .flatMap(response -> processResponse(response));
❌ Null Handling
// 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("");
❌ Thread Context
// WRONG ThreadLocal<String> context = new ThreadLocal<>(); // May not work in reactive streams
// CORRECT return Mono.deferContextual(ctx -> { String value = ctx.get("key"); // Use value });
❌ Error Swallowing
// WRONG .onErrorResume(e -> Mono.empty()) // Silent failure
// CORRECT .onErrorResume(e -> { log.error("Failed to process: {}", input, e); return Mono.just(createErrorResponse(e)); })
COMPLETE EXAMPLE
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; } } }
QUICK REFERENCE
Agent Creation
ReActAgent agent = ReActAgent.builder() .name("AgentName") .sysPrompt("System prompt") .model(model) .toolkit(toolkit) .memory(memory) .hooks(hooks) .maxIters(10) .build();
Message Creation
Msg msg = Msg.builder() .role(MsgRole.USER) .content(TextBlock.builder().text("Hello").build()) .build();
Tool Registration
Toolkit toolkit = new Toolkit(); toolkit.registerTool(new MyTools());
Hook Creation
Hook hook = new Hook() { public <T extends HookEvent> Mono<T> onEvent(T event) { // Handle event return Mono.just(event); } };
Pipeline Creation
SequentialPipeline pipeline = SequentialPipeline.builder() .addAgent(agent1) .addAgent(agent2) .build();