这是系列文章的第四篇,介绍多客户端 Token、Agent 权限、聊天机器人接入、M7 增量发布、原子切换、回滚、备份和日常运维。

公开说明:所有私人 IP、域名、用户名、仓库地址、Token、密钥、源文件名、内部哈希和个人目录均已删除或泛化。请勿把示例占位符直接用于生产。

系列目录

  1. 解析架构与质量基线
  2. 块级融合与人工质量验收
  3. Chunk、Embedding 与混合检索
  4. 服务部署、增量发布与安全运维

1. 服务部署边界

检索服务运行在 Ubuntu Server 24.04 上,核心容器包括:

  • PostgreSQL;
  • REST/MCP Retrieval Service;
  • 可选的 Agent 或聊天机器人网关。

网络原则:

  • PostgreSQL 不发布宿主机端口;
  • 数据库只加入内部 Docker 网络;
  • Retrieval Service 是唯一数据库调用者;
  • 客户端只能通过 REST 或 MCP 访问;
  • 管理端口不直接暴露到公网;
  • 远程访问优先使用私有组网、VPN 或带 TLS 的反向代理。
HTTPS + Token
内部 Docker 网络
独立 Token
客户端 / Agent
REST / MCP Service
PostgreSQL
Query Embedding
Reranker
聊天机器人

2. 健康检查分层

服务至少提供:

/health/live
/health/ready

live

只判断进程是否存活,不依赖数据库和模型。

ready

检查:

  • 数据库连接;
  • 活动 Release;
  • 文档、Chunk、Embedding 数量;
  • Query Embedding 模型;
  • Reranker;
  • 必要索引;
  • Token 注册表。

live=PASS 但 ready=FAIL 时,负载均衡器不应继续发送检索请求。

3. 每个客户端使用独立 Token

不要让多台电脑、多个 Agent 和机器人共享一个总 Token。

每个客户端使用独立名称,例如:

laptop-agent
desktop-agent
automation-worker
chat-bot

Token scope:

Scope权限
search搜索知识库
documents查看文档列表
chunks按 ID 读取 Chunk
mcp调用 MCP
docs查看接口文档

普通 AI 客户端通常只需要:

search,documents,chunks,mcp

4. Token 存储、轮换和吊销

服务端只保存 Token SHA-256,不保存明文。注册表文件应:

  • 位于 Git 之外;
  • 权限为 0600;
  • 只有服务账户可读;
  • 支持热加载;
  • 保存客户端 ID、scope、创建时间和状态。

Token 明文只在创建时显示一次,客户端将其放入:

  • 环境变量;
  • 操作系统凭据管理器;
  • CI/CD Secret;
  • 专用 Secret Manager。

轮换步骤:

  1. 为同一客户端签发新 Token;
  2. 在客户端更新 Secret;
  3. 验证新 Token;
  4. 吊销旧 Token;
  5. 确认旧 Token 返回 401;
  6. 记录轮换结果,不记录 Token 明文。

5. 鉴权验收

必须测试:

  • 无 Token 返回 401;
  • 格式错误 Token 返回 401;
  • 已吊销 Token 返回 401;
  • scope 不足返回 403;
  • 正确 Token 返回结果;
  • Token 热加载生效;
  • 重启后 Token 状态保持;
  • 一个客户端被吊销不影响其他客户端。

不要把 Token 放进 URL query string,因为它可能进入代理日志、浏览器历史和监控系统。

6. 给其他 AI 的 MCP 配置

通用 Streamable HTTP MCP 配置:

{
  "mcpServers": {
    "private-knowledge": {
      "url": "https://kb.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${KNOWLEDGE_API_TOKEN}"
      }
    }
  }
}

配置后要求 AI 完成三项测试:

  1. list_knowledge_documents 返回文档列表;
  2. search_knowledge 能返回命中内容和原始页码;
  3. get_knowledge_chunk 能按返回的 Chunk ID 读取详情。

客户端验收报告至少记录:

client_id
network_status
authentication_status
mcp_initialize_status
tool_list_status
search_status
source_page_status

报告不得包含 Token 明文。

7. REST 调用注意事项

REST 请求示例:

import os
import requests

token = os.environ["KNOWLEDGE_API_TOKEN"]

response = requests.post(
    "https://kb.example.com/v1/search",
    headers={"Authorization": f"Bearer {token}"},
    json={"query": "需要检索的问题", "limit": 5},
    timeout=60,
)
response.raise_for_status()
print(response.json())

PowerShell 中不要手工拼 JSON 字符串,使用对象序列化并写入 UTF-8 文件,能避免引号和中文编码导致 HTTP 422。

常见错误:

状态含义处理
401Token 无效、过期或已吊销重新签发或检查 Secret
403缺少 scope调整最小权限
422JSON 格式错误使用标准 JSON 序列化
502反向代理无法连接后端检查容器和代理目标
Timeout网络、模型或查询超时分别检查网络和阶段耗时

8. Agent 权限边界

Agent 只获得检索接口,不直接获得:

  • 数据库账号;
  • Docker socket;
  • 服务器 shell;
  • 宿主机文件系统;
  • 任意命令执行;
  • 浏览器控制;
  • Token 管理权限。

