跳转至

PostgreSQL、Outbox 与 WISE:一份数据为什么要走三站

先用业务语言讲清楚

我把这三个模块理解成三个不同岗位:

  • PostgreSQL 是保险柜,保管本次活动真正生效的事实和每次修改记录。
  • Outbox 是挂号任务单,保证“需要同步到 WISE”这件事不会因为网络失败而丢失。
  • WISE 是资料检索员,擅长从大量历史文本中找相似经验。

它们不是重复存储,而是在解决三种不同问题:事实正确、同步可靠、检索方便。

小黑把事实留在 PostgreSQL,同时把可重试任务单投入 Outbox 邮筒

先让观众记住“事实先落地,任务不丢”,再解释事务和重试细节。

1. 为什么不能只用 WISE

WISE 的优势是语义相似。例如用户问“高层接待要注意什么”,即使历史文档没有完全相同的关键词,也可能找到相近经验。

但它不适合单独承担这些问题:

  • 本次人数到底是 100 还是 120;
  • 预算是每人 300 元还是总预算 300 元;
  • 食安是否已经由有权限的人确认通过;
  • 哪一条记录来自哪个文件、Sheet 和单元格;
  • 同一文件重导后,哪个版本才是当前有效版本。

这些都需要结构化约束、事务、版本和审计,所以 PostgreSQL 是事实源。WISE 里的内容可以从 PostgreSQL 重建,反过来不成立。

2. 为什么不能在数据库事务里直接调用 WISE

假设一次正式导入要同时写 PostgreSQL 和 WISE:

  1. PostgreSQL 写成功;
  2. 调用 WISE 时网络超时;
  3. 系统现在要决定是否回滚。

回滚会丢掉已经确认的业务事实;不回滚又会出现数据库有、WISE 没有。更麻烦的是,外部 HTTP 调用时间不可控,放在数据库事务里会长期占用连接和锁。

因此当前事务只保存:

正式业务事实 + 知识记录/切片 + index_outbox 任务

三者要么一起提交,要么一起回滚。提交后由独立 Worker 调用 WISE。

sequenceDiagram
    actor Reviewer as 业务 Reviewer
    participant API as 导入服务
    participant PG as PostgreSQL
    participant Worker as Outbox Worker
    participant WISE as WISE

    Reviewer->>API: 确认本次导入
    API->>PG: BEGIN
    API->>PG: 写正式事实 + 知识切片 + Outbox
    API->>PG: COMMIT
    API-->>Reviewer: 数据库已提交

    Worker->>PG: 领取 queued 任务
    Worker->>WISE: 事务外发布
    alt WISE 成功
        WISE-->>Worker: knowledge_id
        Worker->>PG: 标记 succeeded
    else WISE 暂时失败
        WISE--xWorker: 超时 / 5xx
        Worker->>PG: 记录错误并安排重试
    end

3. Outbox 不是一个概念名词,它具体做了什么

Outbox 表里至少记录:操作类型、目标切片、发布内容、尝试次数、下次重试时间、状态和错误码。

Worker 的真实机制包括:

  1. 每批领取有限数量任务,默认批量大小可配置。
  2. 使用数据库行锁和 SKIP LOCKED,多个 Worker 不会重复领取同一行。
  3. 领取后状态改为 processing,尝试次数加一。
  4. Worker 崩溃时,超过租约时间的 processing 任务可以被重新领取。
  5. 外部失败后使用指数退避:基础 5 秒,逐次翻倍,最大 300 秒;默认最多 10 次。
  6. 参数结构错误等永久错误直接终止;网络/服务错误可以重试。
  7. 只有任务当前尝试次数仍匹配时,成功或失败结果才会回写,避免过期 Worker 覆盖新结果。
  8. 同一来源替换时,删除任务优先;旧知识未删完,新 upsert 会被 barrier 阻挡,避免新旧版本同时被检索。

业务页面能看到 queuedpublishingretryingsucceededpartially_failedfailed,因此“数据库已提交”和“WISE 已可检索”是两个明确状态。

stateDiagram-v2
    [*] --> queued: 事务提交
    queued --> processing: Worker 领取
    processing --> succeeded: 远端发布成功
    processing --> retrying: 可重试错误
    retrying --> processing: 到达 next_retry_at
    processing --> failed: 永久错误或耗尽次数
    processing --> queued: Worker 超过租约
    succeeded --> [*]
    failed --> [*]

4. WISE 写入与读取为什么用了不同方式

当前实现不是“所有 WISE 操作都走 MCP”:

操作 当前接入 原因
发布 Markdown 文件 WISE HTTP WISE 已有稳定文件发布接口,Outbox Worker 直接使用
删除远端知识 WISE HTTP 删除是明确资源操作,现有 HTTP 合同清楚
搜索知识 WISE MCP 更适合 Agent 的标准工具发现、参数 Schema 和错误协议
读取被截断的完整切片 WISE MCP read_chunks 与搜索结果中的 chunk ID 直接衔接

