マイクロサービスアーキテクチャにおける Mastra を活用した AI 基盤設計

はじめに

Developer Experience & Performance チームでエンジニアをしている大矢です。2025年10月30日に、CX(顧客体験)プラットフォーム「KARTE」シリーズのプロダクトのAIネイティブ化に向けた方針「KARTE AI」が発表され、いくつかの機能がベータ版としてリリースされました。
それらの機能の開発を進めるにあたって、2025 年 4 月にKARTE AI Project というプロジェクトが組成され、私は主にプラットフォーム観点での開発を担当してきました。この記事では、プロジェクトをどのように進めたか、アーキテクチャ選択における「集中」の判断、Mastra を使うことの優位性について紹介します。

KARTE AI Project について

プロジェクトが始まる前は、KARTE の Product への AI の組み込みはほとんど進んでいないという状況でした。また、一部のプロダクトに AI 機能が実装されることもありましたが、使用されるフレームワークがバラバラだったり、AI 機能開発における知見を共有して全体的に AI 機能の開発を効率的にやっていこうという動きまではできていませんでした。そういった背景から KARTE AI Project が組成されました。

プロジェクトの目的

上記のような課題を踏まえて、下記の2つをプロジェクトの目的としました。

  • AI 開発をリードできる人材を育成する
  • マイクロサービスにおいてどういうアーキテクチャで AI 機能を作っていくかを設計する

AI が組み込まれた機能をリリースすること自体も重要ですが、あくまでこのプロジェクトの中では機能のリリースは手段という位置付けです。

目的に沿ったプロジェクトの体制

単純に AI 担当チームを作って AI 機能を開発するという形をとってしまうと、「各チームに AI Agent の開発スキルを持ったエンジニアがいる」という状態に持っていくことができません。そこでプロジェクトは兼務前提の体制を採用しています。具体的には Project Owner を中心に、各プロダクトチームに所属しながらそれぞれの Product への AI 組み込みを実装する Product Engineer、プラットフォーム的な観点での開発をする Platform Engineer、KARTE に関する質問に回答する社内 Bot を開発する Customer Engineer のメンバーで進めました。 プロジェクトの中では密に連携しつつ、Platform Engineer が AI Service に関する運用の責任を持つような構造にしています。

team2.png

Platform Engineer の役割としては主に下記のようなものを担います。

  • アプリケーション実装が常にシンプルに保てるように環境を整える
    • 知見を集約して適切にレビューする
    • フレームワーク側にバグや微妙な実装があれば直しに行く
    • 実装でハマりそうな部分があれば先回りしてワークアラウンドを考えておく
    • 定期的にフレームワークのバージョンを上げる
  • 探索する
    • 使えそうなライブラリやフレームワークのキャッチアップしておく
    • 新しいバージョンで何ができるようになるか検討する
  • 運用できる状態にしておく
    • 可観測性(o11y)の整備をする
    • テストや Evals などの環境作っておく

プレイドの普段の開発スタイルでは、新規機能の開発初期はスピードを優先させてレビューをせず、βリリースの時などにまとめてレビューをするということもありましたが、今回の AI 機能に関しては小さい変更でも必ず全てレビューを通すようにしました。今回のプロジェクトの目的として、AI 開発をリードできる人材を育成するというところが大きく、そのための手段としてコードレビューは効果的だと考えたからです。
また、実際に Product Engineer がどういったところでハマるかというのは実際に Platform Engineer としても手を動かしてみないとわかりません。そのため Product Engineer と同じように手を動かして Chat UI で動くような AI Agent を含むいくつかの AI 機能を実装するということも並行してやっていきました。

KARTE のマイクロサービスについて

AI システムのアーキテクチャについて説明する前に、前提となる KARTE のマイクロサービスアーキテクチャについて説明します。

