プレイドインターン体験記:Mastraを使ってKARTE Signals AIチャットのメモリ機能を1から設計・実装した話

はじめに

こんにちは。プレイドで2025年12月から2026年5月までの半年間、KARTE Signalsチームでインターンに参加していました森 大地です。普段大学では自律型攻撃エージェントの実装と評価のようなAI×セキュリティを題材に勉強しています。本インターンではKATRE SignalsのAIチャットのバグ修正、機能開発を中心に取り組んできました。本記事では私のインターンでの取り組みやこの会社について感じたことについて書けたらと思います。

プレイドのインターンに参加したきっかけ

まずこの会社を知ったきっかけはセキュリティキャンプに参加した際に、協賛企業としてプレイドが参加していて、食事会でエンジニアのnabekenさんとお話ししたのがきっかけです。そこでこの会社は日本でも有数の規模の顧客行動データを解析、活用をしていて**「データによって人の価値を最大化する」**という企業理念がとても共感できたとともに、高い技術力と全社的にセキュアな意識が高いことが分かり、ぜひ会社についてもっと知ってみたいと感じるようになりました。また、ノベルティが歯ブラシとラムネというのが他の協賛企業に比べて、とてもユニークで印象的でした。

KARTE Signalsとは

KARTE Signals は、KARTEで収集した1st Party Dataを活用し、事業成果につながる広告効果の可視化ができる「Signals Dashboard」と、CV補完や高精度ターゲティングによって広告配信の最適化を支援する「Signals Connector」を提供するサービスです。

そこに伴う分析結果のサポートや改善アクションの提案に利用のできるAI分析チャットが2026年4月28日にGAリリースされましたが、そのチャットに発生するバグ修正や新規機能開発などに取り組んできました。

取り組んだ内容について

AIチャットの機能開発、バグ修正を一任させていただいていた中で、今回は主に以下の3点の機能実装について説明できればと思います。

  • ワーキングメモリ(スレッドを跨いでも保持するメモリ)機能の追加
  • システムプロンプトの最適化
  • ツール使用状況・Reasoning (AIの推論プロセス)機能によるハルシネーションの抑制

実装の前提として、今回のAIチャットはAIエージェント開発フレームワークMastraを使って構築しています。Mastraはエージェントのメモリ管理・ツール呼び出し・スキル管理などの機能を標準で備えており、AIチャットの複雑な処理を効率よく実装できます。また、セキュリティスキャンにはGCPのModel Armor、メモリの永続化にはMongoDBを活用しています。

ワーキングメモリの実装

AIチャットに「ワーキングメモリ」機能を実装しました。これはスレッドをまたいで情報を記憶し続ける機能です。通常のAIチャットはスレッドが切り替わると文脈がリセットされますが、ワーキングメモリを使うことで「このクライアントはECサイト運営者」「夏季キャンペーンに注力中」といった情報をAIが保持し続け、次のスレッドでも、状況を説明し直さずに的確な分析が返ってくるようになります。

この機能はGAリリースに向けてゼロから設計・実装しました。バックエンドの設計だけでなく、自分でデザイナーさんとのMTGを設定させていただき、メモリボタンの配置場所やUIの見せ方を一緒に考えながらFigmaでデザインを作っていただき、そのデザインに沿ってフロントエンドの実装まで一気通貫で担当しました。

また実装がある程度形になった段階で、「ぽちぽち会」という場を自ら設け、チームメンバーに実際のAIチャット機能を触ってもらいました。その場でもらったリアルタイムのフィードバックをもとに最終調整を加えることで、より使いやすい機能に仕上げることができました。

技術選定では、まずMastraが標準で持っているupdateWorkingMemoryの使用を検討しました。しかし、これは自由形式テキストでまとめて保存する方式のため、後から特定の情報だけを削除・更新することができません。たとえば「先週の広告費」を覚えさせた情報が時間が経って古くなっても、その記憶だけをUIから選んで消す手段がなく、不正確な情報がAIの文脈に残り続けてしまいます。そこでChatGPTのようにメモリを個別に管理・削除できる仕組みを実現したいと考え、CustomMemoryクラスを独自に実装し、情報をkey-valueのペアで管理する設計にしました。たとえばindustry: ECサイトcurrent_strategy: 夏季向けキャンペーン重点のような形式で保存されるため、UIから特定のメモリアイテムを選んで削除・更新できるようになっています。

