本指南向你展示原理。 该工具包提供完整配置——Cline + Aider + 已调优的配置,30 分钟内即可就绪。 本地副驾驶工具包 → # 为什么为本地 RAG 选择这套技术栈许多RAG教程都从LangChain或LlamaIndex开始。这些框架功能强大,但掩盖了底层细节。在这里,我们仅使用三个依赖项手动编写管道。您将理解每一步,并能明确后续需要优化的内容。
ChromaDB 开源向量存储,纯Python实现,内置持久化模式(SQLite + HNSW索引)。无需启动服务器。
Ollama 同时为嵌入模型(nomic-embed-text)和聊天 LLM(Qwen 3.5、Granite 4.2、Gemma 4)提供服务。统一的 HTTP 端点位于 localhost:11434。
纯 Python 只需几个函数,无需框架。如果有需要,之后可以接入 LangChain,但起步时并不需要它。 i
您将获得的内容
一个约150行的 Python 脚本,可读取一个文件夹中的 PDF,将其分块并在 ChromaDB 中建立索引,然后用法语回答问题并附上引用来源。全程本地运行,没有任何出站请求。
# 先决条件✓
本地副驾驶套件
本指南带你上手模型。工具包则帮你用上能在你的编辑器中编写代码的编程助手。
Python 3.10+ ChromaDB 需要 3.10 或更高版本。请使用 python --version 检查版本。
Ollama 已安装并启动 守护进程默认监听 http://localhost:11434。如果您从零开始,请先按照 Ollama 安装指南操作。
8 GB 内存 16 GB 内存较为充裕。9B 聊天模型采用 Q4 量化时占用约 6 GB,嵌入模型占用约 300 MB。
无需GPU CPU推理也能正常运行,只是速度较慢。在导入大型语料库时,配备至少6 GB显存的GPU能大幅加快嵌入向量的生成。 # 1. 安装ChromaDB并准备 Ollama创建一个干净的虚拟环境,安装所需的三个库,并在 Ollama 上下载模型。
Python 环境 ⧉ 复制
python -m venv .venv
source .venv/bin/activate # sous Windows : .venv\Scripts\activate
pip install chromadb ollama pypdf三个软件包:chromadb 用于向量存储,ollama 是官方 Python 客户端,pypdf 用于读取 PDF。仅此而已。
Ollama 模型 ⧉ 复制
ollama pull nomic-embed-text
ollama pull qwen3.5:9bnomic-embed-text 是一个拥有 1.37 亿参数的多语言嵌入模型,可生成 768 维向量。它轻量、快速,法语表现良好。Qwen 3.5 9B(6.6 GB,256k 上下文,多语言,Apache 2.0)用于最终的聊天环节:它是 2026 年面向 8 GB 显存配置的默认选择。您可以将它替换为 granite4.2:8b(更节省资源)或 gemma4:12b,无需修改任何代码。
→
检查 Ollama 是否响应
只需执行 curl http://localhost:11434/api/tags,就应该能列出您的模型。如果没有输出,说明守护进程尚未启动:在另一个终端中运行 ollama serve。
# 2. 配置嵌入模型嵌入向量是表示文本语义的向量。语义相近的两个文本具有相近的向量。这是 RAG 的核心:我们寻找与问题嵌入最相似的 chunk。
embed.py — 快速测试 ⧉ 复制
import ollama
resp = ollama.embeddings(
model="nomic-embed-text",
prompt="Le contrat est résilié de plein droit en cas de manquement grave."
)
vec = resp["embedding"]
print(f"Dimension du vecteur : {len(vec)}")
print(f"5 premières valeurs : {vec[:5]}")您应该会看到输出「Dimension du vecteur : 768」。如果程序报错并显示 model not found,说明尚未执行 ollama pull nomic-embed-text。
i
为何选择nomic-embed-text
在法国基准测试(MTEB-fr)中,nomic-embed-text 位列参数量少于 2 亿模型的 top 5。对于纯法语任务,mxbai-embed-large 通常表现更好,但体积达 6.7 亿。nomic 是入门时质量与速度的绝佳平衡选择。
# 3. 导入法语PDF文档加载执行三项操作:读取PDF页面内容,将文本分割为合理大小的块,并将每个文本块及其嵌入向量持久化存储至ChromaDB。
ingest.py ⧉ 复制
import os
import chromadb
import ollama
from pypdf import PdfReader
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection(name="docs")
def chunk_text(text, size=800, overlap=100):
chunks = []
start = 0
while start < len(text):
end = min(start + size, len(text))
chunks.append(text[start:end])
start += size - overlap
return chunks
def ingest_pdf(path):
reader = PdfReader(path)
name = os.path.basename(path)
for page_num, page in enumerate(reader.pages):
text = page.extract_text() or ""
for i, chunk in enumerate(chunk_text(text)):
emb = ollama.embeddings(
model="nomic-embed-text",
prompt=chunk
)["embedding"]
collection.add(
ids=[f"{name}-p{page_num}-c{i}"],
embeddings=[emb],
documents=[chunk],
metadatas=[{"source": name, "page": page_num + 1}],
)
print(f"OK : {name} ingéré ({len(reader.pages)} pages)")
if __name__ == "__main__":
for f in os.listdir("./pdfs"):
if f.endswith(".pdf"):
ingest_pdf(f"./pdfs/{f}")分块器将内容切分为每块 800 个字符,相邻块重叠 100 个字符。这是一个起点:块既不会太小(导致上下文不足),也不会太大(导致信号被稀释)。对于内容非常密集的法律文本,可减至 500 个字符;对于排版疏朗的技术手册,可增至 1200 个字符。
→
ChromaDB 的持久化模式
PersistentClient(path="./chroma_db") 会创建一个在重启后仍存在的目录。SQLite 存储元数据,HNSW 索引存储向量。无需启动服务器,无需 Docker。如需后续切换为客户端/服务器模式,只需将该部分替换为 HttpClient。
启动数据导入,导入包含您文档的 ./pdfs/ 文件夹中的内容:
启动数据导入 ⧉ 复制
mkdir -p pdfs
# placez vos PDF dans ./pdfs/
python ingest.py!
扫描的PDF = 无文本
pypdf 只能提取 PDF 中原有的文本。如果您的 PDF 是图像扫描件,extract_text() 将返回空内容。这时需要先进行 OCR 处理(使用 Tesseract,或通过 Ollama 使用 Qwen 3.5 9B 这样的多模态视觉模型),然后再导入。
# 4. 在 ChromaDB 中执行 top-k 检索文本块建立索引后,检索过程就是先将问题转换为嵌入向量,再向 Chroma 查询按余弦距离衡量最接近的 k 个向量。即使有 100,000 个文本块,这一过程也能瞬间完成。
search.py ⧉ 复制
import chromadb
import ollama
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_collection(name="docs")
def search(question, k=4):
q_emb = ollama.embeddings(
model="nomic-embed-text",
prompt=question
)["embedding"]
results = collection.query(
query_embeddings=[q_emb],
n_results=k,
)
chunks = results["documents"][0]
metas = results["metadatas"][0]
return list(zip(chunks, metas))
if __name__ == "__main__":
hits = search("Quelles sont les conditions de résiliation ?")
for chunk, meta in hits:
print(f"[{meta['source']} p.{meta['page']}]")
print(chunk[:200], "...\n")k=4 是一个不错的默认值。太小会漏掉相关上下文;太大则会让 LLM 被噪声淹没,并超出上下文窗口的容量。对于非常具体的问题,k=2 就足够了。对于涉及多个方面的问题,将 k 提高到 6。
# 5. 带引用的聊天循环现在把各部分串起来:检索相关文本块,构建包含上下文的提示,通过 Ollama 将其发送给 Qwen 3.5,并要求模型注明信息来源。
chat.py ⧉ 复制
import ollama
from search import search
SYSTEM = """Tu es un assistant qui répond uniquement à partir du CONTEXTE fourni.
Si la réponse n'est pas dans le contexte, dis-le clairement.
Cite tes sources entre crochets sous la forme [source.pdf p.X]."""
def ask(question):
hits = search(question, k=4)
context = "\n\n".join(
f"[{m['source']} p.{m['page']}]\n{c}" for c, m in hits
)
prompt = f"CONTEXTE :\n{context}\n\nQUESTION : {question}"
resp = ollama.chat(
model="qwen3.5:9b",
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": prompt},
],
options={"temperature": 0.2, "num_ctx": 8192},
)
return resp["message"]["content"]
if __name__ == "__main__":
while True:
q = input("\nQuestion (vide pour quitter) > ").strip()
if not q:
break
print("\n" + ask(q))三个关键点。首先,temperature=0.2:我们希望得到事实性回答,而非创造性内容。其次,num_ctx=8192:当注入4个800字符的块时,Ollama(默认2048)的上下文窗口过短。第三,系统提示强制模型回答‘我不知道’,而非进行幻觉生成——这是RAG中最重要的反幻觉防护措施。
→
流式传输以提升用户体验
将 ollama.chat 替换为 ollama.chat(..., stream=True),并遍历响应以实时显示 token。一旦将此代码集成到真实界面(如 FastAPI + WebSocket 或 Streamlit)中,这一点至关重要。
# 6. 具体案例:基于合同的法律聊天机器人假设一家事务所想查询 200 份 PDF 格式的服务合同。使用上述技术栈,不到一小时就能搭建一个助手,回答以下这类问题:
典型问题 “哪些合同包含合同终止后期限超过 12 个月的竞业限制条款?”
发生的情况 问题的嵌入向量会检索出包含语义相近关键词(竞业限制、终止后、期限)的文本块。Qwen 3.5 阅读这 4 段内容,并在回答中给出相关文件的名称。
隐私保障 任何数据都不会离开这台电脑。不需要 API 密钥。没有任何遥测。这正是本地 RAG 与 OpenAI 封装工具的区别。 !
需了解的局限性
基础 RAG 能很好地回答针对性问题(如「X 条款是什么」),但不擅长回答汇总类问题(如「有多少份合同包含 X」)。对于后者,要么需要一个分多步查询数据库的智能体,要么需要 GraphRAG。这是另一个话题。
# 故障排除
ChromaDB 数据导入速度慢 瓶颈几乎总是向 Ollama 发起的嵌入计算调用。请使用 ollama ps 检查 nomic-embed-text 是否在 GPU 上运行。在 CPU 上,预计每秒处理约 50 个文本块;在 GPU 上,约 500 个。
« model not found » Ollama 找不到 nomic-embed-text。请重新运行 ollama pull nomic-embed-text,并使用 ollama list 检查。
编造来源的回复 9B模型仍偶尔会产生幻觉。建议切换至mistral-small(24B,约14 GB,法语表现优异)或qwen3.8:27b,前提是您有足够VRAM。或者在ChromaDB之后添加一个reranker(cross-encoder)以过滤误匹配结果。
法语嵌入质量差 nomic-embed-text 支持多语言,但对于纯法语内容并非最佳选择。对于法律或医疗内容,请测试 Solon-embeddings-large-0.1 或 bge-m3(通过 sentence-transformers 加载,在 Ollama 之外运行)。
ChromaDB不断膨胀,没有上限 每次重新索引都会产生重复内容。在重新导入 PDF 之前,请执行 collection.delete(where={"source": name}) 以清除旧的分块。 # 深入了解您已经有了一个可正常运行的 RAG 系统。接下来可以从以下几个方面进一步完善它:
比较法语嵌入模型 我们的指南《法语嵌入模型推荐》对比了BGE、E5、Solon和nomic在法语内容上的表现。
优化分块处理 « 分块策略 » 详细说明语义分块、按 Markdown 标题或段落进行分块——通常这种方式能带来最大的精度提升。
添加重排序器 “在自己的流程中添加重排序器”:在 Chroma 之后加入交叉编码器,可将相关性提高 15%。这是顺理成章的下一步。
混合搜索 “BM25 + 向量混合检索”结合了词汇检索与语义检索,一旦涉及大量专业术语或专有名词,就必不可少。 本地副驾驶套件
这套开箱即用、首次运行就能正常工作的配置
本指南向你展示原理。该套餐 「本地代码副驾」 提供所有可直接粘贴的配置(Ollama + Cline + Aider + Tabby)、已调优的 Modelfiles 以及故障排除章节,让你在 30 分钟内用起来。我们也会坦诚地说明本地运行在哪些方面 ne 无法替代云端。
查看工具包 → 一次性付费 · 终身更新 免费备忘录
接收速查表 显存 → 最佳代码模型 → Ollama 命令 通过电子邮件。一个屏幕,复制粘贴,保持最新。
无垃圾邮件。1 次点击即可退订。你的数据保留在我们这里。
这份指南对您有帮助吗?
有反馈、发现了错误,或想补充说明?请告诉我们,让这份指南对每个人都更有帮助。
👍 有用 👎 不清楚 ✏️ 报告错误