Skip to content

feat(agent): 用检索相关性引导 grep_chunks(RARG 论文落地) - #52

Draft
lyingbug wants to merge 3 commits into
mainfrom
cursor/rarg-relevance-guided-grep-de87
Draft

feat(agent): 用检索相关性引导 grep_chunks(RARG 论文落地)#52
lyingbug wants to merge 3 commits into
mainfrom
cursor/rarg-relevance-guided-grep-de87

Conversation

@lyingbug

@lyingbug lyingbug commented Aug 5, 2026

Copy link
Copy Markdown
Owner

背景

读了腾讯 + 中科院信工所的 RARG 论文(arXiv:2607.24223A New Role for Relevance: Guiding Corpus Interaction in Agentic Search)。核心主张是:相关性分数不应只用来决定"把哪些内容给模型",还应该决定"关键词搜索先扫哪些文档、先显示哪些匹配"。

对照下来,WeKnora 的 grep_chunks 恰好就是论文批评的 DCI(相关性无感的 grep 智能体)。

问题

grep_chunks 取候选池用的是:

query.Order("chunks.created_at DESC").Limit(500)

一个宽泛的正则在几万 chunk 的知识库上轻易匹配上千条,但只有最新的 500 条会被取出来。一份两年前写的、正好包含答案的文档,无论正则写得多好都进不了候选池。随后 500 → 30 的收敛又纯靠正则命中次数,一份反复提到某关键词的长 FAQ 会稳定压过那一段真正回答了问题、但只提了一次该词的正文。

另外,knowledge_search 算出来的相关性排序在它返回之后就被丢弃了,紧接着运行的 grep_chunks 对此一无所知。

改动

引入每轮 Agent 运行共享的 RelevanceScope,由 ToolRegistry 持有,服务端自动填充和消费(模型无感,不需要它遵守任何新协议——论文里较弱的模型守不住多阶段协议正是一个已知风险)。

文档级:候选池按相关性排序(对应论文的 RARG)
取数拆成两趟:先在已排序文档内按排名扫描(CASE ... END 把排名带进 SQL ORDER BY,文档 ID 全部参数绑定),再用剩余预算扫描其余文档。排序只引导、不过滤——这是对论文的有意改造:论文的 scope 覆盖全库 10%,而我们的 scope 只有几百份文档,硬限制会让召回崩掉,而"找到语义检索漏掉的那个字符串"正是 grep 存在的理由。

顺带补上了确定性 tie-break:原来同一排序键下的顺序由存储引擎决定,同一条命令重跑可能给模型看到不同的证据。

匹配级:语义重排(对应 RARG++)
候选数超过输出上限时,对前 120 条按论文的构造式 query 重排:Query: <本轮检索 query 或用户原问题> + Search focus: <从正则提取的字面关键词>。与论文不同的是按 0.6 语义 + 0.4 词频加权而非直接替换——WeKnora 的 grep 有大量查错误码/工单号的用法,这类场景词频命中就是全部信号。

scope 宽度
只记录重排后的结果会让 scope 只有个位数文档,太窄。HybridSearch 内部本来就会过取几百条相关性排序的候选,复用这批零成本,把排序面扩到上百份。重排后的结果仍排在最前。

兼容性与降级

  • 没有相关性排序时 → 退回原来的按时间取数
  • 没有配置 rerank 模型时 → 不做匹配级重排
  • 候选数已装得下输出预算时 → 跳过重排(改变不了模型看到什么,纯属加延迟)
  • rerank 调用失败时 → 降级回词频排序,不丢结果
  • WEKNORA_GREP_RERANK_POOL=0 → 完全关闭匹配级重排

测试

新增 26 个测试,其中 6 个是对真实 SQLite 库的集成测试(构造"关键文档很旧、上面压着大量较新的偶然匹配"的场景),验证:

  • 无排序时退回按时间取数
  • 有排序时按排名遍历,旧的高相关文档被优先扫描
  • 排序不会变成过滤,未排序文档仍能填满剩余预算,且两趟之间无重复
  • 排名足够宽时不浪费第二次查询
  • 重复扫描结果完全一致
  • 语义重排把真正回答问题的片段提到首位,同时保留词法区分度
  • 重排失败/无模型/无 query 信号时的降级路径

go build ./...go vetgo test ./internal/agent/... ./internal/application/service/ 全部通过,新增代码 -race 通过。(TestTenantAPIKeyServiceAuthenticateThrottlesLastUsedUpdates-race 下失败,已确认在 main 上同样失败,与本 PR 无关。)

文档

docs/RARG论文启示与落地方案.md 记录了论文机制、与 WeKnora 现状的逐条对照、已落地的改动,以及建议后续推进的方向——包括论文自己的负面结论:匹配级重排在问答任务上 +6 个点,在需要广泛召回的排序任务上反而 -1.2 个点,因此不宜当成固定升级项,Wiki 生成/文档盘点这类广度任务应该关掉或调低。

Open in Web Open in Cursor 

lyingbug and others added 3 commits August 5, 2026 06:33
grep_chunks selected its candidate pool with ORDER BY created_at DESC LIMIT
500, so a regex matching more chunks than the cap kept whichever ones were
newest. Evidence in an older document was unreachable no matter how well the
regex was written, and the 500 -> 30 narrowing that followed ranked purely on
keyword density.

Carry the document ranking that knowledge_search already computes over to the
lexical tools through a per-run RelevanceScope, and use it two ways:

- Scan ranked documents first, then spend the remaining candidate budget on
  the rest of the corpus, so ranking steers traversal without becoming a
  filter that would defeat the point of grep.
- Rerank the leading matches against the turn's goal combined with the
  regex's literal terms, so a decisive excerpt inside a weakly-ranked
  document can compete for the reply budget. Lexical score keeps 40% weight
  so identifier lookups still behave.

Both degrade to the previous behaviour when no ranking exists or no rerank
model is configured, and the pool ordering is now deterministic.

Follows RARG (arXiv:2607.24223).

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
…pool

Recording only the reranked results left the scope a handful of documents
wide, too narrow to steer a scan over a large knowledge base. Hybrid search
already over-fetches several hundred relevance-ordered candidates before
reranking discards them; reusing that ordering costs nothing and extends the
prior from single digits to hundreds of documents.

Reranked documents are still recorded first so they keep the better rank.

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
Covers what the paper changes about the role of relevance, how WeKnora's
existing grep_chunks lined up with the relevance-agnostic baseline it
criticises, what was implemented, and which follow-ups are worth taking --
including the paper's own finding that match-level reranking helps question
answering but hurts tasks that reward broad recall.

Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant