個人的なトレーニングアドバイスを得るためにミニRAGパイプラインを構築しました (2026/03/20)

個人的なトレーニングアドバイスを得るためにミニRAGパイプラインを構築しました (2026/03/20)

https://www.thatjeffsmith.com/archive/2026/03/i-built-a-mini-rag-pipeline-to-get-personal-training-advice/

この記事は個人的な内容と技術的な内容が混ざっていますので、ご了承ください。個人的な部分は興味を引かないかもしれませんが、ここで紹介するアイデアや技術は、ビジネスやテクノロジーにおけるデータやAIに関する幅広い課題に容易に応用できます。

追伸:ジェラルドのニュースレター経由でここに来た方は、ぜひコメントを残してください。

個人的な

人々は、恋愛相談、メンタルヘルス、投資、パーソナルトレーニングなど、あらゆる個人的なニーズや要望を満たすためにエージェントを利用しています。私もジムに行けない時など、そういった時に利用しています。一人でできるトレーニングが必要な時もあるんです。

私のエージェントと選んだLLMは、フィットネスルーティンを考えるのに非常に長けていますが、私のことを訓練されていません。彼らは私が何が好きで何が嫌いか、私のパフォーマンスはどうか、回復にどれくらい時間がかかるか、私のことについて何も知りません。もちろん、質問するたびに大量のコンテキストエンジニアリングを使ってこの情報を入力することはできますが、もっと良い方法があるはずです…ネタバレ:RAGはこの問題を解決してくれます。

技術的な

私は2013年以来、ハイキング、サイクリング、スパルタンレース、5kmランニング、クロスフィットクラス、カヤック、ウォーキングなど、あらゆる活動を欠かさず記録してきました。🐶そして、この「日記」がたまたま私の26ai Oracleデータベースに保存されているのです。

エージェントがこれらのデータすべてにアクセスできれば、もっと良いアドバイスをもらえるかもしれないと思った。

RAGパイプラインとは何でしょうか? そもそもRAGパイプラインとは何でしょうか?Google検索AIによると…

検索拡張生成(RAG)パイプラインは 外部データを取得してプロンプトに埋め込むことで、大規模言語モデル(LLM)の精度を向上させるAIフレームワークこれは、インデックス作成(データのクリーニング、チャンキング、保存)と検索生成(クエリに関連するコンテンツの検索)から構成され、回答を事実に基づいたデータに基づかせることで、誤った情報を防ぎます。

それはまさに私が探しているものにとても近いですね!

Oracle AI Database の分野で多くの方がこれまで見てきた RAG は、Andy がここで説明している内容と似ています。 「AI ベクトル検索で RAG を使用する」をご覧ください。Andy はこのトピックに関して、この記事を含め素晴らしい記事をいくつか書いていますので、ぜひ彼の最新情報を購読してください。

ユーザープロンプト → Oracle DB → ベクトル検索 → チャンクの取得 → 外部LLMの呼び出し → 応答

つまり、処理の全工程はデータベース自体の中で行われます。誰かがクエリを実行すると、自然言語による質問でLLM(言語レベルモデル)が呼び出されると同時に、AIベクトル類似性検索によって決定されたデータベース自体の情報も取り込まれます。

私としては、ローカルエージェントを使いたいのですが、RAGコンポーネントのメリットも享受したいんです。それに、エージェントやLLMを変更する場合でも、データベースの内容は一切変更したくないんです。

私が作ったもの

ユーザープロンプト → クロード → SQLcl MCP を介したベクトル検索クエリ → Oracle からチャンクを取得 → クロードが応答を合成

左側は、エージェント/LLM(法律サービスマネージャー)がいる場合、誰もが持っているものを表しています。そして右側は、「外部データ」や「関連コンテンツ」を使って、エージェントが単独でできることをどのように拡張できるかを表しています。

それぞれの要素を詳しく見ていきましょう。

Oracle SQLcl MCP

私のデータベースであるMCPサーバーは、エージェントに登録されています。エージェントは、目の前のタスクに必要な関連データを取得する必要がある場合、私のフィットネスデータにクエリを実行することでそれを行うことができます。言い換えれば、エージェントが私のデータベースに対して安全にクエリを実行するためのゲートウェイとして機能します。

ONNX埋め込み

私のデータベースには、ALL_MINILM_L12_V12という文変換モデルがロードされています。これは次の2つの点で役立ちます。

  • 後でRAGクエリを実行するために、データベース内のデータをベクトル化する。
  • 受信クエリから検索文字列をベクトル化し、類似データを見つけるために使用できます。

