一句話先說結論: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 服務端拒絕寫入的原始報錯。
典型特徵:
- 有時第一次啟動能成功,重啟後立刻失敗。
- 報錯維度在
1536 / 1024 / 1532之間反覆橫跳。 - 日誌裡有
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 返回空的坑
維度對上了,還可能有“問什麼都說沒相關信息”的問題,常見兩因:
- 向量庫是空的:要先
add/load 文檔,再查詢;SimpleVectorStore是記憶體態,重啟就丟,得重新灌或改用 PGVector/Redis 這類持久化實現。 - topK 設太大 / 文檔 chunk 沒切好:
QuestionAnswerAdvisor會把檢索到的文檔“塞”進 prompt,topK 太大導致 prompt 撐爆 context window,又慢又費 token。切塊要做小、檢索要有similarityThreshold過濾。
小結
- pgvector 維度是建表寫死的,插入必須精確匹配;報維度衝突,先想“模型是不是換了/沒固定”。
- 改了
dimensions設定不生效,是因為表已經存在、Spring AI 複用舊 schema——DROP 表重建才對。 - 固定 embedding 模型、鎖定 schema、確認刪對庫,三件事一起做才能根治。
- RAG 空結果先查“向量庫是不是空的”和“topK/切塊是否合理”。