这是一种务实的渐进迁移,不为了“协议统一”重写已经稳定的发布链路。

5. HTTP、Skill API、MCP 三种检索方式怎么比较

我用同一组 24 个问题、同一知识库、统一 Top-5 做过对比。

方式 调用形态 优点 代价
应用 HTTP 适配器 应用自己拼 WISE HTTP 请求 控制细、与现有后端集成直接 需要维护 WISE 私有路径、参数和错误兼容
Skill API 调 WISE 的 knowledge_search/invoke 能复用 WISE Agent Tool 仍是 WISE 私有 HTTP 合同
MCP 初始化、发现工具、tools/call 工具可发现、参数结构统一、错误协议标准,适合 Agent 扩展 需要处理 MCP 会话和协议解析

评测里,MCP 与 Skill API 的 24 问 Top-5 内容、Top-1 和完整顺序 100% 相同。原因是它们调用了同一个 WISE Agent Tool,只是传输层不同。

所以我选择 MCP 的正确说法是:

不是 MCP 的检索算法更强,而是它把外部能力包装成标准工具,减少 Agent 内部对 WISE 私有接口的耦合。

小黑用标准量尺检查 MCP 入口,三种接入最终通向同一检索能力

加入 PostgreSQL 元数据过滤后,三路 Hit@5 在这 24 问上都是 100%,约束违规率为 0;MCP Hit@1 为 100%,应用适配器为 91.67%。但样本只有 24 问,这支持一次工程选型,不代表大规模检索质量已经得到证明。

6. 当前 MCP 检索链路的全部细节

一次检索不是只调一个接口:

  1. initialize 建立 MCP 会话。
  2. 先调用 list_knowledge_bases,验证配置的知识库对当前凭证确实可见。
  3. 调用 knowledge_search,参数包含 queriesknowledge_base_ids 和搜索配置。
  4. 远端候选数取业务 top_k × 5,最多 100,先多召回再过滤。
  5. 根据 WISE 返回的 knowledge_id 回 PostgreSQL 批量解析元数据。
  6. domainscenarioreview_status 等结构化条件过滤。
  7. 去重并保留 MCP 原始排序,截取业务需要的 Top-K。
  8. 如果入选结果内容被截断,再调用 read_chunks 读取完整切片;缺任意一个请求切片就报协议错误,不能静默用残缺内容。

为什么还要回 PostgreSQL 过滤?因为 WISE 擅长召回相似文本,而“必须是已审核食安记录”“必须属于菜单域”这种条件更适合由结构化事实校验。

7. 发布到 WISE 的不是整份数据库

字段注册表给字段设置发布策略:

  • 可发布文本:名称、描述、服务要求、食安规则、复盘经验等。
  • 仅做上下文:帮助组成一条可读文本,但不单独发布。
  • 不发布:敏感人员信息、不能可靠解释的值或只应由 SQL 回答的硬事实。

当前写入 WISE 的形式是一个个 Markdown 文件,并附加来源和业务元数据。enable_multimodel=false,因此不能声称已经把 Excel 图片作为多模态知识上传到 WISE。

8. 当前不足和后续优化

当前不足 后续优化
Outbox 是最终一致,不是强一致;短时间内数据库和检索会有延迟 增加积压告警、延迟 SLO、死信处理和人工重放
远端返回 submitted 不等于已完成索引 增加 WISE 解析状态轮询和端到端可检索探针
24 问评测样本小 建立按业务域分层的黄金问题集,持续统计 Hit@K、MRR、nDCG、约束违规率和延迟
现在固定 top_k × 5 过召回 根据领域和过滤淘汰率自适应召回数量
PostgreSQL 元数据回查增加一次 IO 批量缓存稳定元数据,但版本和审核状态仍以数据库为准
MCP 会话与知识库可见集合当前进程内缓存 增加过期刷新、凭证变更检测和更细健康检查
发布/删除 HTTP、读取 MCP 两套协议 保持端口统一,只有在 MCP 写能力稳定且收益明确时再迁移,不为形式统一重写
WISE 暂时不可用时方案会降级 将“无历史经验”和“检索故障”在界面上分开提示,并允许人工继续但禁止虚假引用

9. 技术评委追问时的代码入口

关注点 代码/文档
Outbox Worker banquet_agent/app/application/index_outbox_worker.py
并发领取、租约和状态回写 banquet_agent/app/infrastructure/database/index_outbox_repository.py
WISE HTTP 写入与 MCP 读取 banquet_agent/app/infrastructure/vector/wise_store.py
MCP 协议解析 banquet_agent/app/infrastructure/vector/wise_mcp.py
三路评测报告 banquet_agent/docs/WISE三种检索接入方式对比-2026-07-29.md

本模块一句话收尾

PostgreSQL 保证事实能负责,Outbox 保证失败能恢复,WISE 保证历史经验能找到;三者组合后,AI 才不会为了“搜得到”牺牲“说得准”。