feat(agent): 用检索相关性引导 grep_chunks(RARG 论文落地) - #52
Draft
lyingbug wants to merge 3 commits into
Draft
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
读了腾讯 + 中科院信工所的 RARG 论文(arXiv:2607.24223,A New Role for Relevance: Guiding Corpus Interaction in Agentic Search)。核心主张是:相关性分数不应只用来决定"把哪些内容给模型",还应该决定"关键词搜索先扫哪些文档、先显示哪些匹配"。
对照下来,WeKnora 的
grep_chunks恰好就是论文批评的 DCI(相关性无感的 grep 智能体)。问题
grep_chunks取候选池用的是:一个宽泛的正则在几万 chunk 的知识库上轻易匹配上千条,但只有最新的 500 条会被取出来。一份两年前写的、正好包含答案的文档,无论正则写得多好都进不了候选池。随后 500 → 30 的收敛又纯靠正则命中次数,一份反复提到某关键词的长 FAQ 会稳定压过那一段真正回答了问题、但只提了一次该词的正文。
另外,
knowledge_search算出来的相关性排序在它返回之后就被丢弃了,紧接着运行的grep_chunks对此一无所知。改动
引入每轮 Agent 运行共享的
RelevanceScope,由ToolRegistry持有,服务端自动填充和消费(模型无感,不需要它遵守任何新协议——论文里较弱的模型守不住多阶段协议正是一个已知风险)。文档级:候选池按相关性排序(对应论文的 RARG)
取数拆成两趟:先在已排序文档内按排名扫描(
CASE ... END把排名带进 SQLORDER BY,文档 ID 全部参数绑定),再用剩余预算扫描其余文档。排序只引导、不过滤——这是对论文的有意改造:论文的 scope 覆盖全库 10%,而我们的 scope 只有几百份文档,硬限制会让召回崩掉,而"找到语义检索漏掉的那个字符串"正是 grep 存在的理由。顺带补上了确定性 tie-break:原来同一排序键下的顺序由存储引擎决定,同一条命令重跑可能给模型看到不同的证据。
匹配级:语义重排(对应 RARG++)
候选数超过输出上限时,对前 120 条按论文的构造式 query 重排:
Query: <本轮检索 query 或用户原问题>+Search focus: <从正则提取的字面关键词>。与论文不同的是按 0.6 语义 + 0.4 词频加权而非直接替换——WeKnora 的 grep 有大量查错误码/工单号的用法,这类场景词频命中就是全部信号。scope 宽度
只记录重排后的结果会让 scope 只有个位数文档,太窄。
HybridSearch内部本来就会过取几百条相关性排序的候选,复用这批零成本,把排序面扩到上百份。重排后的结果仍排在最前。兼容性与降级
WEKNORA_GREP_RERANK_POOL=0→ 完全关闭匹配级重排测试
新增 26 个测试,其中 6 个是对真实 SQLite 库的集成测试(构造"关键文档很旧、上面压着大量较新的偶然匹配"的场景),验证:
go build ./...、go vet、go test ./internal/agent/... ./internal/application/service/全部通过,新增代码-race通过。(TestTenantAPIKeyServiceAuthenticateThrottlesLastUsedUpdates在-race下失败,已确认在main上同样失败,与本 PR 无关。)文档
docs/RARG论文启示与落地方案.md记录了论文机制、与 WeKnora 现状的逐条对照、已落地的改动,以及建议后续推进的方向——包括论文自己的负面结论:匹配级重排在问答任务上 +6 个点,在需要广泛召回的排序任务上反而 -1.2 个点,因此不宜当成固定升级项,Wiki 生成/文档盘点这类广度任务应该关掉或调低。