2026年3月14日土曜日

Part 4/6: ツール設計とエージェント間のコンテキスト共有:実務で使えるパターン集

はじめに:前回の振り返りと本稿の位置づけ

Part 3では、Claude Team Agentにおけるオーケストレーターとサブエージェントの役割分担と、タスク委譲のメカニズムを解説した。本稿では一歩踏み込み、実際のビジネスユースケースに即したツール設計パターンと、エージェント間でのコンテキスト・状態共有の実装手法を、サンプルコードとともに紹介する。


1. カスタムツール定義の基本原則

AnthropicのClaude Agent SDKでは、ツールはJSON Schemaによって定義され、エージェントが自律的に呼び出す。ツール設計の質がエージェント全体の信頼性を左右する。[Source: https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/overview]

良いツール定義に共通する原則は3点ある。

  • 単一責任:1ツール1機能に絞り、副作用を最小化する
  • 冪等性:同じ入力で同じ出力を返すよう設計し、リトライ時の安全性を確保する
  • 明示的なスキーマdescriptionフィールドに使用条件・禁止条件を自然言語で記述し、モデルの誤呼び出しを防ぐ
tool_definition = {     "name": "search_internal_docs",     "description": "社内ナレッジベースを全文検索する。外部Webの検索には使用しないこと。",     "input_schema": {         "type": "object",         "properties": {             "query": {"type": "string", "description": "検索クエリ"},             "top_k": {"type": "integer", "default": 5}         },         "required": ["query"]     } } 

2. 再利用可能ツール生成パターン:データ分析エージェントの実装

NVIDIAのNeMo Agent Toolkitを用いたDABStepベンチマーク1位の事例では、エージェントが動的にPythonコードをツールとして生成・登録し、再利用する「Reusable Tool Generation」アーキテクチャが採用されている。[Source: https://huggingface.co/blog/nvidia/nemo-agent-toolkit-data-explorer-dabstep-1st-place]

このパターンをClaude Agentで実装する場合、以下のフローが有効だ。

  1. オーケストレーターがデータ分析タスクを受理
  2. コード生成サブエージェントがPython関数を動的に生成
  3. 生成された関数をツールとして登録し、後続ステップで再呼び出し
generated_tools = {}  def register_dynamic_tool(name: str, func_code: str):     exec(func_code, generated_tools)     return name in generated_tools  # エージェントループ内での使用例 if tool_name in generated_tools:     result = generated_tools[tool_name](**tool_input) 

このアプローチにより、データサイエンティストが手動でツールを追加せずとも、エージェントが自律的にツールセットを拡張できる。


3. エージェント間のメモリ共有:3つの実装パターン

マルチエージェントシステムにおける最大の課題のひとつが、エージェント間でのコンテキスト伝播だ。以下の3パターンを使い分けることで、ユースケースに応じた最適解を選べる。

パターンA:共有外部ストレージ(永続メモリ)

Hugging Face Hubが提供するStorage Bucketsのような外部ストレージを活用し、複数エージェントが共通のキー・バリューストアにアクセスする手法。長期的な調査タスクや、セッションをまたぐリサーチ自動化に適している。[Source: https://huggingface.co/blog/storage-buckets]

import json, boto3  def write_shared_context(bucket, key, data: dict):     s3 = boto3.client("s3")     s3.put_object(Bucket=bucket, Key=key, Body=json.dumps(data))  def read_shared_context(bucket, key) -> dict:     s3 = boto3.client("s3")     obj = s3.get_object(Bucket=bucket, Key=key)     return json.loads(obj["Body"].read()) 

パターンB:メッセージパッシング(短期コンテキスト)

オーケストレーターがサブエージェントへタスクを委譲する際、systemプロンプトに前段エージェントのサマリーを注入する。トークン効率が高く、コードレビューパイプラインのような逐次処理に向く。

パターンC:構造化状態オブジェクト

全エージェントが参照するAgentStateデータクラスを定義し、各ステップで更新・スナップショットを保存する。状態の可視性とデバッグ性が高まり、データ分析ワークフローのような複雑な分岐処理に有効だ。

from dataclasses import dataclass, field from typing import Any  @dataclass class AgentState:     task_id: str     completed_steps: list[str] = field(default_factory=list)     artifacts: dict[str, Any] = field(default_factory=dict)     errors: list[str] = field(default_factory=list) 

4. ビジネスユースケース別:推奨パターンマトリクス

ユースケース ツール戦略 メモリ共有パターン
リサーチ自動化 動的ツール生成 + Web検索 外部ストレージ(永続)
コードレビュー 静的ツールセット(AST解析・linter) メッセージパッシング
データ分析 Reusable Tool Generation 構造化状態オブジェクト

リサーチ自動化では、エージェントが中間成果物(要約・引用リスト)を外部ストレージに書き出し、別エージェントがそれを読み込んで深掘り調査を継続するパターンが実績を持つ。コードレビューでは、PRの差分・コメント履歴をメッセージとして順次渡すことで、コンテキストウィンドウを効率的に使える。


5. 状態管理の落とし穴と対策

複数エージェントが同一の状態を更新する場合、競合状態(race condition)が発生しうる。以下の対策を実装すること。

  • 楽観的ロック:状態更新時にバージョン番号を検証し、競合時はリトライ
  • イベントソーシング:状態そのものではなく変更イベントを記録し、再現性を確保
  • タイムアウト設定:サブエージェントの応答待ち時間に上限を設け、デッドロックを防止

まとめと次回予告

本稿では、カスタムツール定義の設計原則から、3種のメモリ共有パターン、ビジネスユースケース別の実装指針まで体系的に解説した。再利用可能ツール生成のアーキテクチャはデータ分析エージェントの性能を大きく引き上げるが、状態管理の堅牢化も同時に必要となる。

Part 5では、本シリーズの核心テーマのひとつである「エラーハンドリングと自律的リカバリー」を取り上げる。エージェントが失敗から学習し、再試行戦略を動的に調整するメカニズムを、実装コードとともに詳解する予定だ。


Category: LLM | Tags: Claude Agent, マルチエージェント, ツール設計, 状態管理, LLM実装

0 件のコメント:

コメントを投稿