メインコンテンツまでスキップ

reSpeaker Clip AI チャットシステムアーキテクチャ

reSpeaker Clip AI エージェントチャットシステムの動作を説明する平易なガイドです。ここに書かれている内容はすべて README と backend/ 内の実際のコードに基づいています。


reSpeaker Clip

1. 概要

このシステムは Flask + LangGraph + Groq 上に構築された音声ファーストの AI アシスタントです。あなたが音声またはテキストでメッセージを送ると、エージェントが処理方法を判断し、必要に応じてツールを呼び出し、回答を生成し、その返信を音声で話します。

パイプラインの概要は次のとおりです:

pir

この仕組みを成り立たせているのは次の 3 つの考え方です:

  • ルーターがすべてのリクエストを 3 つのパスのいずれかに分類します。
  • それぞれのパス上のエージェントはツール(ウェブ検索、電卓、Notion、過去会話検索)を呼び出すことができます。
  • ベクターストア + メモリレイヤーにより、エージェントはあなたの過去の会話や長期的な事実を想起できます。

2. システムアーキテクチャ図

pir

各コンポーネントは次のように組み合わさります:

  • Flask は API の表面(チャット、ストリーミング、音声、TTS)です。
  • LangGraph はリクエストをルーティングし、選択されたノードを実行するステートマシンです。
  • Groq は LLM、音声認識(Whisper)、音声合成(Orpheus)を提供します。
  • Mem0 が長期メモリを提供し、Pinecone + ローカル埋め込みが過去会話検索を提供します。
  • Supabase(SQLite フォールバック付き)が会話、メッセージ、要約を保存します。

3. 3 つのルーティングパス

すべてのリクエストは必ず 3 つのノードのうち 1 つにルーティングされます。コードは backend/graph/graph.py 内でルート文字列をノードにマッピングします:

simple — コンテキストなしチャット

  • 素の LLM 応答です。ツールも特別な人格もありません。
  • 一般的な質問、説明、通常の会話に使用されます。
  • ノード:simple_nodebackend/graph/nodes/simple.py)、LLM = メインの Groq モデル。

context — エージェント(ツール対応)

  • リクエストに外部情報やツール(ウェブ、数学、Notion、過去会話)が必要になる場合があります。
  • このノードは create_agent で LangChain エージェントを構築し、モデルがループ内でツールを呼び出せるようにします。
  • ノード:agentic_nodebackend/graph/nodes/agentic.py)、LLM = ツール呼び出し専用モデル(gpt-oss-20b)。

persona — スタイル付き

  • ユーザーが明示的にスタイル、人格、役割、教え方などを求めた場合です。
  • 仕組みは simple と同じですが、システムプロンプトが異なり、モデルに口調を適応させるよう指示します。
  • ノード:persona_nodebackend/graph/nodes/persona.py)、LLM = メインの Groq モデル。

4. 分類ロジック

ルーティングは router_nodebackend/graph/router.py)で行われ、キーワードの事前チェックの後に LLM 分類が続きます。

  1. キーワード事前チェック(高速パス)。書き起こしテキストをツール用キーワードでスキャンします。どれかが一致した場合、ルートは強制的に context となり、LLM は完全にスキップされます:

    notion, to-do, todo, task list, to do list, calendar,
    schedule, reminder, note down, create a task, add a task

    例:「add a task to my to-do list」は分類器に送られることすらありません。

  2. LLM 分類。それ以外の場合、リクエストは ROUTER_PROMPT とともにメインの Groq モデルに送られます。これはちょうど 1 語だけを返す分類プロンプトです:

    ルートトリガーとなる入力
    simple一般的な質問、説明、通常の会話。ツールや persona は不要
    context外部情報/ツールが必要になる可能性があるもの、データ管理(Notion/カレンダー)、ユーザーのデバイス/ファイル/保存情報に関する質問
    personaユーザーが明示的にスタイル、人格、教え方、役割や振る舞いを要求する場合
  3. フォールバック。出力は小文字化され、前後の空白が削除されます。{simple, context, persona} に含まれないものはすべて simple にフォールバックします。

