「YouTube動画の内容をAIに質問して、該当するシーンのURL付きで回答させたい!」
そんなYouTube動画を対象にしたRAG(検索拡張生成)システムは、動画学習の効率化や社内研修アーカイブの活用など、非常に人気の高いテーマです。
また、データベースにFirebase(Cloud Firestore)を採用すれば、サーバーレスで手軽に構築でき、Firestore標準のベクトル検索機能(Vector Search)を活用して簡単に類似度検索が行えます。
しかし、いざ実装しようとすると初心者が必ずぶつかる大きな壁があります。それが「チャンク戦略(テキストの分割方法)」です。
YouTube動画のテキストは一般的なWeb記事やPDFとは性質が大きく異なるため、適当に分割すると「回答精度がボロボロになる」「動画の再生位置がズレる」といった悲劇が起きます。
この記事では、YouTube動画テキスト特有の性質を踏まえた「最適なチャンク戦略」と、Firebaseに保存する「具体的なデータ構造」、そして「実装時の注意点」を初心者向けにわかりやすく解説します。
そもそも「チャンク(Chunking)」とは?
RAGでは、動画全体の文字起こしテキスト(数万文字〜十数万文字)をそのままAIに渡すことはしません。
コストが高くなり、検索精度も落ちるためです。
そこで、テキストを「意味の通じる小さなカタマリ(チャンク)」に切り分け、それぞれのチャンクをベクトル化(Embedding)してデータベースに保存します。
【動画全体の長いテキスト(40分)】
↓ チャンク分割
[チャンク1: 00:00〜01:30] オープニングと概要
[チャンク2: 01:15〜02:45] Firebaseのセットアップ
[チャンク3: 02:30〜04:00] APIキーの発行方法
...
ユーザーが質問したとき、「質問の意味に最も近いチャンク」だけをデータベースから引き出し、LLM(GeminiやChatGPTなど)に渡して回答を生成させます。
YouTubeテキスト特有の「3つの難しさ」
Webサイトの記事や書籍と違い、YouTube動画のテキストには以下のような厄介な特徴があります。
- 句読点がない・文が途中でブツ切りになる
自動生成字幕(Transcript)は、話者の息継ぎやタイミングで数秒単位に細切れになっています。句読点(。や、)が存在しないことも珍しくありません。 - 話し言葉特有のノイズが多い
「えーっと」「あー」「そのー」などのフィラー表現や、言い直し・脱線が多く、テキストとしての情報密度が低いです。 - 「時間(タイムスタンプ)」の保持が必須
ユーザーは「回答の根拠となったシーンを動画で見たい」と思うため、各テキストが動画の「何分何秒」の発言なのかを正確に記録しておく必要があります。
YouTube動画におすすめのチャンク戦略
では、具体的にどのように分割すればよいでしょうか? おすすめの3つの戦略をご紹介します。
戦略①:時間+文字数のスライディングウィンドウ(★一番おすすめ)
YouTube字幕の「開始時間・終了時間」を結合しながら、一定の文字数(または秒数)でまとめ、かつ少しだけ重ねる(オーバーラップ)手法です。
- チャンクサイズ:約 300〜500文字(動画時間にして約 60秒〜90秒分)
- オーバーラップ:約 50〜100文字(約 15秒〜20秒分)
[字幕データ(細切れ)]
00:00 こんにちは -> 00:02 今日はFirebaseで -> 00:05 RAGを作ります -> ...
↓ まとめてチャンク化
【チャンク1(00:00〜01:15)】
「こんにちは。今日はFirebaseでRAGを作ります。まずプロジェクトの作成から…」
▲ 重複(オーバーラップ)
【チャンク2(01:00〜02:20)】
「…まずプロジェクトの作成から始めましょう。コンソール画面を開いて…」
なぜ重ねるのか?
ちょうど文の途中で区切られてしまった場合、文脈が途切れて検索にヒットしにくくなります。少し前後のテキストを重ねておくことで、切れ目の情報落ちを防ぐことができます。
戦略②:YouTubeチャプター連動型(構造化アプローチ)
投稿者が動画に「目次(チャプター)」を設定している場合、それを最優先の区切りとして使います。
- 00:00 導入
- 02:15 Firestoreの準備
- 06:30 ベクトル検索の実装
チャプターごとのカタマリを取得し、もし1つのチャプターが長すぎる場合(例: 5分以上)のみ、戦略①を使ってさらに細かくサブ分割します。
メリット:話のテーマが完全に一致した綺麗なチャンクが作れます。
戦略③:LLMによる意味的セグメンテーション(高精度・上級者向け)
字幕テキストを一度軽量なLLM(Gemini 1.5 Flashなど)に渡し、「話題が変わるタイミングで要約しつつタイムスタンプ付きで分割して」と依頼する方法です。
メリット:話し言葉の崩れが補正され、もっとも検索精度の高いチャンクが生成できます。
デメリット:前処理にかかるAPIコストと時間がやや増えます。
Firebase(Cloud Firestore)での推奨データ構造
Firestoreでは、動画そのものの情報(親)と切り分けたチャンク情報(子)を「サブコレクション」または「別コレクション」で管理するのがベストプラクティスです。
Firestoreの階層構造例
videos (コレクション)
└── {videoId} (ドキュメント)
└── chunks (サブコレクション)
└── {chunkId} (ドキュメント)
① 親ドキュメント:videos/{videoId}
動画全体のメタデータを保存します。
{
"videoId": "dQw4w9WgXcQ",
"title": "Firebaseで超簡単!RAG構築チュートリアル",
"channelName": "Tech Channel",
"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"durationSeconds": 600,
"createdAt": "2026-03-15T10:00:00Z"
}
② 子ドキュメント(チャンク):videos/{videoId}/chunks/{chunkId}
ここがRAGの検索対象となる本体です。
{
"chunkIndex": 3,
"text": "次にFirestoreのベクトル検索を有効化します。インデックス作成には数分かかる場合があるため、コンソールから事前に設定を済ませておきましょう。",
"startTime": 145.5,
"endTime": 210.0,
"startUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=145s",
"embedding": [0.0125, -0.0432, 0.0891, ...], // ベクトルデータ(768次元など)
"tokenCount": 128
}
ポイント:startUrl を事前に持たせておく!
&t=145s のように、該当秒数から直接再生できるリンクをデータとして保存しておくのがコツです。LLMが回答を生成するときに、「詳しくはこちらのシーン(URL)をご覧ください」とスムーズに誘導できるようになります。
実装時の注意点とハマりどころ
YouTube×Firebaseの組み合わせで初心者がハマりやすいポイントをまとめました。
① 自動生成字幕の「フィラー除去」を行う
YouTubeの自動字幕はノイズが多いため、チャンク化する前に簡単な正規表現や軽量処理で前処理を行いましょう。
- 「えー」「あー」「そのー」「んー」などの連続する無意味な音の削除
- 音楽や拍手を示す記号(
[音楽],[拍手]など)の削除
② チャンクの「短すぎ」「長すぎ」に注意
- 短すぎる(30文字以下など):文脈が足りず、「はい、そうです」のような発言だけが検索上位に来てしまう。
- 長すぎる(1500文字以上など):ピンポイントな回答位置がボヤけ、再生URLの秒数がズレる。
目安は「1チャンクあたり300〜600文字程度(1〜2分程度)」が最もバランスが良いです。
③ Firestoreのベクトルインデックス仕様を把握する
Firestoreのベクトル検索を利用する場合、以下の点に注意してください。
- 次元数の統一
Embeddingモデル(例:text-embedding-004は768次元)の次元数と、Firestoreで設定するインデックスの次元数を必ず一致させる。 - 距離関数
テキスト埋め込みの場合は基本的に COSINE(コサイン類似度) を選択します。 - コレクショングループ検索
複数の動画を横断して検索したい場合は、サブコレクションchunksに対してコレクショングループインデックスを設定する必要があります。
EmbeddingモデルとFirestoreのインデックスの次元数を一致させるとは?
「次元数の一致」は、Firestoreでベクトル検索を扱う際に最も基本的でありながら初心者が最もエラーを起こしやすいポイントの1つです。
「そもそも次元数とは何なのか?」「なぜ一致させないとダメなのか?」「具体的にどう設定するのか?」を、初心者向けに噛み砕いて徹底解説します。
そもそも「ベクトルの次元数」とは?
超シンプルに言うと、「そのテキストの意味を何個の数字の並びで表現しているか」という数字の個数のことです。
AI(Embeddingモデル)は、文章の意味を理解するために、文章を「数字のリスト(配列)」に変換します。
たとえ話:3次元の世界
もし果物をAIに理解させるために「甘さ」「赤さ」「丸さ」の3つの特徴量で表すとします。
- りんご =
[0.9, 0.8, 0.9](甘くて赤くて丸い) - レモン =
[0.1, 0.0, 0.6](甘くなくて赤くなくて少し丸い)
この場合、数字が3つ並んでいるので「3次元のベクトル」と呼びます。
現実のAIモデル(768次元など)
現実の言葉はもっと複雑です。文法、感情、文脈、トピックなど無数のニュアンスがあります。
そのためGoogleの text-embedding-004 では、言葉の意味を768個の特徴(数字)に分解して表現します。
# text-embedding-004 が出力するベクトルのイメージ(数字が768個並んでいる)
[0.0125, -0.0432, 0.0891, 0.0034, ...(計768個)..., -0.0512]
これが「768次元」の正体です。
なぜFirestoreのインデックスと一致させる必要があるのか?
理由は、「次元数が違うと、数学的に距離(類似度)を計算できないから」です。
Firestoreのベクトル検索は、「質問文のベクトル」と「保存されているチャンクのベクトル」の間の角度(コサイン類似度など)を計算して、一番近いものを探し出します。
もし次元数がズレていたら?
- 保存したデータ:
[0.1, 0.5, 0.8](3次元の空間にある点) - 検索したい質問:
[0.2, 0.4](2次元の空間にある点)
「3次元空間の点」と「2次元平面の点」では、お互いの距離を測ることができませんよね。
Firestore側も、あらかじめ「このコレクションには〇〇次元のデータが入ってくるから、その次元専用の計算用インデックス(地図)を準備しておくよ」という設定を必要とします。
一致させないとどうなるか?
もしインデックスに設定した次元数と、保存したベクトルや検索クエリの次元数が異なっていた場合、Firestoreはエラーを返して処理を拒絶します。
- エラーの例:
INVALID_ARGUMENT: Vector dimensions must match the index definition.
(ベクトルの次元数がインデックスの定義と一致していません)
検索がヒットしないどころか、APIのリクエスト自体が失敗してクラッシュしてしまいます。
主なモデルの次元数一覧
使うEmbeddingモデルによって出力される次元数は固定されています(一部例外あり)。
| 提供元 | モデル名 | 次元数 | 特徴 |
|---|---|---|---|
text-embedding-004 | 768 | Gemini系。コスパと日本語性能が非常に高い | |
| OpenAI | text-embedding-3-small | 1536 | ChatGPT系で最も標準的・安価 |
| OpenAI | text-embedding-3-large | 3072 | 高精度だがデータ容量が大きくなる |
textembedding-gecko (旧型) | 768 | 以前のVertex AI標準 |
※もし途中で「OpenAIからGeminiに切り替えよう」となった場合、1536次元から768次元に変わるため、Firestoreのインデックスを再作成し、過去の全データも再ベクトル化して保存し直す必要があります。
Firestoreでの具体的な設定方法
Firestoreでベクトルインデックスを作る際、この「次元数(dimension)」を明示的に指定します。
方法A:firestore.indexes.json で定義する場合(推奨)
Firebase CLIを使う場合、プロジェクト内の設定ファイルに以下のように記述して firebase deploy --only firestore:indexes を実行します。
{
"indexes": [],
"fieldOverrides": [],
"vectorIndexes": [
{
"collectionGroup": "video_chunks",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "embedding",
"vectorConfig": {
"dimension": 768, // ★ここでモデルの次元数と完全一致させる!
"flat": {} // または "hnsw": {}
}
}
]
}
]
}
方法B:Google Cloud CLI (gcloud) で作る場合
gcloud firestore indexes composite create \
--collection-group=video_chunks \
--query-scope=COLLECTION \
--field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding
【応用・コスト削減】あえて次元数を減らすテクニック
実は、Googleの text-embedding-004 や OpenAIの text-embedding-3-* には、「意味の精度をほぼ落とさずに、次元数を小さくして出力する機能(Matryoshka Embeddings)」が備わっています。
次元数を減らすメリット
次元数が小さくなると、Firestoreのデータ保存容量とネットワーク転送量(Egress)が激減します。
- 768次元:1データあたり 約3KB
- 256次元:1データあたり 約1KB(3分の1に削減!)
例:Google GenAI SDKで256次元に縮小して取得する場合
# APIリクエスト時に出力次元数を指定
response = client.models.embed_content(
model="text-embedding-004",
contents="Firestoreのベクトル検索について",
config=types.EmbedContentConfig(
output_dimensionality=256 # ★768ではなく256次元で出力させる!
)
)
embedding = response.embeddings[0].values #要素数が256個の配列になる
⚠️ 注意点:
このようにAPI側で次元数を「256」に縮小した場合は、Firestore側のインデックス設定も dimension: 256 で作成しなければなりません。
まとめ
- 次元数 = 「意味を表現する数字の個数」
- モデルごとに決まっている(Geminiの
text-embedding-004なら「768」) - Firestoreのインデックス設定の
dimensionと、データの要素数を1つのズレもなく一致させる必要がある - 一致していないとFirestoreがエラーを吐いて検索できない
これさえ押さえておけば、インデックス作成時のトラブルは確実に回避できます!
まとめ:丁寧なチャンク分割がRAGの成否を決める!
YouTube動画を対象にしたRAGシステムの成否は、プロンプトの工夫よりも「動画の字幕をいかに文脈を壊さずに時間付きで切り分けるか」にかかっています。
成功のためのチェックリスト
- 字幕テキストの前処理(フィラーや不要タグの除去)
- スライディングウィンドウの適用(300〜500文字+前後オーバーラップ)
- タイムスタンプ付きURL(
&t=XXs)の付与 - Firestoreのベクトル検索を活用したクエリ設計
まずは戦略①の「時間+文字数スライディングウィンドウ」から実装を始めてみてください。動画の該当シーンへジャンプできる精度の高いAIアシスタントが作れるはずです!

