AutoIndex:为检索学习表征程序

  • 关联论文:2607.18603
  • 作者:flyP
  • 更新:2026-07-23

一句话结论

AutoIndex 把"文档表示"从一次性的预处理选择提升为一类可学习的程序(representation program):由 agent 在验证集信号引导下,自动搜索对文档"切分 / 增强 / 归一化 / 重加权 / 重组"的执行程序,不改检索器本身,就能在固定 BM25 上把 8 个任务 Recall@100 平均 +8.4%,nDCG@10 平均 +8.3%,单任务最高 +30.5% / +43.6%。

解决什么真问题

RAG 工程的常识是"垃圾进,垃圾出"——文档怎么切片、要不要去噪、表格怎么压、能不能追加摘要、相似度公式怎么定,全在索引前就拍板,且写死。这导致:

  • 同一份语料,换个任务就要重做一遍预处理;
  • 检索器(BM25、dense、rerank)的研究铺天盖地,但"索引前"那一公里的工程几乎全靠人肉;
  • 现有"超参搜索"只能调 chunk size、overlap 几个数,对"该加什么字段、按什么权重、怎么归一化"完全无能为力。

AutoIndex 瞄准的就是这一公里:让"把原始文档映射为检索系统看到的表示"这件事本身成为一个优化目标

核心方法

AutoIndex 的关键抽象叫 representation program:一段对单篇文档执行的可执行变换序列,输出是"检索系统真正看到的表示"(即索引时的 token 化、字段权重等)。整个框架可以拆成三块:

1. 程序空间(program space)

把"索引前"的可能操作枚举成一个原子集,组合起来就是一段程序。常见原子包括:

  • slice(window=256, overlap=32):按窗口切分;
  • normalize(field, scheme):归一化字段(lowercase / unicode 规范化 / 数字规范等);
  • enrich(field, op):在文档上追加摘要、关键词、问答对、引用块等;
  • reweight(field, alpha):给某字段加权;
  • reorganize(struct):把段落重排成 Q/A、表格、列表等结构化表示;
  • discard(span):去噪(页眉/页脚/版权声明等)。

一段完整 program = 这五个原子的有序 + 嵌套组合。论文刻意把"程序"做成可执行而不是"自然语言 prompt",这样每一步结果可缓存、可复现、可用单元测试检查。

AutoIndex 不靠离线强化学习,而是每轮迭代跑这套循环:

for iter in 1..K:
    P_t          # 当前程序
    failures     # agent 诊断:在 dev 集上跑 BM25,看哪些 query 失败、为什么失败
    candidates   # agent 依据 failures 合成候选程序变体
    eval(candidates on dev)  # 在 dev 上算 Recall / nDCG
    P_{t+1} = argmax_{c in candidates} quality(c)   # 只保留提升的

其中:

  • 失败诊断由 LLM agent 完成:把 query / 命中文档 / 不命中文本都喂回去,让它指出"是切太粗丢掉细节?还是某字段权重太重把无关段顶到前面?";
  • 候选合成也是 agent 干:根据诊断在原子集里加 / 删 / 改;
  • 保留策略很硬:只接受 dev 集质量有提升的版本,无回退、无温度——避免 LLM agent 漂移到花活。

3. 实验固定检索器:BM25

论文在所有任务里冻结 BM25 检索器,只用程序优化"喂给它的文档"长什么样。这是个非常关键的设计:剥离掉检索器本身(dense、rerank、ColBERT 之类),证明收益完全来自表征侧。

伪代码(核心搜索循环):

def search(documents, queries, dev_qrels, base_program, K=20):
    P = base_program
    for _ in range(K):
        # 1. 评估当前程序
        index = BM25(index_apply(P, documents))
        m_prev = recall_at_k(index, queries, dev_qrels, k=100)

        # 2. Agent 诊断失败
        failures = diagnose(P, index, queries, dev_qrels, llm)

        # 3. Agent 合成候选
        candidates = propose(P, failures, llm, n=4)

        # 4. 只保留提升的
        P_next = P
        m_next = m_prev
        for c in candidates:
            idx_c = BM25(index_apply(c, documents))
            m_c = recall_at_k(idx_c, queries, dev_qrels, k=100)
            if m_c > m_next:
                P_next, m_next = c, m_c
        P = P_next
    return P

