
设计经得起重试的异步记忆写入
2026年8月5日指南

面向生产的文档管线:不可变上传、校验、无损提取、确定性分块、引用、重试与删除。
多数 RAG 教程从分块开始,因为分块是检索器看到的第一件产物。生产系统应该再往前走一步:从一份即使解析器、分块器、嵌入模型和排序策略全部更换之后,仍能被检查的来源开始。
分块之所以适合检索,恰恰因为它是有损的。它只截取一个适合匹配和提示词组装的小区域。因此,分块是一种投影,不是安全的归档格式。
分块文本可能遗漏页面结构、相邻段落、表格、图注、标题和图片。即使是纯文本,也可能丢掉列表、警告和脚注之间的区别。如果分块是唯一保存的产物,更换解析器或切分策略也无法找回已经被移除的信息。
| 产物 | 用途 | 能否重建 |
|---|---|---|
| 原始资产或精确文本 | 证据与重新处理的权威 | 不能,必须保存 |
| 无损结构化提取 | 页面、区块、表格、顺序 | 可以,从原始资产重建 |
| Markdown 或规范化文本 | 便于阅读的下游表示 | 可以 |
| 分块 | 有边界的检索单元 | 可以 |
| 嵌入与索引 | 候选召回 | 可以 |
来源描述至少应包含稳定 source key、标题、媒体类型、字节长度、结构化作用域、元数据,以及能够获得原始字节时的校验和。系统需要区分三种情况:
这既能避免上传重试悄悄变成第二份文档,也能避免变化后的文件覆盖之前引用所依据的证据。
PDF、Office、EPUB、邮件、图片和大型文本的提取可能超过普通请求生命周期。仅靠一条队列消息不够:它可能重复或丢失,也很少带有取消、修复所需的完整审计状态。
FishMem 会记录包含尝试次数、租约、状态、错误、来源版本与结果的操作。提取开始前先保存原始对象;固定版本的提取适配器再通过同一规范文档写入器提交 Markdown 和结构化结果。队列投递只是唤醒,定时修复负责找到遗漏或过期任务。
同一来源版本和同一分块器配置,应产生相同的分块身份与顺序。确定性让重试安全、让测试能够比较投影,也让索引重建成为正常操作,而不是一次迁移赌博。
系统需要记录足以解释一个分块的配置:
有用的结果包括命中分块、所属文档与版本、在来源中的位置,以及真正进入索引的精确内容。相邻扩展可以恢复命中周围的语境,应用再决定引用、概括还是打开原文。
派生结构不能切断来源链。如果实体、事实或摘要来自多个来源区域,应保留所有支撑关联,而不是只保留第一个。当来源合并、更新或让派生结论失效时,来源关系也应随结论演化,而不是被覆盖。
如果一条检索结果无法回到可检查证据,它也许仍然相关,但还不值得作为引用。
向量元数据适合缩小候选范围,但不应成为最终安全边界。候选 ID 应回到规范数据库重新加载,并根据完整命名空间和来源过滤检查后,才返回内容。
索引滞后、文档访问元数据发生变化,或者向量后端无法表达全部策略时,这一步尤其重要。快速找到候选,不等于有权绕过权威作用域检查。
删除一个文档版本时,需要处理由它派生的每项产物:原始对象、提取结果、分块、向量、实体关联、缓存上下文和操作状态。共享派生产物需要带引用的删除;一个实体或事实仍由其他来源支撑时,不应因为删除一份文档而消失。
删除也必须可安全重放。如果对象存储删除成功但数据库事务失败,或顺序相反,操作日志要保留足够状态,能修复不完整来源家族而不会复活未授权内容。
| 失败 | 期望行为 |
|---|---|
| 写入对象后上传中断 | 持久操作恢复,或安全清理孤儿对象 |
| 解析器返回畸形结果 | 来源仍被保存,系统不进入虚假 ready 状态 |
| 分块提交成功、向量写入失败 | 修复投影,不重复文档版本 |
| 同一键被用于不同内容 | 明确冲突 |
| 来源访问策略发生变化 | 规范层复核阻止旧索引泄漏 |
| 部分删除后重试 | 最终收敛到一个完整删除的来源家族 |
FishMem Document 语料保存带版本的来源内容,并把规范化文本、分块和索引视为可重建投影。它与精炼的持久记忆记录分离。应用可以从文档派生记忆,但这些记录需要携带来源,也不能取代证明它们的原始材料。