投研智库 RAG Agent(二):环境底座——Docker Compose 一键拉起向量库全家桶

投研智库 RAG Agent(二):环境底座------Docker Compose 一键拉起向量库全家桶

「投研智库 RAG Agent」专栏第 2 篇。上一篇定了架构和路线,这一篇把地基打好:本机一条命令拉起全部中间件,连通性自检全绿,才有资格写第一行业务代码。


一、这一步要达到的目标状态

期望
Python 环境 项目目录下 .venv(uv 管理),uv sync 一键复现依赖
中间件 etcd / Milvus / MinIO / MongoDB / Attu 全部健康
本地模型 bge-m3bge-reranker-large 下载到本地路径
配置 所有连接信息收敛到根目录 .env
验收 连通性自检脚本四项全绿

二、Python 环境:为什么用 uv 而不是 conda

依赖定义在 pyproject.toml,锁定在 uv.lock------换机只需 uv sync 就能逐字节复现依赖 ,这是 conda 环境导出/导入做不到的确定性。对应 Java 里的心智模型:pyproject.tomlpom.xmluv.lock ≈ 依赖树锁定,.venv ≈ 项目私有的运行时。

项目的真实依赖清单(pyproject.toml,Python ≥ 3.11):

ini 复制代码
dependencies = [
    "fastapi>=0.135.1",        # Web 框架,SSE 用 StreamingResponse
    "langgraph>=1.1.2",        # StateGraph 编排
    "langchain>=1.2.12",       # RecursiveCharacterTextSplitter 等
    "langchain-openai>=1.1.11",# 云端 LLM 的 OpenAI 兼容客户端
    "flagembedding>=1.3.5",    # BGE-M3 本地推理(dense + sparse)
    "pymilvus[model]>=2.6.10", # Milvus SDK(含 sparse 支持)
    "magic-pdf>=1.3.12",       # MinerU 解析
    "minio>=7.2.20",           # 对象存储 SDK
    "pymongo>=4.16.0",         # 会话历史、任务状态
    "modelscope>=1.35.0",      # 模型下载
    "torch>=2.10.0",           # 本地模型推理后端
    "loguru>=0.7.3",           # 统一日志
    "python-dotenv>=1.2.2",    # .env 加载
    ...
]

几个依赖的版本约束是实际踩坑后锁的:flagembeddingtransformers 版本敏感,pymilvus 的 sparse API 在 2.6 之前行为不一致------锁在 uv.lock 里,避免"同事机器上装出另一个版本"这种最难查的问题。

bash 复制代码
cd rag_agent_pro
uv sync          # 依赖装进 .venv,锁定版本

提交规则:pyproject.toml / uv.lock 进 git;.venv.envdocker-data/ 一律忽略。

三、Docker Compose:五个服务一键起

docker-compose.yml 定义了主线需要的全部中间件:

服务 容器 镜像版本 端口 作用 内存上限
etcd rag-etcd etcd:v3.5.18 容器内 Milvus 的元数据存储 512m
MinIO rag-minio minio:2024-12-18 9000/9001 对象存储(一套两用,见下) 512m
Milvus rag-milvus milvus:v2.5.4 19530/9091 向量库(standalone 模式) 2g
MongoDB rag-mongo mongo:7 27017 会话历史、任务状态 512m
Attu rag-attu attu:v2.5 8002 Milvus 可视化管理 512m
Neo4j rag-neo4j neo4j:5-community 7474/7687 知识图谱(可选 profile 1g
bash 复制代码
docker compose up -d                    # 默认四件套 + Attu
docker compose --profile graph up -d    # 需要图谱时才启 Neo4j
docker compose ps                       # 全部 Up / healthy 即通过

编排文件里的关键细节

拉起五个容器谁都会,这份 compose 文件真正值得讲的是这几处:

1)依赖顺序用 service_healthy,不是裸 depends_on

yaml 复制代码
milvus:
  depends_on:
    etcd:
      condition: service_healthy    # 等 etcd 健康检查通过才启动
    minio:
      condition: service_healthy

