作者根据实践经验总结:小规模、需精确调试的Agent记忆用grep;大规模、多语言用户搜索用混合向量检索。两者架构取舍值得参考。
Retrieval 在我们的技术栈中出现了两次,而这两种场景采用了截然不同的架构。Agent 记忆——几百条精挑细选的笔记——基于文件和 grep 实现。用户侧搜索——数万条多语言记录——则采用混合 Embedding 构建。两者的分界线值得明明白白说清楚,因为行业现状是无论什么场景都去买一个向量数据库。
Agent 的记忆体量小、内容精炼、且阅读者本身已经具备推理能力。每条笔记都在索引文件中带有一行描述;Agent 读取这 40 行的索引,判断哪些相关,然后打开两到三个文件。调试就是打开 Agent 刚才读过的那个文件。修正就是编辑一段话。当我们把这套语料改成 Embedding 来做原型时,检索变成了一个概率分布,我们需要去"审问"它才能知道结果;更新需要重新 Embedding;而且没人能回答事故中最关键的问题:它为什么读了那个?
在 40000 条混合了韩语、英语、越南语的记录上做搜索,是另一种完全不同的场景。用户输入"cheap spa near the river",记录里写的是"riverside massage, budget"。关键词匹配会漏掉;向量可以捕获。但纯稠密检索在我们的评估集上输给了混合方案,所以实际运行的是混合方案:
const kw = bm25(query, 40); // exact terms, names, numbers
const dn = cosine(embed(query), 40); // paraphrase, cross-lingual
const pool = dedupe([...kw, ...dn]);
const ranked = await llmRerank(query, pool.slice(0, 30)); // one cheap model call
return ranked.slice(0, 8); // what actually enters the context
在一个从真实搜索日志构建的 400 条标注查询集上,Recall@20 分别是:关键词单独 0.71,稠密单独 0.76,带重排序的混合方案 0.90。那次重排序调用成本不到一分钱,而且比任何换 Embedding 模型都更有效地解决了最后一公里的问题。
固定的 512 token 窗口会把表格和它们的表头拆散、把答案和问题拆散。我们按文档结构、标题和列表边界来分块,每块都携带它的来源路径和字节偏移量,这样模型引用的任何内容都能一键跳转到它出自的那个句子。一个不能溯源的检索系统,就是一个延迟很低的谣言制造机。