TinkerLab
DIAGNOSTIC TREE // RAG-EMPTY-HALLUCINATION-CITATION (3 ROOT CAUSES)

本地知识库 (RAG) 检索报错维度冲突 (1024 vs 768)、召回为空或“检索命中却回答未提及”

一步步排查更换向量模型后的维度不匹配报错、Ollama 默认 2048 上下文静默丢弃头部切片,以及扫描件 PDF 空文本入库。

[核心实测结论 / TL;DR]本诊断树针对「本地知识库 (RAG) 检索报错维度冲突 (1024 vs 768)、召回为空或“检索命中却回答未提及”」提供 4 个交互节点与 3 套实测验证修复方案(涵盖:修复方案:更换 Embedding 模型后必须清空并重建向量集合(严禁混用维度);修复方案:将对话模型 num_ctx 调至 ≥ 8192,消除头部切片静默截断;修复方案:使用本地视觉模型 (Qwen2.5-VL) 或版面分析工具将扫描件/双栏表格转为 Markdown),配套完整实测指南《embedding-dimension-mismatch-and-zombie-vram》。
本地知识库 (RAG) 检索报错维度冲突 (1024 vs 768)、召回为空或“检索命中却回答未提及”
第 1 步:你在查询本地知识库 (RAG) 时观察到了哪种具体现象?

观察知识库问答界面的“引用切片 (Citations)”面板与容器后台日志,选择对应症状:

终端验证命令 (在对应环境执行)
curl -s http://127.0.0.1:11434/api/embed -d '{"model":"bge-m3","input":"test"}' | jq '.embeddings[0] | length'

选择你看到的实际输出或症状:
DIAGNOSTIC PROBE SEQUENCE // 完整分步探测命令与症状分支

「本地知识库 (RAG) 检索报错维度冲突 (1024 vs 768)、召回为空或“检索命中却回答未提及”」的全部 1 步终端探测命令与分支判据

无需逐层点击交互,以下列出本故障决策树每一层的宿主机只读探测命令、预期基线提示与输出流向。

第 1 步:你在查询本地知识库 (RAG) 时观察到了哪种具体现象?step-rag-symptom

观察知识库问答界面的“引用切片 (Citations)”面板与容器后台日志,选择对应症状:

curl -s http://127.0.0.1:11434/api/embed -d '{"model":"bge-m3","input":"test"}' | jq '.embeddings[0] | length'
输出症状分支:
  • • 报错 "Embedding dimension 1024 does not match collection dimension 768/384"→ sol-rag-reindex-dimension
  • • 界面底部的“引用切片”明明已召回正确段落,但模型正文却回答“文档中未提及”→ sol-rag-num-ctx-truncation
  • • PDF 上传显示成功,但检索召回为 0,或双栏表格里的金额与编号完全错乱→ sol-rag-scanned-pdf-vlm
ALL VERIFIED FIXES IN THIS TREE // 本树涵盖的全部根因与修复方案一览

「本地知识库 (RAG) 检索报错维度冲突 (1024 vs 768)、召回为空或“检索命中却回答未提及”」包含的 3 类根因、修复代码与验收命令

修复方案:更换 Embedding 模型后必须清空并重建向量集合(严禁混用维度)

sol-rag-reindex-dimension

不同向量模型输出的维度固定且互不兼容(`all-MiniLM` 为 384 维、`nomic-embed-text` 为 768 维、`bge-m3` 为 1024 维)。当你中途在设置里切换 Embedding 模型后,旧向量表仍锁死在原维度,且旧向量空间与新模型完全正交。

  1. 在中英混合场景下统一锁定 `bge-m3`(1024 维)作为全局唯一 Embedding 模型。
  2. 在后台执行“重置向量库 / 重新索引全部文档”,让所有历史文档统一按 1024 维重新计算入库。
# Verify exact output dimension of your active Ollama embedding model:
curl -s http://127.0.0.1:11434/api/embed \
  -d '{"model":"bge-m3","input":"dimension check"}' | python3 -c '
