mcpField
A
agentscope-java

agentscope-java

github.com/agentscope-aiエージェントワークフロー6.2k101 回インストール今日更新
リポジトリ

agentscope-java について

agentscope-javaは、Project ReactorとJava 17以降を基盤とするリアクティブなマルチエージェントフレームワークです。このスキルは、非ブロッキングな実装や設定など、APIの適切な使い方をコーディングエージェントに案内します。

主な機能

  1. 01Reactorによる非同期エージェント操作
  2. 02メッセージ駆動のエージェント通信
  3. 03直列・並列パイプラインの構成
  4. 04Toolkitのツールとライフサイクルフック
  5. 05Java 17の設定とAPIガイド

活用シーン

  1. 01非ブロッキングなAgentScope Javaロジックの作成
  2. 02モデルとMaven依存関係の設定
  3. 03ツール、フック、エージェントパイプラインの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agentscope-ai/agentscope-java --skill agentscope-java

インストール後、Claude Code で/agentscope-javaを入力して読み込まれたか確認します。

詳細

AgentScope Java のコードを書くよう依頼された場合は、以下の指示に注意深く従ってください。

重大なルール — 絶対に違反しないでください

🚫 絶対に禁止:

  1. サンプルコードで .block() を絶対に使用しないでください — これは最もよくある間違いです。実行可能なサンプルを明示的に作成する場合に限り、main() メソッドまたはテストコードで .block() を使用してください。
  2. Thread.sleep() を絶対に使用しないでください — 代わりに Mono.delay() を使用してください。
  3. ThreadLocal を絶対に使用しないでください — Mono.deferContextual() で Reactor Context を使用してください。
  4. API キーを絶対にハードコードしないでください — 常に System.getenv() を使用してください。
  5. エラーを絶対に黙って無視しないでください — 常にエラーをログに記録し、フォールバック値を指定してください。
  6. 誤ったインポートパスを絶対に使用しないでください — 共有モデルインターフェースは io.agentscope.core.model.* に、プロバイダーモデルは io.agentscope.extensions.model.<provider>.* にあります。

✅ 必ず行うこと:

  1. すべての非同期操作に Mono と Flux を使用してください。
  2. .map()、.flatMap()、.then() で操作をチェーンしてください。
  3. エージェント、モデル、メッセージの作成には Builder パターンを使用してください。
  4. .onErrorResume() または .onErrorReturn() でエラー処理を追加してください。
  5. 重要な操作には SLF4J によるログ記録を追加してください。
  6. 正しいインポートを使用してください:import io.agentscope.extensions.model.dashscope.DashScopeChatModel;
  7. 正しい 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() は 禁止 です

提示するすべてのコード例について:

  1. 確認:.block() を使用していますか?→ はいの場合、main/テスト以外のコードでは 書き直してください。
  2. 確認:すべての操作がノンブロッキングですか?→ いいえの場合、修正してください。
  3. 確認:エラー処理がありますか?→ ない場合、追加してください。
  4. 確認:API キーは環境変数から取得していますか?→ いいえの場合、変更してください。
  5. 確認:インポートは正しいですか?→ プロバイダーモデルを 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> を返します。

主な設計原則
  1. ノンブロッキング:すべての I/O 操作は非同期です
  2. メッセージ駆動:エージェントは不変の Msg オブジェクトを介して通信します
  3. 合成可能:エージェントとパイプラインはネストして組み合わせられます
  4. 拡張可能:フックとカスタムツールにより、細かくカスタマイズできます

コーディング標準とベストプラクティス

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:定型コードの削減

禁止事項

❌ 絶対に行わないでください
  1. リアクティブチェーン内でブロックする

    // ❌ WRONG
    return someMonoOperation().block();
    
  2. Thread.sleep() またはブロッキング I/O を使用する

    // ❌ WRONG
    Thread.sleep(1000);
    
    // ✅ CORRECT
    return Mono.delay(Duration.ofSeconds(1));
    
  3. 同期せずに共有状態を変更する

    // ❌ WRONG
    private List<Msg> messages = new ArrayList<>();
    public void addMessage(Msg msg) {
        messages.add(msg);  // Not thread-safe
    }
    
  4. エラーを黙って無視する

    // ❌ WRONG
    .onErrorResume(e -> Mono.empty())
    
    // ✅ CORRECT
    .onErrorResume(e -> {
        log.error("Operation failed", e);
        return Mono.just(fallbackValue);
    })
    
  5. リアクティブコードで ThreadLocal を使用する

    // ❌ WRONG
    ThreadLocal<String> context = new ThreadLocal<>();
    
    // ✅ CORRECT
    return Mono.deferContextual(ctx -> {
        String value = ctx.get("key");
        // Use value
    });
    
  6. 適切なリソース管理を行わずにエージェントを作成する

    // ❌ WRONG - No cleanup
    public void processRequests() {
        for (int i = 0; i < 1000; i++) {
            ReActAgent agent = createAgent();
            agent.call(msg).block();
        }
    }
    
  7. API キーやシークレットをハードコードする

    // ❌ WRONG
    String apiKey = "sk-1234567890";
    
    // ✅ CORRECT
    String apiKey = System.getenv("OPENAI_API_KEY");
    
  8. 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();

コメント