n8nでRAGを作る:社内資料を検索して答えるチャット

公開 書いた人 大林 諒

「問い合わせ受付」の担当者は、返信を書くたびに料金表や利用規約を開いて「年契約の途中解約はできるか」「30名を超えたら料金はどうなるか」を確かめています。この記事では、その資料をn8nに読み込ませ、チャットで聞けば該当箇所を探して答える仕組み(RAG)を作ります。読み終わると、Google Driveの資料を取り込むワークフローと、資料を根拠に答えるチャットの2本が動き、資料を差し替えたときの取り込み直しまで回せる状態になります。AI Agent・Chat Model・Chat Trigger の置き方は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」で説明しているので、ここではRAGの部分に絞ります。画面と設定項目は2026年10月時点の安定版2.44.7で確かめています。

この記事の内容
  1. RAGの仕組みを問い合わせ受付で見る
  2. 先に決めること
  3. ベクトルストアの選び方と、作るものの一覧
  4. 手順1:資料をそろえる
  5. 手順2:資料を取り込むワークフロー
  6. 手順3:資料を根拠に答えるチャット
  7. 手順4:資料の更新を取り込み直す
  8. 精度を上げる手
  9. 応用
  10. できないことと、その代わり
  11. よく詰まるところ
  12. 次にやること

RAGの仕組みを問い合わせ受付で見る

RAGは、質問に近い資料の一部を探してLLMに渡し、それをもとに答えさせる仕組みです。LLMは資料の全文を読んでいるわけではありません。渡された数個の断片だけを見て答えます。

取り込みでは、資料を数百文字ずつの断片に切り、断片ごとに埋め込み(Embeddings。文章の意味を数字の並びにしたもの)を作ってベクトルストア(意味の近さで探せるデータベース)に入れます。

担当者が「年契約を途中で解約したら返金はありますか」と聞くと、質問も同じ方法で数字の並びにし、近い断片を上から4件ほど取り出します。利用規約の「第12条 中途解約」の断片と、よくある質問の「解約について」の断片が出てくれば、LLMはその2つを読んで答えます。言葉が違っても意味が近ければ見つかります。

答えの質は取り出した断片で決まり、断片にないことは答えられません。精度を上げる作業の大半は、LLMではなく資料の切り方と探し方を直す作業です。

先に決めること

何を入れて、何を入れないか

入れるのは、担当者が返信のたびに見るよくある質問・料金表・利用規約・操作マニュアルです。問い合わせ一覧や顧客の個人情報は入れません。日々変わる件数や対応状況は、シートを直接読むほうが正確です。

誰に使わせるか

最初は社内の担当者向けにします。外れても担当者が資料と照らして気づけます。サイトの訪問者に答えさせるのは、社内で数週間使って外れ方を見てからです。

埋め込みモデルを最初に決める

取り込みと検索は同じ埋め込みモデルでそろえる決まりで、替えると全資料の入れ直しです。2.44.7には Anthropic の埋め込みノードがないので、Claudeで答える場合も埋め込みは Embeddings OpenAI などを使います。資料を社外に出せないなら Embeddings Ollama で社内のサーバーに閉じます。LLMの選び方と費用は「n8nでChatGPT・Claude・Gemini・Ollamaを使い分ける:モデルの選び方と費用」にまとめています。

資料の置き場所と更新の担当

資料はGoogle Driveの1つのフォルダ「問い合わせ対応資料」に集めます。料金改定のたびに誰がファイルを差し替え、いつ取り込み直すかを決めておかないと、古い料金で答え続けます。

ベクトルストアの選び方と、作るものの一覧

2.44.7で使える主なベクトルストアです。どれもノードの作りは同じで、「Operation Mode」で取り込み(Insert Documents)と検索を切り替えます。

