目标:在一台 Windows 11 机器上,跑通当前最火的开源 RAG 引擎 RAGFlow(66k+ star,GitHub Octoverse 2025 增长榜),并搭好 RAGAS 评估闭环------部署 → 使用 → 度量,走完企业落地 RAG 的完整路径。
环境说明:Windows 11 / 32G 内存 / WSL2 Ubuntu 24.04 / Docker Engine 29.x / Python 3.12。全程零外网代理,国内网络环境。
一、为什么选 RAGFlow,以及整体架构
选型对比一句话:RAGFlow (66k star,深度文档理解,企业级 RAG 引擎)vs FastGPT (可视化工作流平台)vs Kotaemon(轻量 RAG UI)。学习企业落地,选 RAGFlow。
RAGFlow v1.0.0-rc2 的架构本身就是一课------Go + Python 混合:性能敏感的服务编排、检索用 Go 重写,AI 密集的文档解析(deepdoc)、切片策略留在 Python。
Windows 11(浏览器访问 http://localhost)
└── WSL2 Ubuntu 24.04(为什么不是 Docker Desktop?开源免费 + 企业生产环境同款)
├── Docker Engine 29.9.2
│ └── RAGFlow 全家:RAGFlow主服务 / ES 8.11.3 / MySQL 8.0.40
│ / Kvrocks / MinIO / NATS
├── embed-server(自建 ONNX 嵌入服务,后文详述)
└── ragas-env(RAGAS 评估环境)
存储分工是 RAG 落地的第一设计决策:MySQL 存元数据、ES 做向量+全文混合检索、MinIO 存原始文件、Kvrocks 做缓存和任务队列------单一数据库方案在企业数据量下活不过 POC。
二、部署:国内网络的三重关卡
2.1 Docker Engine 安装(WSL2 内)
不装 Docker Desktop(对大企业是收费商业软件),直接 WSL 里装开源 Docker Engine:
bash
# apt 换阿里云源 → docker-ce 官方仓库(阿里云镜像) → 安装
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
2.2 关卡一:GitHub 拉不动
git 全局代理指向本机但代理没开,直连也不通。解法:ghfast.top 前置镜像:
bash
git -c http.proxy= -c https.proxy= clone --depth=1 \
https://ghfast.top/https://github.com/infiniflow/ragflow.git
2.3 关卡二:docker.io 镜像全军覆没(本文最硬的坑)
实测结论(无代理环境):
| 路径 | 结果 |
|---|---|
auth.docker.io(docker hub 认证服务) |
被墙,直接拉取死路 |
| 各公共加速站(1ms.run / dockerpull 等) | 元数据通,blob 数据层挂死或损坏 |
| DaoCloud mirror | 服务正常,但 blob 存储在 Cloudflare R2,WSL 侧到 R2 挂死 |
| 华为云 SWR(RAGFlow 官方国内镜像) | ✅ 唯一 WSL 直连可用,速度正常 |
破局组合拳:
bash
# RAGFlow 主镜像 → 华为云(官方提供,README 有写)
RAGFLOW_IMAGE=swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v1.0.0-rc2
# 公共镜像(mysql/es/kvrocks...)→ Windows 侧 crane 中转:
# 关键发现:同一域名 WSL 挂死、Windows 正常(WSL2 NAT 的锅)
./crane.exe pull --format=tarball --platform linux/amd64 \
docker.m.daocloud.io/library/mysql:8.0.40 mysql.tar
# 导入 WSL:
docker load -i mysql.tar && docker tag docker.m.daocloud.io/library/mysql:8.0.40 mysql:8.0.40
2.4 关卡三:WSL2 NAT 的 MTU 黑洞
部分 HTTPS 站点(含 registry)TLS 握手卡死,curl 挂起无响应。根因:WSL2 NAT 模式默认 MTU 1500 导致 TLS 大包丢失:
bash
ip link set dev eth0 mtu 1400
# 用 systemd oneshot 服务持久化,否则 WSL 重启失效
2.5 两个 WSL 运维大坑(必踩,先记下来)
- WSL 空闲自动关机 :无活跃终端会话约 1 分钟后整个 VM 被回收,容器全停。解法:常驻保活进程
wsl -d Ubuntu -- sleep infinity(挂后台),开机自启可注册 HKCU Run 注册表项。 - 启动竞态 :RAGFlow 比 MySQL 起得快 → 连接失败 crash →
restart: unless-stopped自动重拉几轮后收敛,属正常现象,等 1-2 分钟即可。
三、跑通全链路:三个隐蔽的坑
3.1 v1.0 镜像是 slim 形态,没有内置嵌入模型
镜像仅 1.18GB(旧版 full 是 9GB 含模型)。不配嵌入模型,文档解析会永远卡在 progress 0.8,日志特征:
tokenizer: embedding requested but encoder resolution returned nil
官方方案是启用 tei-cpu profile(本地 TEI 服务),但那要再拉 3.9GB 镜像......家里网络拉不动,于是自建轻量嵌入服务(91MB 模型替代 3.9GB 镜像):
ModelScope 下载 Xenova/bge-small-zh-v1.5 的 ONNX 版(91MB)
→ onnxruntime + tokenizers 手写推理(CLS pooling + L2 归一化)
→ FastAPI 暴露 OpenAI 兼容 /v1/embeddings
→ systemd 守护
3.2 SSRF 防护:openai 驱动禁止访问内网地址
把自建嵌入服务注册为 openai provider(base_url 指向 host.docker.internal:8001),调用报错:
URL resolves to a non-public address (172.17.0.1), which is not allowed
RAGFlow v1.0 的 SSRF 防护分两档:openai 等云厂商驱动强制公网校验;xinference/ollama/vllm 等自托管驱动允许内网地址 (读源码 internal/common/http.go 注释可证)。企业内网部署自托管模型的正确姿势:注册成 xinference 类型:
bash
# API 三层注册:provider → instance → model
PUT /api/v1/providers {"provider_name": "xinference"}
POST /api/v1/providers/xinference/instances {
"instance_name": "local-bge", "api_key": "no-key",
"base_url": "http://host.docker.internal:8001/v1",
"model_info": [{"model_name": "bge-small-zh-v1.5",
"model_type": ["embedding"], "max_tokens": 512}]
}
3.3 建库参数(v1.0 API 变化)
POST /api/v1/datasets
{"name": "my-kb", "parse_type": 1, "parser_id": "naive",
"embedding_model": "<32位model_id>"} # 从 GET /api/v1/models 拿
之后 POST /datasets/{id}/documents 上传(注意:requests session 别预设 Content-Type,会毁掉 multipart boundary)、POST /datasets/{id}/chunks 触发解析、轮询 progress==1 即完成。
一个新手必踩的非技术坑 :聊天助手创建后要在"助手配置 → 知识库"里手动关联知识库,否则 LLM 拿不到任何检索内容,只会一本正经地回答"知识库中未找到"。
四、RAGAS 评估闭环(本文核心)
4.1 为什么必须有评估
RAG 输出是自然语言,"对不对"无法 assert。改了参数(切片大小/top_k/提示词/嵌入模型),回答变了一段话,是变好还是变坏?没有度量就没有优化。RAGAS 把质量拆成四个可测维度:
| 指标 | 测什么 | 原理 |
|---|---|---|
| faithfulness | 防幻觉(企业最看重) | 把回答拆成事实句,逐句问裁判 LLM"检索内容里有出处吗" |
| context_recall | 检索找全了吗 | 标准答案拆事实点,逐点问"检索内容里找得到吗" |
| context_precision | 检索排得准吗 | 逐条问"这条对答题有用吗",相关内容越靠前分越高 |
| answer_relevancy | 回答切题吗 | 反向生成法:裁判由回答反推问题 → 算与原问题的嵌入相似度 |
三个判断题交给裁判 LLM ,一个计算题交给嵌入模型------所以搭评估需要两种角色(我用智谱 glm-4.5-air 当裁判 + 本地 bge 做语义计算)。
4.2 数据从哪来:一次 API 拿齐三样
RAGFlow 问答接口把中间产物全吐回来,这是采集的关键:
POST /api/v1/chat/completions {"chat_id": "...", "question": "...", "stream": false}
响应 data: {
answer: "根据公司行政制度,年假15天...", ← LLM 输出
reference: { chunks: [{content: "假期制度..."}] } ← 检索输出(实际喂给LLM的)
}
四元组来源:问题 + 标准答案人工写(评估集),检索内容 + 回答从接口拿(自动)。标准答案是评估的锚点,必须人工准备------这就是企业评估集建设的成本。
采集口径的设计权衡:从问答接口的引用取(而不是单独调检索接口),测的是用户实际体验到的完整链路 (含助手的阈值过滤);若要单独诊断检索层,才改调 /retrieval。
4.3 核心代码(可直接抄)
python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from ragas import evaluate, RunConfig
from ragas.dataset_schema import EvaluationDataset, SingleTurnSample
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
judge = LangchainLLMWrapper(ChatOpenAI(
model="glm-4.5-air",
base_url="https://open.bigmodel.cn/api/paas/v4",
api_key="你的KEY", temperature=0))
emb = LangchainEmbeddingsWrapper(OpenAIEmbeddings(
model="bge-small-zh-v1.5", base_url="http://127.0.0.1:8001/v1",
api_key="no-key", check_embedding_ctx_length=False))
ds = EvaluationDataset([
SingleTurnSample(user_input=q, retrieved_contexts=ctxs,
response=ans, reference=gt)
for q, gt, ans, ctxs in samples # 前一步从 RAGFlow API 采集
])
result = evaluate(ds, metrics=[faithfulness, answer_relevancy,
context_precision, context_recall],
llm=judge, embeddings=emb,
run_config=RunConfig(timeout=180, max_workers=2))
4.4 评估的坑
- 依赖地狱 :最新 ragas 0.4 与新版 langchain-community 模块路径对不上(
chat_models.vertexai消失)→ 降到稳定组合ragas==0.2.15 + langchain==0.2.16 + langchain-openai==0.1.7,并单独 venv 隔离(numpy 1.x/2.x 会和其它库打架) - 裁判 401 :从数据库取 API key 时别用截断显示(
LEFT(api_key,45)把 key 截尾了),症状是全部评估任务秒挂 AuthenticationError - 样本量:10 题只能看趋势,企业评估集要 100+ 题覆盖简单/困难/边界;裁判评自己会偏宽,严肃场景换更强的裁判模型
4.5 评估结果与解读
| 指标 | 得分 | 解读 |
|---|---|---|
| faithfulness | 0.921 | 回答严格基于文档,扣分来自 LLM 的总结性表述(无字面出处) |
| answer_relevancy | 0.768 | 切题但啰嗦------LLM 爱加"根据公司制度..."和顺带信息,这就是优化靶点 |
| context_precision | 1.000 | 库小文档干净,检索排序完美 |
| context_recall | 1.000 | 标准答案所需信息全部被检索到 |
分数本身没意义,比较才有意义 。标准迭代闭环:基线评估 → 每次只改一个参数(提示词/嵌入模型/切片/top_k)→ 重跑评估 → 分数说话。注意 RAGAS 只测量不调参------它是尺子不是医生,看分数做决策并动手改的永远是人。
五、总结:企业落地清单
- 存储分工是第一设计决策:元数据/向量检索/文件/队列各司其职
- 解析质量决定 RAG 上限:RAGFlow 把资源大头花在 deepdoc,企业 POC 失败多死于"PDF 解析是垃圾"
- 切片是策略问题:不同文档类型(合同/论文/手册)需要不同切片模板
- 混合检索 + 重排是质量基线:纯向量检索会被 BM25 打爆(术语/编号场景),反之亦然
- 引用溯源是信任来源:没有溯源企业用户不敢用
- 自托管模型走 xinference/ollama 驱动,绕开 SSRF 公网校验
- 评估闭环不能少:没有 RAGAS 这类度量,调参全靠感觉
- LLM 接入抽象成渠道层:随时换模型/多模型并存
一点诚实提醒:本文的评估集只有 10 题(看趋势够,下结论不够);bge-small 是 512 维小模型(本文为绕开网络限制的选择),生产建议 bge-m3 或云端 embedding;裁判与被评 LLM 同厂(glm 评 glm)会偏宽。
完整脚本(部署/镜像导入/嵌入服务/冒烟测试/RAGAS 评估)都在
setup/目录沉淀,改改就能搬进公司项目。
环境:Windows 11 + WSL2 Ubuntu 24.04 + Docker Engine 29.9.2 + RAGFlow v1.0.0-rc2 + RAGAS 0.2.15 + 智谱 GLM-4.5-air。如果对你有帮助,点赞收藏评论三连~ 部署或评估环节卡住的,评论区交流。