最近の研究論文効果的なエージェントの構築 (英語) において、Anthropic は効果的な大規模言語モデル(LLM)エージェントの構築に関する貴重な知見を共有しました。この研究が特に興味深いのは、複雑なフレームワークよりもシンプルさと構成可能性を重視している点です。これらの原則が Spring AI を用いてどのように実用的な実装に反映されるかを探ってみましょう。

Agent Systems

パターンの説明と図は Anthropic のオリジナル論文から引用していますが、ここではモデルの移植性と構造化された出力を実現する Spring AI の機能を用いてこれらのパターンを実装する方法に焦点を当てます。まずはオリジナル論文を読むことをお勧めします。

spring-ai-examples リポジトリの agentic-patterns [GitHub] (英語) ディレクトリには、後続の例のすべてのコードが含まれています。

エージェントシステム

この研究論文では、2 種類のエージェントシステム間の重要なアーキテクチャ上の区別を示しています。

  1. ワークフロー : LLM とツールが事前定義されたコードパスを通じてオーケストレーションされるシステム (例: 規範システム)

  2. エージェント : LLM が独自のプロセスとツールの使用を動的に指示するシステム

重要なインサイトは、完全に自律的なエージェントは魅力的に見えるかもしれませんが、明確に定義されたタスクに対しては、ワークフローの方が予測可能性と一貫性が向上することが多いということです。これは、信頼性と保守性が極めて重要なエンタープライズ要件に完全に合致しています。

Spring AI が、それぞれ特定のユースケースに対応する 5 つの基本パターンを通じてこれらの概念をどのように実装しているかを見てみましょう。

1. チェーンワークフロー [GitHub] (英語)

チェーンワークフローパターンは、複雑なタスクをより単純で管理しやすいステップに分割するという原則を例示しています。

Prompt Chaining Workflow

いつ使うか : - 明確な連続ステップを持つタスク - レイテンシを犠牲にしてより高い精度を実現したい場合 - 各ステップが前のステップの出力に基づいて構築される場合

Spring AI の実装からの実際の例を以下に示します。

public class ChainWorkflow {
    private final ChatClient chatClient;
    private final String[] systemPrompts;

    public String chain(String userInput) {
        String response = userInput;
        for (String prompt : systemPrompts) {
            String input = String.format("{%s}\n {%s}", prompt, response);
            response = chatClient.prompt(input).call().content();
        }
        return response;
    }
}

この実装は、いくつかの重要な原則を示しています。

  • 各ステップには明確な責任がある

  • 一つのステップからの出力が次のステップの入力となる

  • チェーンは簡単に拡張でき、メンテナンスも容易です

2. 並列化ワークフロー [GitHub] (英語)

LLM はタスクを同時に処理し、その出力をプログラムで集約することができます。

Parallelization Workflow

いつ使うか : - 類似しているが独立した大量のアイテムを処理する - 複数の独立した視点を必要とするタスク - 処理時間が重要で、タスクが並列化可能な場合

List<String> parallelResponse = new ParallelizationWorkflow(chatClient)
    .parallel(
        "Analyze how market changes will impact this stakeholder group.",
        List.of(
            "Customers: ...",
            "Employees: ...",
            "Investors: ...",
            "Suppliers: ..."
        ),
        4
    );

3. ルーティングワークフロー [GitHub] (英語)

ルーティングパターンは、インテリジェントなタスク分散を実装し、さまざまな種類の入力に特化した処理を可能にします。

Routing Workflow

いつ使うか : - 異なるカテゴリの入力を伴う複雑なタスク - 異なる入力に特殊な処理が必要な場合 - 分類を正確に処理できる場合

@Autowired
private ChatClient chatClient;

RoutingWorkflow workflow = new RoutingWorkflow(chatClient);

Map<String, String> routes = Map.of(
    "billing", "You are a billing specialist. Help resolve billing issues...",
    "technical", "You are a technical support engineer. Help solve technical problems...",
    "general", "You are a customer service representative. Help with general inquiries..."
);

String input = "My account was charged twice last week";
String response = workflow.route(input, routes);

4. オーケストレーターワーカー [GitHub] (英語)

Orchestration Workflow

いつ使うか : - サブタスクを事前に予測できない複雑なタスク - 異なるアプローチや視点を必要とするタスク - 適応的な問題解決が必要な状況

