这篇文章的前提
上一篇建好了统一测试集:89 道题,50 道单跳事实查询、20 道多跳推理、19 道边界拒答,文档来源是 LightRAG 和 graphrag 的官方技术文档。
这篇的任务是让两个框架在同一套题上跑,然后把数字摆出来。
但在说数字之前,必须先说部署过程——因为部署本身就是选型的一部分。
两个框架的部署差异
LightRAG:pip install 即用
pipinstalllightrag-hku没有 Docker,没有数据库服务,知识图谱和向量索引存在本地文件里:
rag_storage/ ├── graph_chunk_entity_relation.graphml # 知识图谱 ├── vdb_chunks.json # 文档向量 ├── vdb_entities.json # 实体向量 └── vdb_relationships.json # 关系向量初始化代码:
fromlightragimportLightRAG,QueryParamfromlightrag.utilsimportEmbeddingFunc rag=LightRAG(working_dir="./rag_storage",llm_model_func=llm_func,embedding_func=EmbeddingFunc(embedding_dim=1024,max_token_size=8192,func=embed_func,),)awaitrag.initialize_storages()# v1.5.x 新增,必须调用awaitrag.ainsert(document_text)result=awaitrag.aquery(question,param=QueryParam(mode="mix"))LightRAG 1.5.x 有一个新要求:必须先调initialize_storages(),否则会报PipelineNotInitializedError。
QAnything:5 个 Docker 服务
QAnything v2 需要整套基础设施:
services:elasticsearch# 关键词检索(BM25)etcd# Milvus 的元数据存储minio# Milvus 的对象存储milvus-standalone# 向量数据库mysql# 文档和知识库元数据qanything_local# 主服务(embedding + rerank + API)启动命令:
cdQAnythingmkdir-pvolumes/es/data&&chmod777-Rvolumes/es/datadockercompose-fdocker-compose-linux.yaml up-d等日志出现“qanything后端服务已就绪!”后,访问http://localhost:8777/qanything/。
踩坑记录
部署过程踩了三个坑,每个都值得记录。
坑 1:QAnything 容器默认不用 GPU
机器有 RTX 3060,但 QAnything 容器启动日志始终显示:
embedding和rerank服务将在CPU上运行原因:docker-compose-linux.yaml里的qanything_local服务没有配置 GPU 资源,而且scripts/entrypoint.sh里那行日志是硬编码的字符串,不是实际判断结果,即使挂了 GPU 也照样打印。
修复一:给 compose 文件加 GPU 挂载:
# docker-compose-linux.yamlqanything_local:deploy:resources:reservations:devices:-driver:nvidiadevice_ids:['0']capabilities:[gpu]修复二:同时还需要安装 NVIDIA Container Toolkit(Docker 默认看不到宿主机 GPU,需要这个桥):
# Ubuntudistribution=$(./etc/os-release;echo$ID$VERSION_ID)curl-s-Lhttps://nvidia.github.io/nvidia-docker/gpgkey|sudoapt-keyadd-curl-s-Lhttps://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list|\sudotee/etc/apt/sources.list.d/nvidia-docker.listsudoapt-getupdate&&sudoapt-getinstall-ynvidia-container-toolkitsudonvidia-ctk runtime configure--runtime=dockersudosystemctl restartdocker修复三:entrypoint.sh启动 embedding/rerank 时没传--use_gpu参数:
# scripts/entrypoint.sh,修改前nohuppython3-uqanything_kernel/dependent_server/embedding_server/embedding_server.py>...# 修改后nohuppython3-uqanything_kernel/dependent_server/embedding_server/embedding_server.py--use_gpu>...nohuppython3-uqanything_kernel/dependent_server/rerank_server/rerank_server.py--use_gpu>...修复后,GPU 显存占用从 1.4GB 跳到 8.6GB,embedding 速度从每秒不到 1 个文档提升到约每 15 秒处理 1-2 个。
坑 2:Milvus 因内存压力崩溃
CPU 模式下 QAnything 容器吃了 27GB 内存,导致 Milvus standalone 的 etcd lease 超时崩溃退出:
"etcdserver: requested lease not found" "connection lost detected, shuting down"结果:所有文件卡在gray(等待向量化)状态,永远不会完成。
修复:清理 Milvus 和 etcd 的持久化数据目录后重启(etcd 里有损坏的 session 记录,不清会反复崩溃):
dockercompose-fdocker-compose-linux.yaml downrm-rfvolumes/milvus volumes/etcd volumes/mysqlmkdir-pvolumes/milvus volumes/etcd volumes/mysqldockercompose-fdocker-compose-linux.yaml up-d坑 3:user_id 拼接导致 Web 端看不到数据
QAnything 服务端对每个 API 请求的user_id会做拼接:
# handler.pyuser_info=safe_get(req,'user_info',"1234")# 默认值 "1234"user_id=user_id+'__'+user_info# 最终存储的 user_id如果脚本传user_id=zzp__1234,实际存储的是zzp__1234__1234,而 Web 端的用户是zzp__1234,两者不同,Web 看不到 API 创建的知识库。
修复:脚本传user_id=zzp,服务端拼接后变成zzp__1234,和 Web 端一致。
评测配置
两个框架使用完全相同的 LLM 和测试集:
| 配置项 | LightRAG | QAnything |
|---|---|---|
| LLM | GLM-4-flash | GLM-4-flash |
| Embedding | BGE-large-en-v1.5(SiliconFlow) | QAnything 内置(BCE embedding,GPU 推理) |
| 测试集 | 89 道题(50 单跳 + 20 多跳 + 19 边界) | 同上 |
| 查询模式 | mix(知识图谱 + 向量融合) | 混合检索(BM25 + 向量 + Rerank) |
| 文档数量 | 31 个 Markdown 文档 | 31 个 Markdown 文档 |
评测结果
核心指标对比
| 指标 | LightRAG 1.5.6 | QAnything v2 |
|---|---|---|
| 边界拒答率 | 10.5%(2/19) | 26.3%(5/19) |
| 平均延迟 | 14,674 ms | 40,519 ms |
| P90 延迟 | 19,430 ms | 52,233 ms |
| 单跳答案匹配(Jaccard) | 0.082 | 0.111 |
| 多跳答案匹配(Jaccard) | 0.178 | 0.162 |
注:答案匹配率用 Jaccard 关键词重叠计算,不是 LLM judge。两个框架的答案通常比 ground_truth 更长(加了解释),Jaccard 值偏低是正常的,用于横向对比有效,绝对值没有参考意义。
答案质量
看同一道题的真实输出:
单跳题:What is the condition under which query/document asymmetric embedding is enabled?
Ground Truth: enabled only when EMBEDDING_ASYMMETRIC=true is explicitly set LightRAG: Query/document asymmetric embedding in LightRAG is enabled only when the `EMBEDDING_ASYMMETRIC` setting is explicitly set to true... [直接答出条件,简洁] QAnything: ## Inferred Answer Section According to the reference information, query/document asymmetric embedding in LightRAG is enabled only when... [答案正确,但格式冗余,带了 Markdown 标题]两个都答对了,但 LightRAG 输出更干净,QAnything 的系统 prompt 会让回答带上## Inferred Answer Section这类结构化标题。
边界题:How does the RAG system handle data privacy for users in the EU under GDPR?
LightRAG: The Retrieval-Augmented Generation (RAG) system, as implemented in LightRAG, handles data privacy for users in the EU under GDPR... [没有拒答,用自身知识编造了一个听起来合理的答案] QAnything: 抱歉,检索到的参考信息并未提供任何相关的信息,因此无法回答。 [正确拒答]边界拒答是 QAnything 明显强的一个维度。QAnything 的系统 prompt 里有明确的"参考信息无关时必须拒答"规则,LightRAG 的 mix 模式会优先召回知识图谱里的相关实体,即使文档里没有答案也会尝试推理,容易产生幻觉。
延迟分析
LightRAG 的延迟分布:P50=13.9s,P90=19.4s,min=8.7s,max=30.8s
QAnything 的延迟分布:P50=39.2s,P90=52.2s,min=19.6s,max=62.9s
QAnything 的延迟高有两个原因:
- Rerank 步骤:每次查询都要对召回结果做交叉编码重排,这是额外的模型推理
- LLM 调用更稳定:QAnything 每次都能召回到真实文档(source_count=100%),LLM 要处理的上下文更长
LightRAG 的 mix 模式会构建一次知识图谱查询 + 一次向量查询,然后融合结果,LLM 调用通常比 QAnything 更快,但如果图谱遍历范围大也会慢。
选哪个?
基于这次评测,给一个简单的决策参考:
优先选 LightRAG,如果:
- 需要快速验证 RAG 方案,不想花时间配置基础设施
- 团队没有运维 Milvus/ES/MySQL 的能力
- 文档之间有复杂的关联关系,需要图谱多跳推理
- 对延迟敏感(LightRAG P90 比 QAnything 快约 2.7 倍)
优先选 QAnything,如果:
- 需要更强的拒答能力(边界拒答率高出 2.5 倍)
- 有中文文档,需要中文优化的 embedding(BCE embedding 对中文效果更好)
- 需要 Web 界面让非技术人员上传文档
- 生产环境,需要 ES 全文检索 + 向量检索的混合能力
这次评测没测到的:
- 大规模文档(1 万+ 文档)下的性能
- 中文文档的检索质量(本次测试集全英文)
- 知识库更新的速度和稳定性
- QAnything 的 PDF/图表解析能力(本次只用了 Markdown)
这些会在后续文章里补充。
评测代码
完整代码在llm-in-action/kb-02-lightrag-eval/和llm-in-action/kb-02-qanything-eval/。
LightRAG 评测核心流程:
# 建库rag=LightRAG(working_dir=STORAGE_DIR,llm_model_func=llm_func,embedding_func=EmbeddingFunc(embedding_dim=1024,func=embed_func))awaitrag.initialize_storages()awaitrag.ainsert(doc_content)# 查询answer=awaitrag.aquery(question,param=QueryParam(mode="mix"))QAnything 评测核心流程:
# 建库kb_id=api_post("new_knowledge_base",{"user_id":USER_ID,"kb_name":KB_NAME})["data"]["kb_id"]api_post("upload_files",data={"user_id":USER_ID,"kb_id":kb_id},files={"files":fp})# 等待向量化完成(轮询 status=green)whileany(s!="green"forsinstatus_countifs!="green"):time.sleep(15)# 查询result=api_post("local_doc_chat",{"user_id":USER_ID,"kb_ids":[kb_id],"question":question,"model":LLM_MODEL,"api_base":LLM_BASE_URL,"api_key":LLM_API_KEY,"streaming":False})下一篇:GraphRAG vs HippoRAG——图增强 RAG 的多跳推理测试。同样 89 道题,重点看多跳推理的提升幅度,以及知识图谱构建的时间和成本。
欢迎访问 PrimeSkills —— 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页