(上の画像はイメージです。)

自動保存の対象は「普遍的・再利用可能な情報」に絞りました。業界の傾向・注力戦略・出力形式の好みなど、スレッドをまたいで役立つ情報です。分析結果やKPI目標は会話ごとに変わるものなので保存対象外にしています。さらにユーザー自身がプロンプトでルールを追加できる設計にすることで、クライアントごとに最適化されたAIに育てていける余地を残しています。

セキュリティ面では、GCPのModel Armorを活用してメモリへの保存内容をスキャンし、プロンプトインジェクションの疑いがある入力はブロックする仕組みを入れています。

メモリの保存件数が上限(20件)に達した場合はUIで通知する仕様にしています。上限を無制限にするとコンテキストが汚染されて精度が下がるため、ここは意図的に制限を設けています。

システムプロンプトの最適化

AIチャットが扱う機能が増えるにつれて、システムプロンプト(LLMへの指示)が肥大化し、精度の低下やコストの増大が課題になってきました。この問題にいくつかの角度からアプローチしました。

一つは、システムプロンプトを「Instructions」「Tools」「Skills」の3層に分解し、常時読み込む量を最小化する設計です。以前はスキルを独自実装で管理しようとしていましたが、その方式では外部で定義されたスキルを取り込むことが難しいと判断し、MastraのWorkspace機能という公式の仕組みに移行しました。skills/<skillName>/SKILL.mdというファイルを置くだけでスキルが自動登録され、LLMはリクエストに応じて必要なスキルだけをオンデマンドでロードするため、コンテキストを無駄に消費しません。また、Mastraのバージョンアップに追従するための独自対応も不要になりました。

karte-ai/apps/backend/src/mastra/agents/signals/
├── dashboardAnalyzerAgent.ts
└── skills/
    ├── summarizePerformance/
    │   └── SKILL.md
    ├── proposeImprovements/
    │   └── SKILL.md
    ├── aggregateData/
    │   └── SKILL.md
    └── operateDashboard/
        └── SKILL.md