public class OrchestratorWorkersWorkflow {
    public WorkerResponse process(String taskDescription) {
        // 1. Orchestrator analyzes task and determines subtasks
        OrchestratorResponse orchestratorResponse = // ...

        // 2. Workers process subtasks in parallel
        List<String> workerResponses = // ...

        // 3. Results are combined into final response
        return new WorkerResponse(/*...*/);
    }
}

使用例:

ChatClient chatClient = // ... initialize chat client
OrchestratorWorkersWorkflow workflow = new OrchestratorWorkersWorkflow(chatClient);

WorkerResponse response = workflow.process(
    "Generate both technical and user-friendly documentation for a REST API endpoint"
);

System.out.println("Analysis: " + response.analysis());
System.out.println("Worker Outputs: " + response.workerResponses());

5. 評価者 - 最適化者 [GitHub] (英語)

Evaluator-Optimizer Workflow

いつ使うか : - 明確な評価条件が存在する - 反復的な改善により測定可能な価値が提供される - タスクは複数回の批評から恩恵を受ける

public class EvaluatorOptimizerWorkflow {
    public RefinedResponse loop(String task) {
        Generation generation = generate(task, context);
        EvaluationResponse evaluation = evaluate(generation.response(), task);
        return new RefinedResponse(finalSolution, chainOfThought);
    }
}

使用例:

ChatClient chatClient = // ... initialize chat client
EvaluatorOptimizerWorkflow workflow = new EvaluatorOptimizerWorkflow(chatClient);

RefinedResponse response = workflow.loop(
    "Create a Java class implementing a thread-safe counter"
);

System.out.println("Final Solution: " + response.solution());
System.out.println("Evolution: " + response.chainOfThought());

Spring AI の導入メリット

Spring AI によるこれらのパターンの実装は、Anthropic の推奨事項と一致するいくつかの利点をもたらします。

モデルの移植性

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

構造化された出力

EvaluationResponse response = chatClient.prompt(prompt)
    .call()
    .entity(EvaluationResponse.class);

一貫性のある API

  • 異なる LLM プロバイダー間で統一されたインターフェース

  • 組み込みのエラー処理と再試行

  • 柔軟で迅速な管理

ベストプラクティスと推奨事項

  • シンプルに始める

  • 複雑さを追加する前に基本的なワークフローから始める

  • 要件を満たす最もシンプルなパターンを使用する

  • 必要な場合にのみ洗練度を追加します

  • 信頼性を考慮した設計

  • 明確なエラー処理を実装する

  • 可能な場合は型安全なレスポンスを使用する

  • 各ステップで検証を組み込む

  • トレードオフを考慮する

  • 遅延と精度のバランスをとる

  • 並列処理を使用するタイミングを評価する

  • 固定ワークフローと動的エージェントのどちらかを選択

今後の作業

これらのガイドは更新され、これらの基本パターンと洗練された機能を組み合わせた、より高度なエージェントの構築方法が説明されます。

パターン構成 - 複数のパターンを組み合わせてより強力なワークフローを作成する - 各パターンの長所を活かしたハイブリッドシステムの構築 - 変化する要件に適応できる柔軟なアーキテクチャの作成

高度なエージェントメモリ管理 - 会話全体にわたる永続的なメモリの実装 - コンテキストウィンドウを効率的に管理する - 長期的な知識保持のための戦略の開発

ツールとモデルコンテキストプロトコル(MCP)の統合 - 標準化されたインターフェースを通じて外部ツールを活用する - 強化されたモデルインタラクションのための MCP の実装 - 拡張可能なエージェントアーキテクチャの構築

結論

Anthropic の研究インサイトと Spring AI の実用的な実装を組み合わせることで、効果的な LLM ベースのシステムを構築するための強力なフレームワークが提供されます。

これらのパターンと原則に従うことで、開発者は不必要な複雑さを回避しながら真の価値を提供する、堅牢で保守可能かつ効果的な AI アプリケーションを作成できます。

重要なのは、最もシンプルな解決策が最も効果的である場合もあることを覚えておくことです。まずは基本的なパターンから始め、ユースケースを徹底的に理解し、システムのパフォーマンスや機能が明らかに向上する場合にのみ、複雑さを追加しましょう。