💡ヒント:データベースを使用してデータをベクトル化する必要はありません。データベースの外でベクトル化し、そのベクトルをデータベースに保存するだけで、必要に応じてベクトルのインデックス機能や検索機能を使用することも可能です。

私のユースケースでは、このデータベース機能を使うのは非常にシンプルで簡単なので、そのようにしました。手順についてはこちらをご覧ください。また、Pythonを使って外部からREST APIを使ってベクトルをアップロードした方法も試しました。その方法はこちらで紹介しています

データ(STRAVA_ACTIVITIES)

このテーブルには約80列あります。どの列をベクトル化すべきかについて提案があり、それをエージェントに伝えました。しかし、エージェントにはまず私のデータを見てもらい、その後の質問や類似性クエリのために、何をベクトル化すべきかを独自に提案してもらう機会も与えました。

テーブルにVECTOR列を追加し、ACTIVITY_NAME、ACTIVITY_DESCRIPTION、およびAVERAGE_HEART_RATE列の内容を連結したベクトルを計算します。

このデータは、Sonnet 4.6 Extendedの学習データに、私の13年分のトレーニングデータを補完するために使用されます。

表を自由に閲覧して、提案に役立つと思われる他の列や別の列を提案してください。

私のエージェントはこれをとても気に入りました。MCPサーバーを使用してテーブルを閲覧/詳細化し、run-sql MCPツールを介してクエリを実行して列のリスト、説明、およびデータのプレビューを取得し、VECTORデータに対して次のような提案をしました。

カラムなぜ価値があるのか
ACTIVITY_NAMEセッションには内容を分かりやすく表す名前を付けます(例:「土曜日の持久力トレーニング」、「クロスフィットWOD:シンディ」)。
ACTIVITY_DESCRIPTION金色の部分 ― マイク博士のプログラミングの詳細
ACTIVITY_TYPEワークアウト vs ランニング vs ローイングなど — フィルタリングに重要
AVERAGE_HEART_RATE強度指標
CALORIES努力量
TRAINING_LOAD入力された場合、セッションのストレスを直接反映する
ELAPSED_TIME_2期間のコンテキスト
PERCEIVED_EXERTION主観的な強度

承認しました。新しいVECTOR列の更新を許可しました。はい、実際のデータと一緒にベクトルを保存しています。

