项目 01 · 领域专家(带 RAG)
用 Ollama 跑 Qwen 2.5 + Chroma 本地向量数据库,做一个能"读" 100 份你领域文档的专家系统。完全本地,零成本。
技术栈
- Python 3.10+ / Node.js 20+
- Ollama + Qwen 2.5:7b(本地大模型)
- Chroma(开源向量数据库,可本地)
- nomic-embed-text(开源 embedding 模型,Ollama 自带)
怎么算"成"?
领域内问 10 个具体问题,至少 8 个回答里能引用具体文档片段,且回答准确。
步骤 1 · 准备语料
选一个你真正懂的领域 —— 你部落格的所有文章、你研究的某个话题的 100 篇论文、家族的菜谱、一个开源项目的全部文档…… 整理成一个文件夹,全部转成 .txt 或 .md。
步骤 2 · 装环境
ollama pull qwen2.5:7b
第 1 块 · 拉取本地大模型
qwen2.5:7b 是阿里的开源模型,7B 参数(相对轻量),在个人电脑上能跑。这一行会下载约 5GB 的模型文件到本地。
💡 本地 vs 云:用 Ollama 在本地跑大模型,意味着你的所有数据都不上云。隐私和成本都有保障。
ollama pull nomic-embed-text
第 2 块 · 拉取 embedding 模型
embedding 模型把文字转成向量(数学上的坐标)。nomic-embed-text 是开源的、轻量的、适合本地用。这样你的整个 RAG 系统(模型 + embedding + 向量库)都能在一台机器上跑。
👉 试改:用 ollama list 看已安装的模型,选更强或更轻量的版本。
pip install chromadb
第 3 块 · 安装 Python 向量库
chromadb 是开源向量数据库,Python 包。一条命令,它就在你的环境里了,可以本地存储向量和文档。
💡 工程简洁性:只需三行命令,就把"大模型"、"embedding"、"向量库"三个关键组件都装上了。没有云账户、没有 API 密钥、完全本地。
📋 看 / 复制完整代码
ollama pull qwen2.5:7b ollama pull nomic-embed-text pip install chromadb
步骤 3 · 索引文档
import chromadb from ollama import embed
第 1 块 · 导入依赖
chromadb 是向量数据库库,ollama 的 embed 函数把文字变成向量。两个都是 Python 包。
💡 分工:Ollama 负责"大脑"(embedding 模型),Chroma 负责"记忆"(存储和检索向量)。
client = chromadb.PersistentClient(path="./db")
col = client.create_collection("docs")第 2 块 · 建立数据库和集合
PersistentClient 意思是"数据会保存在硬盘上"(./db 文件夹)。create_collection 在数据库里创建一个叫 "docs" 的集合(类似数据库里的一张表)。
👉 试改:改 path="./db" 到其他地方,比如 path="/mnt/data/kb",这样向量库就存在那里了。
for i, doc in enumerate(docs):
emb = embed(model="nomic-embed-text", input=doc)["embeddings"][0]
col.add(ids=[str(i)], embeddings=[emb], documents=[doc])第 3 块 · 逐文档索引
对每一个文档:① 用 embed(...) 把文字变成向量 ② 调用 col.add(...) 把向量和原文一起存进 Chroma。ids 是文档的编号,embeddings 是向量,documents 是原文本。
💡 这就是 RAG 的"记忆库"工作原理:文档 → 向量 → 存库。查询时反过来:查询 → 向量化 → 向量相似度搜索 → 返回相关文档。
👉 试改:在循环里加进度打印:if i % 10 == 0: print(f"已索引 {i} 个文档")
📋 看 / 复制完整代码
import chromadb
from ollama import embed
client = chromadb.PersistentClient(path="./db")
col = client.create_collection("docs")
for i, doc in enumerate(docs):
emb = embed(model="nomic-embed-text", input=doc)["embeddings"][0]
col.add(ids=[str(i)], embeddings=[emb], documents=[doc])
步骤 4 · 查询 + 生成
from ollama import chat def ask(question):
第 1 块 · 导入和函数定义
导入 chat 函数(用于生成回答),定义 ask 函数接收一个问题。
💡 函数名 ask 很关键:这就是你的"智能体的嘴"——用户问问题,这个函数给出有根据的答案。
q_emb = embed(model="nomic-embed-text", input=question)["embeddings"][0]
results = col.query(query_embeddings=[q_emb], n_results=3)
context = "\n\n".join(results["documents"][0])第 2 块 · 检索相关文档
① 把问题向量化:embed(...) ② 在向量库里查询最相似的 3 个文档:col.query(..., n_results=3) ③ 把返回的文档拼成一段 context(用两个换行分隔,便于模型理解)。
👉 试改:改 n_results=3 到 5 或 10,看更多上下文是否让答案更准。
response = chat(model="qwen2.5:7b", messages=[
{"role": "system", "content": f"基于以下资料回答:\n{context}"},
{"role": "user", "content": question}
])
return response["message"]["content"]第 3 块 · 生成有根据的回答
调用 chat(Ollama 的大模型)。系统提示说"基于以下资料"(这就是 RAG 的关键——把检索到的文档作为上下文),然后是用户的问题。返回模型生成的内容。
💡 RAG 的核心就在这里:不是"模型凭记忆回答",而是"模型基于我给的文档回答"。这大大减少了幻觉(编造)。
📋 看 / 复制完整代码
from ollama import chat
def ask(question):
q_emb = embed(model="nomic-embed-text", input=question)["embeddings"][0]
results = col.query(query_embeddings=[q_emb], n_results=3)
context = "\n\n".join(results["documents"][0])
response = chat(model="qwen2.5:7b", messages=[
{"role": "system", "content": f"基于以下资料回答:\n{context}"},
{"role": "user", "content": question}
])
return response["message"]["content"]
步骤 5 · 评测 10 个问题
列 10 个领域内的具体问题。跑一遍。逐条评估准确度。把不准的查一查 —— 是检索没找到?还是模型答错了?写一份 EVALUATION.md。