2026-09-23·by Sijie Wang#standmeet#architecture#design

corpus-retrieval

语料检索——走图,而不只是走树

上级:corpus

现状(已落地): corpus_search / corpus_read / corpus_list / corpus_links / corpus_grep / corpus_map / corpus_peek / corpus_resolve —— 八个工具,跑在 Meilisearch 词法索引之上(Postgres 全文作退路),外加树形导航(沿 parent_id 逐级走)。没有向量搜索——这是一个刻意的、长期成立的决定:相关性 = 内容主人自己打下的 [[links]],不是嵌入相似度。

corpus_links 最终落成一个 1 跳 的 op,返回 {outgoing, backlinks},而不是最初设想的有界深度 BFS —— 要不要往下爬由 agent 自己决定(对某个邻居再调一次)。每一个邻居都按授权 glob 重新过滤(一条隐藏笔记链到这里,不能被反向边泄露出来)。

当初敲定的设计约束,以及它们守住了没有:

  • (a)索引严格是派生产物——Postgres 仍是唯一真相源,写入时增量更新,开机和 vault 同步后跑 ReindexOwnerephemeral-over-stateful)。守住了。 也正是它让"给一台已经在跑的实例挂上索引"这件事是安全的:开机自动回填,没有空索引的窗口。
  • (b)ACL 搭在搜索句柄上——每一条命中都经 corpus_read 用的那同一个 allowsCorpusURI 重新过一遍,于是搜索不会变成绕开暴露公式的第二条路径(acl-and-quota-granularity)。守住了。
  • (c)Meili 的混合搜索 / embedder 保持关闭。守住了。
  • (d)只有一个搜索句柄,两个消费方。被违背了——而且值得知道。 实际有两个:corpus.search(owner/admin 那个 op)直接打 Postgres 的 SearchNotes永远不碰 Meilicorpus_search(访客 agent 的工具)走 Meili 那条 lister。名字几乎一样,路完全不同 —— 拿 admin 面去量检索改动,说明不了访客那一侧的任何事。

CJK 那条事实,说准

这里原来的措辞是「PG 的全文搜索在 CJK 上偏弱」。这个说法把它说轻了,而说轻本身正是索引在 dev 有、在 prod 没有、躺了几个月没人发现的原因。Postgres 全文根本不切分中文to_tsvector('english', …) 把一整段连续中文塔成一个词元,于是查询只有在逐字等于正文里那一段连续中文时才命中。

挂索引之前在线上量的:无限衍义 找得到它那篇;限衍三个序列资格证 全是 0;三张资格证 连"标题里就有这五个字"的那篇都找不到 —— 因为正文里那一段是 与三张资格证。英文完全不受影响(unlimited semiosis → 8 条)—— 双语语料就是这样从一侧看完全正常,把这个洞无限期地藏住。

部署上的教训比检索本身更大。 dev 的栈里有 Meili;发出去的 prod compose 里没有,默认走退路。于是所有搜索 e2e —— 包括专门为这条缺陷写的那个 spec —— 测的都是 prod 从不走的那条路,而且第一次跑就绿。check-search-index-shipped 现在守着这两套栈不再分叉(deploymentmechanical-guardrails)。

已关闭(2026-08-31,bd45353f4): corpus_search 以前在分词器表示不了查询时返回一个[],告诉 agent 的是"这儿没有",而不是"这条路看不了你这个查询"。现在结果带一个 note 字段,只在空结果时填上,说明空不等于语料里没有这个话题,并点名 corpus_grep(穷举、字面)是该换的那条路(backend/internal/corpus/usecase/corpus_search_wire.go:41;工具描述也在开头就这么说,corpus_index_tooldesc.go:30;守卒是 e2e/test/corpus-search-says-when-it-cannot-see-the-query.spec.ts)。有命中时永远不贴这条 note——每条结果都提醒一遍就是噪音。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

corpus-retrieval