depends_on 只保证"容器启动顺序",不保证"服务真的可用"。Milvus 启动时如果 etcd 还没就绪会直接崩溃退出。每个服务都配了 healthcheck(etcd 用 etcdctl endpoint health,MinIO 用 /minio/health/live,Milvus 用 :9091/healthz,Mongo 用 mongosh ping),Milvus 额外给了 start_period: 90s------它冷启动确实慢,不给宽限期健康检查会误判。

2)每个服务显式 mem_limit

本机开发 Docker 不加限制会把内存吃穿------这是被 Milvus 吃掉 8G 后补的课。此外 Mongo 还加了一层引擎级限制:

bash 复制代码
mongo:
  mem_limit: 512m
  command: ["--wiredTigerCacheSizeGB", "0.25"]   # WiredTiger 缓存也要限,否则容器内 OOM

只限容器不限引擎,Mongo 的存储引擎仍会按"宿主机内存的 50%"计算缓存,然后在容器内被 OOM killer 干掉------两层都要限

3)etcd 的自动压缩参数

yaml 复制代码
etcd:
  environment:
    ETCD_AUTO_COMPACTION_MODE: revision
    ETCD_AUTO_COMPACTION_RETENTION: "1000"
    ETCD_QUOTA_BACKEND_BYTES: "2147483648"   # 2GB 上限

etcd 默认不压缩历史版本,长期跑会把后端存储撑到配额上限,然后 Milvus 的元数据操作集体报错。这三行是 Milvus 官方推荐配置,照抄即可,但要知道它防的是什么。

4)数据卷全部落 ./docker-data/

所有容器数据映射到项目目录下的 docker-data/(已 gitignore)------docker compose down 不丢数据,想彻底重置就删目录,环境状态一目了然。

三个架构层面的决策:

  1. MinIO 一套两用 :Milvus 内部存储用 a-bucket,业务图片用 knowledge-base-files------省一个容器,本机内存有限时很实在;
  2. Neo4j 走可选 profile :知识图谱不是主线(查询图里 KG 节点是占位),默认不启,1G 内存省下来给 Milvus;Neo4j 自身的堆内存也压到了 512m(NEO4J_server_memory_heap_max__size);
  3. Attu 默认启动:它是后面每一篇验收的眼睛------集合建没建、数据进没进、向量维度对不对,浏览器里直接看,比写查询脚本快。

四、本地模型:Embedding / Rerank 为什么不走 API

导入一份几百页的手册要向量化几百个 chunk,查询侧每次要精排几十个候选------这类高频批量调用走 API 又贵又慢,所以 BGE-M3 和 BGE-Reranker 都下载到本地跑。

项目里有现成的下载脚本(app/tool/download_bgem3.py / download_reranker.py),核心就是 modelscope 一行:

css 复制代码
uv run modelscope download --model BAAI/bge-m3 --local_dir <本地路径>
uv run modelscope download --model BAAI/bge-reranker-large --local_dir <本地路径>

两个模型的实际占用(决定了你的硬件预算):

模型 磁盘 加载后内存(CPU/FP32) 用途
bge-m3 ~2.3GB ~3GB dense 1024 维 + sparse 双向量
bge-reranker-large ~2.2GB ~2.5GB query-chunk 成对精排

无 NVIDIA 显卡时 .env 里设 BGE_DEVICE=cpuBGE_FP16=0(CPU 上开 FP16 会直接报错,不是变慢)。首次加载模型要几十秒,属正常现象------工程上的对策是模型单例 :进程内只加载一次,服务启动时预热,而不是每次请求加载(这个封装在 app/lm/embedding_utils.py,第 5 篇细讲)。

生成、改写、图片摘要这类低频高价值调用走云端 OpenAI 兼容接口------这就是上一篇说的"高频本地、低频云端"原则的落地。

五、配置收敛:一个 .env 管所有连接

所有密钥和连接信息集中在根目录 .env(不进 git),由 app/conf/ 下的配置模块统一读取------业务代码不直接碰 os.environ