SQL
UPDATE strava_activities
SET activity_embedding = VECTOR_EMBEDDING(
    ALL_MINILM_L12_V2 USING (
        activity_type || ' ' ||
        NVL(activity_name, '') || ' ' ||
        NVL(activity_description, '') || ' ' ||
        'HR:' || NVL(TO_CHAR(average_heart_rate), '') || ' ' ||
        'calories:' || NVL(TO_CHAR(calories), '') || ' ' ||
        'duration_sec:' || NVL(TO_CHAR(elapsed_time_2), '') || ' ' ||
        'training_load:' || NVL(TO_CHAR(training_load), '') || ' ' ||
        'perceived_exertion:' || NVL(TO_CHAR(perceived_exertion), '')
    ) AS data

完了までに約90秒かかりました。なお、まだVECTORインデックスは作成していません。なぜでしょうか?クエリが既にかなり高速なので、今のところ必要ないからです。

これは私のローカルの2018年製Mac Miniでのことで、データベースインスタンスのvector_memory_sizeを512MBに設定していました。

結果と仕組み

さて、エージェントに必要な情報が揃ったので、いよいよテストしてみましょう。

同じシナリオを2回実行するように指示しました。1回目はRAGなしで、2回目はRAGありで実行しました。

今週の土曜日はジムが休みなので、30分以内のハードな有酸素運動をしたいです。まずは一般的な回答をいただき、その後、私のベクターストアにあるデータに基づいて具体的な提案をお願いします。

一般的な/LLMのみの回答

🧠ステップ1:一般的な回答(データベースを参照しない、初期状態からの回答)

ジムが使えない状況で、30分以内のハードな有酸素運動を行うには、どのコーチも次のような方法を勧めるでしょう。

オプションA — タバタ式トレーニング(20分)ジャンプスクワット → マウンテンクライマー → 縄跳び/ハイニー → バーピーの4つの動作を、20秒間の運動/10秒間の休憩を8ラウンド繰り返す。

オプションB — 5ラウンド(約25分) 400m走 → 腕立て伏せ20回 → エアスクワット30回 → タックジャンプ10回

オプションC — AMRAP 25 5 バーピー → 10 ジャンプランジ → 15 シットアップ → 200m ラン。休憩なしで、できるだけ多くのラウンドを繰り返します。

より良い、RAGを取り入れた回答

🎯ステップ3:RAG強化回答

エージェントが独り言を言っている…これは実に興味深い対比だ。あなたのベクターストアは、私が一般的に提案したものとは全く異なる、より有用なものを返した。

あなたのデータが、30秒未満のハードな有酸素運動セッションについて実際に何を物語っているのか:

あなたの最高得点の試合は、ほぼすべてマイク博士の「サタデー・エンデュランス」形式、つまりランニング、ケトルベルワーク、自重トレーニングを組み合わせた時間制またはAMRAP形式のブロックで構成されており、私が一般的にデフォルトとしていたジャンプスクワット/エアスクワット/腕立て伏せのパターンではありません。

一番上の結果「心拍数が怪しい」(2024年12月)は、平均心拍数176、相対運動強度94、消費カロリー449kcal、所要時間ちょうど29.9分という数字が示唆に富んでいます。これがこの形式の上限です。説明には「はい、私はまだ生きていますし、制限時間もクリアしました」とありますが、これはかなりきつかったことを物語っています。

土曜日のおすすめ:

タイムトライアル(制限時間30分)—マイク博士の土曜エンデュランス形式を、ソロ/ジムなし向けにアレンジしたもの:

5ラウンド:

  • 300メートル走
  • ケトルベルスイング 15回(24kg)
  • ゴブレットスクワット 12回(24kg)
  • 腕立て伏せ9回

続いて:600メートル走

そして:3ラウンド:

  • 腹筋マットを使った腹筋運動12回
  • リバースランジ10回
  • 8回の肩を軽く叩く

これは、ランニングとケトルベル+自重のトライセットを交互に行う8回のセッションで体が覚えている構造と全く同じです。

あなたの履歴に基づくと、平均心拍数は約160、消費カロリーは約350~430kcal、相対的な運動強度は50~60と予想されます。

わかりました。でも、どうやって私の個人データを入手して、そのアドバイスに役立てたのですか?

MCPサーバーとの連携により、エージェントはテーブルにクエリを実行することができました。しかし、データを直接参照するのではなく、探しているものの説明を作成し、AIベクトル類似性検索によって、それに最も近い上位8件のエントリをデータベースに問い合わせました。

SQL
SELECT 
    activity_date,
    activity_name,
    activity_type,
    activity_description,
    average_heart_rate,
    calories,
    ROUND(elapsed_time_2/60, 1) as duration_min,
    relative_effort_2,
    perceived_exertion,
    VECTOR_DISTANCE(
        activity_embedding,
        VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING 'tough intense cardio under 30 minutes high heart rate no equipment needed' AS data),
        COSINE
    ) as distance
FROM strava_activities
WHERE activity_embedding IS NOT NULL
  AND elapsed_time_2 BETWEEN 600 AND 1800
ORDER BY distance ASC
FETCH FIRST 8 ROWS ONLY

データはALL_MINILLM_L12_V2トランスフォーマーを介してテーブル内でベクトル化されたため、「30歳未満のハードで激しい有酸素運動…」という検索語句も同じメカニズムで変換することが重要でした。

いくつか注意点があります。

  • これはハイブリッド検索で、VECTOR_DISTANCE計算とORDER BYを使用して、最も近い8つの一致を取得します。
  • また、正規述語を使用して、十分な長さのデータを提供するアクティビティにデータを限定しています。
  • これはコサイン類似度を使用しており、私たちは意味的類似度を使用しているため、380以上の次元すべてにおいて、2つのベクトルが同じ方向を指していることが重要です。また、ALL_MINIMLM_L12_V2は、会話にコサイン類似度が含まれることを前提として学習されています(HuggingFaceのドキュメントを参照)。

私の2番目の例はより分かりやすかったと思います。

「ハードな土曜日のワークアウト」の提案に続いて、「のんびりとした日曜日のリカバリー活動」についての質問をしてみました。

土曜日にこのワークアウトでハードにトレーニングした後は、日曜日はゆったりとした長めのセッションが必要になりそうです。屋外で、心拍数が130以下になるような、そして何より自分が楽しめるような運動をしたいですね。

🧠一般的な回答(風邪)

ハードな有酸素運動後の回復日には、どのコーチもこう言うでしょう。