即使 Agent 具备工具调用能力,也应该通过三个有限 MCP 工具读取知识,而不是让它自行连接数据库。

返回给 Agent 的结果保留来源页和 Chunk ID,这样回答可以引用证据,而不是只输出无来源结论。

9. 聊天机器人安全配置

聊天机器人使用独立容器和独立最小权限 Token。推荐限制:

  • 非 root 用户;
  • 根文件系统只读;
  • Linux capabilities 全部 drop;
  • 仅状态目录可写;
  • 只加入专用网络;
  • 禁止 shell、文件、浏览器和管理工具;
  • 群聊默认关闭;
  • 私聊使用配对或白名单;
  • 媒体处理默认关闭;
  • 数据库凭据不进入机器人容器;
  • 管理命令不通过聊天消息开放。

如果使用外部大模型 API 生成答案,必须明确说明:用户问题和检索命中片段会离开本地服务器并发送给模型提供方。只有获得数据所有者授权后才能启用。

10. 为什么需要 M7 增量发布器

全量构建完成后,后续新增一份 PDF 不应重跑 41 份文档和 15,801 个向量。

M7 将每次更新视为新的不可变 Release:

新 PDF 或 Canonical 修订
  -> 计算与当前 Release 的差异
  -> 只重建变化文档 Chunk
  -> 复用未变化 Chunk
  -> 只重建变化 Embedding
  -> 构建新 Release
  -> 远端 staging 导入
  -> 全部门禁
  -> 原子切换

11. 增量变更计划

发布前先运行 dry run,生成明确计划:

{
  "base_release_id": "knowledge-YYYYMMDD-r1",
  "target_release_id": "knowledge-YYYYMMDD-r2",
  "added_documents": [],
  "modified_documents": [],
  "deleted_documents": [],
  "reused_documents": [],
  "gate": "READY_FOR_PUBLISH"
}

如果 deleted_documents 出现非预期项目,应立即停止。删除文档必须是显式操作,不能因为某个本地目录临时未挂载就自动删除线上数据。

12. 内容指纹与复用

每份文档计算稳定输入指纹,至少包含:

  • source SHA-256;
  • Canonical Manifest SHA-256;
  • Chunk 配置版本;
  • 文本规范化版本;
  • Embedding 模型 revision 和配置。

指纹未变化时:

  • 复用 Chunk;
  • 复用 Embedding;
  • 复用 Canonical Core;
  • 仍在新 Release Manifest 中明确引用。

指纹变化时只重建对应文档,不影响其他文档。

13. 不可变 Release 与原子切换

服务器目录:

/srv/knowledge/releases/
├── knowledge-YYYYMMDD-r1/
├── knowledge-YYYYMMDD-r2/
└── current -> knowledge-YYYYMMDD-r2

发布过程:

  1. 新 Release 上传到 .incoming;
  2. 校验归档和成员 SHA-256;
  3. 解包到新的不可变目录;
  4. 导入数据库 staging;
  5. 创建索引;
  6. 运行数据库、REST、MCP、RRF 和 Reranker 门禁;
  7. 创建发布前备份;
  8. 事务切换数据库活动 Release;
  9. 原子更新 current;
  10. 执行发布后 smoke test。

任何一步失败都保留当前活动 Release,不让半成品对外服务。

14. 自动回滚

切换后如果健康检查或检索门禁失败:

  1. 数据库活动 Release 恢复为上一版;
  2. current 符号链接恢复;
  3. 重启或热加载 Retrieval Service;
  4. 验证旧查询恢复;
  5. 保留失败 Release 和 staging 用于诊断;
  6. 记录回滚原因和哈希。

服务器至少保留当前版和前一版,使回滚不依赖重新上传大文件。

15. 服务器保留策略

服务器永久保留:

  • 原始 PDF、source manifest 和 SHA-256;
  • Canonical Core;
  • 当前与前一 Release;
  • PostgreSQL 数据卷;
  • Query Embedding 模型;
  • Reranker;
  • 服务代码和数据库 migration;
  • 已验证数据库备份。

服务器不保留:

  • 完整页面 PNG;
  • MinerU middle/intermediate JSON;
  • 布局可视化;
  • 人工校正包;
  • content-list 审计;
  • 重复图片树;
  • 本地虚拟环境;
  • 完整 QC 证据;
  • 运行时绝对路径字段。

这些内容继续留在本地生产端,用于重建和审计。

16. 四层备份体系

16.1 本地生产资料

本地保留全部原始 PDF、完整 Canonical、中间结果、人工证据、Chunk、Embedding、模型和 Manifest。

16.2 数据库逻辑备份

每次导入、迁移和 Release 切换前后生成:

knowledge-database.dump
knowledge-schema.sql
release-manifests.tar.gz
backup-inventory.json
SHA256SUMS
READY

16.3 Git 代码备份

Git 保存代码、Schema、migration、配置模板、Manifest 和文档,不保存 PDF、向量、模型、数据库 dump 和密钥。