ベクトルストア置き場所データの残り方検索時に絞る設定入れ直しの手段向く場面
Simple Vector Storen8nのメモリの中再起動で消える。メモリが足りないと消えることもあるなし取り込み時の「Clear Store」で丸ごと消す試すときだけ
Qdrant Vector Store自分で立てるかQdrant Cloud残る「Search Filter」(Qdrantの書式のJSON)QdrantのAPIで条件を付けて消すセルフホストの本番
Postgres PGVector StorePostgreSQL残る「Metadata Filter」テーブルを分ける(Collection設定)すでにPostgreSQLを使っている
Pinecone Vector StorePinecone(外部サービス)残る「Metadata Filter」「Clear Namespace」、「Update Documents」サーバーを増やしたくない
Supabase Vector StoreSupabase(外部サービス)残る「Metadata Filter」「Update Documents」すでにSupabaseを使っている

Simple Vector Store は公式でも試す用です。保存先の名前(Memory Key)は同じn8nの全ユーザーから見えるので、社外秘の資料は入れません。n8n Cloud では100MB(文書でおよそ8,000件)までで、7日使わないと消えます。

VPSの本番なら、Qdrantを同じDocker Composeに足すのが手軽です。設定は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」に載せています。n8n Cloud なら Qdrant Cloud か Pinecone にします。ほかに Azure AI Search、Chroma、Milvus、MongoDB Atlas、Redis、Weaviate、Zep などのノードもあります。

この記事で作るものです。

ワークフロー動かし方使うノード
資料取り込み手動で実行(初回と全件作り直し)Google Drive、Loop Over Items、Qdrant Vector Store、Embeddings OpenAI、Default Data Loader、Recursive Character Text Splitter
資料の更新取り込みDriveのファイルが変わったら自動Google Drive Trigger、HTTP Request、上と同じ取り込みノード
資料チャット担当者がチャットで聞くChat Trigger、AI Agent、Chat Model、Simple Memory、Qdrant Vector Store、Embeddings OpenAI

手順1:資料をそろえる

Default Data Loader が読める形式は、PDF、Word(.docx)、CSV、テキスト、JSON、EPubです。Excel(.xlsx)やHTMLは「Unsupported mime type」で止まります。

  1. Google Driveに「問い合わせ対応資料」フォルダを作り、「よくある質問.pdf」「料金表.csv」「利用規約.pdf」を置く。
  2. PDFは、文字を選択してコピーできるか確かめる。スキャンした画像のPDFからは文字が取れない。
  3. 料金表をスプレッドシートで持っているなら、CSVで書き出して置く。PDFの表は取り込むと行と列の対応が崩れる。
  4. よくある質問は、1問ごとに空行を入れておく。

このあと使う分割のノードは、空行、改行、空白、1文字の順に区切りを探して切ります。空行があれば1問1答が1つの断片にまとまり、空白のない日本語の長い段落は文の途中で切られます。

手順2:資料を取り込むワークフロー

フォルダのファイルを1件ずつ流す

  1. 新しいワークフロー「資料取り込み」を作り、「When clicking ‘Execute workflow’」(手動実行のトリガー)を置く。
  2. Google Drive ノードを追加し、「Resource」を File/Folder、「Operation」を Search にする。
  3. 「Filter」の「Folder」で「問い合わせ対応資料」を選び、「What to Search」を Files にする。
  4. その後ろに「Loop Over Items」を置き、「Batch Size」を1にする。
  5. 「loop」の出口に Google Drive ノードを追加し、「Resource」を File、「Operation」を Download、「File」を By ID にして {{ $json.id }} を入れる。

Search は指定フォルダの直下しか探しません。

Batch Size を1にするのは、Default Data Loader のようなサブノード(下につなぐ部品)では式が常に1件目で評価されるからです。3ファイルを一度に流すと、全部の断片に1件目のファイル名が付きます。

ベクトルストアと部品をつなぐ

  1. Download の後ろに「Qdrant Vector Store」を追加し、「Operation Mode」を Insert Documents にする。
  2. 「Credential」で Qdrant の認証情報を作る。「API Key」に .env の QDRANT_API_KEY の値、「Qdrant URL」に http://qdrant:6333 を入れる。
  3. 「Qdrant Collection」に company_docs と入れる。初回の取り込みでコレクションが作られる。
  4. ノード下の「Embedding」の「+」から「Embeddings OpenAI」をつなぎ、「Model」を text-embedding-3-small にする。
  5. 「Document」の「+」から「Default Data Loader」をつなぐ。
  6. Qdrant Vector Store の出口を Loop Over Items の入口に戻す。
