☰
MultiHop-RAG 论文速读:用 TaoToken 统一 Key 跑通多跳 RAG 基准测试
2026/9/29 3:12:17 网站建设 项目流程

1. 多跳查询为什么总在 RAG 评测里翻车

MultiHop-RAG 这篇论文做的事情,用一句话说清楚:它把「需要跨多篇文档、经过桥接实体推理才能回答」的问题单独拎出来,做成一个 2556 条规模的基准,专门用来打脸那些在单跳问答上刷分很漂亮、一到多跳就露馅的 RAG 系统。如果你正在做检索增强生成(Retrieval-Augmented Generation)的工程落地,或者想复现论文里的 MAP@10、MRR@10、Hit@10 这些指标,这篇速读会帮你把评测链路拆成能跑的本地流程。

它适合谁?适合已经跑通过基础 RAG demo、想进一步验证「我的检索器到底能不能扛住多跳查询」的开发者。论文把查询分成四类:推断查询(816 条,31.92%)、比较查询(856 条,33.49%)、时间序列查询(583 条,22.81%)、空缺查询(301 条,11.78%)。这四类的难点完全不同——推断要跨文档找因果链,比较要同时命中两个实体再对比,时间序列要判断事件先后,空缺查询则考验系统能不能老实说「信息不足」。

我在本地复现时踩过的第一个坑,不是检索算法,而是评测脚本里散落着多个模型的 Key:嵌入模型一个、重排模型一个、生成模型一个,环境变量改到怀疑人生。所以这篇的重点不是复述论文结论,而是给你一套用统一 Key 接入评测脚本的骨架,让 settings.json 和 config.toml 一次配好,把精力留给检索链路本身。

2. 用 TaoToken 统一 Key 接管评测里的多模型调用

MultiHop-RAG 的评测天然是多模型的:检索阶段要调嵌入模型(论文里对比了 ADA Embeddings、Voyager-02、BGE-Large-EN-V1.5、JINA、e5-Base-V2),重排阶段要调 BGE-ReRanker-Large,生成阶段要调 GPT-4、GPT-3.5、Claude-2 这类 LLM。如果每个模型都单独申请 Key、单独配 base_url,评测脚本会变成一堆 if-else。

TaoToken 在这里的价值就是「一个 Key 走通所有模型调用」。它的接口是 OpenAI 兼容格式,你只需要把 base_url 指向https://taotoken.net/api,把 api_key 换成 TaoToken 的 Key,嵌入、重排、对话三类请求都能发出去。这样评测脚本里只需要维护一份凭证,切换模型只改 model 字段,不用动鉴权逻辑。

具体来说,你需要先拿到 Key。登录后在控制台的 API Keys 页面创建一个,建议按项目命名,比如multihop-rag-eval,方便后面区分。创建入口在这里:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite

拿到 Key 之后,先别急着写评测代码,用模型对话页面手动发一条多跳问题,确认链路是通的。比如把论文里的比较查询例子丢进去,看模型能不能基于你给的证据句做出对比判断。对话入口:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite

如果你打算长期跑这类评测,甚至把评测脚本接进 CI,那按量计费的 API Key 模式会比反复手动调用更省心。Coding Plan 适合这种「脚本长期跑、调用量稳定」的场景,入口:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite

注意:评测脚本里不要把 Key 硬编码进代码。用环境变量或独立的配置文件,后面我会给出 settings.json 和 config.toml 两种写法。

3. settings.json 与 config.toml 骨架:把多模型配置收口

评测脚本的配置分两层:一层是「凭证与端点」,一层是「评测参数」。前者用 settings.json,后者用 config.toml,职责分开,改参数不会误碰 Key。

3.1 settings.json:凭证与模型端点