5. エージェント型ツールシステム

ツール呼び出しの仕組み

context パスは LangChain の create_agentbackend/graph/nodes/agentic.py)を使用します。ループは次のようになります:

pir

注目すべき詳細:

  • エージェントはキャッシュされ、ツールシグネチャが変わった場合にのみ再構築されます(_get_agent)。
  • リトライロジック:最大 MAX_RETRIES = 3 回試行し、一時的な tool_use_failed エラーは 1 秒スリープを挟んで再試行します。
  • ガードレール:再帰は MAX_AGENT_ITERATIONS = 10 に制限されています。上限に達した場合、エージェントはクラッシュする代わりに固定の「上限に達しました…」メッセージを返します。
  • システムプロンプトは、モデルに対してツール呼び出し回数を最小限にし、十分な情報が得られたらすぐに回答するよう指示します。
  • SSE ストリーミング中は、各ツール呼び出しが thinking イベント({"tool": "web_search"})として UI に表示されます。

利用可能なツール

get_available_tools()backend/tools/registry.py)は、エージェントが呼び出せるすべてのツールを返します:

ツールバックエンドサービス目的設定キー
calculatorローカル、安全な AST 評価ホワイトリスト化された + - * / ** % 評価器による数学計算—(常に利用可能)
search_conversationsPinecone + ローカル埋め込みユーザー自身の過去会話を関連度で検索PINECONE_API_KEY
composio_searchComposio自然言語リクエストに一致する Composio アプリツールを検索COMPOSIO_API_KEY
composio_executeComposio一致したツールをスラッグと JSON 引数で実行同上
composio_connectComposioConnect Link を通じてツールキット(例:github)を認可同上
web_searchTavilyライブ/最新のウェブ情報(ニュース、ファームウェア、製品詳細)TAVILY_API_KEY
add_todoNotionTo-do リストにタスクを追加NOTION_API_KEY / NOTION_DATABASE_ID
list_todosNotionステータス付きでタスクを一覧表示同上
complete_todoNotionタスクを完了済みにする(名前/キーワードで一致)同上
delete_todoNotionタスクを削除(名前/キーワードで一致)同上
  • Notion ツールは、Notion が設定されている場合にのみ追加されます。
  • 未設定のツールは(「Web search is unavailable…」のような)フレンドリーなメッセージを返すため、システムは優雅に機能低下します。

設定済みツール

pir


6. ベクター検索の詳細

設定

pir

すべてのベクター設定は config.py / .env にあります:

設定デフォルト意味
PINECONE_API_KEYベクター検索を有効化
PINECONE_INDEX_NAMEconversationsPinecone インデックス名
PINECONE_CLOUDawsサーバーレスのクラウドプロバイダ
PINECONE_REGIONus-east-1サーバーレスリージョン
EMBEDDING_MODELall-MiniLM-L6-v2ローカル sentence-transformers モデル
EMBEDDING_DIM384ベクター次元(インデックスと一致している必要あり)

起動時に、init_index() が Pinecone インデックスを自動作成します(存在しない場合、次元 384、メトリック cosine、サーバーレス)。

埋め込み対象とメタデータとして保存されるもの

設計としてはベクターを小さく保ち、それ以外をメタデータに入れます:

  • 埋め込み(ベクター):"<title>\n\n<overview>" — 会話の短い意味的要約。
  • メタデータ:user_idconversation_idtitlecreated_at
  • ベクターストアには一切入れないもの:完全な書き起こし。実際の会話ターンは Supabase/SQLite に保存され、一致後に id で取得されます。

ベクター id は "{user_id}-{conversation_id}" であり、ユーザーごとの会話ごとに安定しています。

ベクター作成(書き込みパス)

すべてのチャット/音声ターンの後に、システムはバックグラウンドスレッド(index_conversation_asyncsummarize_and_index in backend/services/conversation_service.py)を起動します:

詳細:

  • MIN_TURNS = 2 未満のターン数しかない会話はスキップされます。
  • タイトル/概要は、メインの Groq LLM が 2 行のプロンプト(Title: / Overview:)を使って生成します。
  • 埋め込みは正規化されます(cosine に適した形)。
  • これらはすべて非同期(デーモンスレッド)で行われるため、ユーザーへの応答がインデックス作成でブロックされることはありません。

ベクタークエリ(読み取りパス)

エージェントが search_conversations を呼び出すとき、フローは次のようになります:

重要なポイント:

  • クエリは同じローカルモデルで埋め込まれ、その後 user_id フィルタ付きで検索されるため、ユーザーは自分自身の会話しか見ることができません。
  • 一致結果として id + スコア + メタデータが返され、完全な要約はリレーショナルストアから id で取得されます。
  • 一致結果は類似度スコア付きで整形されるため、エージェントは関連度を判断できます。

7. メモリシステム

長期メモリには Mem0 を使用し、単一ユーザー(MEM0_USER_ID、デフォルトは user-1)にスコープされています。

メモリカテゴリ

コード内にハードコードされたバケットはなく、その代わりに MEM0_CUSTOM_INSTRUCTIONS が、どの永続的事実を優先順で抽出するかを Mem0 に指示します:

  1. 健康上の制約とアレルギー — 特に医師から助言された内容(ユーザーに適用されるものとして解釈)。
  2. スケジュール — 会議、予定、リマインダー。
  3. 嗜好や個人的な詳細。

明示的に除外されるもの:アシスタント自身の応答/レシピ/説明、および一時的な単発リクエスト。

チャットにおけるメモリ検索

プロアクティブなリコールは、ルーティングの前にすべてのリクエストで行われます(backend/routes/chat.pyrecall(text)):

  1. 受信メッセージが Mem0 のセマンティック検索(top_k = 5)に送られます。

  2. 結果はスコア内訳に対する 2 段階の関連度チェックでフィルタされます:

    • semantic ≥ 0.28 → 採用、または
    • semantic ≥ 0.24 かつ bm25 > 0.01(キーワードでブーストされたヒット)→ 採用。
  3. 残ったメモリは created_at(新しいものが先)でソートされ、次の形式で整形されます:

    Relevant context from your past conversations:
    - <memory text> (created 2026-08-30)
  4. そのブロックは、任意のノードに到達する前にユーザーメッセージの前に付加されます — そのため LLM はそれをコンテキストとして認識しますが、トピックに直接関連する場合にのみ使用するよう指示されます。

書き込みはすべてのやり取りの後(save_exchange)に行われます:ユーザー/アシスタントのペアがカスタムインストラクションとともに Mem0 に送信されます。Mem0 キーが存在しない場合、メモリの保存と呼び出しはどちらも正常に失敗(ログに記録され、無視)します。


8. チャットセッションとコンテキスト

セッション構造

「セッション」とは、リレーショナルに保存された会話(Supabase PostgreSQL、または SQLite フォールバック)を指します。スキーマは次のとおりです:

  • usersid, email(単一ユーザー、user-1 をシード)。
  • conversationsid, user_id, title, overview, action_items, timestamps.
  • messages/turnsconversation_id, role (user/assistant), content, timestamp.

1 ターンのフロー:

  1. 会話を作成(または再利用)し、conversation_id を取得します。
  2. コンテキストを読み込みます:recall() によるメモリ + get_recent_messages(conversation_id, 10) による履歴。
  3. AgentState を構築し、LangGraph に通します。
  4. 応答後:両方のターンを保存し、やり取りを Mem0 に保存し、非同期のベクトルインデックス作成を開始します。

コンテキストウィンドウ

LLM 用に組み立てられるコンテキストは、意図的に小さくレイヤー化されています:

コンポーネントソースサイズ
システムプロンプトパスごとの定数(simple/persona/agent SYSTEM_PROMPT固定
会話履歴get_recent_messages(conversation_id, 10)直近 10 ターン(ユーザー 10 件 + アシスタント 10 件)、時系列順
呼び出されたメモリMem0 recall()、上位 5 件、フィルタ済み最大 5 件のメモリ
現在のユーザーメッセージformat_memories(...) + transcriptリクエスト

pir

注意:

  • 履歴はベクトルストアではなくリレーショナルストアから取得されます(ベクトルストアにはターンではなく要約が保存されます)。
  • メモリはユーザーメッセージにインラインで挿入されるため、モデルはそれらを「過去の会話からの関連コンテキスト」として扱います。
  • エージェントパスはメッセージを [history..., ("user", memories + transcript)] として構築し、ツールを用いたエージェントループを行います。

9. システムプロンプト構造

システムプロンプトは 4 種類あります:

プロンプト場所使用者役割
ROUTER_PROMPTbackend/graph/router.pyルーター分類1 語で返答:simple / context / persona
SIMPLE_PROMPT (simple.py)backend/graph/nodes/simple.pysimple_nodeシンプルなチャット用の親切な音声アシスタント
PERSONA_PROMPT (persona.py)backend/graph/nodes/persona.pypersona_nodesimple と同様だが、ユーザーのリクエストに合わせてスタイル/教え方を調整
SYSTEM_PROMPT (agentic)backend/graph/nodes/agentic.pyagentツール対応アシスタント;各ツールとその使用タイミングを説明

simple、persona、agentic の各プロンプトは、共通のハウススタイルの末尾を共有します:

  • 最大 2〜3 文の短い文章で回答します。
  • プレーンテキストのみ — markdown、アスタリスク、絵文字は禁止。
  • 呼び出されたメモリは、トピックに直接関連する場合にのみ使用します(スケジュールと食事の分離は明示的に強制されます)。
  • 矛盾がある場合は、最も最近作成されたメモリを信頼します。
  • ユーザーが新しい事実を述べた場合、その事実のみに言及し、無関係なメモリを反復しないでください。

agentic プロンプトはさらに次のことを行います:

  • ツールに名前を付けます(web_search, calculator, search_conversations, Notion ツール)。
  • ツール呼び出しを最小限にするよう指示し、十分な情報が集まったら停止します。
  • 短くプレーンテキストの回答形式を維持します。

10. 使用している LLM モデル

すべてのモデルは Groq 上で動作します。backend/llm/client.pyconfig.pygroq_client.py で定義されています:

役割環境変数デフォルトモデルTemperature備考
メイン LLM(router、simple、persona、要約)GROQ_LLM_MODELqwen/qwen3.6-27b0.7ChatGroq インスタンス llm
エージェント / ツール呼び出し用 LLMGROQ_AGENT_MODELopenai/gpt-oss-20b0.0ChatGroq インスタンス agent_llmcreate_agent で使用
音声認識(Speech-to-text)GROQ_STT_MODELwhisper-large-v30.0Whisper 文字起こし
音声合成(Text-to-speech)GROQ_TTS_MODELcanopylabs/orpheus-v1-englishVoice = TTS_VOICEautumn
埋め込み(ローカル、Groq ではない)EMBEDDING_MODELall-MiniLM-L6-v2sentence-transformers、384 次元、正規化済み

LLM 推論のデフォルト値(groq_client.chat 経由):max_completion_tokens = 2048top_p = 1.0、temperature は上書き可能です。

11. エージェント呼び出しデモ

メールを読む

pir

カレンダーを追加

pir

技術サポート & 製品ディスカッション

当社の製品をお選びいただきありがとうございます。私たちは、製品をできるだけスムーズにご利用いただけるよう、さまざまなサポートを提供しています。お好みやニーズに応じて選べる、複数のコミュニケーションチャネルをご用意しています。

Loading Comments...