KARTE のマイクロサービスは、大きく Web API と Internal API、Frontend に分類されます。

  • Frontend

    React.js や Vue.js などを使用して SPA で動く JavaScript ファイルをビルドし、Web API から配信します。

  • Web API

    Frontend の HTML, CSS, JavaScript ファイルの配信と、その SPA アプリケーションから呼び出される API です。

  • Internal API

    他のサービスの Web API や Internal API から呼び出される API です。内部のネットワークからしかアクセスできないという制約があります。Web API で認証・認可済みの情報を使うことを前提としており、Internal API 自体は認証・認可の仕組みを持ちません。

microservice2.png

それぞれのサービスは対応するチームが開発・運用・保守をしており、以下の原則に従って独立性を保っています。

  1. 独立したデプロイ: 各サービスは個別にビルド・デプロイ可能
  2. 独立した DB: 各サービス専用の DB を使用
  3. API 経由の通信: サービス間の通信は Internal API を使用

サービスの技術選定は基本的に各チームに委ねられています。API は TypeScript で DB には MongoDB を使うことが多いです。Web API や Internal API、Frontend は monorepo 構成(pnpm workspace などを使用)になっており、コードベースの依存は自由になっています。そのため Web API の型を Frontend に共有して使うといったことが可能です。

AI 機能を構成する要素と SDK について

AI 機能といっても単純なものから高度なものまであると思います。KARTE の AI 機能としては AI Agent と呼ばれるものを多く作っているため、今回の記事では AI Agent について解説します。

AI Agent とは Tool や Memory などを使用し、必要に応じて複数ステップで LLM 呼び出しを行いながらタスクを遂行する仕組みです。

https://mastra.ai/ja/docs/agents/overview

agents-overview.webp

Tool は多くの LLM 関連の SDK で実装されていますが Memory は TypeScript だと LangChain.js、Mastra、VoltAgent のようなフレームワークがサポートしています。
これらのフレームワークはさらに、RAG, Workflows, Evals, Observability, Web API などを備えています。私たちは AI Agent を実装するにあたって、最初から何かしらのフレームワークを使うことを考えていました。フレームワークを使用することにより柔軟性は失われてしまいますが、そもそも AI Agent をどうやって実装したら良いかわからない、どんなことができるのかよくわからないという状態では、フレームワークに乗らないデメリットの方が大きいと考えました。私たちは Mastra を使用していますが、特に Memory に関しては自前で実装するよりもかなり楽に実装できていると思います。

Mastra

Mastra は Vercel の AI SDK という SDK の上層として機能するフレームワークです。AI SDK が model routing や tools などの基本的な機能を提供するのに対して、Mastra は Memory や Evals などのよりアドバンスドな機能を提供しています。

https://mastra.ai/ja/docs/frameworks/ai-sdk

mastra-ai-sdk.webp

ちなみに、Mastra Cloud というサービスがあり Mastra を利用したアプリケーションをマネージドな環境により簡単にデプロイできる機能がありますが、KARTE では既存のアプリケーションに組み込む形になるので、使用していません。記事の中で「Mastra サーバ」というように書かれている場合は Mastra Cloud のことではなく、Mastra がインストールされた KARTE 側のサーバということになります。

KARTE AI システムのアーキテクチャ

マイクロサービスアーキテクチャにおいて、私たちは Mastra を使った KARTE AI というマイクロサービスを1つ作成し、他のサービスからそれを呼び出すという形(集中)をとっています。各々のマイクロサービスの中に Mastra をインストールする(分散)というやり方もありますが、それぞれの選択肢のメリットを次のように考えました。

分散

  • 各チームに AI Agent の開発スキルを持ったエンジニアがいる場合は開発スピードが出やすい
  • マイクロサービスの依存関係が増えない
    • システムの安定性が増す
    • バージョンアップや設定の変更などを段階的にしやすい

集中

  • AI システム内に作った Agent 同士は容易に連携できる(プロダクトを跨いだ Multi-Agent 化)
  • 知見を集約できる

AI Agent 開発のパターン自体まだまだ変化が激しく、各チームに AI Agent の開発スキルを持ったエンジニアがいるわけではありません。また、KARTE のプロダクトの方向性の観点でも、マルチプロダクト戦略の要素を強めて行きたいため、将来的に Multi-Agent の構成が取りやすいアーキテクチャにしておく必要があります。以上のようにまずは集中させる方がメリットが大きいと考え、1つのマイクロサービスに AI 関連のコードを集約させる方針としました。

