0. はじめに
こんにちは、menu事業部でエンジニアをしている25新卒の中坪です。
早速ですが、皆さんが考える「いいAIエージェント」とは一体何でしょうか?
昨今では、様々なエージェントを多様な場面で活用するようになり、いいエージェントの定義も様々かもしれません。例えば、速度や精度といった観点は、いいエージェントかどうかを客観的に判断するための揺るがない基準でしょう。確かに、速く正確なパフォーマンスがあれば、特定のタスクにおいてエージェントが十分に機能することを期待できそうです。私もここ3ヶ月ほど、社内の業務効率化プロジェクトのもと、Google ADKを活用しマルチエージェントシステムを実装する中で、エージェントの出力に対する速度と精度の向上に取り組んできました。
しかし、エージェントのパフォーマンス追求は、終わりのないテーマでもあります。速度と精度には一定トレードオフの関係があり、モデルごとにその性能のばらつきが見られます。今後より良いモデルの登場により全体のパフォーマンス改善が見込めるとしても、100%の精度を求められない以上おそらく一定のラインで頭打ちするでしょう。また、エージェントの精度を高めるには継続的な取り組みとそれなりのコストが伴います。十分なデータを用意したり、適切なワークフローを設計・実装・運用したりするのには、一定の時間と人手を要するからです。マルチエージェントで専門エージェントが連立していくと尚更、その実装コストも指数関数的に増えかねません。したがって、特にこのマルチエージェントシステムの開発という文脈においては、いいエージェントかどうかの評価基準には速度や精度だけではなく、別の視点も必要だと考えています。
例えば、特定のタスクで80点のパフォーマンスを出すエージェントがあるとします。そのエージェントに対して、精度改善をめちゃくちゃ頑張って85点に引き上げるのと、少し工夫を凝らして80点のままだが複数の場面で活躍できるエージェントとして改善するのとでは、総合的にどちらがいいエージェントと言えるでしょうか?最近、私が速度や精度と同じくらいあるいはそれ以上に重要だと考えているのは、エージェントの再利用性を担保するための設計です。ここで重要なのは、再利用性の高いエージェントとは、単体で大きい機能満載の万能エージェントではありません。真の再利用性とは、様々な場面で発生するタスクごとに、それぞれ明確な責務と専門性を持ったエージェントが、相互連携しやすく効率的で柔軟に対応できる構造にあります。再利用性を考慮したエージェントを設計することで、最小限の工数で優れたエージェントの活用機会を最大化し、長期的な価値を生み出すことを期待できるのではないでしょうか。
この記事では、menuでのGoogle ADK(Agent Development Kit)の業務活用事例を参考に、エージェントを実装・設計する上で重要なポイントをいくつか紹介します。特に、A2A(Agent-to-Agent)プロトコル(エージェント同士が連携するための通信規約)を活用したマルチエージェントシステムで、エージェントの再利用性を高める設計パターンと、その実装における実践的な知見についてご興味のある方はぜひご一読ください。
1. menuのADK活用事例
そもそも何でエージェント開発を行ったの?
menuには、営業やCSなど外部のお客様と接する部署と開発チームを繋ぐ問い合わせチャンネルがあります。プロダクトの仕様詳細の確認や商用データの調査依頼などは、この窓口を介して開発チームに依頼されることが多く、一定のリソース(エンジニアの時間、調査コスト、ドメイン知識の習得など)が必要です。開発チームの定常業務を中断して対応することもあり、チーム全体の開発リソースに負担がかかっていました。
そこで、AIエージェントを活用してお問い合わせ対応を効率化することを目指しました。具体的には、社内でプロダクトに関するデータを集約し、誰でもエージェントを通して仕様を把握できるシステムを構築することで、営業やCSといった部署が開発チームに依頼しなくても解決できるお問い合わせ件数を増やし、開発リソースを確保できるようにすることを目標としました。また、新卒エンジニアや特定のドメイン知識に乏しいメンバーでも、必要な情報を揃えやすく調査に取り掛かりやすくなるのではと期待しています。
エージェントじゃないとダメだったの?
この事例においてAIエージェント(LLM駆動)の活用が特に有効であるとされる理由は、主に以下の2点に集約されます。
1. 自動化が困難な「思考能力が必要なタスク」であるため
対応しようとしている問い合わせは、従来のプログラム化では対応できない複雑性を持っています。営業・CSからの問い合わせは、ドメインが複雑に絡み合うため、回答のパターン化やプログラム化がほぼ不可能です。また、利用者には非エンジニアの方が含まれるため、技術的用語を避け、自然言語で説明する必要があります。ソースコードなどの生データから、必要な情報を抽出し、自然言語で仕様として説明するには、エージェントの推論能力が役立ちます。
2. 社内利用という環境特性により、リスクマネジメントとデータ活用の両立が可能なため
このエージェントは、社内での利用を前提としており、LLM特有の不確実性が一定許容されます。外部の方に向けて直接回答を生成させることはリスクを伴いますが、この事例では社内の人間による最終的な判断・確認・回答が前提にあるため、万が一AIエージェントがエラーを起こしても被害が最小限に収まります。また、社内限定の利用環境であれば、セキュリティ上の懸念から外部には公開できない情報を含む、より多くのデータソースを活用できるため、LLMのポテンシャルを最大限に引き出すことができます。
実装内容
menuの社内問い合わせ対応システムは、A2A (Agent-to-Agent) プロトコルを採用したマルチエージェントシステムとして実装されています。オーケストレーターエージェントには、物知りで親しみやすく誰でも気軽に質問できる雰囲気作りができるといいなという願いを込めて、「まーちゃん」と命名しました。
ma_chan(オーケストレーターエージェント)
- Slackチャンネルからの問い合わせを最初に受け取る窓口担当
- 問い合わせ内容を分析し、適切な専門エージェントにタスクを振り分け
- 専門エージェントからの結果を受け取り、ユーザー向けに統合・整形して回答
code_analysis_agent(コード分析専門エージェント)
- GitHubリポジトリ内のコード検索・分析に特化
- GitHub MCPサーバーとFile Search MCPサーバーを活用してコード検索を実行
- プロダクトのユビキタス言語やコードのインデックス情報などを定義した中間データを参照し、精度の高い回答を生成
knowledge_agent (ナレッジ管理専門エージェント)
- プロダクトに関する情報をドキュメントとして自動生成し、必要に応じて閲覧・編集して動的管理を行うエージェント
big_query_agent (データベース検索専門エージェント)
- BigQueryから情報を検索・分析
- 商用データの調査依頼に対応
integrated_search_agent (過去事例参照エージェント)
- Slackの過去のお問い合わせ対応履歴から似た事例や参考情報を検索
file-mcp-server(ファイル検索MCPサーバー)
- Gitリポジトリのクローン・同期を自動化
- Luceneベースの検索インデックスを構築・管理
- MCPプロトコルに準拠したファイル検索APIを提供
技術スタック
- フレームワーク: Google ADK (Agent Development Kit)
- 言語: Python 3.11+、Java 21
- パッケージマネージャー: uv、Maven
- コンテナ化: Docker Compose
- Slack連携: Slack Bolt Framework
- プロトコル: A2A (Agent-to-Agent)、http
- MCP: GitHub MCP、File Search MCP
- LLMサービス: Vertex AI(Google Cloud)
- 検索エンジン: Apache Lucene(ファイル検索インデックス)
- Javaフレームワーク: Spring Boot 3.3.4(file-mcp-server)
実装の特徴
1. A2A連携によるマルチエージェント構成
このシステムでは、タスクごとにエージェントを分割してA2A連携させる設計を採用しました。A2A連携により、責務が明確で柔軟性が高く、依存性の低い構成を実現できました。各エージェントがどのエージェントとどのように連携できるのかを明確に定義することで、エージェントが迷子になることを防止しています。各エージェントはエージェントカードといった情報を持っており、連携先で自分のステータスを共有できる仕組みになっています。
{
"name": "code_analysis_agent",
"display_name": "コード分析エージェント",
"description": "コード分析専門エージェント。ソースコードの静的解析、コード品質チェック、依存関係分析、リファクタリング提案を提供します。",
"version": "0.1.0",
"url": "http://code_analysis_agent:51002/a2a/code_analysis_agent",
"author": "integrations_ai_agent",
"protocolVersion": "0.2.6",
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"supportsAuthenticatedExtendedCard": false,
"capabilities": {},
"tags": ["code-analysis", "static-analysis", "adk", "a2a", "code-quality"],
"skills": [
{
"id": "code_analysis",
"name": "Code Analysis",
"description": "コード分析専門エージェント。ソースコードの静的解析、コード品質チェック、依存関係分析、リファクタリング提案を行います。",
"tags": ["llm", "code-analysis", "static-analysis"]
}]
}
また、リモートエージェントをオーケストレーター側でAgentToolでラップし、toolsとして登録することで、オーケストレーターエージェントが最終回答権を持ち、ツールの結果を評価・フィルタリングしてからユーザーに提示できるようにしています。A2A連携には、専門エージェントを「ツール」として扱う方法と「サブエージェント」として扱う方法がありますが、サブエージェントとして連携すると最終回答権を移譲してしまうため、他の専門エージェントから提供される情報を統合した総合的な判断ができなくなります。したがって、今回はオーケストレーターが複数の専門エージェントの結果を統合して回答する必要があるため、ツールとして扱う方式を採用しました。
# code_analysis_agentをA2Aリモートエージェントとして定義
# 公式ドキュメントのサンプルに従って実装
code_analysis_agent = RemoteA2aAgent(
name="code_analyzer",
description="コード分析専門エージェント。ソースコードの静的解析、コード品質チェック、依存関係分析、リファクタリング提案を提供します。",
agent_card=agent_card_url,
timeout=300.0, # HTTP timeout (seconds)
)
# Remote A2A AgentをAgentToolでラップ
# AgentTool化により、LLMエージェントがツールとして明示的に呼び出せるようになる
code_analysis_agent_tool = AgentTool(
agent=code_analysis_agent
)
# ------------------------------------------------------
# ma_chanのルートエージェント定義
root_agent = Agent(
name=AGENT_NAME,
model=AGENT_MODEL,
description=AGENT_DESCRIPTION,
instruction=AGENT_INSTRUCTION,
tools=[
code_analysis_agent_tool, # AgentToolをtoolsとして登録
PreloadMemoryTool(), # Vertex AI Memory Bankから過去の会話を自動検索
],
sub_agents=[], # sub_agentsではなくtoolsとして使用
after_agent_callback=save_session_to_memory_callback, # 各エージェント実行後にMemory Bankに保存
)
2. 中間データの活用
巨大なリポジトリをプロンプト情報のみで検索すると、LLMやGitHub MCPのトークンが枯渇する問題がありました。また、エージェントにプロダクトの前提知識がなく、一般的な回答を生成してしまう問題も発生していました。これらの課題を解決するため、プロダクトのユビキタス言語(ドメイン駆動設計における、チーム内で統一された用語定義)を定義した情報と、リポジトリ構造をインデックス化した情報を中間データとして活用することにしました。これにより、プロンプトと中間データを参照した上で効率的なファイル検索クエリを生成できるようになり、エージェントがより精度の高い回答を生成できるようになりました。
3. Slackをインターフェースとして採用
このシステムでは、Slackをインターフェースとして採用しました。これにより、既存のワークフローにエージェントが参画でき、エージェントの出力を複数人で共有・確認できるようになりました。ユーザーが特定のタスクのために特定のUIを立ち上げる必要がなく、エージェントが自発的に動作し、既存のSlack UIを活用できる点は大きなメリットです。また、SlackのBlock Kitを活用し、構造的な文章フォーマットを表現したり、フィードバックボタンの実装を行うなど、ユーザビリティの向上が図りやすい点も魅力です。
さらに、SlackスレッドごとにセッションIDを管理し、会話履歴を保持することで、ADK WebServerのセッションサービスを使用して会話の継続性を実現しています。この機能により、スレッド内のコンテキストを把握した上でよりユーザーに寄り添った調査や回答ができるようになりました。今後は、ADKのネイティブ機能であるMemoryBankを活用し、セッションを超えて長期的に記憶を保持させることで、より効率的なエージェントの動作を目指していきます。
2. 再利用性のある設計とは?
まーちゃん実装時の試行錯誤
実装初期段階では、下記のようなシークエンシャルエージェントの開発を進めました。この設計では、複雑なタスクを細かなステップに分けることで順番に正確に行えるという利点がありました。また、GitHub MCPを利用するにはトークンがかなり消費されるため、重たい処理を複数のエージェントで分担させたいといった意図がありました。
しかし、いざ本番環境で試してみるとユーザーの要求は想定していたものよりも多岐に渡り、この設計では様々な依頼の意図に対応しづらいということがわかりました。ワークフローエージェントは、サブエージェントを規則通りに動かすという役割を持ち、プロンプトを渡せないため思考することはありません。つまり、毎回決まったワークフローに落とし込めることができれば一定のパフォーマンスは期待できるのですが、イレギュラーな依頼に対する柔軟性には欠けるといった問題がありました。この場合、シークエンシャルエージェントをサブエージェントとして別のメインエージェントに使用させることで改善できそうに思えますが、これではエージェントのネストが深くなりやすく運用しやすい構造だとは言えません。
そこで、エージェントを階層化することなく比較的柔軟に連携することができないかと考え、A2A連携を採用することにしました。タスクごとに各エージェントの責務を明確にし、再利用性の高い構造を作れば必要な専門エージェントをいつでも呼び出すことができ、複雑な要求が来たときにそれらのエージェントが協力し、オーケストレータが包括的に情報を処理できればより良いパフォーマンスにつながるのではないかと考えました。
コールセンターで例えると、、
このシステムをコールセンターに例えるなら、オペレーターと各専門担当者に分かれている構造と似ています。
まーちゃん(オペレーター役):
- ユーザーから質問を受け取る
- 問い合わせ内容を分析して適切な専門エージェントに依頼
- 専門エージェントから受け取った情報を統合・整形してユーザーに回答
各専門エージェント(専門担当者役):
- それぞれの領域に特化したタスクを処理
- コード分析エージェント:任意のリポジトリ内のファイルを検索してコードの情報を読み取り、分析
- データ分析エージェント:データベースから任意の情報を検索し必要な情報を取得
- 過去事例分析エージェント:過去のお問い合わせから似た事例や参考にできる情報を検索
この設計であれば、もし仮にオペレーターBがいた場合、同じようなタスクは既存の専門担当者に依頼することができます。目的ごとあるいはクライアントごとにオペレーターを立て、専門担当者は各責務を全うするといった構造は、理想的なマルチエージェントシステム設計のひとつだと考えています。
エージェント設計はマイクロサービスアーキテクチャ的な考えが大事かも
もちろん、どの粒度でエージェントの責務を分割すれば良いかは難しい議論になってきます。大きければ大きいほどおそらく保守性は失われ、小さければ小さいほど一貫性がなくなり複雑化します。そのような時は、その責務を全うするエージェントのタスクに再利用性があるかどうかの基準で判断するといいのではないかと私は個人的に考えています。
例えば、非エンジニアからのお問い合わせに対応するまーちゃんですが、それとは別にエンジニアの実装&調査を補佐するエージェント「みーちゃん」を作ることになったとします。その場合、みーちゃんという別のオーケストレーターを作成し、内部では既存のコード分析エージェントにタスクを依頼するといった構造を設計することができます。みーちゃんはエンジニア向けに技術的な回答を生成する役割に特化し、コード分析エージェントは自分の責務を同様に全うすることだけに集中し、みーちゃんにプロンプトを与える以外で追加の実装をする必要は特にありません。
一方で、一つのオーケストレーターに全専門エージェントへの依頼権限を与えるといった発想もできるかと思います。一見、機能満載でなんでもこなせるつよつよエージェントのようにも見えます。しかし、これだと一つのオーケストレーターが複数の目的を持つことになり、どの依頼をどのサブエージェントあるいはツールに割り振るのかが困難になってしまいます。したがって、オーケストレーターの責務も必ず明確にし、基本的には一つの目的ごとにひとつの窓口を設けるのが正解な気がしています。
このように、各機能を持った専門エージェントが独立しまーちゃんと切り離されることで、別のオーケストレーターに必要とされた時に各専門エージェントが出張する(各責務に基づいた機能を提供する)ことが可能になります。出張可能であるということは、複数の目的で同じ責務を全うできる再利用性の高いエージェントだと言えるでしょう。
再利用性の高いエージェントが相互連携できる構造は、効率的な実装・運用・保守・拡張を容易にし、エージェントのパフォーマンス最大化につながります。つまり、社内でAIを活用していくネットワークを構築する際には、エージェントの再利用性を考慮した設計が、長期的な価値創出において重要な鍵となるのです。
3. まとめ
いかがでしたでしょうか?私が最近の業務で得た知見を今一度振り返り、言語化して残しておければと思い、このような記事を書かせていただきました。少しでも再利用性という視点でエージェント設計を考えるきっかけになれば幸いです。
私は初めてGoogle ADKに触れてからまだ3ヶ月足らずですが、皆さんと一緒にこれからも変化する新しい技術を楽しく学んでいけたらと思っています。
最後まで読んで頂きありがとうございました!
▼新卒エンジニア研修のご紹介
レアゾン・ホールディングスでは、2025年新卒エンジニア研修にて「個のスキル」と「チーム開発力」の両立を重視した育成に取り組んでいます。 実際の研修の様子や、若手エンジニアの成長ストーリーは以下の記事で詳しくご紹介していますので、ぜひご覧ください!
▼採用情報
レアゾン・ホールディングスは、「世界一の企業へ」というビジョンを掲げ、「新しい"当たり前"を作り続ける」というミッションを推進しています。 現在、エンジニア採用を積極的に行っておりますので、ご興味をお持ちいただけましたら、ぜひ下記リンクからご応募ください。





