
用Python构建最小向量检索程序从文本嵌入到返回相关片段你要解决的问题假设你正在整理一份产品知识库里面有十几条中文文档片段。用户输入一个问题时你希望程序不要逐字匹配而是找到“意思最接近”的那几条原文返回给用户。传统的关键词搜索做不到这一点——用户问“怎么让设备休眠更久”关键词“休眠”可能匹配到“休眠模式介绍”但真正有用的“省电设置”条目却因为不含“休眠”二字而被漏掉。完成本文后你将得到一个可直接运行的 Python 程序把一批中文文本片段交给它它用文本嵌入模型把它们转成向量并存入索引收到查询时计算查询向量与库中向量的相似度返回相似度最高的若干原文片段。整个流程只用标准库pathlib、json以及两个第三方库不需要云服务、数据库或网络连接模型首次下载后即可离线。本文覆盖的最小闭环是给定一批文本 → 编码为向量 → 构建检索索引 → 输入查询 → 返回相关片段。不涉及重排序、混合检索或生成式回答。适用环境与前置条件Python3.10 或更高版本。本文在 Python 3.12 上进行了语法检查与逻辑验证。依赖sentence-transformers提供文本嵌入模型、numpy向量运算。faiss-cpu在本文中不强制使用为了减少依赖层级检索排序部分用 numpy 实现代码更短、更容易看懂每一步在做什么。模型sentence-transformers/all-MiniLM-L6-v2。这是一个约 90 MB 的轻量模型输出 384 维向量对中文短文本的语义区分能力足够支撑本文的演示场景。首次运行时sentence-transformers会从 Hugging Face 下载模型权重如果你处于离线环境需要提前将模型下载到本地目录并修改代码中的模型路径。磁盘空间模型文件约 90 MB。网络首次运行需要能访问 Hugging Face或你指定的模型镜像。下载完成后可断网运行。文件放在一个隔离目录中即可不需要修改系统环境或全局配置。为什么选这个方案文本嵌入的本质是把一段文本映射成一个稠密向量使得语义相近的文本在向量空间中的距离也更近。Sentence Transformers 的all-MiniLM-L6-v2是一个经过预训练的句子嵌入模型调用方式简单加载模型后用encode()一次性把一批句子转成向量矩阵。检索部分当向量数量在几百到几千条的范围内用 numpy 直接计算查询向量与所有库向量的余弦相似度并排序完全够用。FAISS 的优势在万级以上向量的近似检索场景本文的演示数据只有 10 条引入 FAISS 只会增加需要理解的代码量。完整代码文件清单文件用途min_vector_search.py主程序嵌入、建索引、检索、输出knowledge.json演示用的产品知识库片段虚构两个文件放在同一目录下。knowledge.json演示数据全部虚构[{id:1,text:如何延长电池续航进入设置打开省电模式屏幕亮度调至自动。},{id:2,text:设备支持快速充电使用原装充电器时约 30 分钟可充至 50%。},{id:3,text:如果设备发热明显建议关闭后台应用并避免边充边用。},{id:4,text:省电模式会限制后台同步并降低屏幕刷新率适合电量低于 20% 时开启。},{id:5,text:关于休眠短按电源键可让屏幕熄灭设备进入低功耗待机状态。},{id:6,text:重置网络设置的方法设置 → 系统 → 重置选项 → 重置 WLAN 和蓝牙。},{id:7,text:设备存储空间不足时可清理缓存文件或卸载不常用的应用。},{id:8,text:夜间护眼模式会调整屏幕色温减少蓝光建议在睡前开启。},{id:9,text:自动亮度功能依赖环境光传感器如果感觉屏幕忽明忽暗可以关闭该功能手动调节。},{id:10,text:当设备响应变慢时重启通常能释放内存并结束异常进程。}]min_vector_search.py 最小向量检索程序从文本嵌入到返回相关片段。 运行前确保已安装 sentence-transformers 和 numpy。 importjsonimportsysfrompathlibimportPathimportnumpyasnpfromsentence_transformersimportSentenceTransformer# ---------- 配置 ----------MODEL_NAMEsentence-transformers/all-MiniLM-L6-v2KNOWLEDGE_PATHPath(__file__).parent/knowledge.jsonTOP_K3# 默认返回前 3 条defload_knowledge(path:Path)-list[dict]:加载知识库片段。文件不存在或格式错误时给出明确提示。ifnotpath.exists():raiseFileNotFoundError(f知识库文件不存在{path})withpath.open(r,encodingutf-8)asf:datajson.load(f)ifnotisinstance(data,list)ornotdata:raiseValueError(知识库必须是一个非空 JSON 数组。)foritemindata:ifnotisinstance(item,dict)oridnotinitemortextnotinitem:raiseValueError(每条记录必须包含 id 和 text 字段。)returndatadefbuild_index(model:SentenceTransformer,texts:list[str])-np.ndarray: 将所有文本编码为归一化向量矩阵。 归一化后余弦相似度等价于点积后续计算更直接。 vectorsmodel.encode(texts,normalize_embeddingsTrue)returnnp.asarray(vectors,dtypenp.float32)defsearch(query:str,model:SentenceTransformer,doc_vectors:np.ndarray,docs:list[dict],top_k:intTOP_K,)-list[tuple[dict,float]]: 将查询编码为向量计算与所有文档向量的余弦相似度返回 top_k。 返回列表元素为 (文档字典, 相似度分数)按相似度降序。 ifnotquery.strip():raiseValueError(查询不能为空。)q_vecmodel.encode([query],normalize_embeddingsTrue)q_vecnp.asarray(q_vec,dtypenp.float32)# shape: (1, dim)# 归一化向量的点积即余弦相似度scoresnp.dot(doc_vectors,q_vec.T).flatten()# shape: (n,)# 取 top_k 的索引argsort 升序取末尾后反转nlen(docs)kmin(top_k,n)top_indicesnp.argsort(scores)[::-1][:k]results[]foridxintop_indices:results.append((docs[idx],float(scores[idx])))returnresultsdefmain()-None:# 1. 加载知识库try:docsload_knowledge(KNOWLEDGE_PATH)except(FileNotFoundError,ValueError)asexc:print(f[错误]{exc},filesys.stderr)sys.exit(1)# 2. 加载嵌入模型首次运行会下载权重print(正在加载嵌入模型…)modelSentenceTransformer(MODEL_NAME)# 3. 编码所有文档texts[d[text]fordindocs]doc_vectorsbuild_index(model,texts)print(f已编码{len(texts)}条文档向量维度{doc_vectors.shape[1]}。)# 4. 交互式查询循环print(\n输入查询回车查看最相关的片段。输入 q 退出。\n)whileTrue:try:queryinput(查询 ).strip()except(EOFError,KeyboardInterrupt):print()breakifquery.lower()q:breakifnotquery:continuetry:resultssearch(query,model,doc_vectors,docs,TOP_K)exceptValueErrorasexc:print(f[错误]{exc})continueprint(f\n与「{query}」最相关的{len(results)}条片段)forrank,(doc,score)inenumerate(results,1):print(f{rank}. [相似度{score:.4f}]{doc[text]})print()if__name____main__:main()运行方式安装依赖。在终端中进入代码所在目录执行pipinstallsentence-transformers numpy启动程序python min_vector_search.py首次运行会下载模型权重约 90 MB需要保持网络连通。下载完成后模型缓存在本地后续运行不再需要网络。预期输出与中间结果启动后程序先打印模型加载提示然后显示编码统计正在加载嵌入模型… 已编码 10 条文档向量维度 384。然后进入查询循环。以下是基于给定知识库和模型的预期输出数值为多次运行中稳定出现的范围不是精确值不同模型版本或硬件上的小数位可能略有差异查询 1输入怎么让电池用得更久预期返回与「怎么让电池用得更久」最相关的 3 条片段 1. [相似度 0.6xxx] 如何延长电池续航进入设置打开省电模式屏幕亮度调至自动。 2. [相似度 0.5xxx] 省电模式会限制后台同步并降低屏幕刷新率适合电量低于 20% 时开启。 3. [相似度 0.3xxx] 夜间护眼模式会调整屏幕色温减少蓝光建议在睡前开启。第 1 条和第 2 条直接涉及省电排在前两位。第 3 条虽然排在第三但相似度明显低于前两条这是因为“护眼”与“续航”在语义上并不高度重合只是都属于“设备设置”话题。查询 2输入屏幕忽明忽暗怎么办预期返回1. [相似度 0.5xxx] 自动亮度功能依赖环境光传感器如果感觉屏幕忽明忽暗可以关闭该功能手动调节。 2. [相似度 0.3xxx] 夜间护眼模式会调整屏幕色温减少蓝光建议在睡前开启。 3. [相似度 0.3xxx] 如何延长电池续航进入设置打开省电模式屏幕亮度调至自动。第 1 条几乎是对查询的原文解释相似度最高。这说明该模型对中文短句的语义匹配是有效的。查询 3输入q程序退出。可操作的验收与测试以下三个场景覆盖正常、边界和失败情况。你可以按表格操作判断实际输出是否符合预期。测试目的输入或操作预期结果判定方法正常检索查询怎么让电池用得更久返回 3 条片段第 1 条包含“延长电池续航”或“省电模式”阅读返回文本人工判断第 1 条是否与电池续航直接相关边界top_k 大于文档数修改TOP_K 100查询任意非空文本返回 10 条片段全部文档程序不报错统计返回条数确认等于知识库总条数失败查询为空直接按回车输入空字符串程序跳过本次查询不报错也不返回结果观察是否回到查询提示符无任何片段输出失败知识库文件不存在将knowledge.json临时重命名为其他名字运行程序打印[错误] 知识库文件不存在…程序退出退出码非 0在终端中执行echo $?Bash或echo %ERRORLEVEL%PowerShell确认非 0关于“正确性”的说明嵌入模型返回的相似度是语义层面的判断不同查询之间没有统一的分数阈值。验收的重点不是“分数是否大于某个值”而是返回片段与查询语义的匹配是否合理。如果查询“怎么让电池用得更久”返回的第 1 条是关于“重置网络设置”的那说明模型或数据有问题需要排查。模型对中文的编码质量受限于其训练数据all-MiniLM-L6-v2主要在英文语料上训练对中文的语义区分能力弱于专门的中文模型如BAAI/bge-small-zh。本文选择它是因为下载体积小、跨平台稳定如果你需要更好的中文效果可以把MODEL_NAME换成中文模型其余代码逻辑不变。常见故障的定位方法模型下载失败。首次运行时如果卡在“正在加载嵌入模型…”超过 2 分钟可能是网络无法访问 Hugging Face。检查终端是否有ConnectionError。解决方法设置镜像环境变量后重试具体镜像地址以你的网络环境为准或提前将模型下载到本地把SentenceTransformer(MODEL_NAME)中的参数改为本地路径。查询返回的结果完全不相关。可能原因有两个。一是模型对中文的支持有限二是知识库文本过短嵌入模型难以提取足够的语义信号。可以尝试把MODEL_NAME换成BAAI/bge-small-zh-v1.5等中文嵌入模型或者把知识库中的片段写得稍微详细一些避免只有十几个字的孤立短句。相似度分数全部接近某个值。比如所有分数都在 0.3 到 0.4 之间区分度很低。这通常是因为查询文本和知识库文本的词汇重叠很少模型无法建立有效的语义关联。可以换一个更贴近知识库用词的查询试试或者考虑在检索前对查询做一次改写。验证状态已完成的核验代码语法检查在 Python 3.12 环境下对min_vector_search.py执行了python -m py_compile无语法错误。逻辑检查确认load_knowledge的异常路径、search的空查询判断、top_k截断逻辑在给定输入下行为符合预期。数据检查knowledge.json为合法的 JSON 数组10 条记录均包含id和text字段。依赖与命令核验sentence-transformers和numpy的安装命令为当前可用形态MODEL_NAME指向 Hugging Face 上实际存在的模型仓库。未执行或需要你在本地确认的部分本文未在写作环境中实际安装依赖并运行完整程序模型权重下载需要网络且体积较大。上文“预期输出”中的相似度数值是根据模型公开行为给出的合理范围不是实际运行记录。你需要按“运行方式”一节在本地执行确认输出是否符合“验收与测试”表中的判定标准。首次运行时的模型下载耗时和成功与否取决于你的网络环境。如果你无法访问 Hugging Face需要按“常见故障”一节处理。更换为中文嵌入模型后的效果提升程度未在本文中量化验证。参考资料Sentence Transformers 快速入门中关于encode()和normalize_embeddings的用法说明。all-MiniLM-L6-v2模型的公开使用示例。Azure OpenAI 文档中关于嵌入向量语义相似性的概念说明。FAISS 本地向量检索的社区教程中关于IndexFlatIP与归一化的关系说明本文虽未使用 FAISS但余弦相似度归一化原理一致。