オプションA — 長めのイージーラン(60~90分)ゾーン2のペース、会話ができる程度、心拍数130以下。インターバルなし、無理なペースはなし。ただひたすら走るだけ。

オプションB ― 長距離ハイキング(2~3時間)トレイルコースあり、標高差は任意。変化に富んだ地形と新鮮な空気により、心拍数が自然に抑制されます。

オプションC ― 簡単なサイクリング(60~90分)平坦なルートで、ゆったりとしたペース。脚に負担をかけずにリフレッシュするのに最適です。

普遍的なアドバイスは、有酸素運動を続け、屋外で過ごし、景色を楽しみ、ペースを気にしないことだ。

RAG搭載の回答

まず、LLMを情報提供し強化するためのデータが必要です。

SQL
...
VECTOR_DISTANCE(
        activity_embedding,
        VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING 'easy long outdoor recovery session low heart rate run hike walk enjoyable outside nature' AS data),
        COSINE
    ) as distance
...
WHERE activity_embedding IS NOT NULL
  AND average_heart_rate < 130
  AND average_heart_rate > 90
  AND elapsed_time_2 > 2400
  AND activity_type IN ('Run','Hike','Walk','Ride')
ORDER BY distance ASC
FETCH FIRST 8 ROWS ONLY

🎯RAG強化回答

あなたのベクターストアは迷うことなく、検索結果をすべてハイキングと表示しましたランニングでもサイクリングでもありません。ハイキングはあなたにとって最適なアウトドアリカバリーアクティビティであり、データによると、52分から3時間以上のセッションで心拍数を98~127に維持できることが一貫して証明されています。

あなた自身の説明が全てを物語っています。

  • 「美しく、見やすい…ただ、長い。」 — 2021年11月10日、128分、HR 105
  • 「気持ちの良い、楽なハイキングだった…明日はもっとハードなハイキングが待っている」 — 2019年8月12日、131分、108時間
  • 「今回は子供たちが本当に楽しんでいた」 — 2021年6月5日、HR 104
  • 「ワシントンとジェファーソンの裏庭🇺🇸 — 2021年7月4日、HR 98、151分

そして、ちょっと混沌とした記録もあります。 「4マイルが7.5マイルになってしまった、おっと」 ――2024年6月、190分、7.5マイル、標高差254フィート、1,659カロリー。これはまさにあなたの性格を表していると言えるでしょう。

RAG(Reach Ag)のおすすめを日曜日にお届けします。

簡単なハイキングコース — 2~2.5時間

  • 目標心拍数:常に125未満に維持する
  • 距離:4~5マイル、緩やかな標高差(100~200フィート)あり。適度な運動量で、心拍数が急上昇するほどではない。
  • ペース目標は設けない。立ち止まって周囲を見渡そう。
  • 家族が同意するなら連れてきてください。あなたのデータによると、あなたは実際にそうしたことがありますし、少なくとも一度は子供たちが楽しんでいました。

私の個人データが現実になる

私は体重が200ポンド(約90kg)をはるかに超える大柄な体格です。そのため、走る時は、たとえ速く走らなくても、決してリラックスしたり回復を目的とした運動ではありません。私のデータはそれを証明しており、そのおかげでエージェントは私にとってより適切な提案をすることができたのです。

これこそがRAGの真の利点です。LLMに事実、背景情報、実際のシナリオの概要などを提供しましょう。しかも、既に(Oracle!)データベースにあるデータを使って。しかも、別のベクトル解析専用データベースにコピーする必要もありません。なぜなら、Oracle AIデータベースは既に全てのベクトル解析機能をサポートしているからです。

スキルで締めくくる

おそらく実際に使うことになると思うので、エージェントにやり方や今回の作業内容をいちいち説明する手間を省けると助かります。必要なことをすべてやってくれるようにしてほしいです。

チャットセッションを締めくくるにあたり、私たちのタスクに基づいて新しいスキルを生成し、それを自動的に設定に追加するように指示しました。

AIスキルは素晴らしい!Oracleデータベース向けの100種類以上のAIスキルをぜひご覧ください!

コメント

このブログの人気の投稿

Oracle Database 19cサポート・タイムラインの重要な更新 (2024/11/20)

ミリ秒の問題: BCCグループとOCIが市場データ・パフォーマンスを再定義する方法(AWSに対するベンチマークを使用) (2025/11/13)

OCIサービスを利用したWebサイトの作成 その4~Identity Cloud Serviceでサイトの一部を保護 (2021/12/30)