アーキテクチャの話の中で Tool をどこで実行するかや Storage をどう設計するかは重要だと思うので、それぞれについて先に解説し、その後実際に運用した2つのアーキテクチャについて説明します。

Tool

Tool は AI Agent が学習済みのデータ以外のものを参照する場合や、何かしら処理を実現する(例えば Claude Code の場合はコードを書き換える、シェルコマンドを実行するなど)機能です。Mastra を利用する上では実行場所によって3種類の Tool を意識します。

  • server tool

    Mastra サーバ上で実行されます。Mastra の Playground を使用してデバッグすることができます。Mastra の中に、server tool を定義しておくと実行してくれる仕組みがあります。

  • client tool

    Mastra サーバから見たクライアントで実行されます。クライアント側で Tool を実行する処理を書く必要があります。 @mastra/client-js では clientTools の execute プロパティに関数を渡しておくと自動で実行してくれます。 @ai-sdk/reactuseChat では onToolCall で任意の処理を実行できます。

  • provider tool

    Provider 側の環境で実行される Tool です。例えば Vertex AI であれば Google Search や Code Execution などがあります。

自前の tool を server tool として実装するか client tool として実装するかはアプリケーションによってどちらを選択するか考える必要あります。自前の Tool では任意のコードを実行できますが、主に KARTE の DB にアクセスしてデータを取ってくる処理、KARTE の設定値(例えば Action の設定)を作らせる処理、メインの Agent とは別の LLM を呼びだす処理などがあります。provider tool ではより汎用的な Python による数値計算や、Google 検索などの用途が考えられます。

Storage

Storage には主に AI Agent との会話履歴を残す Memory と AI Agent の性能評価の Evals を使用しています。Mastra を集中的に管理する方針では、Storage も基本的には1つになると思います。現状では PostgreSQL 以外はデフォルトでインデックスが作成されないため、特にインデックスを貼らない場合は Agent が増えると性能が劣化する可能性があります。それ以外には AI Agent が扱うデータの取り扱い区分が異なる場合(センシティブな情報を扱うなど)に分離することが難しいという問題はありそうです。また Memory はインスタンスごとに Storage のインスタンスを切り替えることができる(つまりクラスタや DB を分けることはできる)が Evals に関しては Mastra インスタンスに対して1つ Storage を設定するような仕様になっているので、おそらく切り替えることはできません。Memory が切り替えられたとしても Evals が切り替えられなければあまり意味がない(Evals のなかに評価のために Memory に入ってるようなデータが含まれる可能性がある)ので分離するということは考えませんでした。今後、機密度の高いデータを扱うような AI Agent を開発することになった場合には分離を考えるかもしれません。

初期のアーキテクチャ

最初のアーキテクチャは、KARTE AI を Internal API として実装するというものにしました。Frontend からは直接呼び出すことはできず、各サービスの Web API から @mastra/client-js ライブラリを使用して Mastra の API を呼び出す方式です。

karte-ai.png

Tool 呼び出しについて、データを取得したり更新したりする処理を行う場合、そのユーザにデータアクセスの権限があるかを確認することが必要になります。認可は基本的には Web API(つまり Frontend からリクエストが来る)に備わっています。Internal API では Web API で認証・認可を済ませていることが前提で、その情報を引き回して使用するため認証・認可の仕組みがありません。一方で Tool 呼び出しのパラメータは基本的には LLM が自由に決められるため、実装の仕方次第では本来ユーザがアクセスできないデータを取得しにいくという懸念があります。そのため Tool 呼び出しについては client tool として Frontend で実行することを想定していました。このアーキテクチャにはいくつか問題点がありました。

  • @mastra/client-js に依存していること

    サーバサイドから @mastra/client-js を使用することはあまり想定されておらず、バージョンアップの際に Stream や Tool 実行のあたりで問題が起きることがありました。また @mastra/client-js@mastra/server に依存しています。 @mastra/server のバージョンを一定以上に上げると @mastra/client-js の古いバージョンのものは動かなくなることがありますが、まだ Mastra のメジャーバージョンが 0 であるということもあり、互換をそこまで長く維持しない傾向があるように感じました。実際に Mastra のバージョンを最新に追従するタイミングでクライアント側のサービスの @mastra/client-js のバージョンをあげて同時にデプロイしないといけない場面もありました。また、結局フロントエンドでは @ai-sdk/reactuseChat に依存しているため、@mastra/server@mastra/client-js@ai-sdk/react の3つのコンポーネントを意識する必要があります。

  • 冗長な Web API を作ることになる

    Mastra サーバの API を呼び出す Web API の多くは、各Service で実装が同じになります。

