StackTraces
Spring AI已驗證

Spring AI PGVector:expected 1536 dimensions

@debug_master32 天前更新閱讀 6 min00

一句話先說結論:pgvector 的 VECTOR(N) 維數是建表時寫死的,之後不能改;插入的向量維度必須和它精確一致,否則直接 expected 1536 dimensions, not 1024 報錯。這個坑幾乎都出在“embedding 模型沒固定”或“換模型後舊錶沒重建”上——而且光改 application.yml 裡的 dimensions 是不生效的,因為 Spring AI 只在表不存在時才按設定建表,表在就照舊複用。

背景

RAG 裡向量數據庫存的是 embedding 結果。pgvector 在建列時就要聲明維度 VECTOR(1536),它是個硬類型約束——插入一個 1024 維的向量進去,就像往 int 列塞字符串,直接拒。每個 embedding 模型輸出的維度是固定的(OpenAI 的 text-embedding-3-small 是 384、text-embedding-3-large 是 3072,通義的 text-embedding-v1 是 1536),所以“模型輸出維度”和“表的維度”必須嚴格一致。

現象

啟動或寫入時報錯,報錯原文是 PG 拋出的維度不匹配:

ERROR: expected 1536 dimensions, not 1024

完整堆疊從業務程式碼一路走到 PostgreSQL 驅動:

org.springframework.dao.DataIntegrityViolationException:
PreparedStatementCallback; SQL [INSERT INTO public.vector_store ...];
nested exception is org.postgresql.util.PSQLException:
    ERROR: expected 1536 dimensions, not 1024
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1042)
    at org.springframework.ai.vectorstore.pgvector.PgVectorStore.doAdd(PgVectorStore.java:116)
    at com.example.service.RagService.addDocument(RagService.java:57)
    at com.example.controller.IngestController.ingest(IngestController.java:33)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
Caused by: org.postgresql.util.PSQLException: ERROR: expected 1536 dimensions, not 1024
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2731)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2420)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:562)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:498)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:188)
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:158)

最內層的 PgPreparedStatement.executeUpdate 就是真正把 INSERT 發給數據庫的那一幀,Caused by 裡才是 PG 服務端拒絕寫入的原始報錯。

典型特徵:

  1. 有時第一次啟動能成功,重啟後立刻失敗。
  2. 報錯維度在 1536 / 1024 / 1532 之間反覆橫跳。
  3. 日誌裡有 Is empty: false,說明表已經存在、維度對不上。

根因分析

坑 1:embedding 模型沒固定,輸出維度漂移

有些模型本身維度可變。比如通義 text-embedding-v3 可以輸出 1024 / 1536 / 2048,如果不顯式指定 model,SDK 自動選,第一次啟輸出 1024、第二次輸出 1536——這就是“剛成功又失敗”的來源。

坑 2:Spring AI 建表策略——表在就複用

Spring AI 的邏輯是:

if (表存在) {
    直接复用现有表结构,完全无视 dimensions 配置
} else {
    只有表不存在时才按 dimensions 建表
}

所以你在設定文件裡把 dimensions 從 1024 改成 1536,只要舊錶還留著,這個設定就完全不生效,照樣拿舊維度去校驗。

坑 3:明明刪了表還在報錯

常見於阿里雲 RDS 這類多 schema 環境:預設 search_path 可能指向別的 schema,你刪的是 postgres 庫的表,應用實際連的是業務庫;或者另一個 schema 裡還有同名表。

解決方案

按這條鏈順序來:

Step 1:顯式固定 embedding 模型

spring:
  ai:
    dashscope:
      embedding:
        options:
          model: text-embedding-v1   # 固定 1536 维

Step 2:維度與模型強綁定 + 允許初始化 schema

spring:
  ai:
    vectorstore:
      pgvector:
        dimensions: 1536
        initialize-schema: true
        index-type: HNSW
        distance-type: COSINE_DISTANCE

Step 3:徹底刪掉舊錶重建

DROP TABLE IF EXISTS public.vector_store CASCADE;

刪完重啟,Spring AI 會按新維度建表。成功標誌是啟動日誌裡看到:

Creating table: vector_store
embedding VECTOR(1536)

只要還看到 Is empty: false 或舊維度,就說明刪錯了庫/沒刪乾淨。

Step 4:排查 schema 干擾

  • spring.sql.init.mode: never 防止 schema.sql 搶先建表。
  • JDBC URL 用 ?currentSchema=public 鎖定 schema。
  • 確認你刪的表和應用實際連的表是同一張(用 SELECT ... WHERE relname='vector_store' 查真實維度核對)。

順帶一提:RAG 返回空的坑

維度對上了,還可能有“問什麼都說沒相關信息”的問題,常見兩因:

  1. 向量庫是空的:要先 add/load 文檔,再查詢;SimpleVectorStore 是記憶體態,重啟就丟,得重新灌或改用 PGVector/Redis 這類持久化實現。
  2. topK 設太大 / 文檔 chunk 沒切好:QuestionAnswerAdvisor 會把檢索到的文檔“塞”進 prompt,topK 太大導致 prompt 撐爆 context window,又慢又費 token。切塊要做小、檢索要有 similarityThreshold 過濾。

小結

  • pgvector 維度是建表寫死的,插入必須精確匹配;報維度衝突,先想“模型是不是換了/沒固定”。
  • 改了 dimensions 設定不生效,是因為表已經存在、Spring AI 複用舊 schema——DROP 表重建才對。
  • 固定 embedding 模型、鎖定 schema、確認刪對庫,三件事一起做才能根治。
  • RAG 空結果先查“向量庫是不是空的”和“topK/切塊是否合理”。

References(參考來源)

最後更新於 2026-09-09

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法