
1. 自托管知识库为什么总在“联网搜索”这一步卡住如果你已经在本地跑过 RAG大概率遇到过这种尴尬文档问答挺准但一问“最近发布的某个库版本有什么变化”模型就开始一本正经地胡说。原因不复杂——向量库里只有你喂进去的静态资料模型没有联网能力而直接调云端搜索 API 又意味着查询内容离开内网隐私和成本都不好控制。这套方案要解决的就是这条链路用 SearXNG 做自托管元搜索用 mcp-searxng 把搜索能力通过 MCP 协议暴露给 Claude Code、Cursor、OpenCode 这类客户端用 Infinity 在本地 CPU 上跑 bge-m3 向量化用 R2R 做文档摄取与检索编排用 pgvector 做向量持久层。搜索线和知识线相互独立又互补前者补“最新信息”后者补“你的私有资料”。适合谁手里有一台 8GB 内存以上的机器、想完全掌控数据流向、又希望 AI 客户端能同时具备联网和私有知识检索能力的开发者。整套东西全部容器化端口只绑本机搜索记录不出内网。下面按“先起服务、再配 Key、最后验证”的顺序走一遍配置片段可以直接复制。2. TaoToken 统一 Key把生成模型这一环收口前面那套自托管组件里Infinity 负责 Embeddingpgvector 负责存储R2R 负责编排但 R2R 的 RAG 问答最终还是要一个 LLM 来生成答案。这个生成模型如果每家客户端各配一套 Key管理起来很乱。我的做法是统一走 TaoToken 的 API 通道一个 Key 覆盖 Claude、OpenAI 兼容模型等多种调用客户端侧只维护一份配置。TaoToken 在这里的角色是“统一入口”R2R 的 completion 配置、Claude Code 的模型通道、Cline 的 provider 都指向同一个 base URL 和同一个 Key。这样换模型只改一个 model 名不用满项目找 Key。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 根地址是 https://taotoken.net/api 。需要提前准备的东西一个 TaoToken API Key在控制台创建地址 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteKey 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建后复制保存页面只显示一次接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 字段名以文档为准想先验证模型通不通用模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite注意TaoToken 是 API 通道不是编辑器替代品。它只负责把请求转发到模型代码补全、文件读写仍然由你的客户端完成。3. 可复制配置从 compose 到客户端片段3.1 目录结构与 .env先在/opt/ai-stack/下建好目录敏感信息全部进.env不要写进 compose/opt/ai-stack/ ├── docker-compose.yml ├── .env ├── searxng/ │ └── settings.yml ├── r2r/ │ └── r2r.toml ├── infinity-cache/ └── pgdata/.env示例数据库密码和 TaoToken Key 都换掉POSTGRES_USERr2r POSTGRES_PASSWORDplease-change-me POSTGRES_DBr2r HF_ENDPOINThttps://hf-mirror.com TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api3.2 SearXNG 必须开 JSON 格式这是最常见的坑SearXNG 默认只输出 HTMLmcp-searxng 拿不到数据会直接报错。searxng/settings.yml最小可用配置use_default_settings: true server: secret_key: change-me-to-random-string limiter: false image_proxy: true search: safe_search: 0 formats: - html - json engines: - name: google disabled: false - name: bing disabled: false - name: duckduckgo disabled: falsesecret_key用openssl rand -hex 32生成。formats里没有json的话后面 curl 会返回 403。3.3 Infinity 指向 bge-m3Infinity 用 CPU 版镜像模型选BAAI/bge-m31024 维、多语言、支持长文本中文知识库够用。compose 片段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:79970.0.77 版本起命令行入口是v2子命令。首次启动要下载约 2GB 权重日志出现Uvicorn running再测接口。3.4 R2R 的 r2r.toml 与 TaoToken 对接R2R 负责文档解析、切片、调 Infinity 向量化、写 pgvector。r2r/r2r.toml关键部分[completion] provider litellm concurrent_request_limit 16 [completion.generation_config] model openai/gpt-4o-mini temperature 0.1 max_tokens_to_sample 1024 stream true [embedding] provider openai base_model bge-m3 [database] provider pgvector这里有个容易混的点[embedding]走的是本地 Infinity而[completion]走的是 TaoToken。两者都用 OpenAI 兼容协议但 base URL 不同。R2R 容器里通过环境变量区分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注意上面OPENAI_API_BASE指向 Infinity 是为了让 R2R 的 embedding 走本地。生成模型那一路需要在 R2R 的 completion 配置里单独指定 TaoToken 的 base URL 和 Key字段名以 R2R 官方模板为准不同小版本偶有调整。3.5 Cline / CC Switch 配置片段Cline 这类客户端走 OpenAI 兼容协议配置里填 TaoToken 的 base URL 和 Key{ provider: openai, baseUrl: https://taotoken.net/api, apiKey: sk-你的key, model: gpt-4o-mini }CC Switch 用来在多个模型通道之间切换把 TaoToken 作为一个 profile 存进去切换时只改 profile 名。长期跑编码和 Agent 任务的话Coding Plan 更划算入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。3.6 mcp-searxng 注册到客户端mcp-searxng 是独立 Node 进程跑在客户端一侧不进 compose。Claude Code 用户级注册claude mcp add --scope user \ --env SEARXNG_URLhttp://127.0.0.1:8080 \ --transport stdio searxng -- npx -y mcp-searxngClaude 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 } } } }4. 验证请求从向量维度到检索结果服务全起来后按顺序验证别跳步。第一步确认 pgvector 扩展可用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 extnamevector;第二步测 Infinity 返回的向量维度curl http://127.0.0.1:7997/embeddings \ -H Content-Type: application/json \ -d {model:bge-m3,input:[你好世界]}返回 JSON 里data[0].embedding应该是长度 1024 的数组。维度不对说明模型没加载对。第三步测 SearXNG 的 JSON APIcurl http://127.0.0.1:8080/search?qdockersearxngformatjson返回results数组就成功返回 403 说明formats里没开json。第四步上传文档并做一次 RAG 问答curl -X POST http://127.0.0.1:7272/v1/documents \ -F file./manual.pdf curl -X POST http://127.0.0.1:7272/v1/retrieval/rag \ -H Content-Type: application/json \ -d {query:设备保修政策是什么}如果回答能引用你上传的文档内容说明“文档 → Infinity 向量化 → pgvector 存储 → 检索 → TaoToken 生成”这条链路通了。第五步在 AI 客户端里说一句“搜一下 xxx”看到工具调用 SearXNG 并返回网页摘要搜索线就通了。5. 本篇常见错排查R2R 启动即退出多半是 pgvector 没就绪或密码不对。先docker compose ps看 postgres 是不是 healthy再看docker logs r2r的具体报错。入库报维度不匹配换过 Embedding 模型导致的。bge-m3 是 1024 维换模型必须删掉对应 collection 重新入库不能混用。SearXNG 返回 403settings.yml的formats里没加json这是最高频的坑。mcp-searxng 连不上检查SEARXNG_URL是不是http://127.0.0.1:8080注意容器内外的地址不一样客户端跑在宿主机就用 127.0.0.1。入库很慢Infinity 首次在下载模型CPU 下 bge-m3 吞吐有限属正常可以调大--batch-size但别超过内存承受范围。TaoToken 调用 401Key 复制错了或者没带Bearer前缀去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成一个。字段格式以 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 为准。端口裸奔风险SearXNG、R2R、Infinity、PG 都只绑 127.0.0.1别直接暴露公网。mcp-searxng 如果以 HTTP 模式对外务必设MCP_HTTP_AUTH_TOKEN。6. 收口Key 统一之后剩下的是运维这套组合跑通之后日常维护其实就三件事定期备份pgdata/目录升级 Infinity 或 R2R 前先跑一遍第四节的验证清单以及把 TaoToken 的 Key 集中管理。搜索线和知识线各自独立哪条出问题都不会拖垮另一条。如果你主要用 Claude Code 或 OpenCode 做长期编码和 Agent 任务建议把模型通道统一到 Coding Plan省得每个客户端各配一套https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。只想先验证模型通不通用模型对话页最快https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。接入过程中遇到字段对不上直接翻接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。