PostgreSQL、Outbox 与 WISE:一份数据为什么要走三站¶
先用业务语言讲清楚¶
我把这三个模块理解成三个不同岗位:
- PostgreSQL 是保险柜,保管本次活动真正生效的事实和每次修改记录。
- Outbox 是挂号任务单,保证“需要同步到 WISE”这件事不会因为网络失败而丢失。
- WISE 是资料检索员,擅长从大量历史文本中找相似经验。
它们不是重复存储,而是在解决三种不同问题:事实正确、同步可靠、检索方便。

先让观众记住“事实先落地,任务不丢”,再解释事务和重试细节。
1. 为什么不能只用 WISE¶
WISE 的优势是语义相似。例如用户问“高层接待要注意什么”,即使历史文档没有完全相同的关键词,也可能找到相近经验。
但它不适合单独承担这些问题:
- 本次人数到底是 100 还是 120;
- 预算是每人 300 元还是总预算 300 元;
- 食安是否已经由有权限的人确认通过;
- 哪一条记录来自哪个文件、Sheet 和单元格;
- 同一文件重导后,哪个版本才是当前有效版本。
这些都需要结构化约束、事务、版本和审计,所以 PostgreSQL 是事实源。WISE 里的内容可以从 PostgreSQL 重建,反过来不成立。
2. 为什么不能在数据库事务里直接调用 WISE¶
假设一次正式导入要同时写 PostgreSQL 和 WISE:
- PostgreSQL 写成功;
- 调用 WISE 时网络超时;
- 系统现在要决定是否回滚。
回滚会丢掉已经确认的业务事实;不回滚又会出现数据库有、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 的真实机制包括:
- 每批领取有限数量任务,默认批量大小可配置。
- 使用数据库行锁和
SKIP LOCKED,多个 Worker 不会重复领取同一行。 - 领取后状态改为
processing,尝试次数加一。 - Worker 崩溃时,超过租约时间的
processing任务可以被重新领取。 - 外部失败后使用指数退避:基础 5 秒,逐次翻倍,最大 300 秒;默认最多 10 次。
- 参数结构错误等永久错误直接终止;网络/服务错误可以重试。
- 只有任务当前尝试次数仍匹配时,成功或失败结果才会回写,避免过期 Worker 覆盖新结果。
- 同一来源替换时,删除任务优先;旧知识未删完,新 upsert 会被 barrier 阻挡,避免新旧版本同时被检索。
业务页面能看到 queued、publishing、retrying、succeeded、partially_failed 或 failed,因此“数据库已提交”和“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 私有接口的耦合。

加入 PostgreSQL 元数据过滤后,三路 Hit@5 在这 24 问上都是 100%,约束违规率为 0;MCP Hit@1 为 100%,应用适配器为 91.67%。但样本只有 24 问,这支持一次工程选型,不代表大规模检索质量已经得到证明。
6. 当前 MCP 检索链路的全部细节¶
一次检索不是只调一个接口:
initialize建立 MCP 会话。- 先调用
list_knowledge_bases,验证配置的知识库对当前凭证确实可见。 - 调用
knowledge_search,参数包含queries、knowledge_base_ids和搜索配置。 - 远端候选数取业务
top_k × 5,最多 100,先多召回再过滤。 - 根据 WISE 返回的
knowledge_id回 PostgreSQL 批量解析元数据。 - 按
domain、scenario、review_status等结构化条件过滤。 - 去重并保留 MCP 原始排序,截取业务需要的 Top-K。
- 如果入选结果内容被截断,再调用
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 才不会为了“搜得到”牺牲“说得准”。