ミツモア Tech blog

「ミツモア」を運営する株式会社ミツモアの技術ブログです

全てのアプリの可能性を開花させるAIオープンソースuse-aiを公開しました


はじめまして、Foundationチームの Zac(Zachary Davison) です。 ミツモアでFoundationチームのマネージャーをしています。普段はプロワンの基盤まわり(データ同期やOpenSearch、インフラ構成の整備)などを担当しています。 今回はプロワンで利用しているAI基盤のオープンソースについてお話ししたいと思います。

概要

この記事は元記事をベースに日本語バージョンを作成しています。

use-ai は、ReactアプリにAI機能を簡単に組み込むためのクライアントサイドライブラリおよびサーバーです。

ページ上のコンポーネントを操作可能なAIチャットをすばやくアプリに追加できます。ごくわずかなコードを書くだけで、ユーザーの操作を自動化する多様なユースケースを実現できるようになります。

1. TodoList.tsx

export default function TodoList() {
  const { todos, addTodo, deleteTodo, toggleTodo } = useTodoLogic();

  const { ref } = useAI({
    tools: { addTodo, deleteTodo, toggleTodo },
    prompt: `Todo List: ${JSON.stringify(todos)}`,
  });
}

2. index.tsx

root.render(
  <UseAIProvider serverUrl="ws://localhost:8081">
    <App />
  </UseAIProvider>
);
  1. 各コンポーネントで useAI を呼び出し、そのコンポーネントが持つツール(関数)と状態(prompt)を use-ai に宣言します。
  2. UseAIProvider はフローティングアクションボタン型のチャットUIを提供し、すべての子コンポーネントから useAI のツールとプロンプトを集約します。
  3. @meetsmore/use-ai-server が、フロントエンドとLLMの間の調整役として機能します。
  4. ✨ これにより、LLMはフロントエンド側で定義された tools 関数をMCP(Model Context Protocol)として呼び出せるようになります。

なぜ開発したのか?

私たちが use-ai を開発した理由は、自社プロダクトである「ミツモア (MeetsMore)」や「プロワン (ProOne)」において、AIを活用してユーザーに素早くタスクをこなしてもらうための多様なユースケースが存在したからです。

ユーザーに実行してもらいたいプロンプトの要望リストがあり、例えば以下のようなものでした:

  • 「間違えて登録してしまった案件を削除したい」
  • 「すでに受注フェーズ(またはそれ以降)にある特定の案件を、受注前の状態に戻したい」
  • 「今月の売上とコストを知りたい」

社内の多くのメンバーがAI活用のアイデアを持っていました。そのため、各チームが独自にAI機能を開発するのではなく、これらのアイデアを実現できるシンプルで拡張性の高いツールを提供したいと考えたのです。

アーキテクチャ

  1. 💻 [クライアント]useAI の呼び出しにより、ツールとして使用されるメタデータ付きのJavaScript関数が提供されます。
  2. 💻 [クライアント]UseAIProvider は、useAI フックを持つすべてのマウント済みコンポーネントを収集し、そのツール群を UseAIServer に送信します。
  3. ☁️ [サーバー]UseAIServer はクライアント側とLLMの連携を調整し、クライアント側のツールをMCPツールとしてLLMに提供します。
  4. 🤖 [LLM] → LLMエージェントが実行され、必要に応じてクライアント側のツールを呼び出します。
  5. ☁️ [サーバー] → サーバーは、LLMから渡された引数を用いてクライアント側ツールを実行するよう、クライアント側にリクエストを送ります。
  6. 💻 [クライアント] → クライアントは、指定された引数でリクエストされた関数を実行します。

use-ai が解決する課題

状態管理 (State)

内部的に、use-ai はAIとアプリの状態を同期させるための複雑な処理の大部分を担っています。これにより、AIはUIコンポーネントとその状態の変化を常に把握できます。

状態の表現には useAI フックの prompt 引数を使用するため、Reactコンポーネントの実際のステート(生データ)はそのままでは送信されず、トークンの浪費を防ぎます。

基本的に、変更の前後を問わず、use-ai とそのチャットペインは常にUIの正しい状態を保持していると確信して間違いありません。

マウントされているコンポーネントの状態・ツールのみが含まれるため、アプリ内で画面を切り替えた場合、AIは「今見えている」ものだけを認識します。

また、不可視コンポーネント(Providerなど)を使用すれば、「グローバル」なツールや状態をAIに提供することも可能です。

フル機能のチャットUI