{ "provider": { "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "timeout_seconds": 60, "max_retries": 3 }, "models": { "embedding": { "name": "text-embedding-ada-002", "batch_size": 64 }, "reranker": { "name": "bge-reranker-large", "top_n": 10 }, "generator": { "name": "gpt-4", "temperature": 0.0, "max_tokens": 512 } } }

这里的关键是api_key_env,它指向环境变量名而不是 Key 本身。运行时这样注入:

export TAOTOKEN_API_KEY="你的Key" python run_eval.py --config config.toml --settings settings.json

temperature设成 0.0 是为了评测可复现——生成质量评估要的是稳定输出,不是创意。max_tokens给 512 足够覆盖论文里那些答案长度,空缺查询的「insufficient information」也不会被截断。

3.2 config.toml:评测参数与数据集路径

[dataset] name = "MultiHop-RAG" root = "./data/multihop_rag" query_file = "queries.jsonl" corpus_file = "corpus.jsonl" evidence_file = "evidence.jsonl" [retrieval] top_k = 10 metrics = ["MAP@10", "MRR@10", "Hit@10"] rerank_enabled = true [generation] eval_split = "retrieved" # 可选 retrieved / ground_truth answer_metric = "exact_match" [query_types] enabled = ["inference", "comparison", "temporal", "null"]

eval_split这个字段对应论文里的两组实验:retrieved是用检索到的文本喂给 LLM,ground_truth是直接用真实证据句,用来测性能上限。论文表 3 里 GPT-4 在 retrieved 下是 0.56,在 ground_truth 下是 0.89,这个差距就是检索链路要补的空间。

rerank_enabled打开后会走 BGE-ReRanker-Large,论文里 Voyager-02 加 BGE-ReRanker-Large 的组合在命中率上表现最好,但 k=4 时 Hit@4 也只有 0.6625,说明重排不是银弹,多跳场景下前 k 个结果的召回仍然是瓶颈。

3.3 检索脚本里怎么读这两份配置

import json, os, tomllib from openai import OpenAI with open("settings.json") as f: settings = json.load(f) with open("config.toml", "rb") as f: config = tomllib.load(f) client = OpenAI( base_url=settings["provider"]["base_url"], api_key=os.environ[settings["provider"]["api_key_env"]], ) def embed(texts): resp = client.embeddings.create( model=settings["models"]["embedding"]["name"], input=texts, ) return [d.embedding for d in resp.data]

这段代码里没有任何模型专属的鉴权分支,换嵌入模型只改 settings.json 里的name,评测逻辑不动。这就是统一 Key 接入的实际收益。

4. 跑一次多跳问答:检索命中验证动作

配置就绪后,先别跑全量 2556 条,用一条比较查询做冒烟测试。论文里的例子是「Product Line A 和 Product Line B 哪个同比增长率更大」,答案是 Product Line B。这条查询需要同时命中两个产品线的财报段落,是典型的多跳。

4.1 构造检索请求并检查命中

query = "According to the evidence, which product line showed a larger year-over-year growth rate, Product Line A or Product Line B?" q_vec = embed([query])[0] corpus_texts = load_corpus(config["dataset"]["corpus_file"]) corpus_vecs = embed(corpus_texts) scores = cosine_similarity(q_vec, corpus_vecs) top_k_idx = scores.argsort()[::-1][:config["retrieval"]["top_k"]] retrieved = [corpus_texts[i] for i in top_k_idx] for rank, doc in enumerate(retrieved, 1): print(f"[{rank}] {doc[:120]}...")

跑完之后你要看的不是「有没有返回结果」,而是「两个产品线的证据句是否都进了 top_k」。如果只命中一个,那这条多跳查询在检索阶段就已经断了,后面的生成再强也救不回来。论文里 Hit@10 最高的 BGE-Large-EN-V1.5 是 0.6718,意味着大约三分之一的查询在 top 10 里凑不齐全部证据。

4.2 把检索结果喂给生成模型

context = "\n\n".join(retrieved) prompt = f"Based on the following evidence, answer the question.\n\nEvidence:\n{context}\n\nQuestion: {query}\nAnswer:" resp = client.chat.completions.create( model=settings["models"]["generator"]["name"], messages=[{"role": "user", "content": prompt}], temperature=settings["models"]["generator"]["temperature"], max_tokens=settings["models"]["generator"]["max_tokens"], ) print(resp.choices[0].message.content)

如果检索阶段两个证据句都进了上下文,GPT-4 这类模型基本能给出正确对比;如果只进了一个,你会看到它要么猜、要么答「insufficient information」。这个对比动作就是验证检索链路是否达标的最小闭环。

4.3 批量跑指标

单条验证通过后,把上面的逻辑包成循环,对四类查询分别统计 MAP@10、MRR@10、Hit@10。空缺查询要单独处理——它的正确答案是「信息不足」,如果检索器硬凑出一堆不相关文档,生成模型反而容易被带偏。论文把空缺查询单列一类(301 条,11.78%),就是在测系统「知道什么时候该说不知道」的能力。

5. 复现时最容易卡住的几个点

Key 读不到:最常见的是api_key_env指向的环境变量没 export,或者 export 在了另一个 shell 会话里。跑脚本前先echo $TAOTOKEN_API_KEY确认非空。如果用的是 config.toml 直接写 Key,注意别把配置文件提交到仓库。

嵌入维度对不上:不同嵌入模型输出维度不同,如果你先建了向量库再换模型,余弦相似度计算会直接报错。换模型时要么重建索引,要么在 settings.json 里记录维度并做校验。

top_k 设太小:多跳查询需要多个证据句同时进上下文,k=4 时论文里最好的组合 Hit@4 也只有 0.6625。评测阶段建议 k 至少给 10,重排后再截断到生成模型能接受的上下文长度。

重排模型没生效:rerank_enabled = true但脚本里没实际调用重排接口,指标会和纯向量检索一样。检查日志里有没有重排请求发出,以及 top_n 是否小于 top_k。

空缺查询被当成普通查询:如果评测脚本对四类查询用同一套 prompt,空缺查询的准确率会虚低。给空缺查询单独准备「若无相关证据请回答 insufficient information」的指令模板。

超时与重试:批量跑 2556 条时,偶发的网络超时会让整批中断。settings.json 里的max_retries和timeout_seconds要配合使用,重试时对同一条查询做幂等处理,避免重复计费。

6. 把评测接进日常开发流

跑通单条验证和批量指标之后,下一步是让这套流程可重复。我的做法是把 settings.json 和 config.toml 都纳入版本管理(Key 走环境变量),每次改检索策略就重跑一遍四类查询的分项指标,而不是只看总分。因为 MultiHop-RAG 的四类查询难度差异很大,总分会被占比最高的比较查询和推断查询主导,时间序列和空缺查询的退化容易被掩盖。

如果你要把评测脚本接进 CI 或者做成定时任务,用 Coding Plan 会比每次手动配 Key 更顺,调用额度稳定,脚本可以长期挂着跑。接入文档里有 OpenAI 兼容接口的完整说明,包括嵌入、对话、重排的请求格式,照着改 base_url 就行:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

最后留一个实用习惯:每次换嵌入模型或重排模型,先只跑空缺查询和时间序列查询这两类小样本(合计 884 条),它们对检索噪声最敏感,能最快暴露链路问题。等这两类稳了,再跑全量。这样一轮验证从几小时压到几十分钟,迭代速度会快很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询