jollen.org

Jollen 的 Blog
Jollen's email: jollen # jollen.org
more:  Jollen's Training

RAG 向量化與語意搜尋概論

.作者:Jollen/
.日期:Sat Nov 08 2025 08:00:00 GMT+0800 (台北標準時間)


「Node.js & LLM 原理與實務」書上第 4 章提到 Text Embeddeding(向量化),其中 4.4 節「向量化文本資料」介紹如何使用 text-embedding-3-small 來取得語意向量的部份,延續前二篇文章的說明(詳見 Also read),本文再行補充 2 個實例,說明向量化與語意搜尋的概念。

延續「退貨政策」

以下範例示範如何使用 OpenAI Embedding API 將句子轉換成語意向量。延續書上範例,本文同樣採用 text-embedding-3-small 模型作為實例:

// src/examples/text-embedding.js
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function main() {
  const sentences = ['退貨政策', '退款流程', '物流延誤'];

  const response = await client.embeddings.create({
    model: 'text-embedding-3-small',
    input: sentences
  });

  console.log('\n向量表示結果:');
  response.data.forEach((item, index) => {
    console.log(`${sentences[index]} → 向量維度:${item.embedding.length}`);
  });
}

main();

執行後可看到輸出類似:

退貨政策 → 向量維度:1536
退款流程 → 向量維度:1536
物流延誤 → 向量維度:1536

這代表每個句子都被轉換為一組「1536 維」的向量。在這個階段,語言不再只是文字,而是「可計算的語意單位」。

到這裡,讀者應該會有一個疑問:「為什麼這三句話的維度都是 1536」,那這三句話有什麼差異嗎?這要先回到「語意向量」的觀念來說明。Text embeddeding 模型輸回傳的向量維度(dimension 或稱長度)是固定的,以 ‘text-embedding-3-small’ 模型來說,任何句子的維度都是 1536。

不論輸入什麼文字,‘text-embedding-3-small’ 模型都會會產出 1536 維的向量維度。也就是說:

「退貨政策」 → 一個有 1536 個數字的向量。 「退款流程」 → 另一個不同內容、但長度同樣是 1536 的向量。 「物流延誤」 → 第三個不同內容的 1536 維向量。

即便它們的長度都相同(1536),但句子內容完全不同。維度的數值,代表該文字在某個語意特徵上的位置,這是該模型在訓練過程中自動學習的「語意特徵空間」;所以,這 1536 個維度對人類是「不可解釋」的,只對模型有意義。

文字向量化

維度長度一樣,那這三句話有什麼差異嗎?確實有差異。簡單說,每個句子的「向量內容」不同,因此,每個句子在在語意空間中的「座標位置」當然也不同。例如:

句子 向量(假設為 3 維) 語意距離(餘弦相似度)
退貨政策 [0.9, 0.1, 0.2] -
退款流程 [0.88, 0.15, 0.25] 相似度高
物流延誤 [0.1, 0.8, 0.9] 相似度低

這個例子說明:

  • 「退貨政策」與「退款流程」在數值上非常接近,表示語意相似
  • 「退貨政策」與「物流延誤」則距離很遠,語意關聯很弱

也就是說,這三個句子的的空間座標是完全不同的。到這裡,只需要先建立「維度相同、但語意不同(維度相同 ≠ 語意相同)」的觀念即可。

可以知道這三個句子,在 ‘text-embedding-3-small’ 模型中的實際座標嗎?當然可以,以下是這個例子:

// src/examples/text-embedding-diff.js
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function main() {
  const sentences = ['退貨政策', '退款流程', '物流延誤'];

  // 生成 Embedding 向量
  const response = await client.embeddings.create({
    model: 'text-embedding-3-small',
    input: sentences
  });

  console.log('\n各句向量的前 8 維取樣:');
  response.data.forEach((item, index) => {
    // 僅取前 8 維顯示,四捨五入至小數點三位
    const sample = item.embedding.slice(0, 8).map(v => v.toFixed(3));
    console.log(`${sentences[index]} → [ ${sample.join(', ')} ... ]`);
  });
}

main();

執行結果:

各句向量的前 8 維取樣:
退貨政策 → [ 0.024, -0.012, 0.025, 0.003, -0.026, 0.010, -0.031, 0.058 ... ]
退款流程 → [ 0.007, 0.016, 0.005, 0.004, -0.046, -0.002, -0.015, 0.046 ... ]
物流延誤 → [ 0.034, 0.039, 0.017, 0.002, -0.005, -0.011, 0.009, 0.011 ... ]

上述結果,就是這三句文字的向量。

語意搜尋

在語意空間中,每一個向量都是一個「語意位置」。以上例來說:

  • 三個句子的向量長度相同(1536 維)
  • 從前 8 維的取樣可明顯看出:「退貨政策」與「退款流程」的「語意相似」
  • 從前 8 維的取樣可明顯看出:「物流延誤」的數值分佈差異較大,表示它跟前二個句子的距離較遠,所以語意較不相似

Text Embedding 模型的用途正是如此,我們可以用 Text Embedding 模型來將自已的語料,生成「向量數值」;接著,再把這些向量數值存進資料庫使用(如 PostgreSQL + Redis、Pinecone、Weaviate)。

有了向量資料庫,之後再根據使用者問題,算出其與 RAG 資料庫所有文字的語意距離,再將最接近的內容,連同使用者問題,一併送給 LLM 。


Tags: rag, llm, text-embedding

純手工打造說明:技術專欄文章為 Jollen 原創,內容皆為人工撰寫,無 AI 生成。轉載請註明出處與作者,並全文引用。轉載時請在文章開頭或結尾明顯處註明「本文出處: https://www.jollen.org, 已取得原作者同意並授權使用.」。

Copyright(c) 2001–2025 www.jollen.org. All rights reserved.
Last update: November 10, 2025 at 7:30 PM