
先说清楚要做什么
很多人一听“本地知识库”就想到一整套向量数据库、框架、服务。其实核心就三步:把文档切成小块、每块算一个向量、提问时找最像的几块喂给模型。向量库只是帮你存和检索,小规模完全可以用字典代替。
准备:装 Ollama 并拉两个模型
ollama pull llama3.1 # 负责回答的模型
ollama pull nomic-embed-text # 负责把文字变成向量的嵌入模型nomic-embed-text 是 Ollama 官方推荐的嵌入模型,中文表现稳,本地跑不联网。回答用 llama3.1,老机器换 8b 也行。
核心代码(30 行)
思路:读取你的 txt/md 文档 → 按段落切块 → 调嵌入接口得到向量 → 存到内存字典。提问时把问题也转成向量,算和每个块的余弦相似度,取前 3 块拼进提示词让模型回答。
import requests, math, os
EMB = "http://localhost:11434/api/embeddings"
GEN = "http://localhost:11434/api/generate"
def emb(text):
return requests.post(EMB, json={"model": "nomic-embed-text", "prompt": text}).json()["embedding"]
def cos(a, b):
return sum(x*y for x, y in zip(a, b)) / (math.sqrt(sum(x*x for x in a)) * math.sqrt(sum(y*y for y in b)))
docs = []
for f in os.listdir("docs"):
if f.endswith(".txt"):
for chunk in open(f"docs/{f}").read().split("\n\n"):
if chunk.strip():
docs.append({"text": chunk, "vec": emb(chunk)})
question = "退款政策里写明几天到账?"
qvec = emb(question)
top = sorted(docs, key=lambda d: cos(qvec, d["vec"]), reverse=True)[:3]
ctx = "\n".join(d["text"] for d in top)
r = requests.post(GEN, json={
"model": "llama3.1",
"prompt": f"根据以下资料回答,不要编造:\n{ctx}\n\n问题:{question}",
"stream": False,
})
print(r.json()["response"])注意 split("\n\n") 是按空行切块;如果你的文档用别的分隔,改这里就行。返回的 ctx 是检索到的原文片段,模型只负责组织语言,不会凭空编数字。
什么时候够用,什么时候不够
- 个人笔记、几十份产品文档、合同查询:这个方案完全够,启动快、零依赖。
- 文档上万份、需要并发检索:再上 Chroma / Qdrant,代码只改检索那一段。
- 想要网页界面:套一个 Streamlit,十分钟出个可点的小工具。