需要幫助將資料儲存在向量資料庫 (Supabase)

你好,我正在構建一個工作流來抓取 WordPress 網站內容,並將其存儲在向量數據庫中,以便稍後可以使用該數據來餵送 AI 代理。

描述問題/錯誤/問題

我已經將數據分塊,並且可以在 Hugging Face 的幫助下準備用於嵌入。一旦數據被嵌入,它會以數組形式返回數據,但我需要將響應追加到現有的元數據對象中。

錯誤信息是什麼(如果有的話)?

請分享你的工作流

Data Chunk Node:

{
  "nodes": [
    {
      "parameters": {
        "jsCode": "function chunkText(text, size = 1200, overlap = 150) {\n  const chunks = [];\n  let start = 0;\n\n  while (start < text.length) {\n    const end = Math.min(start + size, text.length);\n    const chunk = text.slice(start, end).trim();\n\n    if (chunk.length > 0) {\n      chunks.push(chunk);\n    }\n\n    start += size - overlap;\n  }\n\n  return chunks;\n}\n\n\nconst items = $input.all();\n\nlet output = [];\n\nfor (const item of items) {\n\n  const input = item.json;\n\n  const text = input.clean_text || '';\n\n  if (!text) {\n    continue;\n  }\n\n  const chunks = chunkText(text);\n\n  chunks.forEach((chunk, index) => {\n\n    output.push({\n      json: {\n        id: input.id,\n        title: input.title,\n        slug: input.slug,\n        link: input.link,\n        modified: input.modified || '',\n        source_type: input.source_type || 'page',\n\n        chunk_index: index,\n\n        chunk_text: chunk,\n\n        // 稍後我們將使用此項進行 upsert/update\n        chunk_hash: `${input.id}-${index}-${chunk.length}`\n      }\n    });\n\n  });\n}\n\nreturn output;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        832,
        0
      ],
      "id": "2984be2c-5f97-4833-8d5e-a0912be70559",
      "name": "ChunkText"
    }
  ],
  "connections": {
    "ChunkText": {
      "main": [
        []
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "a12d2939883b6ee4645287d26116bd7830ea910b3e2205baee32d9bb5d782014"
  }
}


Hugging Face Node:


{
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://router.huggingface.co/hf-inference/models/BAAI/bge-small-en-v1.5",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer <<API TOKEN>>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{\n{\n  inputs: $json.chunk_text\n}\n}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        992,
        -144
      ],
      "id": "2f3d7b70-454c-4e1d-8345-33ef8e3b047c",
      "name": "Hugging Face API"
    }
  ],
  "connections": {
    "Hugging Face API": {
      "main": [
        []
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "a12d2939883b6ee4645287d26116bd7830ea910b3e2205baee32d9bb5d782014"
  }
}



分享最後一個節點返回的輸出

Hugging Face 模型的響應:
[
-0.03779905289411545,
-0.027235977351665497,
0.04070237651467323,
0.02413654699921608,
-0.04879286512732506,
-0.016459722071886063,
-0.0015258010243996978,
0.07087898999452591,
-0.03721754252910614,
0.019904209300875664,
0.022374162450432777,
-0.05740947648882866,
-0.017154762521386147,
0.04529924318194389,
0.0001327882637269795,
-0.023566097021102905,
0.008483237586915493,
0.013999130576848984,
-0.057878654450178146,
0.0066631208173930645,
0.034953076392412186,
-0.02018037624657154,
-0.013172738254070282,
-0.030672265216708183,
0.007248119916766882
]

向量數據庫架構:
create table public.site_chunks (
id bigserial not null,
url text not null,
title text null,
chunk_text text not null,
chunk_hash text not null,
source_type text not null,
modified_at timestamp with time zone null,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
embedding public.vector null,
constraint site_chunks_pkey primary key (id)
) TABLESPACE pg_default;

create unique INDEX IF not exists site_chunks_url_hash_idx on public.site_chunks using btree (url, chunk_hash) TABLESPACE pg_default;

create index IF not exists site_chunks_source_type_idx on public.site_chunks using btree (source_type) TABLESPACE pg_default;

create index IF not exists site_chunks_modified_at_idx on public.site_chunks using btree (modified_at) TABLESPACE pg_default;

注意:我提供了單個數組輸出,但我有 53 個文本塊,我將進行嵌入,API 返回總共 20352 個項目,所以我不確定如何將所有這些數據與我現有的記錄進行映射。

樣本輸入:
[
{
“id”: 1383,
“title”: “blog”,
“url”: “TEST”,
“content”: “”,
“modified”: “2026-07-29T23:06:08”,
“source_type”: “page”,
“clean_text”: “”
},
{
“id”: 1222,
“title”: “Laser Dentistry”,
“slug”: “”,
“link”: “”,
“date”: “”,
“clean_text”: “Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum”
} ]

注意:我在主體參數中發送 clean_text 以進行嵌入。

預期輸出:
{ "url": "", "title": "Laser Dentistry", "chunk_text": "LASER DENTISTRY...", "chunk_hash": "1222-0-1200", "source_type": "page", "modified_at": null, "embedding": "[-0.037799,-0.027235,0.040702,...]" }

有關你的 n8n 設置的信息

  • n8n 版本:
  • 數據庫(預設:SQLite):
  • n8n EXECUTIONS_PROCESS 設置(預設:own、main):
  • 通過以下方式運行 n8n(Docker、npm、n8n cloud、桌面應用):Azure App Service
  • 操作系統:Linux

Hammad 的診斷是對的——這是實際的修復方法。

將 HTTP Request 節點設定為傳回文字而不是 JSON:
Options > Response > Response Format = Text

這樣可以防止 n8n 將陣列分割成 384 個單獨的項目。然後
在其後添加一個 Code 節點:

return $input.all().map(item => {
const src = $(‘ChunkText’).item.json;
return {
json: {
url: src.link,
title: src.title,
chunk_text: src.chunk_text,
chunk_hash: src.chunk_hash,
source_type: src.source_type,
modified_at: src.modified || null,
embedding: JSON.parse(item.json.data)
}
};
});

$(‘ChunkText’).item 使用配對項目追蹤,所以每個回應
都會保持與產生它的塊的連結。這只在陣列未被分割的情況下有效,
這就是文字格式重要的原因。

還有兩件值得現在而不是稍後修復的事情。

你的欄位宣告為沒有維度的向量:

embedding public.vector null

這對插入有效,但你無法在上面建立索引。
bge-small-en-v1.5 輸出 384 維,所以:

embedding vector(384)

沒有維度,你無法建立 HNSW 或 IVFFlat,沒有
索引,每次相似度搜尋都會掃描整個表。在 53
個塊時沒問題,但在 50,000 個時就不行了。

第二點,你的 chunk_hash 包括塊長度:

${input.id}-${index}-${chunk.length}

你提到這是為了稍後進行 upsert。但這不會有效。如果一個
頁面被編輯,塊的長度改變,雜湊值就會改變,所以你在
(url, chunk_hash) 上的唯一索引將無法配對,你會得到一個新列
而不是更新。舊塊會保留下來。幾個月後,你
會在表中有每個編輯過的頁面的兩個版本,代理
會從兩者都檢索。

使用一個穩定的行識別碼:

chunk_hash: ${input.id}-${index}

如果你想偵測實際的內容變化,可以添加一個帶有塊文字 SHA 的
separate content_hash 欄位。

嗨 @Akash22
在 Hugging Face 節點中開啟 Options > Response > Include Response Headers and Status。向量隨後會嵌套在 body 下,每個區塊一項,並保持完整。
然後新增一個 Merge 節點,將 Mode 設定為 Combine,Combine By 設定為 Position,ChunkText 放入輸入 1,Hugging Face 放入輸入 2。兩邊都產生 53 個項目,順序相同,所以每個輸出列都包含自己的中繼資料和自己的向量,可以直接對應到 Supabase 節點,無需 Code 步驟。
將嵌入列以字串形式傳送:

{{ JSON.stringify($json.body) }}

PostgREST 會將 JSON 陣列轉換為 Postgres 陣列語法,而向量類型會拒絕此語法。Supabase 自己產生的類型基於相同原因,將向量列公開為字串。

行列進入後,檢索端詳情如下:
https://axshul.site/n8n/guide/vector-stores-and-rag/

嗨 Akash,

Hammad 在第 2 篇文章裡有你真正的問題,53 乘以 384 的算術是對的。

你發文裡還有兩件事值得比映射問題更關注,因為它們後來不會自己宣布。

你的 chunk_hash 是 id-index-length,你的唯一索引在 (url, chunk_hash) 上,有一則註解說你會用它來做 upsert。那個雜湊不包含任何內容。如果有人編輯一個頁面,chunk 出來的長度一樣,價格從 50 漲到 60,一個詞交換位置,一個打字錯誤用等長的詞修正,雜湊完全相同。upsert 匹配,什麼都不更新,向量存儲繼續提供舊文本。沒有錯誤,工作流保持綠色。鑑於你的範例標題之一是「雷射牙科」,過時的東西很容易是價格或政策。

另一個方向也好不了多少。當長度確實改變時,雜湊改變,所以它插入新行而不是更新舊行。前面的 chunk 永遠不會被移除,經過幾次重新擷取後,你有同一段落的多個版本在檢索中競爭。代理就能引用不再在網站上的文本。

兩個都由同樣的兩個改變修正。對內容本身做雜湊而不是其長度,chunk_text 的 sha256 就夠了。然後在每次重新擷取時,刪除該 url 的列,其雜湊不在你剛產生的集合裡。那樣索引就成了網站的鏡像,而不是它曾經存在過的一切的累積。

你在 schema 裡時還有一個較小的事。你的 embedding 欄位宣告為 vector 沒有維度。pgvector 不會在那上面建立 ivfflat 或 hnsw 索引,所以每個查詢都是對整個表的順序掃描。在 53 個 chunks 時你不會注意到。在 50,000 時你會。既然 bge-small 是 384,vector(384) 是你要的。

Adam

感謝您提供詳細的資訊,也感謝您發現了chunk_hash鍵和向量大小的問題。我修正了我的工作流程,並保持JSON格式的回應,但我在HTTP節點上啟用了「Include Response Headers and Status」。

感謝你提供詳細的資訊,以及你提到我那樣做的方式,它運作良好。

非常感謝您提供的資訊。我會檢查並相應地修正工作流程。