跳转到内容

摄入生命周期

如果你好奇 OMem 每隔几分钟到底在忙活什么——一个文件怎么就成了一页 wiki、为什么再跑一遍往往一眨眼就完、为什么偏偏有个 flag 会烧钱、删掉点东西之后又会怎样——这一页讲清楚。

OMem 每跑一次,每个新来的或改过的条目都会顺着一条流水线走一遭。往下滚,看一个文件怎么从一团二进制变成一页你的 AI 能找到的内容——每一步,都讲到点子上:

parse

那些别的工具悄悄放弃的内容,它照样读得进来。

你的工作藏在 PowerPoint、Excel、扫描版 PDF 里——这些格式没法自己变成干净的文本。大多数转换器会悄悄丢掉最难啃的部分:嵌在表格里的图表、合并的单元格、扫描的页面。OMem 给每种格式配了专属的 parser,就是为了把别人弄丢的东西留住——而且它是确定性的,同一个文件三年后读出来还是一模一样。

vlm + ocr

图片没法待在纯文本的 wiki 里。于是 OMem 把它看到的写下来。

wiki 是纯文本——正因如此你才能读它、grep 它、给它做版本管理。可图表或扫描页是一堆像素,塞不进纯文本里。于是 OMem 把每一张图都读一遍,再变成文字:OCR 把扫描件、截图这类含文字的图片转写出来;视觉模型则描述图表和示意图。图里原本承载的信息,如今活在了 wiki 里,和其他一切内容一样可被搜索。

curate

它只做梳理——绝不改写你的数字。

刚解析出来的文本还是乱的。LLM 会把它整理成一个干净的页面:一句话摘要、一段好读的正文、若干标签——同时把每个数字和专有名词原封不动地保留。它绝不会偷偷把 11.3% “约”成 11%。页面以 Markdown 形式保存,你可以打开、编辑、做版本管理——而且这步成果会缓存,没变过的条目不会再花第二次成本。

index

它变得可被找到的那一刻。

在此之前,页面只是静静躺在磁盘上——写好了,却还够不着。这一步把它加进搜索索引,就在那一瞬间,你的 agent 一查询就能找到它。索引只是覆在 wiki 之上的一种看法:删掉它,它会从 Markdown 重建。wiki 才是真相。

有几步会随 kind 不同而变(mail 把一整条往来拢成一页,calendar 把重复事件铺开),这部分见 Kind 与 Source

这条流水线里有两件事值得真正弄明白,因为 OMem 平时是什么脾气,全看它俩:为什么重跑很便宜,还有为什么有一步偏偏不便宜

重跑为什么便宜:内容寻址加缓存

Section titled “重跑为什么便宜:内容寻址加缓存”

OMem 几分钟就跑一次,可它几乎从不做重复功。靠的是两样东西:

  • 内容寻址(raw 阶段):每个条目都拿它的内容算一个指纹(SHA256)。只要字节没动,指纹就跟磁盘上那份对得上,后面整条流水线直接跳过。
  • 整理缓存:最烧钱的那一步,也就是写 wiki 页时的那次 LLM 调用,是按输入哈希缓存的。你新加一百份文档,就只有这一百份去碰 LLM,另外那一万份纹丝不动。

对一个动辄几万条目的收件箱来说,这就是 LLM 花 $10 还是 $5,000 的分水岭。正是这个缓存,才让”每几分钟跑一次、一直跑下去”这件事在经济上说得通。

Bootstrap 和增量,以及”cursor”到底是什么

头一回跑叫 bootstrap:这时还没有 cursor,所以 source 会把范围内的东西全翻出来摄一遍。之后每一次都是增量跑:每个 source 都有一个 cursor,记着上回摄到哪儿了(文件记的是改动时间的水位线,mail/calendar 记的是序列位置)。下次跑只看水位线之后的条目,所以一个塞了一万文件、又什么都没改的文件夹,远不到一秒就跑完退出。

cursor 还盯着一个 failed set——上回出过岔子的那些条目(LLM 限流了、读取临时失败了),下次会再试一遍,哪怕它们早已”过气”也照试不误。这么一来,一次小抽风,不会就把某个条目永远晾在那儿。

(其实没有 --bootstrap 这个 flag,那个概念早退役了。所谓 bootstrap,不过就是”还没有 cursor 时第一次跑”的样子;范围永远从你的配置里读。)

omem ingest --now 会把 cursor 忘掉,从头到尾重扫一遍。听着没什么杀伤力——只要什么都没变,内容寻址加整理缓存确实能让它保持便宜。但坑就埋在这儿:重扫会把每个条目的内容哈希重算一遍,而 mail/calendar 的这个哈希是连元数据一起算进去的(主题、日期、参会人)。这里头但凡动了一个,哈希就变,整理缓存就 miss,LLM 就又得跑一趟。

放到一个几千条目、其中不少元数据还时不时变动的语料上,一次 --now 就可能跑出几百到几千次 LLM 调用,几美元到几十美元就这么出去了。你几乎永远用不着它;那些改动,正常的增量循环本来就会接住。

一个文件从你磁盘上没了(或者一封邮件被删了),OMem 不会跟着把那页 wiki 删掉。它给这页立个墓碑(tombstone);来源哪天要是又回来了,这页就跟着复活。按 Play 看一遍这个过程:

: Q3-budget-review.pptx ✓ 在磁盘上wiki: wiki 页 · 在线
文件在磁盘上;它的 wiki 页处于在线状态,可被查询。

这是有意做的一道保险。万一同步抽了下风,或者一个手滑的 rm 让一百个文件凭空蒸发,你绝不希望一百页 wiki 当场灰飞烟灭。立碑的做法是把它们先留着,从日常查询里暂时撤下,然后静观其变。真正会把它们彻底抹掉的,只有 omem lint --orphans --purge 这一条命令——而这道令,是你亲口下的。

这一切底下的三个存储层

上面说的所有东西,都坐在三层之上,按谁说了算从高到低排:

  • raw/——解析器产物那份不可变、按内容寻址的归档(parsed.md 加上抽出来的资源),永不删除。解析器是确定性的,同一个文件三年后还是产出同一个 parsed.md——这正是这份归档可信、整理缓存能稳稳命中的根本。
  • wiki/——整理好的 Markdown 页,从 raw/ 生成出来。它就是 真相:索引删了,从这里重建;wiki 删了,从 raw/ 重建。
  • 索引——FTS5(或者 qmd)架在 wiki 之上,单纯为了让查询快。它是一种说法,不是源头。

每一页的历史也都留着:重新摄入一个改过的文件时,旧的那版解析不会被盖掉,而是留存下来(omem raw get … --version N)。

你已经看着 wiki 被出来了。下一步:插件架构——这条流水线每一步各自插进去的那几个扩展点(source、parser、index),以及它们为什么没一个会把你锁死。