はじめに
AnthropicのCLIツール「Claude Code」は、初期バージョンでRAGとローカルベクトルDBを使っていた。しかし、現在のClaude Codeにベクトル検索は搭載されていない。捨てたのだ。
開発者のBoris Chernyはこう語っている:
初期版ではRAGとローカルベクトルDBを使用していたが、agentic searchの方が概ね優れていることが判明した。シンプルでセキュリティ・プライバシー面でも問題がない。
代わりに採用されたのは、Glob(ファイル名検索)、Grep(テキスト検索)、Read(ファイル読み取り)という、一見レガシーなツール群。ただし、それをLLMが自律的に組み合わせる「Agentic RAG」というアプローチだ。
なぜベクトル検索では駄目だったのか? そもそもベクトル検索とは何で、Embeddingとはどう違うのか? Agentic RAGとは何か?
この記事では、Embeddingの仕組みを基礎から解説し、その上で**「ベクトル検索 vs Agentic RAG」の判断軸**を示す。
目次
- 前提知識 — ベクトルと次元
- ベクトル化・ベクトル検索・RAG — 3つの違い
- Embeddingの仕組み — テキストがベクトルになるまで
- 学習方法 — モデルはどう訓練されるか
- ベクトル空間と類似度
- ベクトル検索の実際 — DB・チャンキング・インデックス
- Agentic RAG — ベクトル検索を超えるアプローチ
- どちらを選ぶべきか — 判断フレームワーク
- まとめ
1. 前提知識 — ベクトルと次元
Embeddingの話に入る前に、「そもそもコンピュータは言葉をどう扱うのか」を押さえておこう。
コンピュータは数字しか分からない
コンピュータの中身は0と1だ。文字列 "犬" も内部的にはUTF-8で 0xE7 0x8A 0xAC(3バイトの数値)として保存されている。
しかしこの数値は文字のIDに過ぎない。「犬」が「猫」に似ているとか、「経済学」とは無関係だとか、そういった意味は一切含まれていない。
文字コード(UTF-8):
"犬" → 0xE78AAC
"猫" → 0xE78CAB
"犬" と "猫" の数値は近い? → たまたま近いだけ。意味的根拠はない
"dog" → 0x646F67
"犬" → 0xE78AAC
"dog" と "犬" の数値は近い? → まったく違う。しかし意味は同じ
問題: 文字コードでは意味を扱えない。「意味を反映した数値表現」が必要になる。
ベクトルとは何か
ベクトルとは**数値の並び(配列)**だ。プログラマなら float[] や list[float] と思えばいい。
# 2次元ベクトル(地図上の座標のようなもの)
position = [3.0, 4.0]
# 768次元ベクトル(Embeddingの実際の出力)
embedding = [0.21, -0.53, 0.82, ..., 0.31] # 768個のfloat
なぜ「ベクトル」と呼ぶのか?数学的に、ベクトルは空間上の点、あるいは原点からの矢印を表す。2次元ベクトル [3, 4] はXY平面上の点 (3, 4) だ。これが768次元になっても本質は同じ——768次元空間上の1つの点を指す。
次元とは何か
「768次元」と聞くと構えてしまうが、原理は単純だ。
1次元: 数直線上の位置 → [5.0] → 1つの数値で表せる
2次元: 平面上の位置 → [3.0, 4.0] → 2つの数値で表せる
3次元: 立体空間の位置 → [1.0, 2.0, 3.0] → 3つの数値で表せる
768次元: 768個の軸がある空間 → [0.2, -0.5, ..., 0.3] → 768個の数値で表せる
人間は3次元までしか直感的に想像できないが、数学的には何次元でも同じ演算(距離計算、内積など)が成立する。次元が多いほど、より多くの「意味の側面」を表現できる。
例えるなら:
- 2次元で人を表す: [身長, 体重] → 大まかな体格しか分からない
- 5次元で人を表す: [身長, 体重, 年齢, 視力, 握力] → もう少し詳しい
- 768次元で文を表す: 768個の「意味の軸」で、ニュアンスや文脈まで捉える
「近い」= 「似ている」
Embeddingの最も重要な性質はこれだ:
意味が似ているデータは、ベクトル空間で近い位置に配置される
この「近さ」を数値で測る方法がコサイン類似度(Cosine Similarity)だ(セクション5で詳述)。
この記事で使う用語
| 用語 | 意味 |
|---|---|
| ベクトル | 数値の配列。[0.2, -0.5, 0.8] のようなもの |
| 次元(dimension) | ベクトルの要素数。768次元 = 768個のfloat |
| トークン(token) | テキストの最小処理単位。単語やサブワード |
| Transformer | 現代のEmbeddingモデルの基盤アーキテクチャ |
| Cosine Similarity | 2つのベクトルの方向の近さを測る指標(-1〜+1) |
2. ベクトル化・ベクトル検索・RAG — 3つの違い
この記事で最も重要な区別。この3つは混同されやすいが、まったく別のレイヤーの技術だ。
テキスト ──[ベクトル化]──> ベクトル ──[ベクトル検索]──> 類似文書 ──[RAG]──> LLM回答
^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^
変換する技術 探す技術 活用する技術
Embeddingモデル ベクトルDB LLM + プロンプト
ベクトル化(Embedding)
テキストを数値のベクトルに変換する技術。
入力: "犬が公園で走っている"
出力: [0.20, -0.50, 0.80, ..., 0.30] (768次元)
担当するのはEmbeddingモデル(OpenAI text-embedding-3, BGE, Cohere等)。
ベクトル検索(Vector Search)
ベクトルDB内から、クエリベクトルに近いベクトルを高速に見つける技術。
入力: [0.20, -0.50, 0.80, ..., 0.30] (クエリベクトル)
出力: 類似度Top-K件の文書
担当するのはベクトルDB(Pinecone, Qdrant, pgvector等)。
RAG(Retrieval-Augmented Generation)
検索で取得した情報をLLMに渡して回答を生成するパターン。
入力: ユーザーの質問 + 検索で取得した関連文書
出力: 外部知識に基づくLLMの回答
重要: RAGは「検索 + LLM」の組み合わせパターンの総称であり、検索手段はベクトル検索に限らない。キーワード検索でも、Grep検索でも、RAGは成立する。
なぜこの区別が重要か
Claude Codeが捨てたのはベクトル検索であって、RAG自体は捨てていない。Grep/Glob/Readで情報を検索し、LLMに渡して回答する——これもRAGの一形態だ。
従来のClaude Code: テキスト → [ベクトル化] → [ベクトル検索] → [LLM生成]
現在のClaude Code: テキスト → [Grep/Glob] → [LLM生成]
↑ ↑
ベクトル化不要 RAGは成立している
では、ベクトル検索を支えるEmbeddingとは、具体的にどういう仕組みなのか。
3. Embeddingの仕組み — テキストがベクトルになるまで
ここが本記事の技術的核心だ。テキストが768次元のベクトルになるプロセスを、ステップごとに分解する。
全体フロー
Step 1: トークン化(Tokenization)
テキストを処理可能な最小単位に分割する。現代のモデルはサブワード分割を使う。
入力: "機械学習は面白い"
単語分割の場合: ["機械学習", "は", "面白い"]
サブワード分割: ["機械", "学習", "は", "面白", "い"]
代表的なトークナイザ:
- BPE(Byte Pair Encoding): GPT系で使用。頻出するバイト列を結合
- WordPiece: BERT系で使用。語彙に基づくサブワード分割
- SentencePiece: 言語非依存。日本語でも前処理不要
Step 2: トークンID変換
各トークンを語彙テーブルのインデックス(整数)にマッピングする。
["[CLS]", "機械", "学習", "は", "面白", "い", "[SEP]"]
→ [101, 3245, 8901, 142, 5567, 234, 102]
[CLS]と[SEP]は特殊トークン。[CLS]は文全体の表現を集約する役割を持つ。
Step 3: Embedding層
各トークンIDをルックアップテーブルから密なベクトルに変換する。
トークンID 3245 ("機械") → [0.12, -0.34, 0.56, ..., 0.78] (768次元)
トークンID 8901 ("学習") → [0.45, 0.23, -0.11, ..., 0.92] (768次元)
この時点では、各トークンは文脈を考慮しない独立したベクトルだ。「銀行」が「お金の銀行」なのか「川の土手(bank)」なのかは、まだ区別できない。
さらに、トークンの順序情報を注入するために位置エンコーディングを加算する。
最終入力 = トークンEmbedding + 位置エンコーディング
Step 4: Transformer Encoder — ここが魔法の場所
TransformerのSelf-Attention機構が、各トークンに文脈情報を注入する。これがEmbeddingの品質を決定的に左右する。
Self-Attentionの仕組み
各トークンから3つのベクトルを生成する:
- Query(Q): 「自分は何を探しているか」
- Key(K): 「自分は何を持っているか」
- Value(V): 「自分の情報」
Attention(Q, K, V) = softmax(Q · K^T / √d_k) · V
具体例で考えよう:
入力: "彼は銀行に預金した"
"銀行"のQueryが、他のすべてのトークンのKeyと照合される:
"彼" → 関連度: 低
"は" → 関連度: 低
"に" → 関連度: 低
"預金" → 関連度: 高 ★
"した" → 関連度: 低
→ "預金"との関連度が高いため、"銀行"のベクトルに
「お金に関する銀行」の文脈情報が注入される
これが文脈依存表現だ。同じ「銀行」でも、「川の銀行」と「預金の銀行」で異なるベクトルが生成される。
Multi-Head Attention
実際には、複数のAttention「ヘッド」が並列に異なる関係性を学習する。
ヘッド1: 構文的関係(主語-述語)
ヘッド2: 意味的関係(銀行-預金)
ヘッド3: 位置的関係(近い単語同士)
...
ヘッド12: その他のパターン
BERTでは12ヘッド × 12層 = 144回のAttention計算が行われる。
層の階層性
浅い層(1-4層): 構文・文法を捉える
└ 品詞、係り受け、語順パターン
中間層(5-8層): 意味関係を捉える
└ 同義語、反義語、エンティティ関係
深い層(9-12層): 高次の抽象概念を捉える
└ 感情、意図、トピック
Step 5: Pooling — トークン群を1つのベクトルに
Transformer Encoderの出力は、トークン数 × 768次元の行列だ。これを文全体を表す1つのベクトルに集約する必要がある。
Encoder出力:
[CLS] → [0.12, -0.34, ..., 0.78]
機械 → [0.45, 0.23, ..., 0.92]
学習 → [0.33, -0.11, ..., 0.56]
は → [0.01, 0.05, ..., 0.02]
面白い → [0.67, 0.44, ..., 0.81]
[SEP] → [0.08, -0.02, ..., 0.15]
主要なPooling手法:
| 手法 | やり方 | 特徴 |
|---|---|---|
| [CLS]トークン | [CLS]の出力をそのまま使用 | BERTのデフォルト。最新モデル(BGE等)で再び主流に |
| Mean Pooling | 全トークンの平均を取る | sentence-transformers系で広く使用。安定して高性能 |
| Max Pooling | 各次元の最大値を取る | 特定の特徴が強調される |
歴史的にはBERTの[CLS]→Sentence-BERTのMean Pooling→最新モデルで[CLS]回帰、と潮流が変化している。モデルのドキュメントで推奨Pooling方式を確認するのが確実だ。
# Mean Poolingの擬似コード
token_embeddings = transformer_output # shape: (seq_len, 768)
attention_mask = ... # パディングを除外
# マスク付き平均
sentence_embedding = (token_embeddings * attention_mask).sum(dim=0) / attention_mask.sum()
# shape: (768,)
4. 学習方法 — モデルはどう訓練されるか
Embeddingモデルの品質は、訓練方法で決まる。大きく分けて2段階ある。
事前学習: Masked Language Modeling (MLM)
BERTで導入された手法。テキストの一部を隠し、それを予測させる。
元の文: "私は東京に住んでいます"
マスク: "私は[MASK]に住んでいます"
タスク: [MASK] = "東京" を予測せよ
これにより、モデルは双方向の文脈理解を獲得する。教師データのラベル付けが不要なため、Wikipedia全文やWeb文書など大規模データで訓練できる。
Contrastive Learning(対照学習)— 現代Embeddingの鍵
MLMだけでは、文レベルの良質なEmbeddingは得られない。ここでContrastive Learningが登場する。
基本的な考え方
正例ペア(近づける):
"犬が公園で走っている" ↔ "公園を駆け回る犬"
負例ペア(遠ざける):
"犬が公園で走っている" ↔ "株式市場が急落した"
損失関数(InfoNCE Loss):
L = -log( exp(sim(anchor, positive) / τ) / Σ exp(sim(anchor, negative_i) / τ) )
τ: 温度パラメータ(一般的に0.02-0.2。MoCo: 0.07、SimCLR: 0.5)
sim: コサイン類似度
SimCSE: シンプルだが強力
面白いアプローチ: 同じ文を2回Transformerに通すだけ。Dropoutによってわずかに異なる表現が得られ、それを正例ペアとして使う。
"今日は天気がいい" → Dropout(0.1)を通す → ベクトルA
"今日は天気がいい" → Dropout(0.1)を通す → ベクトルB(わずかに異なる)
A と B を正例ペアとして訓練
→ 追加のデータ拡張なしで高品質なEmbeddingが得られる
教師ありContrastive Learning
NLI(自然言語推論)データセットを活用し、より高品質な訓練を行う。
含意(Entailment)= 正例:
前提: "2人の男がカートを押している"
仮説: "2人の男が荷物を移動している"
矛盾(Contradiction)= 強い負例:
前提: "2人の男がカートを押している"
仮説: "2人の男が座って休んでいる"
訓練データの規模感
| モデル | データ規模 | パラメータ数 |
|---|---|---|
| BERT (2018) | 3.3B tokens | 110M |
| E5 (2023) | 270M text pairs | 330M-7B |
| BGE-M3 (2024) | 数億ペア | 567M |
| OpenAI text-embedding-3 | 非公開(推定数十億ペア) | 非公開 |
5. ベクトル空間と類似度
Cosine Similarity — なぜこれが標準か
2つのベクトルの類似度を測る最も一般的な指標。
cosine_similarity(A, B) = (A · B) / (||A|| × ||B||)
結果の範囲: -1 〜 +1
+1: 完全に同じ方向(最も類似)
0: 直交(無関係)
-1: 正反対
なぜCosine Similarityが選ばれるか?
ベクトルの大きさ(magnitude)を無視し、方向だけを比較する。テキストの長さに左右されず、純粋に意味の類似性を測れる。
import numpy as np
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# 実行例
sim_dog_cat = cosine_similarity(embed("犬"), embed("猫")) # → 0.82
sim_dog_econ = cosine_similarity(embed("犬"), embed("経済学")) # → 0.15
他の距離指標との使い分け
| 指標 | 数式 | 用途 |
|---|---|---|
| Cosine Similarity | A·B / (||A|| × ||B||) | テキストEmbedding(標準) |
| Dot Product | A · B | 正規化済みベクトル(高速) |
| Euclidean Distance | √Σ(a_i - b_i)² | 画像、座標データ |
正規化済みのベクトルでは、Cosine SimilarityとDot Productは等価になる。多くのEmbeddingモデルは正規化済みベクトルを出力するため、実質的にDot Productで十分。
ベクトル演算の直感
Word2Vec(2013年)で発見された有名な例:
vec("King") - vec("Man") + vec("Woman") ≈ vec("Queen")
ベクトル空間には意味の方向が存在する。King - Manで「男性性」を除去し、+ Womanで「女性性」を加える。結果として「女性の王」= Queenに近いベクトルが得られる。
これはベクトル空間が意味構造を持つことの直接的な証拠だ。ただし注意点がある。この線形的な演算はWord2Vecなどの静的Embeddingの性質であり、TransformerベースのEmbeddingでは各単語が文脈に応じて異なるベクトルを持つため、同じ演算が常に成立するわけではない。
6. ベクトル検索の実際 —
Embeddingで生成したベクトルを、実際に「検索」として使うために何が必要か。
ベクトル検索の全体像
ここで注目してほしいのは、事前準備が必要なことだ。文書をチャンクに分割し、全てをEmbedding化し、ベクトルDBに格納しておかなければならない。これが後述するClaude Codeの判断に効いてくる。
チャンキング — 分割品質が検索品質を決める
長文をそのままEmbedding化すると、情報が希薄化する。適切なサイズに分割(チャンキング)が必要。
推奨設定:
チャンクサイズ: 200-500 トークン(汎用的な推奨値)
オーバーラップ: 10-20%(50-100トークン)
ただしクエリタイプによって最適値は異なる:
- 事実確認クエリ(「〇〇の設立年は?」): 小さめ(256-512トークン)
- 分析クエリ(「〇〇のメリデメを比較して」): 大きめ(1024+トークン)
手法比較:
| 手法 | やり方 | 長所 | 短所 |
|---|---|---|---|
| 固定サイズ | N文字/トークンで切る | シンプル | 文の途中で切れる |
| 文単位 | 句点で分割、N文ずつ結合 | 意味の境界を保持 | サイズ不均一 |
| 段落単位 | 改行2つで分割 | 論理的まとまり保持 | 段落サイズのばらつき |
| Recursive | 階層的に分割を試行 | 柔軟、構造保持 | 実装が複雑 |
| Semantic | 意味の切れ目で分割 | 意味的まとまり最大化 | 計算コスト高 |
# LangChainのRecursiveCharacterTextSplitter(実務で最も使われる)
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "、", " ", ""]
)
chunks = splitter.split_text(document)
オーバーラップの効果:
チャンクサイズ: 500トークン、オーバーラップ: 75トークン(15%)
チャンク1: トークン[0:500]
チャンク2: トークン[425:925] ← 75トークン重複
チャンク3: トークン[850:1350] ← 75トークン重複
→ チャンク境界での情報損失を防ぐ
7. Agentic RAG — ベクトル検索を超えるアプローチ
ここからが本記事の核心だ。セクション3-6で解説したEmbedding+ベクトル検索は強力な技術だが、万能ではない。Claude Codeが下した判断を理解するために、Agentic RAGを深掘りする。
従来のRAG(ベクトル検索型)の弱点
セクション6で解説した従来のRAGには、構造的な弱点がある。
1. 事前準備コストが高い
文書追加・更新のたびに:
テキスト抽出 → チャンク分割 → Embedding化 → DB格納 → インデックス更新
コードベースの場合:
git commitのたびにインデックスを再構築する必要がある
→ 頻繁に変わるコードとの相性が悪い
2. 検索が1回きり
従来のRAG:
質問 → ベクトル検索(1回) → Top-K取得 → LLM回答
検索結果が的外れでも → そのまま回答してしまう
情報が足りなくても → 再検索しない
3. チャンキングの罠
例: 関数の定義が500行ある場合
→ 200トークンのチャンクに分割される
→ 関数の冒頭と末尾が別チャンクになる
→ 「この関数の全体像」を1回の検索で取得できない
Agentic RAGとは
LLM自身が検索戦略を考え、実行し、評価し、必要なら再検索するアプローチ。
従来のRAG:
質問 → 検索 → 回答 (パイプライン固定)
Agentic RAG:
質問 → LLMが考える
→「まずファイル構造を見よう」→ Glob実行
→「この関数が怪しい」→ Read実行
→「呼び出し元も確認したい」→ Grep実行
→「十分な情報が集まった」→ 回答
(LLMが動的に判断)
Claude Codeの実装
Claude Codeが使う検索ツールは3つだけ:
| ツール | 役割 | 従来のRAGで言えば |
|---|---|---|
| Glob | ファイル名パターン検索 | ベクトルDBのメタデータフィルタ |
| Grep | ファイル内容のテキスト検索 | キーワード検索(BM25) |
| Read | ファイル内容の読み取り | チャンクの取得 |
ベクトル化もベクトルDBも使わない。代わりに、LLMが「次にどのツールを使うか」「どんなクエリを投げるか」を自律的に判断する。
具体例: Claude Codeの検索フロー
ユーザー: 「認証機能のバグを直して」
Claude Codeの思考:
1. 認証関連のファイルを探そう
→ Glob("**/auth*") → src/auth/login.ts, src/auth/middleware.ts 等
2. login.tsを読んでみよう
→ Read("src/auth/login.ts") → コードを理解
3. エラーハンドリングが怪しい。他にこの関数を呼んでいる箇所は?
→ Grep("authenticateUser") → 3箇所見つかる
4. テストファイルもチェック
→ Glob("**/auth*.test.*") → テストの期待値を確認
5. 原因を特定。修正案を提示。
このフローをベクトル検索で実現しようとすると:
- コードベース全体を事前にEmbedding化する必要がある
-
git commitのたびにインデックス更新 - 「authenticateUserの呼び出し元」のような構造的な検索はベクトル検索が苦手
なぜClaude Codeはベクトル検索を捨てたのか
| 観点 | ベクトル検索 | Agentic Search |
|---|---|---|
| 事前準備 | インデックス構築が必要 | ゼロ |
| コード変更への追従 | 再インデックスが必要 | 常に最新を検索 |
| 検索の柔軟性 | 意味的類似度のみ | パターン検索、構造検索、意味検索を動的に使い分け |
| 精度(意味検索) | 高い | Grep/Globでは意味的類似は捉えられない |
| 精度(構造検索) | 低い | 高い(呼び出し元、型定義など) |
| インフラ | ベクトルDB必要 | ファイルシステムのみ |
| セキュリティ | ベクトルの保存に懸念 | ローカルファイルのみ |
| コスト | Embedding + DB運用 | LLM呼び出し増 |
Claude Codeのケースでは、コードベースは頻繁に変更され、構造的な検索(呼び出し元、型定義、テストファイルの対応)が重要で、事前準備のコストが見合わなかった。
8. どちらを選ぶべきか
ベクトル検索が強い領域
- 大量の静的文書を検索する場合(社内ドキュメント、FAQ、ナレッジベース)
- 意味的な検索が重要な場合(「リモートワークのメリット」で「在宅勤務の利点」もヒットさせたい)
- 多言語検索が必要な場合
- データの更新頻度が低い場合
- レコメンデーション、クラスタリング、異常検知など検索以外のベクトル活用もある場合
Agentic RAGが強い領域
- 頻繁に変更されるデータを扱う場合(コードベース、日々更新されるログ)
- 構造的な検索が重要な場合(ファイル名パターン、関数の呼び出し関係)
- マルチステップの探索が必要な場合(1回の検索では答えが出ない複雑な質問)
- 事前準備のコストをゼロにしたい場合
- ツールの組み合わせが必要な場合(DB検索 + Web検索 + ファイル読み取り)
組み合わせという選択肢
二者択一ではない。Agentic RAGのツールの1つとしてベクトル検索を組み込むのが最も柔軟なアーキテクチャだ。
Agentic RAG
├── ツール1: ベクトル検索(意味的に近い文書を探す)
├── ツール2: Grep(キーワード完全一致検索)
├── ツール3: SQL(構造化データを問い合わせ)
├── ツール4: Web検索(最新情報を取得)
└── LLMが状況に応じて使い分ける
Claude Codeの場合、コードベースという特性上ベクトル検索のメリットが薄かっただけで、ドキュメント検索が主な用途なら、ベクトル検索はAgentic RAGの中でも依然として最強のツールだ。
9. まとめ
- ベクトル化とベクトル検索は別物。変換する技術(Embedding)と探す技術(Vector Search)を区別する
- Embeddingの核心はTransformerのSelf-Attention。文脈を理解したベクトルを生成する
- ベクトル検索は強力だがコストがある。事前準備、インデックス管理、チャンキング設計が必要
- Agentic RAGという選択肢がある。LLMが自律的に検索戦略を決め、複数ツールを使い分ける
- Claude Codeはベクトル検索を捨てた。コードベースという特性上、事前準備コストと構造検索の弱さが致命的だった
- どちらかではなく、組み合わせ。Agentic RAGのツールの1つとしてベクトル検索を使うのが最も柔軟
Embeddingは死んだのか?
まったく逆だ。 Claude Codeのケースはコードベースという特殊な領域での判断であり、Embeddingとベクトル検索は以下の場面で依然として最適解:
- 大量の文書を意味的に検索するとき
- 多言語対応が必要なとき
- レコメンデーション・クラスタリング・異常検知
- マルチモーダル検索(画像×テキスト)
技術を理解した上で、用途に応じて適切な道具を選ぶ。それがこの記事の結論だ。
参考文献
- Claude Codeがベクトル検索を捨ててAgentic RAGにした話 | Zenn
- An intuitive introduction to text embeddings | Stack Overflow
- What is Vector Embedding? | IBM
- Vector embeddings | OpenAI API
- The Illustrated Transformer
- SimCSE GitHub
- What Is Cosine Similarity? | IBM
- Chunking Strategies for RAG | Weaviate
- Best Embedding Models in 2026 | Elephas
- Jina ColBERT v2 | Jina AI
- CLIP Overview | Towards Data Science