多くの開発者は「AI検索エンジンを作りたい」と考えたとき、
まずスクレイピングやデータベース構築から始めます。
しかし実際に壁になるのは、その後に登場する
「Embedding(埋め込みベクトル)」です。
私もRailsとPostgreSQLを用いて意味検索エンジンを構築する過程で、
- OpenAIライブラリが読み込めない
- API認証エラーが出る
- pgvectorの型が認識されない
- cosine類似度がどこに保存されているのかわからない
といった数多くの問題に直面しました。
特に興味深かったのは、単なるエラー修正ではなく、
「OpenAIを使うことは本当にインハウス開発と言えるのか」
という設計思想の問題でした。
本記事では、実際のエラー画面や試行錯誤を交えながら、Rails初心者
にも分かるようにベクトル検索システムの内部構造を解説します。
第1章 Embeddingとは何なのか
今回のトラブルは単なるGem不足ではありませんでした。
本質的には、「文章を意味空間へ変換する仕組み」
そのものを理解する過程だったのです。
Embeddingは文章を数値へ変換する技術
まず最初に理解しなければならないのは、
EmbeddingはAIそのものではないということです。
例えば、
AIはすごいという文章があったとします。人間は意味を理解できます。
しかしコンピュータは理解できません。
そこでAIモデルを使って、
[0.12, -0.83, 0.55, ...]のような数値列へ変換します。
これがEmbeddingです。
OpenAIは何をしているのか
今回利用したコードは以下でした。
client = OpenAI::Client.new
response = client.embeddings(
parameters: {
model: "text-embedding-3-small",
input: text
}
)ここでOpenAIは、
文章
↓
ベクトル
↓
返却
だけを行っています。
検索はしていません。
比較もしていません。
インハウス開発との違和感
ここで私は違和感を覚えました。
「ローカルAIを作っているつもりなのに、
なぜOpenAIを呼び出しているのか?」
これは非常に自然な疑問です。
実際には、
検索システム
≠
Embedding生成器です。
今回作っているのは検索基盤。
Embeddingは仮にOpenAIを借りているだけです。
第2章 最初に遭遇したエラーと解決
ベクトル検索で最初に遭遇した壁は、
OpenAIそのものではなくRailsのロード機構でした。
uninitialized constant エラー
最初のエラーはこれです。
uninitialized constant EmbeddingService::OpenAI見た瞬間、
「OpenAIが壊れた?」
と思います。
しかし違います。
Railsが
OpenAIというクラスを知らないだけです。
require漏れ
原因は単純でした。
require 'openai'がありません。
そのため、
client = OpenAI::Client.newで落ちます。
Gem未導入問題
さらに進むと、
cannot load such file -- openaiが発生。
これは
gem 'ruby-openai'がGemfileに無かったためです。
そこで、
bundle installを実行。
ログには、
Installing ruby-openai 8.3.0が表示されました。
この瞬間、
一歩前進です🚀
第3章 401エラーと「OpenAIを使う意味」
Gem導入後もEmbeddingは生成されませんでした。
しかしここで私は重要なことを理解します。
401 Unauthorized
実行結果。
Embedding failed for CrawledPage 1:
the server responded with status 401これは認証エラーです。
APIキーが存在しない
確認すると、
echo $OPENAI_API_KEYが空。
つまりOpenAI側は、
「誰だお前?」
状態です。
なぜEmbeddingがゼロのままなのか
モデル側では、
rescue => eで例外を握り潰していました。
結果、
embedding: []のままになります。
初心者が最も混乱するポイントです。
OpenAIは最終形ではない
ここで理解したことがあります。
OpenAIは目的ではありません。
目的は、
crawl
↓
embedding
↓
検索の流れを完成させること。
将来的には、
LocalEmbeddingModel.encode(text)へ置き換え可能です。
第4章 cosine類似度とpgvectorの正体
最終的に私は、cosine類似度とは何かを理解することで、
ベクトル検索の本質に到達しました。
cosine類似度は保存されない
最初の疑問。
cosine角の情報はどこにあるのか?
答えは驚くほど単純です。
どこにもありません。
保存されるのはベクトルだけ
DBに保存されるのは、
[0.12, -0.44, 0.87]だけです。
cosineは、
cosine(A, B)としてその場で計算されます。
pgvectorエラー
次に遭遇したのが、
unknown OID 123027です。
これはRailsが
vector型を理解できない状態。
float[]とvectorの違い
実際には、
t.float :embedding, array: trueで動かすことも可能です。
しかし検索性能は落ちます。
pgvectorの真価は、
vector(1536)型にあります。
ここで見えた未来
この時点で完成していたものは、
✅ スクレイピング
✅ Rails
✅ PostgreSQL
✅ 非同期処理
✅ Embedding生成
です。
残るのは、
意味検索だけでした。つまり、Googleのようなキーワード検索ではなく、
「意味で探す検索」
の入口に立ったのです。
全体のまとめ
今回のトラブルは、
require漏れ
↓
Gem不足
↓
API認証
↓
pgvector型という一連のエラーでした。しかし振り返ると、
本当に得られたものはエラー解決ではありません。
- Embeddingとは何か
- OpenAIの役割は何か
- cosine類似度はどこにあるのか
- pgvectorは何を高速化しているのか
を理解できたことです。そして最も大きな収穫は、
「検索エンジンはAIモデルではなく、データ構造で決まる」
という事実でした。
OpenAIを外しても検索基盤は残ります。つまり今回作っていたものは、
単なるAPI接続ではなく、ローカルLLM時代にも流用できる
意味検索エンジンの土台だったのです。
〆最後に〆
以上、間違い・ご意見は
以下アドレスまでお願いします。
全て返信できていませんが 見ています。
適時、改定をします。
nowkouji226@gmail.com
【全体の纏め記事】に戻る
【思想の纏め記事】に戻る
