想让一小批中文说明文档在本地可搜索,可以先用 scikit-learn 的 TF-IDF 建索引,再按余弦相似度排序。它适合建立可解释的词面检索基线:读者能看见命中的原文,也能检查查询完全没有匹配片段时发生什么。
下面四条文档都是人为编写的演示数据。脚本已在 Windows、Python 3.11.15、scikit-learn 1.7.2 环境运行;验证范围是这四条样例的排序和无匹配处理,没有测试读者的真实文档或检索准确率。

先准备环境和一份有编号的文档表
在一个新目录里创建虚拟环境。Windows 下可直接调用环境中的解释器,避免把包安装到另一个 Python:
python -m venv .venv
.venv\Scripts\python.exe -m pip install "scikit-learn==1.7.2"
安装包需要能够访问包仓库;安装完成后,本文的计算在本地进行,不调用聊天模型接口,也不下载模型权重。每条记录保留稳定的 id 和完整 text,返回结果时才能找到原文。正式替换数据时只使用有权处理的材料。
把文档建成索引,再转换查询
默认的词分析器不会自动给中文断词。本例选择字符分析器,把连续2至4个字符作为特征;例如“模型删除”会产生“模型”“型删”“删除”等片段。因此不需要先安装中文分词工具,但匹配仍依赖文字重合。
把以下代码保存为 search_demo.py。文档调用一次 fit_transform 建立词表和权重;每次查询只调用同一个 vectorizer 的 transform,保证查询与文档使用同一组特征。不能对查询重新 fit,否则两套坐标无法正确比较。
from pathlib import Path
import csv
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
rows = [
{"id": "install", "text": "Ollama 本地安装:下载模型、查看列表,运行前检查内存。"},
{"id": "delete", "text": "Ollama 模型删除:先用 ollama list 核对名称,再用 ollama rm 删除。"},
{"id": "rag", "text": "RAG 知识库检索:导入文档、切分片段,并核对引用来源。"},
{"id": "image", "text": "图像分类数据准备:统一图片尺寸并核对类别标签。"},
]
vectorizer = TfidfVectorizer(analyzer="char", ngram_range=(2, 4))
matrix = vectorizer.fit_transform([row["text"] for row in rows])
def search(query, top_k=3):
q = vectorizer.transform([query])
if q.nnz == 0:
return [] # 查询在当前词表里没有任何片段
scores = cosine_similarity(q, matrix).ravel()
order = sorted(range(len(rows)), key=lambda i: (-scores[i], rows[i]["id"]))
return [dict(rows[i], score=float(scores[i])) for i in order
if scores[i] > 0][:top_k]
hits = search("Ollama 模型删除")
for hit in hits:
print(hit["id"], f'{hit["score"]:.4f}', hit["text"])
print("无匹配查询:", search("陶瓷釉烧制"))
assert hits[0]["id"] == "delete"
assert search("陶瓷釉烧制") == []
with Path("search_results.csv").open("w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=["id", "text", "score"])
writer.writeheader()
writer.writerows(hits)
运行:
.venv\Scripts\python.exe search_demo.py
怎样判断排序和零命中处理正确
在上述固定样例和环境中,终端输出如下。浮点分数可能随依赖版本略有差异,优先核对记录编号、排序和空结果行为:
delete 0.6436 Ollama 模型删除:先用 ollama list 核对名称,再用 ollama rm 删除。
install 0.2461 Ollama 本地安装:下载模型、查看列表,运行前检查内存。
无匹配查询: []
第一条应为 delete,因为查询与删除说明有较多共同文字。install 也会得到正分,因为它同样出现了 Ollama 和模型。search_results.csv 保存编号、原文和分数,可直接查看;编码使用 UTF-8 BOM,方便常见表格软件读取中文。
“陶瓷釉烧制”在这四条文档的词表中没有匹配片段,q.nnz 为0,因此返回空列表。代码还过滤分数为0的文档,避免把任意文档当作答案。断言失败或没有生成 CSV 时,停止使用这份索引并检查代码与输入。
换成自己的文档时,先做一组有预期的问题
- 先打印文档数量和编号,拒绝空文本,并确认同一编号没有对应多条不同内容。
- 写出几条能确定应命中哪篇文档的查询,再加入库中不存在的主题。不要只用文档标题原样搜索。
- 检查前几条原文是否真的回答问题,而不是仅含相同的产品名。把错匹配的查询和文档一起保存。
- 新增或修改文档后重新拟合整个索引,并重新跑同一组问题。词表和 IDF 权重会随语料变化,旧分数不能直接当成固定阈值。
分数衡量向量夹角上的相似程度,不是回答正确的概率。短语改写、同义表达、否定条件和新型号容易产生漏检或误检;需要更强的语义能力时,应另做可核对的模型评估。空词表报错通常意味着输入为空或长度不足以产生特征,先核对原文,再考虑调整 ngram_range。
为什么结果只返回两条,而 top_k 是3?
top_k 是上限。没有正相似度的文档不会补位;文档库没有可用依据时,应保留空结果,而不是为了凑数量显示无关文档。
来源与接口依据
官方资料核对日期:2026年10月1日。本文用到的接口见:TfidfVectorizer 参数;cosine_similarity 定义。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30791.html