源文件:chapter3/dense-embedding/README.md 向量相似度检索服务 一个用于教学的 HTTP 向量相似度检索服务,基于 BGE-M3 嵌入模型,可在 ANNOY 与 HNSW 两种索引后端之间灵活切换。 命令行工具:稠密检索与 ANN 对比(cli.py,实验 3-4) 除了上面的 HTTP 服务,本项目还提供一个开箱即用、可离线复现的命令行工具 , 把书中实验 3-4 的两个观察点直接跑成可量化的数字,无需先启动服务: 稠密嵌入检索的语义能力——在带标注的小型语料上计算 ; ANN 索引后端对比(实验 3-4 的重点)——复用服务端 里的 ANNOY / HNSW 实现,测量二者相对精确暴力检索的召回率、建索引耗时与查询延迟。
源文件:chapter3/dense-embedding/README.md
一个用于教学的 HTTP 向量相似度检索服务,基于 BGE-M3 嵌入模型,可在 ANNOY 与 HNSW 两种索引后端之间灵活切换。
除了上面的 HTTP 服务,本项目还提供一个开箱即用、可离线复现的命令行工具 cli.py,
把书中实验 3-4 的两个观察点直接跑成可量化的数字,无需先启动服务:
recall@k / precision@k / MRR;indexing.py 里的 ANNOY / HNSW# 1) 单条稠密查询(默认查询 "a cat playing",需要嵌入模型) python cli.py -q "model distillation" -k 3 # 2) 检索质量评测:recall@k / precision@k / MRR python cli.py --eval # 2') 离线复现:用已缓存的小模型(无需下载 2.3GB 的 BGE-M3) python cli.py --embedding-model sentence-transformers/all-MiniLM-L6-v2 --eval # 3) ANN 后端对比(合成向量,完全离线、无需任何模型) python cli.py --compare-ann -k 10 python cli.py --compare-ann --backend hnsw --hnsw-ef-search 200 -k 10 # 调 ef_search 看召回随之上升 # 自定义语料 / 标注 / 输出 python cli.py --corpus my.json --labels my_labels.json --eval -o result.json
python cli.py --help 提供完整的中文参数说明(--corpus / --query / --embedding-model / --top-k / --output,以及 ANN 对比的各项索引超参)。
| 参数 | 说明 |
|---|---|
-q, --query |
查询字符串(默认 a cat playing) |
-c, --corpus |
语料文件(.json 数组 或 .jsonl 每行一篇);缺省用内置示例语料 |
-k, --top-k |
返回前 k 条结果(默认 5) |
-o, --output |
把结果 / 评测指标写入 JSON 文件 |
--embedding-model |
嵌入模型名(默认 BAAI/bge-m3;离线可用 sentence-transformers/all-MiniLM-L6-v2) |
--pooling |
池化方式 auto(bge* 用 cls,其余 mean)/ mean / cls |
--eval |
在标注集上评测 recall@k / precision@k / MRR |
--compare-ann |
对比 ANNOY / HNSW(合成向量,无需模型) |
--ann-base / --ann-dim / --ann-queries |
合成底库规模 / 维度 / 查询数(默认 3000 / 128 / 100) |
--annoy-n-trees / --hnsw-M / --hnsw-ef-search |
两类 ANN 的关键索引超参 |
稠密检索质量(内置 12 篇语料,all-MiniLM-L6-v2,离线):
宏平均 recall@5=1.000 precision@5=0.320 MRR=1.000
其中查询 a cat playing 的相关文档只用 kitten / feline 表达、不含字面 "cat",
稠密检索仍把它们排到第 1、2 名——这正是稠密相对稀疏 BM25(实验 3-5 会漏召回)的语义优势。
ANN 后端对比(3000 条 128 维随机单位向量,100 条查询,top-10):HNSW 的召回率随ef_search 单调上升,体现"精度 / 速度"取舍:
| 配置 | recall@10 | 平均查询延迟 |
|---|---|---|
HNSW ef_search=20 |
0.562 | 0.05 ms |
HNSW ef_search=200 |
0.991 | 0.25 ms |
环境提示:本工具对每个后端会先做"自查询自身向量"的健康检查。在部分 macOS/arm64 环境下,
annoy==1.17.3的预编译轮子存在缺陷(连查询库中已有向量都只返回它自己),此时工具会打印[警告] ...疑似当前环境下损坏并把该后端的数字标记为不可信。HNSW 不受影响。若要复现完整的
ANNOY vs HNSW 对比,请在 annoy 正常工作的环境(如 Linux x86_64)中运行。
BGE-M3 模型:业界领先的多语言嵌入模型,支持:
双索引后端:
教学型日志:详尽的调试日志,展示:
RESTful API:简洁的 HTTP 接口,用于:
内存存储:为保持简洁采用纯内存运行(不做持久化)
┌──────────────────┐ │ HTTP Client │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ FastAPI Server │ └────────┬─────────┘ │ ▼ ┌────┴────┐ │ │ ▼ ▼ ┌──────────┐ ┌──────────────┐ │ Document │ │ Embedding │ │ Store │ │ Service │ └──────────┘ │ (BGE-M3) │ └──────┬─────────┘ │ ▼ ┌─────────┴──────────┐ │ │ ▼ ▼ ┌──────────┐ ┌──────────┐ │ ANNOY │ │ HNSW │ │ Index │ │ Index │ └──────────┘ └──────────┘
cd projects/week3/dense-embedding pip install -r requirements.txt
python main.py
python main.py --index-type annoy
python main.py --index-type hnsw --host 0.0.0.0 --port 4242 --debug --show-embeddings
--index-type:选择索引后端(annoy 或 hnsw,默认:hnsw)--host:服务监听地址(默认:0.0.0.0)--port:服务端口(默认:4240)--debug:开启调试模式,输出详细日志--show-embeddings:在日志中显示嵌入向量(教学用途)服务启动后,可访问:
POST /index
索引一篇新文档或更新已有文档。
请求:
{ "text": "Machine learning is a subset of artificial intelligence.", "doc_id": "doc_001", // 可选,未提供时自动生成 "metadata": { // 可选元数据 "category": "AI", "author": "John Doe" } }
响应:
{ "success": true, "doc_id": "doc_001", "message": "Document indexed successfully using hnsw", "index_size": 1 }
POST /search
检索相似文档。
请求:
{ "query": "What is deep learning?", "top_k": 5, "return_documents": true }
响应:
{ "success": true, "query": "What is deep learning?", "results": [ { "doc_id": "doc_001", "score": 0.8543, "text": "Machine learning is a subset...", "metadata": {"category": "AI"}, "rank": 1 } ], "total_results": 5, "search_time_ms": 12.5 }
DELETE /index
从索引中删除一篇文档。
请求:
{ "doc_id": "doc_001" }
响应:
{ "success": true, "message": "Document doc_001 deleted successfully", "index_size": 0 }
GET /stats
获取服务统计信息。
响应:
{ "index_type": "hnsw", "index_size": 100, "document_count": 100, "embedding_dimension": 1024, "model_name": "BAAI/bge-m3" }
GET /documents?limit=10
列出存储中的文档。
演示客户端用示例文档展示全部功能:
python test_client.py
用合成数据测试索引和检索性能:
python test_client.py --performance
索引一篇文档:
curl -X POST http://localhost:4240/index \ -H "Content-Type: application/json" \ -d '{"text": "This is a test document about machine learning."}'
检索相似文档:
curl -X POST http://localhost:4240/search \ -H "Content-Type: application/json" \ -d '{"query": "artificial intelligence", "top_k": 5}'
优点:
缺点:
最适用于:
优点:
缺点:
最适用于:
可通过以 VEC_ 为前缀的环境变量来配置服务:
export VEC_INDEX_TYPE=hnsw export VEC_MODEL_NAME=BAAI/bge-m3 export VEC_USE_FP16=true export VEC_MAX_SEQ_LENGTH=512 export VEC_MAX_DOCUMENTS=100000 export VEC_LOG_LEVEL=DEBUG # ANNOY 专用 export VEC_ANNOY_N_TREES=50 export VEC_ANNOY_METRIC=angular # HNSW 专用 export VEC_HNSW_EF_CONSTRUCTION=200 export VEC_HNSW_M=32 export VEC_HNSW_EF_SEARCH=100 export VEC_HNSW_SPACE=cosine
本服务为教学目的内置了详尽的日志:
开启完整的教学日志:
python main.py --debug --show-embeddings
针对 ANNOY:
n_trees 可提升精度(但构建更慢)angular 度量针对 HNSW:
M 可提升召回(但更耗内存)ef_construction 可提升索引质量(但构建更慢)ef_search 在速度与精度间权衡通用:
max_seq_length内存不足:
索引过慢:
检索质量差:
本项目为学习用途的教学项目。