そのため次のようなアーキテクチャを選択しました。

Frontend から Mastra サーバにリクエストする方式

front-mastra.png

Frontend から直接 Mastra サーバにリクエストする方式では、 @mastra/client-js に依存しないため、ちょっとした不具合を踏む可能性や、デバッグのコストを抑えることができます。 Vercel ai-sdk にも直近で大きな変更が入りましたが、このアーキテクチャにしておいたおかげで Frontend の useChat の移行に集中できました。Web API を冗長に作る必要がある問題についても解決しました。

これまでは Frontend からは同じ Service の Web API しか呼び出せないという制約を作っていましたが、今回はその制約を外しました。それによって多少の混乱は生むかもしれませんが、今回のケースでは上記のメリットが上回ると判断しました。そもそも、Frontend からは同じ Service の Web API しか呼び出せないというのは、同じ Service の Frontend のために作った Web API に他のサービスが依存してしまうと Service の独立性が損なわれるためです。今回の KARTE AI は他の Service から呼び出す前提となっているため独立性のデメリットは考えなくて良いと思いました。

Mastra を使う優位性と活用例

Mastra と他のフレームワークとの比較に関してはこの記事では細かくは書きませんが、最初の決め手としては TypeScript のフレームワークであること、VoltAgent よりも公開されたのが早いこと、LangChain.js と比較してもドキュメントが充実していてスター数の勢いもあることなどが良いと思ったポイントでした。実際に使ってみるとアップデートが早くどんどん機能が追加されることや、Discord での質問や Pull Request に対してすぐにリアクションが来るというスピード感・柔軟さを感じました。 これらの要素は変化の早い AI 開発において重要だと思っているので、Mastra を気に入っている大きな理由です。Mastra のコードベース自体もわかりやすく開発しやすいため、私自身もこれまで Bug Fix などの 24 つの PR を取り入れてもらいました。ちなみに、アップデートの頻度が高いことを不安に思う方もいるかもしれませんが、ちょうど Mastra v1 β がアナウンスされたため、今後はより安定するんじゃないかと思っています。

また、少し前の話になりますが今年の5月に Mastra の CTO が来日した際にはオフィスに来ていただき、プレイドの AI や Mastra のロードマップについてディスカッションをしたり、こちらからの要望を伝えさせていただきました。

mastra-plaid.png

TypeScript で AI Agent を開発したいほとんどの場合に Mastra は良い選択肢であると考えていますが、個人的に特に重要だと思っている機能について Memory と Stream に絞って少し解説します。

Memory

Mastra の Memory のような機能を一から実装するのは現実的ではなかったかなと思います。Memory は簡単にいうと会話履歴ですが、AI Agent に必要な幾つかのオプションを持っています。KARTE の管理画面で使用できる AI Agent には全てにこの Memory の機能が組み込まれています。

リソースとスレッド

スレッドには複数のメッセージが含まれていて、同一スレッド内の過去のメッセージも LLM にコンテキストとして渡すことで都度プロンプトを書く手間を省略したり、同じ間違いを防ぐことができます。リソースは複数のスレッドをまとめた単位です。現状 KARTE では管理画面を触るユーザの単位で閲覧できるチャットのメッセージを制限しています。たとえば同じテナントであっても、他のユーザがどのような指示を KARTE AI にしているかという情報にはアクセスできないようにしています。リソース ID には認証済みのユーザの ID を prefix に含めることで、他のユーザの会話履歴を閲覧することを防いでいます。