ini 复制代码
# 大模型
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://.../compatible-mode/v1
LLM_DEFAULT_MODEL=qwen-flash

# 本地模型
BGE_M3_PATH=D:\ai_models...\bge-m3
RERANKER_MODEL_PATH=D:\ai_models...\bge-reranker-large
BGE_DEVICE=cpu
BGE_FP16=0

# 中间件(本机 Docker 全指向 localhost)
MILVUS_URL=http://localhost:19530
MONGO_URL=mongodb://localhost:27017
MINIO_ENDPOINT=localhost:9000      # 注意:不带 http:// 前缀
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin

配置分层对应关系:.env 管"值",app/conf/*.pymilvus_config / minio_config / embedding_config / lm_config 等)管"读取和默认值",docker-compose.yml 里用 ${MINIO_ACCESS_KEY:-minioadmin} 语法与 .env 共享同一份凭证------一处改,处处生效

一个踩过的坑:MinIO 的 endpoint 不能带 http:// 前缀 (SDK 自己拼协议),而 Milvus 的 URL 要带------两个 SDK 约定不一致,配置错了报的却是含糊的连接错误,排查了半天。类似约定不一致的地方,最好的办法就是像上面那样直接把注释写进 .env 模板里。

六、里程碑验收:连通性自检

写业务代码前先跑一段自检脚本,四项全绿才算地基完工:

python 复制代码
from pymilvus import MilvusClient
from pymongo import MongoClient
from minio import Minio

print('Milvus', MilvusClient(uri=MILVUS_URL).list_collections())       # ① 向量库通
print('Mongo', MongoClient(MONGO_URL).admin.command('ping'))           # ② 文档库通
print('MinIO', [b.name for b in minio_client.list_buckets()])          # ③ 对象存储通
# ④ LLM 探测:让模型回一个字,验证 Key 和 base_url

这一步看似多余,实际挡掉了大量"以为是代码 bug、其实是环境问题"的排查成本------四项自检把"环境错"和"代码错"提前切开了。

Attu(http://localhost:8002)此时能连上 Milvus 但集合为空------这是正常的,集合由导入流程首次运行时创建。

七、本篇小结

  • uv 管依赖(版本锁定,逐字节复现),Docker Compose 管中间件(healthcheck + service_healthy 依赖 + 双层内存限制);
  • 编排细节有讲究:etcd 自动压缩防撑爆、Mongo 容器/引擎两层限内存、Milvus 启动给 90s 宽限期;
  • 高频调用(Embedding/Rerank)本地模型 + 进程内单例,低频调用(生成)云端 API;
  • 配置全收敛 .env + conf/ 模块,换机只改一个文件;
  • 验收动作:连通性自检四项全绿。

地基打好了,下一篇进入正题:导入链路的 LangGraph 图设计------先定义 State 契约和图骨架,再逐个实现节点。

上一篇:《项目背景与总体架构》 · 下一篇:《导入链路(上):LangGraph 图设计与前置解析节点》

相关推荐
IT_陈寒16 小时前
Vite静态资源路径这个坑差点让我加班到凌晨
前端·人工智能·后端
神经蛙199616 小时前
🌍 别再硬编码中文了!Python Web 项目国际化(i18n)完全指南
后端·python
二月龙16 小时前
Spring 事务失效的 8 种场景,很多老手依然频繁踩雷
后端
掘金酱16 小时前
「TRAE Work 实战帮」征文启动!你沉淀的经验,值得被看见!
前端·人工智能·后端
长大198816 小时前
MyBatis 常见性能陷阱:N+1 查询、一级缓存踩坑解决方案
后端
用户18615580086016 小时前
MinIO Java 对接试用:从连接、上传到下载的完整示例
后端
爱勇宝16 小时前
DeepSeek V4-Flash 更新:代码与 Agent 能力全面增强
前端·后端·deepseek
极客悟道16 小时前
SDKMAN vs jEnv vs JetTUI,JDK 版本管理到底选哪个
后端
长大198816 小时前
Java8 新特性到底要不要吃透?工作中高频使用的 5 个功能总结
后端