手動トリガーから Google Drive の Search、Loop Over Items、Download、Qdrant Vector Store とつながり、Qdrant Vector Store の下に Embeddings OpenAI と Default Data Loader が付いた取り込みワークフロー
手動トリガーから Google Drive の Search、Loop Over Items、Download、Qdrant Vector Store とつながり、Qdrant Vector Store の下に Embeddings OpenAI と Default Data Loader が付いた取り込みワークフロー

Default Data Loader を設定する

  1. 「Type of Data」を Binary にする。
  2. 「Mode」を「Load Specific Data」にし、「Input Data Field Name」を data のままにする。
  3. 「Data Format」は「Automatically Detect by Mime Type」のままにする。
  4. 「Text Splitting」を Custom にし、出てきた「Text Splitter」の「+」から「Recursive Character Text Splitter」をつなぐ。
  5. 「Chunk Size」を800、「Chunk Overlap」を150にする。
  6. Default Data Loader の「Add Option」から「Metadata」を追加し、次の3つを登録する。
NameValue使い道
file_name{{ $json.name }}答えに根拠の資料名を出す
file_id{{ $json.id }}資料を差し替えたとき、そのファイルの断片だけ消す
doc_type{{ $json.name.includes('料金') ? 'price' : $json.name.includes('規約') ? 'terms' : 'faq' }}料金の質問は料金表だけから探す

「Text Splitting」が Simple だと1,000文字ごとに200文字重ねて切ります。試すだけならそれで足ります。

「Mode」を既定の「Load All Input Data」のまま複数ファイルを流すと、同じファイルが重複して入ることがあります。Load Specific Data にして、読むファイルの欄を名指ししておくのが安全です。

Default Data Loader で Type of Data が Binary、Mode が Load Specific Data、Text Splitting が Custom、Metadata に file_name・file_id・doc_type を入れたところ
Default Data Loader で Type of Data が Binary、Mode が Load Specific Data、Text Splitting が Custom、Metadata に file_name・file_id・doc_type を入れたところ
Recursive Character Text Splitter の Chunk Size 800、Chunk Overlap 150
Recursive Character Text Splitter の Chunk Size 800、Chunk Overlap 150

実行して中身を確かめる

  1. 「Execute workflow」を押す。
  2. Qdrant Vector Store の出力で、断片の件数と中身を見る。
  3. 断片の metadata に file_name・file_id・doc_type が入り、PDFなら loc.pageNumber(ページ番号)が付いていることを確かめる。

PDFは既定でページごとに読み込まれ、ページ番号が付きます。本文が空や記号だらけなら、文字が取れていません。

手順3:資料を根拠に答えるチャット

土台を置く

「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」の手順2と同じく、新しいワークフロー「資料チャット」に Chat Trigger、AI Agent、Chat Model、Simple Memory を置きます。AI Agent の「System Message」には、RAG用に次を書きます。

役割:example.co.jp の社内向けアシスタント。問い合わせへの返信を書く社員の質問に答える。
- 料金・契約条件・解約・納期の質問は、必ず company_docs で調べてから答える
- 調べた資料に書いてあることだけで答える。資料にない場合は「資料に記載がありません」と答え、推測で埋めない
- 金額・日数・条番号は、資料の表記のまま書く
- 1回で見つからなければ、言い換えてもう一度調べてよい
- 回答の最後に、根拠にした資料の file_name と、あればページ番号(loc.pageNumber)を書く

「資料にない場合」を省くと、LLMがそれらしい料金を作ります。

