Skip to content

ADR-0001:业务数据与 AI 数据双库分离 ​

  • 状态:已采纳
  • 日期:2026-09-15
  • 决策者:毕设作者
  • 相关文档:docs/05-数据库设计.md、docs/adr/0002-mcp-gateway-as-sole-egress.md、CONTEXT.md

背景 ​

Agent 由大模型驱动,存在两类固有风险:

  1. 幻觉:模型可能生成看似合理但错误的查询意图;
  2. 提示注入:Agent 读取的任何不可信内容(故障手册文本、设备日志、工具返回值)都可能夹带指令,诱导 Agent 去读取与其任务无关的数据。

如果 Agent 与业务系统共用一套数据库连接,那么一次成功的提示注入就可以让 Agent 直接查询 user_account(账号与密码哈希)、audit_log(审计日志)、work_order(工单)等敏感表。这类越权读取在日志上表现为"一条合法的 SQL",事后很难与正常查询区分。

决策 ​

将数据存储拆分为两个在账号、进程、凭证上完全分离的库:

  • business-db:用户账号、RBAC 权限、设备档案、遥测时序数据、运维工单、安全审计日志、Agent 分析报告。
  • ai-db:故障知识库的文档切片与向量索引、Agent 会话短期记忆。

两者的权限边界:

角色business-dbai-db
Web 前端(经业务接口服务)完整增删改查无权限
业务接口服务完整增删改查无权限
MCP 安全网关受限读写(按工具契约的字段白名单)只读(向量检索)
Agent无账号,零权限无账号,零权限(经网关间接读取)
边缘终端无(只经 MQTT 上报,由网关写入)无

关键点:Agent 侧不部署 ai-db 的客户端,也不持有 ai-db 的连接串。Agent 想检索知识库,调用的是网关的 kb.search 工具,由网关去连 ai-db。

理由 ​

  1. 边界隔离:Agent 与业务敏感数据之间不存在网络通路,也就不存在"绕过应用层逻辑的越权读取"。攻击面从"模型是否听话"变成"网关的工具契约是否写对"——后者是可测试、可审计的确定性代码。
  2. 故障域隔离:Agent 或向量库崩溃、被拖垮、需要重建索引时,business-db 与业务服务不受影响。工业场景下"业务系统必须能独立正常工作"是硬要求。
  3. 可解释的答辩叙事:数据访问路径被收敛成一张有限的工具清单,每个工具的参数与返回字段都可以逐条列举,安全论证可被验证。

备选方案与拒绝理由 ​

方案为什么没选
单库 + 给 Agent 分配只读账号只读只约束读写动作,不约束访问范围。Agent 仍可 SELECT * FROM user_account。
单库 + 视图(view)隔离视图仍需给 Agent 账号,且视图权限配置散落在 DDL 里,易被后续迁移破坏;返回字段也无法按调用上下文动态裁剪。
单库 + 行级安全策略(RLS)PostgreSQL RLS 能限制行,但难以表达"按 Agent 当前任务动态决定返回哪些列";且原型期用 SQLite 无此能力。
Agent 直连 ai-dbai-db 本身不含敏感数据,风险较低,但会让"Agent 的对外访问必须全部经网关"这条不变量出现例外,例外一多边界就失效。

后果 ​

正面

  • 越权读取业务数据在架构上不可达,而不是"靠策略拦住"。
  • 审计日志的完整性有保障:Agent 无法篡改或删除 audit_log(它没有账号)。
  • 后续可以安全地把 ai-db 换成任意向量库,不影响业务侧。

负面 / 代价

  • 多一层网关转发,读遥测数据多一次网络跳转与序列化开销。
  • 需要为每个数据需求定义 MCP 工具契约,前期设计成本高于"直接写 SQL"。
  • 网关成为单点,必须做好进程守护(PM2)与故障降级设计。

落地期演进 ​

原型期采用逻辑隔离(两套独立 SQLite 文件、独立进程、独立账号配置),落地期升级为两台物理实例(甚至不同主机/网段),本 ADR 的结论不变,只是隔离强度提高。