use-ai には、以下のような機能を備えたフル機能のチャットUIが標準で組み込まれています:

  • チャット履歴のローカルストレージ保存
  • 複数チャットの管理
  • エージェントの切り替え
  • / (スラッシュ) コマンド
  • ファイルアップロード(ドラッグ&ドロップ対応)

創発的な振る舞い (Emergent Behaviour)

use-ai の最も素晴らしい点のひとつは、シンプルなツールを追加するだけで強力な機能を実現できることです。

export default function TodoList() {
    const tools = useMemo(() => {
    const addTodo = defineTool(
      'Add a new todo item to the list', // リストに新しいTodoアイテムを追加する
      z.object({
        text: z.string().describe('The text content of the todo item'), // Todoアイテムのテキスト内容
      }),
      (input) => addTodoFn(input.text)
    );

    const deleteTodo = defineTool(
      'Delete a todo item by its ID', // IDを指定してTodoアイテムを削除する
      z.object({
        id: z.number().describe('The ID of the todo item to delete'), // 削除するTodoアイテムのID
      }),
      (input) => deleteTodoFn(input.id),
      { confirmationRequired: true }
    );

    const toggleTodo = defineTool(
      'Toggle the completed status of a todo item', // Todoアイテムの完了ステータスを切り替える
      z.object({
        id: z.number().describe('The ID of the todo item to toggle'), // 切り替えるTodoアイテムのID
      }),
      (input) => toggleTodoFn(input.id)
    );

    return {
      addTodo,
      deleteTodo,
      toggleTodo,
    };
  }, []);

  const { ref } = useAI({
    tools: { addTodo, deleteTodo, toggleTodo },
    prompt: `Todo List: ${JSON.stringify(todos)}`,
  });
}

このようなシンプルなツールを用意するだけで、以下のような「一括編集」機能が可能になります:

  • 「豚骨ラーメンを作るための買い物リストを追加して」
  • 「野菜はもう全部あるから、チェックをつけておいて」

LLMは1回のレスポンスで複数のツールを呼び出す(マルチツールコール)ことをサポートしているため、use-ai はこれらのアクションを一度にまとめて実行できるのです!

AG-UI

Agent-User Interaction Protocol (AG-UI) は、AIエージェントとユーザー向けアプリケーションの接続方法を標準化するイベントベースのプロトコルです。

use-ai はこのプロトコルに準拠しています。つまり、同じプロトコルに準拠しさえすれば、技術的には独自のバックエンドやフロントエンドを実装することも可能です。

機能一覧

use-ai には多くの機能があり、詳細は公式の README.md に記載されています。

それぞれの詳細には踏み込みませんが、その他に備えている機能を簡単にご紹介します。

コンポーネントの識別 AIは、同じコンポーネントの複数のインスタンスを区別できます。
不可視コンポーネント 画面には描画されず、AIにツールやコンテキストを提供するためだけの「Provider」コンポーネントを作成できます。
サジェスト機能 useAI フックでプロンプトの提案を提供でき、チャットが空のときにチャットペインに表示されます。
confirmationRequired LLMがツールを実行する前にユーザーの確認を必須にする設定ができます(破壊的なアクションに有用です)。
フル機能のチャットUI 多くの便利機能を備えた、本番環境で使えるレベルのチャットUI。
テーマ設定 チャットUIのテーマ(見た目)をカスタマイズ可能です。
多言語対応 (i18n) チャットUIで使用されるすべての文字列を独自のテキストに置き換えることができます。
すぐに使えるサーバー ほとんどのケースでそのまま使える、必要な機能が揃った UseAIServer が同梱されています。
サーバーサイドMCP サーバーサイドMCPを追加可能。フロントエンドの UseAIProvider にコードを渡すことで、これらのMCPに認証情報を渡すことができます。
レート制限 トークンの乱用を防ぐため、UseAIServer にレート制限機能が組み込まれています。
Langfuse連携 Langfuse を標準サポートしています。
プラグインサポート プラグインを使ってサーバーを拡張し、新機能を追加できます。
ヘッドレスワークフロー Workflowsプラグインを使用して、トリガー可能なヘッドレスワークフローを定義できます。

オープンソースへの取り組み

開発者の皆様がAIを使って素晴らしいプロダクトを素早く構築できるよう、私たちは use-ai のオープンソース化を決定しました。

リポジトリ へのコントリビューション(貢献)をお待ちしております!

プロジェクトへ多大なる貢献をしていただいた 増田さん に心より感謝申し上げます。