自托管 AI 知识库与联网搜索全套方案:SearXNG + Infinity + R2R + pgvector
自托管 AI 知识库与联网搜索全套方案:SearXNG + mcp-searxng + Infinity + R2R + pgvector
目标:用 4 个开源组件 + 1 个 MCP 桥接服务,在本地搭一套完全自托管的「向量知识库(RAG)+ 隐私联网搜索」基础设施,供 Claude、OpenCode、Cursor 等 AI 客户端调用。
一、方案总览
1.1 组件与镜像
| 组件 | 镜像 | 作用 |
|---|---|---|
| SearXNG | searxng/searxng:latest |
自托管元搜索引擎,聚合 Google/Bing/DuckDuckGo 等结果,不追踪用户 |
| mcp-searxng | isokoliuk/mcp-searxng:latest(或 npx 本地运行) |
MCP 协议桥,把 SearXNG 的搜索能力暴露给 AI 客户端(Claude Desktop/Code、Cursor、OpenCode 等) |
| Infinity | michaelf34/infinity:0.0.77-cpu |
高吞吐 Embedding/Rerank 推理服务,提供 OpenAI 兼容的 /embeddings 接口,CPU 版无需显卡 |
| R2R (RAG to Riches) | sciphiai/r2r:3.6.6-amd64 |
生产级 RAG 服务:文档解析、切片、向量化入库、混合检索、RAG API |
| pgvector | pgvector/pgvector:pg16 |
内置 pgvector 扩展的 PostgreSQL 16,作为向量数据库持久层 |
1.2 架构图
┌──────────────────────────────┐
│ AI 客户端(Claude/OpenCode)│
└──────┬───────────────┬───────┘
MCP 协议 │ │ HTTP API
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ mcp-searxng │ │ R2R :7272 │◄── 你自己的应用
│ (搜索工具桥) │ │ RAG 服务 │ (/v1/retrieval 等)
└────────┬────────┘ └───────┬──────────┘
│ HTTP JSON │ 调用 embedding
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ SearXNG :8080 │ │ Infinity :7997 │
│ 元搜索 │ │ bge-m3 向量模型 │
└────────┬────────┘ └──────────────────┘
│ 抓取各搜索引擎 │ 向量读写
▼ ▼
Google/Bing/... ┌──────────────────┐
│ pgvector (PG16) │
│ 5432 向量存储 │
└──────────────────┘
两条能力线相互独立又互补:
- 联网搜索线:客户端 → mcp-searxng → SearXNG → 各大搜索引擎。解决「模型不知道最新信息」的问题。
- 私有知识线:文档 → R2R → Infinity 向量化 → pgvector 存储 → 检索问答。解决「模型不知道你的私有资料」的问题。
1.3 为什么选这套组合
- 全部可自托管:搜索记录不出内网;Embedding 在本地 CPU 跑,不依赖 OpenAI。
- 版本都经过社区验证:Infinity 0.0.77 与 R2R 3.6.x 是各自项目稳定期版本,镜像多架构齐全。
- 标准协议对接:MCP 是 AI 工具生态事实标准;Infinity 和 R2R 都讲 OpenAI 兼容 HTTP,替换任何一个组件都不影响整体。
二、环境准备
| 要求 | 说明 |
|---|---|
| Docker ≥ 24 + Compose v2 | 所有服务容器化部署 |
| 内存 ≥ 8GB | Infinity 加载 bge-m3 约占 2~3GB;R2R 约占 1GB |
| 磁盘 ≥ 20GB | 含模型权重(bge-m3 约 2GB)、Postgres 数据 |
| 可访问 HuggingFace | 首次启动拉取模型;国内建议配 HF_ENDPOINT=https://hf-mirror.com |
目录结构约定:
/opt/ai-stack/
├── docker-compose.yml
├── .env # 密码、密钥等敏感配置
├── searxng/
│ ├── settings.yml # SearXNG 主配置
│ └── limiter.toml
├── r2r/
│ └── r2r.toml # R2R 配置(指向 infinity + pgvector)
└── pgdata/ # PG 数据卷
三、逐个部署
3.1 pgvector:PostgreSQL 16 + 向量扩展
pgvector/pgvector:pg16 就是官方 PostgreSQL 16 镜像加了 pgvector 扩展,用法与普通 PG 完全一致:
postgres:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-r2r}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set in .env}
POSTGRES_DB: ${POSTGRES_DB:-r2r}
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-r2r}"]
interval: 10s
timeout: 5s
retries: 5
验证扩展可用(R2R 会自动建表,但可以手动确认扩展存在):
docker compose exec postgres psql -U r2r -c "CREATE EXTENSION IF NOT EXISTS vector;"
docker compose exec postgres psql -U r2r -c "SELECT extname, extversion FROM pg_extension WHERE extname='vector';"
注意:pgvector 的索引(HNSW/IVFFlat)和查询参数由 R2R 自动管理,一般不需要手工建表。向量维度必须与 Embedding 模型输出一致——本文用 bge-m3(1024 维),中途换模型必须重建集合并重新入库。
3.2 Infinity:CPU 版 Embedding 服务
选 BAAI/bge-m3:多语言(中英效果好)、1024 维、支持长文本(8192 tokens),是中文知识库的主力选择。
compose 片段:
infinity:
image: michaelf34/infinity:0.0.77-cpu
restart: unless-stopped
environment:
HF_ENDPOINT: ${HF_ENDPOINT:-} # 国内镜像加速,如 https://hf-mirror.com
command: >
v2
--model-id BAAI/bge-m3
--served-model-name bge-m3
--port 7997
--batch-size 16
volumes:
- ./infinity-cache:/app/.cache # 缓存模型权重,重启不用重新下载
ports:
- "127.0.0.1:7997:7997" # 只暴露给本机/容器网络
验证(OpenAI 兼容接口):
curl http://127.0.0.1:7997/embeddings \
-H "Content-Type: application/json" \
-d '{"model":"bge-m3","input":["你好,世界"]}'
# 返回 JSON 中 data[0].embedding 应为长度 1024 的数组
要点说明:
0.0.77版本起命令行入口为v2子命令,所有参数也可用INFINITY_前缀环境变量代替(如INFINITY_MODEL_ID=BAAI/bge-m3;)。-cpu镜像内置 ONNX/CPU 优化,无 NVIDIA 显卡时的最佳选择;有卡请换默认镜像并加--gpus all。- 还可以再挂一个 rerank 模型(如
--model-id BAAI/bge-reranker-base)供 R2R 重排用,按需增加。 - 首次启动要下载约 2GB 权重,耐心等待日志出现
Uvicorn running再测接口。
3.3 R2R:RAG 编排服务
R2R 负责最重的活:文档摄取(PDF/DOCX/HTML/MD)、语义切块、调 Infinity 向量化、写入 pgvector、提供检索与 RAG 问答 API。
r2r/r2r.toml 关键配置(把 embedding 指到本地 Infinity,把存储指到本地 pgvector):
[completion]
# 生成模型仍需要一个 LLM 提供方;provider 用 litellm 时模型名要带 openai/ 前缀
provider = "litellm"
concurrent_request_limit = 16
[completion.generation_config]
model = "openai/gpt-4o-mini" # 也可以指向任意 OpenAI 兼容的本地大模型
temperature = 0.1
max_tokens_to_sample = 1024
stream = true
[embedding]
provider = "openai" # 走 OpenAI 兼容协议 → 即本地 Infinity
base_model = "bge-m3" # 对应 --served-model-name
[database]
provider = "pgvector"
配套环境变量(写入 .env 或 compose 的 environment):
# 让 R2R 的 openai/litellm 客户端打到本地 Infinity
OPENAI_API_BASE=http://infinity:7997/v1
OPENAI_API_KEY=empty # Infinity 不校验,占位即可
# pgvector 连接
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_USER=r2r
POSTGRES_PASSWORD=your-strong-password
POSTGRES_DB=r2r
提示:不同小版本的 toml 字段名偶有调整,以上结构以 R2R GitHub 仓库 v3.6.x 的示例配置为准,冲突时以官方模板为准。
compose 片段:
r2r:
image: sciphiai/r2r:3.6.6-amd64
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
infinity:
condition: service_started
environment:
OPENAI_API_BASE: http://infinity:7997/v1
OPENAI_API_KEY: empty
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_USER: ${POSTGRES_USER:-r2r}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB:-r2r}
volumes:
- ./r2r/r2r.toml:/app/config/r2r.toml
- ./r2r/data:/app/data
ports:
- "7272:7272" # R2R API
常用操作:
# 上传文档建立知识库(会自动切片→Infinity向量化→入pgvector)
curl -X POST http://127.0.0.1:7272/v1/documents \
-F "file=@./manual.pdf"
# RAG 问答:检索 + LLM 生成
curl -X POST http://127.0.0.1:7272/v1/retrieval/rag \
-H "Content-Type: application/json" \
-d '{"query":"设备保修政策是什么?"}'
排错速查:
| 现象 | 原因与处理 |
|---|---|
| R2R 启动即退出 | pgvector 未就绪或密码不对,看 docker logs r2r |
| 入库报维度不匹配 | 换过 Embedding 模型;删掉对应 collection 重新入库 |
| 入库很慢 | Infinity 首次下载模型中;CPU 下 bge-m3 吞吐有限属正常,可调大 --batch-size |
3.4 SearXNG:隐私元搜索
SearXNG 必须开启 JSON 输出格式,否则 mcp-searxng 拿不到数据(最常见的坑)。
searxng/settings.yml 最小可用配置:
use_default_settings: true
server:
secret_key: "change-me-to-random-string" # openssl rand -hex 32 生成
limiter: false # 仅本机/内网使用时可关
image_proxy: true
search:
safe_search: 0
formats: # ← 关键!默认没有 json
- html
- json
engines:
- name: google
disabled: false
- name: bing
disabled: false
- name: duckduckgo
disabled: false
compose 片段(SearXNG 官方推荐 Redis 做缓存):
redis:
image: valkey/valkey:8-alpine
restart: unless-stopped
command: valkey-server --save 30 1 --loglevel warning
searxng:
image: searxng/searxng:latest
restart: unless-stopped
depends_on:
- redis
environment:
SEARXNG_BASE_URL: http://127.0.0.1:8080/
volumes:
- ./searxng/settings.yml:/etc/searxng/settings.yml:ro
ports:
- "8080:8080"
验证 JSON API:
curl "http://127.0.0.1:8080/search?q=docker+searxng&format=json"
# 返回 results 数组即成功;返回 403 说明 formats 里没开 json
3.5 mcp-searxng:给 AI 客户端装上搜索
mcp-searxng 不是 SearXNG 插件,而是独立的 MCP Server(Node.js 进程),唯一必填变量是 SEARXNG_URL。它通常跑在客户端一侧(STDIO 模式),不必进 compose。
Claude Code 注册(用户级):
claude mcp add --scope user --env SEARXNG_URL=http://127.0.0.1:8080 --transport stdio searxng -- npx -y mcp-searxng
Claude Desktop / Cursor / OpenCode 等(JSON 配置):
{
"mcpServers": {
"searxng": {
"command": "npx",
"args": ["-y", "mcp-searxng"],
"env": {
"SEARXNG_URL": "http://127.0.0.1:8080",
"SEARXNG_MAX_RESULTS": "10",
"SEARXNG_DEFAULT_LANGUAGE": "zh-CN"
}
}
}
}
常用可选变量:
| 变量 | 默认 | 说明 |
|---|---|---|
SEARXNG_URL |
必填 | SearXNG 地址,支持分号分隔多实例做故障转移 |
SEARXNG_FANOUT |
false | true 时并行查询所有实例并合并去重 |
SEARXNG_DEFAULT_LANGUAGE |
all | 默认搜索语言,如 zh-CN |
SEARXNG_MAX_RESULTS |
10 | 返回结果条数上限 |
SEARXNG_TIMEOUT_MS |
10000 | 单次搜索超时 |
配置完成后在客户端里说一句"搜一下 xxx",能看到工具调用 SearXNG 即接入成功。
四、完整 docker-compose.yml(汇总)
services:
postgres:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-r2r}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set in .env}
POSTGRES_DB: ${POSTGRES_DB:-r2r}
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-r2r}"]
interval: 10s
timeout: 5s
retries: 5
infinity:
image: michaelf34/infinity:0.0.77-cpu
restart: unless-stopped
environment:
HF_ENDPOINT: ${HF_ENDPOINT:-}
command: >
v2 --model-id BAAI/bge-m3 --served-model-name bge-m3
--port 7997 --batch-size 16
volumes:
- ./infinity-cache:/app/.cache
ports:
- "127.0.0.1:7997:7997"
r2r:
image: sciphiai/r2r:3.6.6-amd64
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
OPENAI_API_BASE: http://infinity:7997/v1
OPENAI_API_KEY: empty
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_USER: ${POSTGRES_USER:-r2r}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB:-r2r}
volumes:
- ./r2r/r2r.toml:/app/config/r2r.toml
- ./r2r/data:/app/data
ports:
- "7272:7272"
redis:
image: valkey/valkey:8-alpine
restart: unless-stopped
command: valkey-server --save 30 1 --loglevel warning
searxng:
image: searxng/searxng:latest
restart: unless-stopped
depends_on:
- redis
environment:
SEARXNG_BASE_URL: http://127.0.0.1:8080/
volumes:
- ./searxng/settings.yml:/etc/searxng/settings.yml:ro
ports:
- "8080:8080"
.env 示例:
POSTGRES_USER=r2r
POSTGRES_PASSWORD=please-change-me
POSTGRES_DB=r2r
HF_ENDPOINT=https://hf-mirror.com
一键启停:
docker compose up -d
docker compose ps # 全部 healthy/running 即就绪
docker compose logs -f infinity # 观察模型加载
五、验收清单
| # | 验证项 | 命令 | 预期 |
|---|---|---|---|
| 1 | pgvector 扩展 | psql -U r2r -c "CREATE EXTENSION IF NOT EXISTS vector;" |
无报错 |
| 2 | Embedding 服务 | curl 127.0.0.1:7997/embeddings ... |
返回 1024 维向量 |
| 3 | RAG 入库+问答 | POST /v1/documents 后 POST /v1/retrieval/rag |
能引用上传文档作答 |
| 4 | 搜索 JSON API | curl ".../search?q=test&format=json" |
返回 results 数组 |
| 5 | MCP 搜索 | AI 客户端内发起一次搜索 | 工具调用成功返回网页摘要 |
六、安全与运维建议
- 不要把端口裸奔公网:SearXNG/R2R/Infinity/PG 只绑
127.0.0.1或走反向代理加认证;mcp-searxng 若以 HTTP 模式对外,务必设置MCP_HTTP_AUTH_TOKEN并启用 hardened 模式。 - 密钥管理:
settings.yml的secret_key、.env里的数据库密码都要换掉示例值;csdcn.env类凭据文件不要进 git。 - 备份:定期备份
pgdata/目录(或用pg_dump),向量库重建成本高。 - 升级策略:Infinity 与 R2R 升级可能改变向量维度或表结构,升级前备份,升级后先跑第五节验收清单。
更多推荐

所有评论(0)