注意 evaluate 永远用 dev 集,避免过拟合到 test。

关键实验与数据

基准:CRUMB——一个异构检索任务集合,覆盖 8 个任务。原文未公开逐任务编号的细粒度分布,但明确"任务分布广、领域多样、长短文档混合"。

指标

  • Recall@100:检索召回前 100 条的能力;
  • nDCG@10:前 10 条的排序质量。

结果

  • 8/8 任务上,AutoIndex 都严格超过静态 full-document BM25;
  • 平均 +8.4% Recall@100,+8.3% nDCG@10;
  • 最高单任务 +30.5% Recall@100,+43.6% nDCG@10;
  • 检索器全程是 BM25,没换 dense、没上 rerank。

这一组数字的含义:哪怕你"换汤不换药"——只是把喂给 BM25 的文档结构好好改了——就能在不变检索器的前提下拿到接近 dense + rerank 的收益。

亮点与局限

亮点

  • 抽象干净:把"文档表示"显式建模为程序,搜索空间可枚举、可审计、可复现;
  • 工程友好:BM25 不动,意味着现有 ElasticSearch / OpenSearch 栈 0 改动;
  • 数据友好:只搜程序、不微调模型,对算力、显存、数据规模都不挑;
  • Agent 设计克制:诊断 + 合成两段够用,复杂的 RL/树搜索都用不上;
  • 增益可观测:8/8 任务正收益,没有"在某个任务上暴跌"的塌方。

局限

  • 搜索上限受原子集限制:如果原子里没有"PDF 表格结构化提取"之类的操作,再聪明的 agent 也搜不出来;
  • 验证集依赖:dev 集大小、分布会显著影响程序走向,跨域迁移不保证;
  • LLM agent 诊断的稳定性没给完整消融(同一 agent 跑两次,结果是不是稳定提升?原文未明确);
  • BM25 之外没做实验:能否和 dense / ColBERT / rerank 叠加?还是会让它们的 gain 缩小?原文未明确;
  • 计算成本:每轮迭代要重新索引 + 全量跑 dev,CRUMB 量级不痛,亿级语料就贵了。

对工程落地的启发

  • 现有 RAG 系统 0 改动就能吃增益:先不改检索器、不动 embedding 模型,单独跑一次 AutoIndex 看增益,再决定要不要进一步升级检索器;
  • 索引质量是被低估的杠杆:很多团队一上来就换 embedding、换 rerank,文档结构没动——AutoIndex 提示"先优化表示,再优化检索";
  • 审计友好:program 是白盒的,每一步都看得见;相比 prompt 微调或 fine-tune embedding,可解释性高一档;
  • 可作为冷启动流程:新业务接 RAG,先跑一遍 AutoIndex 找最合适的文档结构,再去做长期微调;
  • 可作为嵌入模型的"对手"基线:在升级 dense 之前,先用 AutoIndex + BM25 当参考基线。

与同方向工作的关系

  • vs 检索器调优(ColBERT / SPLADE / E5 / BGE):那些调"打分函数",AutoIndex 调"喂给打分函数的输入"——两者正交,可叠加;
  • vs 文档预处理经验法则(chunk size、overlap):把经验法则变成自动搜索,并把窗口 / overlap 之外更多原子纳入;
  • vs RAG 端的 query rewriting / HyDE / step-back prompt:那些动 query 侧,AutoIndex 动 doc 侧;
  • vs AutoML / AutoML for IR:把自动机器学习的思路搬到"表示程序"这一更窄、但更可控的搜索空间;
  • vs LLM agent for IR(如 RankZephyr、PromptRanker 等):那些 agent 是在检索时在线决策,AutoIndex 的 agent 在索引时离线决策,且决策产物是程序不是 prompt。

适合谁读

  • RAG 平台 / 搜索中台工程师:最强实战向文档,0 模型改动就能落地;
  • 搜索基础设施 / IR 工程师:把"文档表示"从黑盒变成可编程对象,启发后续工作;
  • AI Agent 应用开发者:学习"agent 诊断 + 候选合成 + 验证保留"的最小可行 loop;
  • 数据 / 知识管理产品经理:理解"索引前"为何是工程重灾区。