sequenceDiagram
  participant U as ユーザー
  participant L as LLM
  participant S as skills/*/SKILL.md

  Note over L: SkillsProcessor が自動生成(workspace 設定時)
  U->>L: ① リクエスト
  L->>S: ② skill でスキルをロード
  S-->>L: ③ SKILL.md 本文を返却(オンデマンド)
  L->>U: ④ 実行・回答

もう一つの取り組みとして、システムプロンプトを英語で記述した場合にトークン数と精度がどう変わるかを検証しました。英語にすることでトークン数が削減され、コンテキストウィンドウを節約でき、結果として精度も上がるんじゃないかという試みでした。ただ実際に試してみると、Output in Japaneseのような指示をシステムプロンプトに入れても、ユーザーが日本語で入力しているのに英語で回答が返ってくるケースが発生しました。また、システムプロンプトへの変更はLLMが毎回同じ挙動をするとは限らないという難しさもあり、小さな変更でも十分な回数の検証が必要だと実感した取り組みでした。

ダッシュボードとの日付連携では、ダッシュボードが表示している期間と「今日」の日付をAIに正確に渡すことで、「先週」「去年の同月」といった相対表現を正しく解釈できるようにしました。これがないと、ダッシュボードは過去データを表示しているのにAIが現在の日付を基準に回答してしまうという噛み合わせのズレが生じます。また、ワーキングメモリに情報を保存する際にも具体的な日付を付与することで、「この戦略は2026年3月時点のもの」のように時期と紐づいた形で記憶を残すことができます。こうしてAIがメモリを参照することで、クライアントの業界・戦略・出力形式の好みを毎回説明し直さなくても会話が成立するようになります。回答のブレが少なくなり、「このクライアントならこういう切り口で分析してほしい」という期待に沿った回答が安定して返ってくることが、ユーザーにとっての直接的なメリットです。

ツール使用状況・Reasoning 表示

AIが回答を生成する過程で何をしているのか見えないことは、開発者にとって大きな課題です。計算を本当にしているのか、どのツールを呼び出したのかが分からないまま結果だけが返ってくると、ハルシネーションの検出が難しくなります。そこでAIの思考プロセスとツール使用状況を開発者向けに可視化する機能を実装しました。

各ツールの実行状態をリアルタイムで確認できるため、LLMが「計算しているつもり」で実は暗算している、といったケースを即座に検知できるようになりました。計算ツールが呼び出されなかった場合は警告が表示されるため、回答の品質チェックが格段にしやすくなりました。

加えて、Reasoningの可視化についても開発者向けのデバッグツールとして内部実装・検証を進めています。LLMが答えを導くまでの推論プロセスを追跡できることで、ハルシネーションの根本原因の特定が容易になります。

その他細々機能実装

AIチャットに停止機能を追加し、停止後に入力欄へ実行中のプロンプトが保持される仕様にしました。長い処理の途中でプロンプトを修正したくなった場合でも、ゼロから入力し直す手間が省けます。GeminiのAIチャットにある同様の機能がとても使いやすいと感じたことがきっかけで、自分でIssueを立てるところから実装まで一貫して取り組みました。

データベース管理についても、メモリデータの管理をKARTEプロジェクト(クライアント)ごとに分離しました。万が一プロンプトインジェクションやDBの混線が発生した場合でも、別クライアントのメモリデータが混入するリスクを予防的に排除できます。

また、GCPで動かしているAIチャットのモデルのバージョン管理・更新にも携わっています。モデルのバージョンを上げれば良いというものではなく、実際に動作を検証した上で選定する必要があります。各バージョンでの日本語応答の品質や挙動を検証し、ユーザー体験を最大化できるバージョンへの更新対応と検証結果をチームに共有しました。

インターンを通して

このインターンでは、インターン生としてではなく1人の社員として扱っていただける環境で、自走力が強く求められました。それでも自分から声をあげると、周囲のメンバーがいつでも親身に助けてくれ、その安心感が自分の成長を後押ししてくれたと感じています。エンジニア一人ひとりのレベルが全員高いにもかかわらず、それぞれの得意領域へのリスペクトがあり、対等にコミュニケーションが取れる雰囲気のよい職場でした。また、クライアント様に実際にサービスを使っていただき、現場からのフィードバックをもとに実装を柔軟に改善する経験を通じて、臨機応変に対応する力が養われたと感じています。

加えて印象的だったのが、AIをはじめとした技術的進化に対して社員の多くが能動的にキャッチアップしていることです。チームや立場をまたいだ勉強会に人数が殺到するほど技術的好奇心が高く、そんな環境の中でメンバーみんなで高め合っていける、そこがこの会社の強みだと感じました。中でも、面談からメンターとして一貫してサポートしてくださったterashinさん、セキュリティについて深くお話していただいたkobachanさんをはじめ、多くの社員さんに温かく支えていただいたおかげで、インターンを通じて大きく成長できたと感じています。

インターンを考えている学生へ

フルスタックかつ最前線のAI駆動開発が行われている環境です。インターン生であっても、機能の設計・実装・リリースまでを1人の社員として主導できる、インターンとは思えない大きな裁量が与えられます。自分でIssueを立てるところから始まり、大小さまざまな課題に一貫して向き合う中で、どこでも通用する自走力が自然と身についていきます。能動的に動ける人、技術的好奇心が強い人にぜひおすすめしたいインターンシップです。

私はAIに興味があってSignalsチームで開発に携わりましたが、プレイドには他にも幅広い領域でのポジションがあります。他のインターン体験記やテックブログも参考に、自分の興味に合ったポジションをぜひ探してみてください。

↓下記リンクからぜひ応募してみてください。
https://plaid-newgrads.snar.jp/jobboard/detail.aspx?id=2B9662ZbwLU