ベクトルストアを道具としてつなぐ

  1. AI Agent の「Tool」の「+」から「Qdrant Vector Store」を追加する。
  2. 「Operation Mode」を「Retrieve Documents (As Tool for AI Agent)」にする。
  3. ノード名を company_docs に変える。2.44.7で新しく置いたノードには名前の欄がなく、ノード名がそのままツール名としてLLMに渡る。
  4. 「Description」に「サービスのよくある質問・料金表・利用規約を調べるときに使う。料金、契約、解約、支払い、サポート範囲の質問では必ず使う」と書く。
  5. 「Qdrant Collection」で company_docs を選ぶ。
  6. 「Limit」(取り出す断片の数。既定は4)は4のまま始める。
  7. 「Include Metadata」をオンのままにする。オフにすると資料名とページ番号がLLMに渡らない。
  8. ノード下の「Embedding」に、取り込みと同じ「Embeddings OpenAI」を同じモデルでつなぐ。
AI Agent の Tool に company_docs という名前の Qdrant Vector Store がつながり、その下に Embeddings OpenAI が付いたキャンバス
AI Agent の Tool に company_docs という名前の Qdrant Vector Store がつながり、その下に Embeddings OpenAI が付いたキャンバス
company_docs の設定。Operation Mode が Retrieve Documents (As Tool for AI Agent)、Description、Limit 4、Include Metadata がオン
company_docs の設定。Operation Mode が Retrieve Documents (As Tool for AI Agent)、Description、Limit 4、Include Metadata がオン

試して、外れ方を見る

  1. キャンバス下の「Open chat」を押し、「年契約を途中で解約したら返金はありますか」と聞く。
  2. ログで company_docs の呼び出しを開き、AIが検索に使った文(input)と、返ってきた断片を見る。
  3. 答えの末尾に、取り出した断片と同じ資料名とページが出ているか確かめる。
  4. よくある質問から10問ほど選んで同じように聞き、外れた質問を控える。

外れたら、まず返ってきた断片を見ます。正しい断片があるのに外れるならSystem Messageとモデル、無いなら切り方と探し方を直します。

社内への公開と認証は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」のとおりです。

AI Agent 以外の組み方

資料にだけ答えるなら、AI Agent でなくても組めます。

組み方つなぐもの動き向く場面
AI Agent+ベクトルストアを道具にQdrant Vector Store を「Retrieve Documents (As Tool for AI Agent)」で Tool に調べるかどうか、何回調べるかをAIが決める資料のほか、シートや顧客管理も調べさせたい
Question and Answer Chain「Retriever」に「Vector Store Retriever」、その下に Qdrant Vector Store を「Retrieve Documents (As Vector Store for Chain/Tool)」で毎回必ず1回検索して1回答える資料だけに答える。費用が読みやすい
Vector Store Question Answer ToolAI Agent の Tool に置き、下にベクトルストアと安いモデルをつなぐ検索結果を別のモデルでまとめてからエージェントに渡す高いモデルのトークンを節約したい

Question and Answer Chain は会話を覚えないので、続きの質問には向きません。

手順4:資料の更新を取り込み直す

Insert Documents は追記しかしません。料金表を差し替えて同じワークフローをもう一度実行すると、旧料金と新料金の断片が両方入り、どちらかで答えます。入れ直す前に、古い断片を消します。

全部作り直す

資料が10本前後なら、月1回やり直すのがいちばん確実です。資料取り込みワークフローの手動トリガーの直後に HTTP Request を1つ足し、コレクションごと消します。

  1. 「Method」を DELETE、「URL」を http://qdrant:6333/collections/company_docs にする。
  2. 「Authentication」を「Generic Credential Type」の「Header Auth」にし、Name に api-key、Value に QDRANT_API_KEY の値を入れる。
  3. ワークフローを実行し、消えたあとに全ファイルが入り直したことを確かめる。

Simple Vector Store で試しているなら、Insert Documents の「Clear Store」をオンにすれば同じことになります。