Processor

メモリプロセッサを使用すると、メモリから取得したメッセージの一覧を、エージェントのコンテキストウィンドウに追加して LLM に送信する前に変更できます。これは、コンテキストサイズの管理、コンテンツのフィルタリング、パフォーマンスの最適化に役立ちます。KARTE の AI Agent では TokenLimiter という Processor を使用しています。これは LLM のトークン制限に合わせて、トークン数が上限を超えないように過去のメッセージからどこまで遡るかを決めてくれます。

Semantic Recall

ユーザからの質問や指示に対して、意味的に近い過去の会話履歴をコンテキストに入れてくれる機能です。クロススレッドにも対応していて、アクセスできる複数のスレッドを跨いで効果的なコンテキストを見つけてきてくれます。技術的には RAG を使用しています。KARTE では過去に一度試したことがありますが、当時は MongoDB での実装にバグがあり(そのバグはすぐに修正されましたが)まだ使えていません。今後このような機能が欲しくなった時に Mastra に実装されていてすぐ使えるというのは大きな利点だと思います。

メッセージの変換

最近 Vercel ai-sdk が v4 から v5 にバージョンアップし、メッセージの形式にもかなり大きな変更がありました。ai-sdk v4 の時代に保存されたチャットメッセージが、ai-sdk v5 でも使用できる必要がありました。Mastra は内部でこの辺りの互換をよしなにやってくれるので、かなりスムーズに移行することができました。

Stream

https://mastra.ai/blog/mastra-streaming

Mastra は Stream に関しては独自の実装(Mastra streaming と呼びます)を持っています。Multi-Agent や Workflow などの複雑な構成の場合にも Frontend ではうまく可視化する必要があります。以前までは Agent の Stream メソッドは AI SDK の stream protocol で定義されたストリームを返すため、Workflow の中での LLM 呼び出しやTool 内での LLM 呼び出しは output をストリーミングすることができませんでした。Mastra streaming ではこのようにネストされた LLM の出力に関してもストリームを扱うことができます。実際に KARTE の管理画面にある AI Agent でも、Tool から Agent を呼び出す形の Multi-Agent の構成をとっており、Tool 呼び出しの output を Stream で表現することができています。
Mastra はこの Mastra streaming を Vercel ai-sdk v5 の stream protocol に対応した型に変換することで互換を実現しています。stream protocol には Tool の output の stream に対応する part は定義されていませんが、Custom Data を使用してこれを実現しています。https://ai-sdk.dev/docs/ai-sdk-ui/streaming-data Custom Data に関してもユーザ側で型を定義することができて開発者体験も良いです。
今後は AG-UI などにも対応していくと思いますし、 @mastra/react という package も開発されているようなので、より Mastra の機能を活かした UI が作りやすくなるのではないかと思います。

まとめ

今回のプロジェクトでは、KARTE のマイクロサービス環境で AI 機能を継続的に開発していくために、Mastra を活用した AI 基盤をまずは「集中」型で設計・運用してみました。Mastra を基盤の中心とすることで、自分たちで開発しなくても次々に新しい機能が開発されたり、より使いやすくなっていくというところは大きなメリットでした。
「集中」「分散」に関しては今後は変わっていくと思います。Product 組織全体に AI 機能の開発力がインストールされ、Multi-Agent がネットワークを介してもスピーディに、セキュアに開発できるということがわかってくれば徐々に「分散」させていくことになると思います。KARTE のマイクロサービスアーキテクチャ自体も、AI 機能の開発がしやすいようにアップデートしていく(Internal API の認証・認可の仕組みの実装や内部の MCP の提供など)必要があると感じました。

また、この記事では書けませんでしたが、実際に KARTE AI システムを使った個別の機能についても今後は記事にしていく予定ですのでそちらもぜひ読んでください。