agentscope-java について
agentscope-javaは、Project ReactorとJava 17以降を基盤とするリアクティブなマルチエージェントフレームワークです。このスキルは、非ブロッキングな実装や設定など、APIの適切な使い方をコーディングエージェントに案内します。
主な機能
- 01Reactorによる非同期エージェント操作
- 02メッセージ駆動のエージェント通信
- 03直列・並列パイプラインの構成
- 04Toolkitのツールとライフサイクルフック
- 05Java 17の設定とAPIガイド
活用シーン
- 01非ブロッキングなAgentScope Javaロジックの作成
- 02モデルとMaven依存関係の設定
- 03ツール、フック、エージェントパイプラインの作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agentscope-ai/ agentscope-java --skill agentscope-javaインストール後、Claude Code で/agentscope-javaを入力して読み込まれたか確認します。
詳細
AgentScope Java のコードを書くよう依頼された場合は、以下の指示に注意深く従ってください。
重大なルール — 絶対に違反しないでください
🚫 絶対に禁止:
- サンプルコードで
.block()を絶対に使用しないでください — これは最もよくある間違いです。実行可能なサンプルを明示的に作成する場合に限り、main()メソッドまたはテストコードで.block()を使用してください。 Thread.sleep()を絶対に使用しないでください — 代わりにMono.delay()を使用してください。ThreadLocalを絶対に使用しないでください —Mono.deferContextual()で Reactor Context を使用してください。- API キーを絶対にハードコードしないでください — 常に
System.getenv()を使用してください。 - エラーを絶対に黙って無視しないでください — 常にエラーをログに記録し、フォールバック値を指定してください。
- 誤ったインポートパスを絶対に使用しないでください — 共有モデルインターフェースは
io.agentscope.core.model.*に、プロバイダーモデルはio.agentscope.extensions.model.<provider>.*にあります。
✅ 必ず行うこと:
- すべての非同期操作に
MonoとFluxを使用してください。 .map()、.flatMap()、.then()で操作をチェーンしてください。- エージェント、モデル、メッセージの作成には Builder パターンを使用してください。
.onErrorResume()または.onErrorReturn()でエラー処理を追加してください。- 重要な操作には SLF4J によるログ記録を追加してください。
- 正しいインポートを使用してください:
import io.agentscope.extensions.model.dashscope.DashScopeChatModel; - 正しい API を使用してください(多くのメソッドは存在しないか、変更されています):
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()メソッドがありません - ToolResultBlock に
getToolUseName()メソッドはありません。代わりにevent.getToolUse().getName()を使用してください - ToolResultBlock.getOutput() は
StringではなくList<ContentBlock>を返すため、変換が必要です - @ToolParam の形式:
@ToolParam(name="x")ではなく、必ず@ToolParam(name = "x", description = "y")を使用してください
コードを生成する場合
最初に:コンテキストを特定してください
- これは
main()メソッドまたはテストコードですか?→.block()を使用できます(ただし警告コメントを追加してください) - これはエージェントのロジック、サービスメソッド、またはライブラリコードですか?→
.block()は 禁止 です
提示するすべてのコード例について:
- 確認:
.block()を使用していますか?→ はいの場合、main/テスト以外のコードでは 書き直してください。 - 確認:すべての操作がノンブロッキングですか?→ いいえの場合、修正してください。
- 確認:エラー処理がありますか?→ ない場合、追加してください。
- 確認:API キーは環境変数から取得していますか?→ いいえの場合、変更してください。
- 確認:インポートは正しいですか?→ プロバイダーモデルを
io.agentscope.core.model.*からインポートしている場合は、io.agentscope.extensions.model.<provider>.*に 修正してください。
エージェントのロジックにおける標準的なコード構成:
// ✅ 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
main() メソッドの場合のみ(警告コメントを追加してください):
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());
}
プロジェクトのセットアップ
新しい AgentScope プロジェクトを作成する場合は、正しい Maven 依存関係を使用してください:
Maven の設定(pom.xml)
本番環境での使用(推奨):
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.12</version>
</dependency>
</dependencies>
ローカル開発(ソースコードを扱う場合):
<properties>
<agentscope.version>1.0.12</agentscope.version>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>${agentscope.version}</version>
</dependency>
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-model-dashscope</artifactId>
<version>${agentscope.version}</version>
</dependency>
</dependencies>
⚠️ 重要:バージョンの選択
- 本番環境では
agentscope:1.0.12を使用してください(安定版、Maven Central から入手可能) - AgentScope 自体を開発する場合に限り
agentscope-core:1.0.12を使用してください - バージョン
0.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>
✅ 正しい — 安定版リリースを使用してください:
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope</artifactId>
<version>1.0.12</version>
</dependency>
利用可能なモデルクラス(プロバイダー固有の拡張モジュール)
// DashScope (Alibaba Cloud)
import io.agentscope.extensions.model.dashscope.DashScopeChatModel;
// OpenAI
import io.agentscope.extensions.model.openai.OpenAIChatModel;
// Gemini (Google)
import io.agentscope.extensions.model.gemini.GeminiChatModel;
// Anthropic (Claude)
import io.agentscope.extensions.model.anthropic.AnthropicChatModel;
// Ollama (Local models)
import io.agentscope.extensions.model.ollama.OllamaChatModel;
オプションの拡張機能
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-extensions-mem0</artifactId>
<version>${agentscope.version}</version>
</dependency>
<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> を返します。
主な設計原則
- ノンブロッキング:すべての I/O 操作は非同期です
- メッセージ駆動:エージェントは不変の
Msgオブジェクトを介して通信します - 合成可能:エージェントとパイプラインはネストして組み合わせられます
- 拡張可能:フックとカスタムツールにより、細かくカスタマイズできます
コーディング標準とベストプラクティス
2.1 Java バージョンとスタイル
互換性を最大限に高めるため、Java 17(LTS)を対象にします:
- Java 17 の機能(レコード、switch 式、instanceof のパターンマッチング、
var、シールドクラス)を使用します - Java 21 以降のプレビュー機能は避けてください(switch でのパターンマッチング、レコードパターン)
- 標準的な Java の規約に従います(クラスは PascalCase、メソッドと変数は camelCase)
- 適切な場合は Lombok を使用します(DTO/メッセージには
@Data、@Builder) - データクラスでは 不変性 を優先します
- ドメインの概念を反映した 意味のある名前 を使用します
⚠️ 重要:プレビュー機能を避けてください
// ❌ 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 リアクティブプログラミング(重要)
⚠️ エージェントのロジックでブロックしてはいけません
ブロッキング操作はリアクティブチェーンを壊し、パフォーマンスの問題を引き起こします。
ルール:
- ❌ エージェントのロジックで
.block()を使用しないでください(mainメソッドまたはテスト内のみ可) - ✅ 単一の結果には
Monoを使用します(例:agent.call()) - ✅ ストリーミングレスポンスには
Fluxを使用します(例:model.stream()) - ✅
.map()、.flatMap()、.then()を使用して操作を連結します - ✅ 遅延評価には
Mono.defer()を使用します - ✅ リアクティブコンテキストへのアクセスには
Mono.deferContextual()を使用します
例:
// ❌ 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 メッセージの処理(Msg)
Builder パターンを使用してメッセージを作成します:
Msg userMsg = Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder().text("Hello").build())
.name("user")
.build();
コンテンツブロック:
TextBlock:テキストコンテンツ用ThinkingBlock:Chain of Thought(CoT)推論用ToolUseBlock:ツール呼び出し用ToolResultBlock:ツール出力用
ヘルパーメソッド:
// 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 エージェントの実装
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, "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 アノテーションを使用します。ツールは次を返せます:
String(同期)Mono<String>(非同期)Mono<ToolResultBlock>(複雑な結果向け)
⚠️ 重要:@ToolParam の形式
- ✅ 正しい:
@ToolParam(name = "city", description = "City name") - ❌ 誤り:
@ToolParam(name="city", description="...")(=の前後にスペースがありません) - ❌ 誤り:
@ToolParam("city")(name= と description= がありません)
同期ツールの例:
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";
}
}
非同期ツールの例:
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()));
}
}
Toolkit に登録:
Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());
toolkit.registerTool(new AsyncTools());
フックシステム
フックを使用すると、ライフサイクルのさまざまな段階でエージェントの実行をインターセプトし、変更できます。
Hook インターフェース
public interface Hook {
<T extends HookEvent> Mono<T> onEvent(T event);
default int priority() { return 100; } // Lower = higher priority
}
一般的な Hook イベント
PreReasoningEvent:LLM 推論の前(変更可能)PostReasoningEvent:LLM 推論の後(変更可能)ReasoningChunkEvent:推論チャンクのストリーミング(通知)PreActingEvent:ツール実行の前(変更可能)PostActingEvent:ツール実行の後(変更可能)ActingChunkEvent:ツール実行のストリーミング(通知)
フックの例
Java 17+ 対応(推奨):
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();
別の方法:従来の 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;
}
};
優先度のガイドライン:
- 0-50:重要なシステムフック(認証、セキュリティ)
- 51-100:優先度の高いフック(検証、前処理)
- 101-500:通常の優先度のフック(ビジネスロジック)
- 501-1000:優先度の低いフック(ロギング、メトリクス)
パイプラインのパターン
パイプラインは、複数のエージェントを構造化されたワークフローでオーケストレーションします。
順次パイプライン
エージェントを順番に実行します(1つの出力が次の入力になります)。
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();
長期メモリ
// 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();
メモリモード:
STATIC_CONTROL:フレームワークがメモリを自動的に管理します(フック経由)AGENTIC:エージェントがメモリを使用するタイミングを決定します(ツール経由)BOTH:両方のアプローチを組み合わせます
MCP(MODEL CONTEXT PROTOCOL)統合
AgentScope は、外部のツールやリソースとの統合に MCP をサポートしています。
// 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/[email protected]", "/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();
テスト
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();
}
外部依存関係のモック化
@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
}
テストのベストプラクティス:
- リアクティブチェーンは必ず
StepVerifierでテストします - 外部依存関係(モデル、API)をモック化します
- エラーケースと境界条件をテストします
- フックが正しく呼び出されることを検証します
- タイムアウトとキャンセルのシナリオをテストします
コードスタイルガイド
ロギング
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);
エラー処理
// 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 安全性
// 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);
主なライブラリ
- Reactor Core:リアクティブプログラミング用の
Mono、Flux - Jackson:JSON のシリアライズ/デシリアライズ
- SLF4J:ロギング(
private static final Logger log = LoggerFactory.getLogger(MyClass.class);) - OkHttp:モデル API 用の HTTP クライアント
- MCP SDK:Model Context Protocol との統合
- JUnit 5:テストフレームワーク
- Mockito:モック化フレームワーク
- Lombok:定型コードの削減
禁止事項
❌ 絶対に行わないでください
-
リアクティブチェーン内でブロックする
// ❌ WRONG return someMonoOperation().block(); -
Thread.sleep() またはブロッキング I/O を使用する
// ❌ WRONG Thread.sleep(1000); // ✅ CORRECT return Mono.delay(Duration.ofSeconds(1)); -
同期せずに共有状態を変更する
// ❌ WRONG private List<Msg> messages = new ArrayList<>(); public void addMessage(Msg msg) { messages.add(msg); // Not thread-safe } -
エラーを黙って無視する
// ❌ WRONG .onErrorResume(e -> Mono.empty()) // ✅ CORRECT .onErrorResume(e -> { log.error("Operation failed", e); return Mono.just(fallbackValue); }) -
リアクティブコードで ThreadLocal を使用する
// ❌ WRONG ThreadLocal<String> context = new ThreadLocal<>(); // ✅ CORRECT return Mono.deferContextual(ctx -> { String value = ctx.get("key"); // Use value }); -
適切なリソース管理を行わずにエージェントを作成する
// ❌ WRONG - No cleanup public void processRequests() { for (int i = 0; i < 1000; i++) { ReActAgent agent = createAgent(); agent.call(msg).block(); } } -
API キーやシークレットをハードコードする
// ❌ WRONG String apiKey = "sk-1234567890"; // ✅ CORRECT String apiKey = System.getenv("OPENAI_API_KEY"); -
Java のプレビュー機能を使用する(--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); }
よくある落とし穴と解決策
❌ ブロッキング操作
// WRONG
Msg response = agent.call(msg).block(); // Don't block in agent logic
// CORRECT
return agent.call(msg)
.flatMap(response -> processResponse(response));
❌ null の処理
// 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.extensions.model.dashscope.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();