差し替えたファイルだけ入れ直す

  1. 新しいワークフロー「資料の更新取り込み」を作り、「Google Drive Trigger」を置く。
  2. 「Trigger On」を「Changes Involving a Specific Folder」、フォルダを「問い合わせ対応資料」、「Watch For」を「File Updated」にする(新しい資料も拾うなら、同じ設定で「File Created」のトリガーをもう1つ置く)。
  3. その後ろに Loop Over Items(Batch Size 1)を置く。
  4. 「loop」の出口に HTTP Request を置き、「Method」を POST、「URL」を http://qdrant:6333/collections/company_docs/points/delete にして、上と同じ Header Auth を使う。
  5. 「Send Body」をオンにし、「Body Content Type」を JSON、「Specify Body」を「Using JSON」にして次を入れる。
  6. その後ろに手順2と同じ Download と Qdrant Vector Store をつなぐ。Download の File By ID と、Default Data Loader の Metadata の式は $json ではなく $('Loop Over Items').item.json.id のように Loop Over Items から取る。
  7. 保存して公開する。
{
  "filter": {
    "must": [
      { "key": "metadata.file_id", "match": { "value": "{{ $json.id }}" } }
    ]
  }
}

Qdrant Vector Store は Metadata を metadata の下に入れるので、消す条件のキーは metadata.file_id です。

Google Drive Trigger から Loop Over Items、HTTP Request、Download、Qdrant Vector Store と並んだ更新取り込みワークフロー
Google Drive Trigger から Loop Over Items、HTTP Request、Download、Qdrant Vector Store と並んだ更新取り込みワークフロー
HTTP Request で points/delete に送る JSON の Body を入れたところ
HTTP Request で points/delete に送る JSON の Body を入れたところ

Google Drive Trigger はサブフォルダの変更では動かず、「Watch For」に削除もありません。差分の取り込みと月1回の作り直しを両方回します。

精度を上げる手

分割の長さと重なり

「Chunk Size」は1つの断片の文字数、「Chunk Overlap」は前の断片と重ねる文字数です。公式は、短い断片は細かく探せる一方、長い断片は文脈を持てるが中身が薄まると書いています。問い合わせ受付の資料で落ち着いた値です。

資料Chunk SizeChunk Overlap考え方
よくある質問50001問1答が1つに収まる大きさ。重ねると隣の質問が混ざる
利用規約800150条が途中で切れても前後がつながる
操作マニュアル1,200200手順の途中で切れないよう大きめ
料金表(CSV)分割しない程度に大きく0CSVは1行が1つの断片になる。行ごとに意味が通る形にしておく

資料ごとに値を変えるなら、Switch で doc_type ごとに別の Qdrant Vector Store に流します。値を変えたら全部作り直してから試します。

断片を大きくするほど、1回の質問でLLMに渡る文字数も増えます。Limit 4 で Chunk Size 800 なら、質問のたびに最大3,200文字が入力に足されます。

メタデータで絞る

料金を聞いても、料金に触れたよくある質問ばかり返ることがあります。料金表だけを探す道具を分けます。

  1. company_docs を複製し、ノード名を price_table、Description を「料金・プラン・人数ごとの金額を調べるときに使う」にする。
  2. price_table の「Options」の「Add Option」から「Search Filter」を追加し、次を入れる。
  3. company_docs の Description から料金の文言を外す。
{
  "must": [
    { "key": "metadata.doc_type", "match": { "value": "price" } }
  ]
}
price_table の Options に Search Filter の JSON を入れたところ
price_table の Options に Search Filter の JSON を入れたところ

Postgres PGVector Store・Pinecone・Supabase では「Metadata Filter」で名前と値を入れます。複数入れるとすべてに当てはまる断片だけが返ります。

取り出す数と並べ直し

正しい断片が5件目以降に埋もれるなら Limit を6〜8に上げ、「Rerank Results」をオンにして「Reranker Cohere」をつなぎます。多めに取り出した断片を質問との関係の強さで並べ直し、上位(「Top N」既定3)だけ渡します。CohereのAPIキーが別に要ります。

資料のほうを直す

本番で一番効くのは資料の書き方を直すことです。よくある質問の答えに「上記のとおり」とあると、その断片だけでは意味が通りません。1問1答ごとに主語と条件を省かずに書き直します。

応用

問い合わせのSlack通知に、近い資料を添える

検索だけを使う手もあります。「問い合わせ受付」で Slack に送る手前に Qdrant Vector Store を置き、「Operation Mode」を Get Many、「Prompt」を {{ $json.body.message }}、「Limit」を3にします。出力の document.pageContent と document.metadata.file_name を Slack の本文に「参考になりそうな資料」として足せば、担当者は通知の時点で該当箇所が分かります。答えを作らないので、外れても害がありません。

出力の score(近さ)の目安は資料とモデルで変わるので、実際の問い合わせを流して見てから Filter で足切りします。

社外に出さずに動かす

埋め込みを Embeddings Ollama、LLMを Ollama Chat Model にすれば、資料も質問も社内から出ません。マシンの大きさは「n8nでChatGPT・Claude・Gemini・Ollamaを使い分ける:モデルの選び方と費用」にあります。資料の管理画面や権限ごとまとめて欲しいなら、Difyと組み合わせる手もあります。比べ方は「n8nとDifyの違い:どちらを使うか、組み合わせて使う方法」に書いています。

できないことと、その代わり

できないこと理由代わりの手
料金の合計を正しく計算する(「35名で年払いなら総額は」)断片を読んで文章を作るだけで、計算は保証されない料金の計算はスプレッドシートかCodeノードのサブワークフローで組み、Call n8n Workflow Tool で呼ばせる
PDFの表を表のまま読む文字にした時点で行と列の対応が崩れる表はCSVにするか、「スタンダードプランは1名あたり月額〜」のような1行1文に書き直す
最新の資料で答える取り込み直すまで古い断片が残る手順4の更新取り込みと月1回の作り直し。資料の冒頭に改定日を書いておく
根拠を必ず正しく示すAIが書く資料名やページは、断片と食い違うことがある厳密さが要るなら Get Many の断片をそのまま答えの下に並べて出す
資料全体を見渡す(「解約の条件を全部挙げて」)見るのは Limit 件の断片だけ条ごとに見出しを付けた一覧を別の資料として作って入れる。短い資料なら全文を System Message に入れてしまう
画像やスキャンしたPDFを読む文字が取れない文字入りのPDFで出し直すか、外でOCRしてから入れる
見る人ごとに資料を出し分けるベクトルストアに利用者ごとの権限はない。Simple Vector Store は全ユーザーから見える公開範囲ごとにコレクションとチャットを分け、Chat Trigger の認証で入口を絞る

よく詰まるところ

症状原因直し方
的外れな断片しか返らない取り込みと検索で埋め込みモデルか「Dimensions」が違う両方を同じモデル・同じ設定にそろえ、全部作り直す
同じ断片が2つずつ返る同じファイルを2回 Insert Documents したコレクションを消して作り直す。以後は手順4で消してから入れる
全部の断片の file_name が同じ資料名になっているサブノードの式は1件目で評価されるLoop Over Items の Batch Size を1にして1ファイルずつ流す
「Unsupported mime type」で止まるExcel や HTML など読めない形式CSV・PDF・Wordにして置く。GoogleドキュメントやスプレッドシートはDownload の「Google File Conversion」で形式を選ぶ
PDFを入れたのに断片が空か記号だらけスキャン画像のPDF文字を選択できるPDFで出し直す
n8nを再起動したら答えなくなったSimple Vector Store はメモリにしか残らないQdrant などに替えて入れ直す
AIが資料を調べずに一般論で答えるDescription があいまいで、使う場面が伝わっていないDescription と System Message に「料金・契約の質問では必ず使う」と書く
ツールを呼ぶところでエラーになるツールのノード名に日本語や記号があるノード名を英数字・空白・ハイフン・アンダースコアにする
改定前の料金で答える古い断片が残っているfile_id で消してから入れ直す。迷ったら全部作り直す
Qdrant につながらないURLが localhost になっている、またはAPIキーが違う同じComposeなら http://qdrant:6333、キーは .env の値
Search Filter を入れたら何も返らないキーに metadata. を付けていないmetadata.doc_type のように書く
「A Retriever sub-node must be connected」Question and Answer Chain に Retriever がない「Retriever」の「+」から Vector Store Retriever をつなぐ

次にやること

あわせて読む