16.4 密钥加密备份

Token 和 .env 不进入普通备份。确需备份时,单独生成加密归档,解密口令离线保管。

17. 什么叫“备份通过”

pg_dump 成功只代表生成了文件。正式 PASS 还需要:

  1. 计算备份 SHA-256;
  2. 创建临时恢复数据库;
  3. 从 dump 恢复;
  4. 检查 Release、文档、页面、Chunk 和 Embedding 数量;
  5. 检查活动 Release;
  6. 运行最小查询;
  7. 删除临时恢复数据库;
  8. 将恢复结果写入 backup inventory;
  9. 最后创建 READY 标记。

没有恢复演练的备份,只能视为候选备份。

18. 备份保留周期

适合个人知识库的起始策略:

每日备份:7 份
每周备份:4 份
迁移前快照:至少 1 份
Release 切换前:1 份

还应定期把服务器最新备份拉回本地或另一台设备,并复算 SHA-256。服务器磁盘故障时,只存在同一块磁盘上的“备份”没有意义。

19. Git 的边界

Git 应保存:

  • 应用和发布脚本;
  • 数据库 Schema 与 migration;
  • JSON Schema;
  • 非敏感配置模板;
  • 小型 Manifest;
  • 自动化测试;
  • 不含正文的摘要报告;
  • 运维文档。

Git 不应保存:

  • 原始 PDF;
  • 完整 Canonical;
  • Chunk 和 Embedding 大文件;
  • 模型;
  • 页面图片;
  • 数据库 dump;
  • 日志;
  • Token、密码、私钥和 .env。

Git 负责可复现性,Release 和备份体系负责数据资产。

20. 日常健康检查

建议每天或每次更新后检查:

  • Retrieval /health/ready;
  • 活动 Release ID;
  • documents/chunks/embeddings 数量;
  • PostgreSQL 容器健康;
  • Retrieval 容器健康;
  • Reranker 状态;
  • Token 注册表加载状态;
  • 最近错误日志;
  • 最新备份时间和恢复测试状态。

每月执行一次真实恢复演练,而不是只查看备份文件是否存在。

21. 一次标准更新流程

发布前

  • 服务器和本地各有可恢复备份
  • 新 PDF 已计算 SHA-256
  • 使用新的稳定 document ID
  • MinerU、融合和人工 QC 已完成
  • Canonical 没有 UNRESOLVED
  • dry run 中新增、修改、删除符合预期
  • 新 Release ID 从未使用

发布中

  • 变化文档 Chunk 构建通过
  • 未变化 Chunk/Embedding 正确复用
  • 传输包和远端成员哈希通过
  • staging 数据库导入通过
  • HNSW/BM25 索引通过
  • REST/MCP/RRF/Reranker 门禁通过
  • 原子切换成功

发布后

  • /health/ready 返回新 Release
  • 旧内容仍能查询
  • 新内容能查询
  • 页码和来源 SHA 正确
  • Agent/机器人查询正常
  • 新备份已完成恢复测试
  • 代码与小型 Manifest 已提交 Git

22. 最值得保留的工程经验

VLM 不能覆盖健康原文

技术字段的价值通常高于语言流畅度。VLM 看起来更自然,不代表更忠实。

字符合法不代表语义健康

乱码可能完全由合法符号组成。必须检查语言脚本和可读性。

页面归属是一等数据

表格内容正确但页码错误,引用仍然错误。page fragment 必须独立保存。

文件存在不是质量门禁

合法 JSON 也可以包含空正文、重复表格和错误阅读顺序。

人工修改必须可审计

原始错误、证据页、修正范围、审核结果和哈希缺一不可。

增量发布必须先做差异计划

意外删除往往来自挂载失败或目录暂时不可见。dry run 是保护线上数据的最后一道门。

备份必须实际恢复

只有成功恢复并通过计数和查询校验,备份才真正可用。

23. 最终安全检查表

  • PostgreSQL 未暴露公网端口
  • 服务使用 HTTPS 或私有网络
  • 每个客户端独立 Token
  • 服务端只保存 Token 哈希
  • Token 注册表权限为 0600
  • Agent 没有 shell、数据库和 Docker 权限
  • 外部模型的数据外发已经明确授权
  • 密钥不在 Git、日志和普通备份中
  • 当前及前一 Release 可回滚
  • 最新备份已通过恢复测试
  • 本地仍保留原始 PDF 和完整 QC 证据

24. 结语

一个可靠的私有知识库,核心不是某个单独模型,而是每一层都拥有清晰的权威边界:

  • 原始 PDF 是来源真值;
  • 健康 native 文字是正文真值;
  • MinerU 提供结构、视觉资产和坏文字层 OCR;
  • Canonical 固化块级来源、页面和坐标;
  • Chunk 与 Embedding 只消费通过门禁的内容;
  • PostgreSQL 和混合检索负责在线查询;
  • Release、Token、备份和回滚负责长期运营。

最终得到的不再是一次性的 RAG Demo,而是一条可以持续更新、可以解释、可以审计、可以回滚,并能安全交给不同 AI 客户端使用的知识库生产线。