import sys, json
vecs = json.load(sys.stdin).get("embeddings", [[]])
print("Embedding Dimension:", len(vecs[0]))
'

[边界说明]切记:即便两个不同模型恰好都是 768 维,它们的语义向量空间也完全不同——只要更换了 Embedding 模型,就必须全量重新向量化。

验证命令: ollama ps

修复方案:将对话模型 num_ctx 调至 ≥ 8192,消除头部切片静默截断

sol-rag-num-ctx-truncation

Ollama 默认将所有模型的上下文窗口限制为 `num_ctx = 2048`。当知识库检索出 5–8 个文档片段(约 3,500 tokens)拼入 Prompt 时,Ollama 不会报错,而是从 Prompt 最开头静默截断——排名第一的核心切片直接被丢弃了!

  1. 在对话模型高级参数或自定义 `Modelfile` 中显式设置 `PARAMETER num_ctx 8192`(或 `16384`)。
  2. 开启 `OLLAMA_FLASH_ATTENTION=1` 与 `OLLAMA_KV_CACHE_TYPE=q8_0`,并设置 `OLLAMA_MAX_LOADED_MODELS=2` 确保向量模型与对话模型同时驻留显存。
# Create a RAG-ready 16K context Modelfile
cat << 'EOF' > Modelfile.rag
FROM qwen2.5:14b-instruct-q4_K_M
PARAMETER num_ctx 16384
PARAMETER temperature 0
EOF
ollama create qwen2.5-14b-rag -f Modelfile.rag

[边界说明]发起一次知识库提问后,立即在终端执行 `ollama ps` 查看 `CONTEXT` 列,确认实际生效的上下文长度已不再是 2048。

验证命令: ollama ps

修复方案:使用本地视觉模型 (Qwen2.5-VL) 或版面分析工具将扫描件/双栏表格转为 Markdown

sol-rag-scanned-pdf-vlm

传统 PDF 解析器(如 `pypdf`)只能读取原生文字流:遇到纯图片扫描件会提取出 0 字符,遇到双栏排版或跨页表格则会按水平行把左右两栏文字硬拼成乱码。

  1. 入库前先用 `pdftotext` 检查每页提取出的字符数,若少于 50 字符则判定为扫描件。
  2. 使用 `pdftoppm -png -r 150` 将复杂 PDF 转为图片,通过本地 `qwen2.5vl:7b` 视觉模型重构为结构化 Markdown 表格后再导入知识库。
# Check if PDF has an embedded text layer (if close to 0, it's a scanned image PDF):
pdftotext sample.pdf - | wc -c

# Rasterize at 150 DPI for local Vision LLM OCR:
pdftoppm -png -r 150 sample.pdf ./page

[边界说明]实测表明 150 DPI(约 1240×1754 像素)是视觉大模型识别精度与显存占用的最佳甜点;盲目用 300 DPI 会使图像 Token 暴增 4 倍并引发显存溢出。

验证命令: ollama run qwen2.5vl:7b "Describe this table in Markdown"
PAIRED REPRODUCIBLE GUIDE // 配套实测指南与硬件环境基线

本地 RAG 向量维度冲突 (1024 vs 768) 报错与显存“幽灵占用”深度排障指南

解决本地知识库搭建后最高频的两大疑难杂症:切换 Embedding 模型后向量库报 `InvalidDimensionException (1024 vs 768)` 或召回率突然归零,以及 `ollama ps` 为空但 `nvidia-smi` 显存依然被占满的清理方案。

实测硬件与系统

Linux / macOS / WSL2 · Ollama 0.5.7 · Open WebUI / Dify / Continue(ChromaDB, Qdrant, PGVector)

锁定版本栈

Ollama 0.5.7 · bge-m3 (1024-dim) · nomic-embed-text (768-dim) · mxbai-embed-large (1024-dim)

验证日期 (2026-10-26)

100% 本机向量库校验与显存进程诊断 — 所有向量维度探测与 GPU 进程清理均在本地终端通过 `curl` 与 `nvidia-smi` 完成。