RAG を動かして基本を理解する
こんにちは。ジェダイパンくず☁️ です。 RAG (Retrieval Augmented Generation) という言葉はよく聞く様になったものの最初はどこからどこまでが RAG システムの責務なのかが分かりにくいと感じていました。「検索含め AI が勝手に全部やってくれるのでは?」もしくは「あくまでの検索結果を返すだけなのか?」等。そこで RAG を段階的に理解するための学習用環境を2つ作って理解する作業を行いました。ここではそれを順に説明しようと思います。 コードは下記に置いてあります。 https://github.com/jedipunkz/rag-playground 構成名 フォルダ名 構成内容 検索のみ構成 retrieve-only LLM 無し構成。Go のハッシュベース埋め込み + PostgreSQL(pgvector) で検索だけを行う LLM を使った構成 full-rag-ollama Ollama (all-minilm / llama3.2) + PostgreSQL(pgvector) で検索からプロンプト化、回答生成まで行う 2つの構成図 まず両者の構成を図で示します。 検索のみの構成 (フォルダ名: retrieve-only) flowchart LR M[Markdown files] --> L[load command<br/>Go] L -->|1 chunk + embed| P[(PostgreSQL<br/>pgvector)] U[curl / browser] -->|2 /search| S[search app<br/>Go HTTP API] S -->|3 embed query + vector search| P P -->|4 top-k chunks| S S -->|5 JSON results| U load コマンドが Markdown をチャンクに分割し、ベクトル化して PostgreSQL(pgvector) に保存する ユーザーが curl で /search?q=... を呼び出す search アプリがクエリを同じ方法でベクトル化し、pgvector で cosine 距離の近いチャンクを検索する PostgreSQL が近い順に top-k チャンクを返す search アプリが検索結果を JSON で返す LLM を使った構成 (フォルダ名: full-rag-ollama) flowchart LR M[Markdown files] --> L[load command<br/>Go] L -->|1 embed chunks| O[Ollama<br/>all-minilm] L -->|1 save chunks + vectors| P[(PostgreSQL<br/>pgvector)] U[curl / browser] -->|2 /answer| A[app<br/>Go HTTP API] A -->|3 embed query| O A -->|4 Retrieve| P P -->|top-k chunks| A A -->|5 Augment| PR[/prompt/] PR -->|6 Generate| G[Ollama<br/>llama3.2] G -->|answer| A A -->|7 JSON answer + sources| U load コマンドが Markdown をチャンクに分割し、Ollama (all-minilm) でベクトル化して PostgreSQL(pgvector) に保存する ユーザーが curl で /answer?q=... を呼び出す app がクエリを Ollama (all-minilm) でベクトル化する app が pgvector で近いチャンクを検索し、top-k チャンクを受け取る (Retrieve) app が質問と検索結果からプロンプトを組み立てる (Augment) app がプロンプトを Ollama (llama3.2) に渡し、LLM が回答を生成する (Generate) app が回答と根拠チャンクを JSON で返す 見比べると、LLM を使った構成は検索のみの構成に Ollama を足し、検索結果をプロンプト化して LLM に渡す経路が増えているだけ、というのが分かると思います。この「増えた部分」が RAG の Augment と Generate に相当します。それらについて次の項目で説明します。 ...