速查术语

  • Representation program:表征程序,把原始文档映射成检索系统所见表示的可执行变换序列;
  • Validation-guided program search:验证引导的程序搜索,dev 集信号驱动的程序迭代;
  • BM25:经典词袋检索打分函数,AutoIndex 实验中冻结使用;
  • Recall@100 / nDCG@10:召回率 / 归一化折损累积增益,本文主指标;
  • CRUMB:异构检索基准,8 任务集合,AutoIndex 的实验平台。

一句话回顾

不要再去调"chunk size = 512 还是 1024"——把整条预处理管线变成可学习的程序,让 agent 帮你在 dev 集上搜索最优结构。这个抽象是 AutoIndex 给 RAG 工程界留下的最干净遗产。

与实际 RAG 需求的对照表

RAG 业务场景 传统预处理难点 AutoIndex 可能的程序化思路
客服 FAQ 问句-答句分块困难 enrich 出问句对,加入检索词表
法律合同 跨条款引用多、条款嵌套 reorder 切分为"条款标题+正文"二级结构
技术文档 代码块、表格、API 签名混合 表格抽取、代码块作为独立字段重加权
论文/学术 公式/图注/附录干扰 discard 页眉页脚,enrich 出 abstract / TLDR
多语言 FAQ 翻译质量不一 normalize 统一术语表,reweight 中英字段
商品检索 类目属性嵌套 reorganize 成 属性-值 表格,重加权属性列

这个表只是示例,原文并未提供这些场景下的实测数字。但表里任何一行都是传统 chunk 调参无法一次性搞定的——AutoIndex 提供了可编程路径。

如何在 1 天内验证 AutoIndex 思路

  1. 抽取你现有 RAG 索引里的失败 query 20-50 条作为 dev 集;
  2. 列出你能用脚本实现的原子(切分、归一化、字段重加权、摘要追加)到 5-10 个;
  3. 让一个 LLM agent 读 dev 集失败原因 + 原子集,合成 4 个候选 program;
  4. 离线跑 BM25 评估 Recall@100,只保留提升的版本;
  5. 到生产用 5% 流量 A/B

如果这 5 步在 24 小时内让 Recall 涨 5%+,那 AutoIndex 的思路至少在你这个领域是值得的。这是个低成本快验证路径。

拆解 agent 诊断阶段的"提示骨架"

论文里诊断 agent 拿到的东西无非三类:

  • 当前 program 是什么:原子序列和参数;
  • dev 集上失败 query:query、命中的 top-K 文档、该 query 的 qrels;
  • 检索指标分项:哪些 query 类型召不回、哪些 query 类型 nDCG 低。

拼出来大概是这样一段提示:

你是检索诊断助手。当前索引程序是:
{program}
以下是 dev 集中召回失败 / 排序错误的 query 及上下文:
{failures}
请只依据以上信息输出:
1. 3 条最可能的程序缺陷(例:切分太粗、某字段权重过低、未抽取表头);
2. 对应原子的推荐调整方向。
不要修改检索器、不要推荐换 dense 模型。

原文并未公开完整提示,但这个骨架是论文方法的最小可复现主体。它能让你在不读 PDF 的前提下复现 80% 的能力。

为什么这个抽象"在点上"

检索这个领域里有三类变量:语料侧(怎么加工)、检索侧(怎么打分)、交互侧(怎么读 query)。过去 5 年大家几乎全扑在检索侧:ColBERT / SPLADE / E5 / BGE / Qwen3-Embedding 都在打分函数上堆创新。语料侧一直是被默认拍板的冷门区。AutoIndex 的贡献是在语料侧点了一个可以发力的灯:既然打分函数是可微可学习的,那表达函数也应该可搜索。把"可优化对象"从打分函数拓到"表达函数",这个动作是本文真正的理论价值。


工程落地与核查(Jay)

1. 工程复现路径

最小可跑命令流(BM25 + 5 原子版)

# 依赖:rank_bm25 / llmx / jsonlines
# 硬件:CPU 即可,无 GPU 要求;CRUMB 规模单次索引 < 5 分钟

from bm25 import BM25Okapi
from atoms import slice, normalize, enrich, reweight, discard
import jsonlines, llmx

# 1. 准备 dev 集
queries = [...]      # list[str], 20-50 条
qrels  = {...}       # dict[query_id, list[relevant_doc_ids]]
documents = [...]    # list[str], 原始文档

# 2. 定义原子集(可按需扩展)
def apply_program(doc, program):
    for op in program:
        op_name, params = op["op"], op["params"]
        doc = {
            "slice":    lambda p: slice(doc, **p),
            "normalize": lambda p: normalize(doc, **p),
            "enrich":   lambda p: enrich(doc, **p),
            "reweight": lambda p: reweight(doc, **p),
            "discard":  lambda p: discard(doc, **p),
        }[op_name](params)
    return doc

# 3. 初始化 base program
base_program = [{"op": "slice", "params": {"window": 256, "overlap": 32}}]

# 4. 搜索循环(K=20 轮,n=4 候选)
P = base_program
for _ in range(20):
    indexed = [apply_program(d, P) for d in documents]
    bm25 = BM25Okapi(indexed)
    m_prev = recall_at_k(bm25, queries, qrels, k=100)

    failures = diagnose(P, bm25, queries, qrels, llm)
    candidates = propose(P, failures, llm, n=4)

    P_next, m_next = P, m_prev
    for c in candidates:
        idx_c = BM25Okapi([apply_program(d, c) for d in documents])
        m_c = recall_at_k(idx_c, queries, qrels, k=100)
        if m_c > m_next:
            P_next, m_next = c, m_c
    P = P_next

print("Best program:", P)

⚠️ 存疑:原文未公开 diagnose() / propose() 完整提示词;上图为方法骨架推断,prompt 工程质量直接影响程序搜索效果。原文明确说 BM25 冻结、无 rerank,所有实验结论以此为前提。

2. 已知坑位清单

描述 规避方案
agent 诊断不稳定 同一套 failures 输入,LLM 两次诊断可能不同,导致搜索路径分叉 对候选合成加 temperature=0,或多次采样后取共识缺陷描述
原子集覆盖决定收益上限 若业务文档有 PDF 表格/脚注/公式,且原子集无对应操作,agent 搜不到有效 program 开盖前先盘点文档类型,覆盖率不足时先扩展原子集再搜索
dev 集过拟合 程序在 dev 集上涨点,生产流量掉点(分布漂移) 留 20% dev 集做"真 held-out"验证;跨域场景至少在 2 个不同业务 dev 上同步评估
重新索引成本 亿级语料每轮迭代全量重索引,P99 成本高 先在小样上搜索(1% 语料),确定 program 再全量应用;或限制 K≤10
与 dense/rerank 叠加效果未知 原文只测 BM25,与 dense 或 ColBERT 叠加是否让 gain 缩小或放大,未知 建议按「AutoIndex + BM25」→「AutoIndex + dense」→「AutoIndex + rerank」三阶段 A/B 验证
enrich 原子引入幻觉 若 enrich 用 LLM 生成摘要/问答对,生成内容可能带错误信息 enrich 输出加 ROUGE-L 与原文重叠率校验,过低则丢弃该原子

3. 核查清单

  • [ ] arXiv ID 核验2607.18603 于 2026-07 提交,摘要与解读一致 ✓(本核查已验证)
  • [ ] 数字核查:摘要原文 "+8.4% Recall@100, +8.3% nDCG@10, +30.5% / +43.6%" 全部与摘要一致 ✓
  • [ ] 基准核查:CRUMB = 8 任务,BM25 全程冻结 ✓;dense / rerank 未测试 ✓
  • [ ] 原子集核查slice / normalize / enrich / reweight / reorganize / discard 六原子,reorganize 原文为 "reorganize" 非 "reorder",解读中两词混用已统一为 reorganize
  • [ ] 代码可用性:原文 GitHub 链接需查阅 arXiv PDF;解读骨架不含源码,生产落地前需获取原始实现