ReContext 原理解析与使用教程

> **ReContext: Recursive Evidence Replay as an LLM Harn...

> ReContext: Recursive Evidence Replay as an LLM Harness for Long-Context Reasoning > Princeton Language and Intelligence (PLI) · MIT License · 2026

目录

一、缘起:长上下文之困二、核心思想:以模型之眼,照见自身所重三、整体架构一览四、四步算法详解五、代码架构:模块与数据流六、RescaleConfig 配置详解七、数据集与评估体系八、基线方法对比九、使用教程:从零复现十、实战示例与调参指南十一、设计哲学与局限附录:术语表

一、缘起:长上下文之困

长上下文推理之难,非唯”塞得下”,更在”看得准”。

今之 LLM,动辄支持 128K 乃至百万 token 之上下文。然则上下文愈长,模型于生成时能否反复聚焦于那寥寥数行真正相关的证据,方是成败关键。研究者发现一现象,可谓触目惊心:

!teaser.png

在 128K token 的上下文中,仅占 0.1% 的问题相关 token,就已囊括了不同 LLM 累积相关性分数的约 50% 至 80%。

此即”稀疏相关性”(sparse relevance)——证据虽藏于浩瀚文卷之中,然其分布极不均匀,真正要紧者不过凤毛麟角。然模型生成时,注意力弥散于全文,未必能反复回到那几处关键句上。

传统对策有三,皆有其弊:

对策思路弊端
检索增强(RAG)外接检索器,抽出 top-k 段落丢弃上下文,模型所见已非全貌;检索器一错,全盘皆输
上下文压缩用摘要/裁剪把长文压短信息有损,压缩过程本身可能丢失关键证据
微调/长训练让模型学会长上下文代价高昂,且未必泛化

ReContext 另辟蹊径——不增不改,唯”重放”耳。既不外接检索器,亦不裁剪上下文,更不重训模型。它所做的,是借模型自身之注意力,照见其本已重视之 token,将这些 token 所在之句,复抄录一份,置于问题近旁,令模型生成时再读一遍。

二、核心思想:以模型之眼,照见自身所重

ReContext 之名,可拆而解之:

Recursive —— 递归。证据池非一蹴而就,可经数轮精炼。 – Evidence —— 证据。从上下文中物化出的、可读的句子。 – Replay —— 重放。将证据复抄一份,插入 prompt 近问题处,令模型再读。

其精髓可一言蔽之:

> 长上下文中,模型本已”心知”哪些 token 要紧(体现于注意力)。ReContext 所为,不过是把这层”心知”显式化——把模型重视的 token 所在之句,原样复制,置于问题之前,使生成时再度入眼。

此处有四要,须铭记:

1. 证据来自原 prompt,非他模型所生成。无幻觉之虞,无信息失真。 2. 原 prompt 全数保留,不予裁剪。重放是”追加”而非”替换”,未选中的上下文依旧在位。 3. 不改动模型权重。纯推理时干预,与训练无关,开箱即用。 4. 可递归精炼。多轮重放,每轮在新 prompt 上重计注意力,证据池逐步聚焦。

!main.png

三、整体架构一览

ReContext 仓库之结构,可图示如下:

ReContext/ ├── recontext/ # 核心算法包 │ ├── custom_mixin.py # ★ 生成逻辑核心:RescaleConfig + CustomGenerationMixin │ ├── custom_modeling_llama.py # Llama 改造版(注入 intervention 接口) │ ├── custom_modeling_qwen3.py # Qwen3 改造版 │ ├── custom_modeling_qwen3_moe.py# Qwen3-MoE 改造版 │ └── dac_compressor.py # DAC 基线的 prompt 压缩器 ├── eval_datasets/ # 数据集加载器 │ ├── helmet/ # HELMET 基准(KILT QA / NarrativeQA / InfiniteBench) │ ├── clipper/ # CLIPPER 真伪判别 │ ├── longbenchv2/ # LongBench v2 多选 │ ├── longproc/ # LongProc 路径行走 │ └── mrcr/ # MRCR 多针检索 ├── recontext_cfgs/ # 三大 backbone 的超参配置 │ ├── qwen3_4b.yaml │ ├── qwen3_8b.yaml │ └── llama3_8b.yaml ├── reproduce_scripts/ │ └── reproduce_recontext_qwen3_4b.sh # Qwen3-4B 全数据集复现脚本 ├── run_eval.py # ★ 评测主入口 ├── data_utils.py # 数据集分发器 ├── env.yml # conda 环境 └── assets/ # 论文配图 ├── teaser.png ├── main.png └── exp.png

设计上有三层分离,颇为清爽:

算法层recontext/):模型改造 + 生成逻辑。与数据集无关。 – 数据层eval_datasets/):各数据集的加载与后处理。与算法解耦。 – 调度层run_eval.py + data_utils.py):解析参数、装配模型与数据、跑评测、存结果。

四、四步算法详解

ReContext 之核心流程,分四步而行。见于 recontext/custom_mixin.py_sentence_replay_sample_multi_round_sentence_replay_sample 方法。

步骤一:注意力读取(Attention Readout)

模型对原始长 prompt 做一次”通读”。ReContext 从问题侧的提示 token(query-side cue tokens)出发,在选定的检索头(retrieval heads)上聚合注意力,为上下文中每个 token 打一个”相关性分数”。

何谓”检索头”?乃注意力机制中某些特定 (layer, head) 对,它们天生善于把 query 与相关 context 对应起来。配置中 selected_heads 即此,例如 Qwen3-4B 选了 16 个头:

selected_heads: “[(20,15),(21,18),(21,31),(22,4),(21,11),(24,29),…]”

聚合之术,见 _aggregate_head_attention

def _aggregate_head_attention(attention_outputs, selected_heads): per_head = [] for layer, head in selected_heads: per_head.append(attention_outputs[layer][:, head,]) return torch.stack(per_head, dim=0).mean(dim=0).squeeze(1)

即:取出每个选定头在该位置的注意力分布,跨头取均值,得一 [B, L] 的向量,L 为已生成的上下文长度。此向量即每个上下文 token 的”当下相关性”。

然单步注意力噪声颇大。故 ReContext 引入衰减累积_apply_importance_decay):

def _apply_importance_decay(cur_importance, past_importance, decay_factor): cur_importance[:, :-1] += past_importance * decay_factor cur_importance = cur_importance / torch.sum(cur_importance, dim=1) return cur_importance

即:新注意力 = 当前注意力 + 过去注意力 × 衰减因子,再归一化。decay_factor=0.75 意味历史权重逐步淡去,但不全忘。如此在 context_warmup_steps(默认 8)步内累积,得一个较为稳健的相关性估计。

具体执行时,ReContext 取问题末尾 context_warmup_steps 个 token 作为”回看窗口”,逐个 forward,每步聚合注意力并衰减累积。如此既不重跑全文,又能借问题邻近 token 之注意力(此正处于”即将回答”之状态,注意力最聚焦)。

步骤二:证据物化(Evidence Materialization)

得到每个上下文 token 的相关性分数后,ReContext 选出 top-k(或 top-p 核采样)的 token,然后把这些 token 映射回它们所在的句子

此步关键在于 token → 句子的映射,见 _selected_tokens_to_sentence_info

1. 把选定 token 的 piece 解码为字符串,拼成全文; 2. 用 nltk.sent_tokenize(或回退到正则)切句,得句子边界; 3. 对每个选定 token,找到其字符区间所属的句子; 4. 去重,得”被选中的句子集合”。

如此,稀疏的 token 重要性,便化为可读的、完整的句子——此即”证据池”。这些句子直接从原 prompt 复制而来,非模型生成,无幻觉之虞。

选 token 之术有三,由 selection_method 决定:

方法含义触发条件
top_k取分数最高的前 K 个 token仅设 top_k
top_percentile核采样:取累积概率达 p 的 token仅设 top_p
hybrid先核采样,若超 K 则退回 top_k同时设 top_ktop_p

复现脚本中多用 top_k(设 top_p=0 关闭核采样)。

步骤三:递归重放(Recursive Replay)

此步乃 ReContext 之精髓。证据池非一成不变,可经 replay_rounds 轮精炼:

单轮(replay_rounds=1:算一次重要性 → 选 token → 物化句子 → 插入 prompt。

多轮(replay_rounds>1:每轮在前一轮的”扩展 prompt”上重新计算注意力,选新的 token,物化新句子。新句子经去重后追加到证据池。

多轮之妙,在于”递归聚焦”——第一轮选出最显眼的证据,插入后改变了 prompt 结构;第二轮在新结构上重算注意力,可能发现第一轮被遮蔽的次级证据;如此层层深入。

去重逻辑见 _filter_sentence_info_for_replay:以归一化后的句子文本为 key,已插入过的句子不再重复。若某选定 token 所对应句子已全被覆盖(”wasted selected position”),还会触发后备选择(backup selection)——从 top-k 之后的排名区间(如 top 9~16)补选,确保每轮的有效证据数不亏。

步骤四:最终生成(Final Generation)

证据池既定,ReContext 构造”重放 prompt”,其结构依 replay_position 而异:

replay_position重放 prompt 结构说明
before_question(默认)[context] + [evidence] + [question]证据插在 context 与 question 之间
after_question[context] + [question] + [evidence]证据追加在末尾
after_question_user_side重建用户消息,证据在问题后但仍在 user 侧适配 chat template

注意:原 context 全数保留,evidence 是”追加”而非”替换”。模型生成时所见之 prompt,比原来更长,但信息无损。

生成时,ReContext 复用步骤一保存的 context KV cache 快照——context 部分已在前向时算过 KV,无需重算。只需对 [evidence] + [question] 部分做一次前向填充 cache,然后正常自回归解码。如此省去对超长 context 的重复计算,效率可保。

证据插入时,默认用模板包裹(wrap_sentence_txt=True):

below are possible supporting evidence from the context: {evidence_sentences}

最终生成之 token 序列中,evidence 部分的 token 会被跳过(不计入输出),仅解码 evidence 之后的真正回答。

五、代码架构:模块与数据流

5.1 模型改造:注入干预接口

ReContext 不改模型权重,但需在 attention 的 softmax 之前插入一个”干预向量”。故对 Llama / Qwen3 / Qwen3-MoE 三类模型各做了一份改造版(custom_modeling_*.py),核心改动有二:

改动一:Attention forward 新增 attention_logits_intervention_vector 参数

以 Qwen3 为例(eager_attention_forward):

attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling if attention_mask is not None: attn_weights = attn_weights + causal_mask

★ 关键:在 softmax 之前加上干预向量

if attention_logits_intervention_vector is not None: attn_weights = attn_weights + attention_logits_intervention_vector attn_weights = nn.functional.softmax(attn_weights, dim=-1, …)

此即 dysco 基线的核心机制:选定 token 的 logit 加 log(strength)(放大其注意力),其余加 0。strength>=99 时则直接 mask 非选定 token 到 -inf(硬选)。

改动二:新增 skip_update_past_key_value 参数

用于回滚 KV cache 的最后一次更新——ReContext 重放前向时,需”借用”模型 forward 算注意力,但不想污染生成用的 cache。此参数在 forward 末尾把刚写入的最后一个 token 的 KV 删去,恢复原状。

改动三:Flash Attention 回退

FlashAttention 不返回 attention weights,亦不支持 logit 干预。故当检测到 attention_logits_intervention_vector is not Noneoutput_attentions=True 时,自动回退到 eager attention。这是性能与功能之权衡。

5.2 生成逻辑:CustomGenerationMixin

CustomGenerationMixin 继承自 HF GenerationMixin,覆写 generaterescale_generate,并按 decoding_method 分派:

rescale_generate(input_ids, rescale_config=…, prompt_segment_context=…, …) │ ├─ 若 rescale_config is not None → _rescale_sample() │ │ │ ├─ 若 prompt_segment_context is not None(即 ReContext 模式) │ │ → _sentence_replay_sample() │ │ │ │ │ ├─ replay_rounds > 1 → _multi_round_sentence_replay_sample() │ │ └─ replay_rounds == 1 → 单轮逻辑(内联) │ │ │ └─ 否则(dysco 模式)→ 逐 token 在线干预 │ └─ 若 use_attnsharp → _sharp_sample()

5.3 Prompt 边界检测

ReContext 须知道 prompt 中”哪里是 context,哪里是 question”,方能决定证据插入位置。此逻辑在 run_eval.pybuild_prompt_segment_context 中,按数据集分四种策略:

数据集类型边界检测方式
MRCRchat template 对话格式,找最后一个 user 消息的起始 token
LongBench v2</text>nnWhat is the correct answer 标记
CLIPPER</context>nnn<statement> 标记
HELMET 系列找最后一个 nnQuestion:nQuestion: 标记

边界一旦确定,得 prompt_context_token_length(context 长度)与 prompt_question_token_length(question 长度),传给生成逻辑。

5.4 完整数据流

一次 ReContext 评测的完整流程:

run_eval.py main() │ ├─ 1. 解析参数 → args ├─ 2. 检测模型类型 (llama / qwen3 / qwen3_moe) ├─ 3. 若需长上下文,启用 YaRN RoPE 缩放 ├─ 4. 构建 RescaleConfig (YAML + CLI 覆盖) ├─ 5. 加载数据集 (data_utils.load_eval_data) │ → 返回 [{input_prompt, reference_output, item}, …] + eval_func ├─ 6. 加载改造版模型 (RescaleXxxForCausalLM) │ ├─ 7. 对每条样本: │ ├─ prepare_input_ids() → input_ids │ ├─ build_prompt_segment_context() → context/question 边界 │ └─ model.rescale_generate(input_ids, rescale_config=…, prompt_segment_context=…) │ │ │ ├─ 保存 context KV cache 快照到 CPU │ ├─ 回看 context_warmup_steps 个 token,聚合注意力,衰减累积 │ ├─ 选 top-k token → 映射回句子 → (多轮递归) │ ├─ 构造重放 prompt = [context] + [evidence] + [question] │ ├─ 从 context cache 快照恢复,前向填充 [evidence+question] │ └─ 自回归解码至 EOS │ ├─ 8. 对每条样本调 eval_func(output, ex) → metrics └─ 9. 聚合指标,存 results/{method}/{model}/{dataset}_*.json

六、RescaleConfig 配置详解

RescaleConfig 是 ReContext 全部超参的载体,定义于 recontext/custom_mixin.py。配置来源有二:YAML 文件(recontext_cfgs/*.yaml)+ 命令行覆盖。后者优先级更高。

6.1 完整参数表

参数类型含义典型值
selected_headsList[Tuple[int,int]]检索头(层, 头)列表16 个头
top_kint选 top-k token8 / 16 / 32
top_pfloat核采样阈值0.975(或 0 关闭)
strengthfloatdysco 模式的干预强度2.0
decay_factorfloat重要性衰减因子0.75
context_warmup_stepsint回看窗口大小8
intervention_warmup_stepsint干预起始偏移(dysco 用)auto
dynamic_rescalebooldysco 是否动态重计True
wrap_sentence_txtbool证据是否用 <Extra Info> 包裹True / False
replay_positionstr证据插入位置before_question
use_extension_inputsbool是否真的插入证据(False 则只算不插,作消融)True
replay_roundsint重放轮数1 / 2 / 3
selection_scopestrtoken 选择范围context / full_prompt
dedup_inserted_sentencesbool跨轮句子去重True
template_sequencesList[Tensor]模板 token 序列(不参与选择)各模型的 chat template
scale_template_tokensbool是否允许选模板 tokenFalse(默认排除)

6.2 三大 backbone 的默认配置

以 Qwen3-4B 为例(recontext_cfgs/qwen3_4b.yaml):

selected_heads: “[(20,15),(21,18),(21,31),(22,4),(21,11),(24,29), (21,10),(20,16),(21,8),(21,17),(23,30),(20,14), (20,13),(24,13),(20,4),(18,19)]” top_k: 4096 top_p: 0.975 strength: 2.0 decay_factor: 0.75 context_warmup_steps: 8 intervention_warmup: ‘auto’ scale_template_tokens: False template_sequences: – [151644, 8948, 198] # systemn – [151644, 872, 198] # usern – [151644, 77091, 198] # assistantn – [151667, 271, 151668, 271] # nnnn dynamic_rescale: True

几个要点:

selected_heads 因模型而异。Qwen3-4B / 8B / Llama3-8B 各有一组不同的头。这些头是通过分析模型在检索任务上的注意力行为筛选出来的(论文中有述)。 – template_sequences 列出 chat template 的 token 序列。这些位置的 token 不参与证据选择——否则可能选出 之类无意义 token。 – intervention_warmup: 'auto':自动检测 chat template 生成前缀的 token 数(即 assistantn 等的开销),作为 dysco 干预的起始偏移。 – top_k: 4096 + top_p: 0.975:默认是 hybrid 模式。但复现脚本中通常用 --recontext_top_p 0 关闭核采样,纯用 top_k

6.3 命令行覆盖

run_eval.py 提供了大量 --recontext_* 参数,覆盖 YAML。例如:

–recontext_top_k 32 –recontext_top_p 0 –recontext_replay_rounds 2 –recontext_selection_scope context –recontext_replay_position before_question

凡命令行未显式设(None)者,沿用 YAML;显式设者,覆盖之。此设计便于扫参。

七、数据集与评估体系

ReContext 在八大长上下文任务上评测,覆盖 QA、多选、叙事、合成、真伪判别五类。数据由 data_utils.py 统一分发。

7.1 数据集一览

数据集类型上下文长度评估指标边界检测
kilt_nq / triviaqa / hotpotqa / popqa多跳 QA~128Ksubstring exact match + ROUGEnQuestion:
narrativeqa长篇叙事 QA~130Ksubstring + ROUGEnQuestion:
infbench_qa_eng长书 QA~130Ksubstring + ROUGEnQuestion:
infbench_choice_eng长书多选~130Ksubstring exact matchnQuestion:
clipper真伪判别~128K配对准确率(TRUE+FALSE 皆对才算对)</context>
longbenchv2多选128K~256K选项抽取准确率</text>
mrcr_{2/4/8}needle多针合成检索8K~256KSequenceMatcher 相似度chat template
path_walking路径推理4K~32K路径精确匹配 + 部分匹配(LongProc 自有格式)

7.2 数据文件结构

评测数据须置于 data_eval/ 下(可从 Google Drive 下载):

data_eval/ clipper/test-100.json infbench/infbench_choice_eng_130862_100.json infbench/infbench_qa_eng_130862_100.json kilt/hotpotqa-dev-multikilt_100_k1000_dep3.json kilt/nq-dev-multikilt_100_k1000_dep6.json kilt/popqa_test_100_k1000_dep6.json kilt/triviaqa-dev-multikilt_100_k1000_dep6.json narrativeqa/narrativeqa_130772_100.json

data_utils.load_eval_data(dataset_name) 按数据集名前缀分派到对应的 load_xxx_data 函数,每个函数返回 (data_list, eval_func)data_list 中每条样本形如:

{ “input_prompt”: “…”, # 已套好模板的完整 prompt 字符串(或对话列表) “reference_output”: “…”, # 标准答案 “item”: {…} # 原始字段,供 eval_func 用 }

7.3 评估指标

各数据集的 eval_func 各异,但都返回 (metrics_dict, details_dict)。例如 CLIPPER 的配对评估(run_eval.py 末段):

if “clipper” in args.dataset: num_pairs = len(all_metrics) // 2 paired_correct = sum( 1 for i in range(num_pairs) if all_metrics[2i][“accuracy”] == 1 and all_metrics[2i+1][“accuracy”] == 1 ) avg_metrics = {“accuracy”: paired_correct / num_pairs}

即:TRUE 样本答对对应 FALSE 样本答错,方算一对正确。此评估比单条准确率更严苛,能惩罚”全答 TRUE”之类偷懒策略。

7.4 主结果

Main benchmark comparison across long-context tasks

ReContext 在 Qwen3-4B、Qwen3-8B、Llama3-8B 三个 backbone 上,于八任务平均排名皆居首。增益跨 QA、多选、叙事、合成四类任务一致,无需重训模型。

八、基线方法对比

run_eval.py 通过 --decoding_method 切换五种方法:

方法--decoding_method模型类核心机制
Flash(标准生成)flash原版直接 model.generate(),无干预
DySCOdyscoRescale 版逐 token 在线干预:每步重计注意力,选 top-k token,加 log(strength) 到其 attention logit
ReContextReContextRescale 版句子重放:算一次注意力,物化句子,插入 prompt,再生成
AttnSharpattnsharpRescale 版注意力锐化:Q,K /= sqrt(temperature),整体 sharpen
DACDAC原版离线 prompt 压缩:用预压缩的数据(keep 0.5 / 0.25)

DySCO 与 ReContext 之别(同属”注意力重缩放”家族,但机制迥异):

维度DySCOReContext
干预时机生成时逐 token生成前一次性
干预对象attention logits(连续值)prompt 结构(插入证据句)
证据形态token 级(隐式)句子级(显式可读)
是否改 prompt是(追加 evidence)
是否保留全文
计算开销每步重算注意力一次性 + cache 复用
多轮精炼否(每步独立)是(replay_rounds

DySCO 是 ReContext 的”前身”思路——直接在 attention 上动手脚。ReContext 则更进一步:把注意力信号转化为可读的句子证据,插入 prompt,让模型”看见”自己重视什么,而非仅在注意力分布上做手脚。后者更接近”显式推理”,效果亦更佳。

DAC(Prompt 压缩)recontext/dac_compressor.py 实现了一个基于 PPL + 注意力融合的压缩器。但仓库中 DAC 评测用的是离线已压缩数据data_eval_compressed/keep_0p5 等),而非在线压缩。此为基线,对比”压缩 vs 重放”两种长上下文处理哲学。

九、使用教程:从零复现

9.1 环境准备

1. 克隆仓库

git clone ReContext cd ReContext

2. 创建 conda 环境

conda env create -f env.yml conda activate recontext

3. 安装 FlashAttention(关键!无则无法跑 128K 上下文)

python -m pip install flash_attn==2.8.3

若源码编译失败(CUDA/PyTorch 版本不匹配),从 FlashAttention releases 选对应 wheel,例如:

wget -O flash_attn-2.8.3+cu12torch2.6cxx11abiTRUE-cp310-cp310-linux_x86_64.whl “https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.3/flash_attn-2.8.3+cu12torch2.6cxx11abiTRUE-cp310-cp310-linux_x86_64.whl” python -m pip install ./flash_attn-2.8.3+cu12torch2.6cxx11abiTRUE-cp310-cp310-linux_x86_64.whl

环境关键依赖env.yml 摘要):

– Python 3.10 – PyTorch 2.6.0 + CUDA 12.4 – transformers 4.57.3(★ 重要:代码适配此版本,他用或报错) – flash_attn 2.8.3 – nltk(句子切分,ReContext 物化证据时用) – pytrec-eval, rouge-score(评估用)

9.2 数据准备

从 Google Drive 下载 data_eval 压缩包,解压至仓库根目录

最终结构:

ReContext/data_eval/clipper/test-100.json

ReContext/data_eval/kilt/…

ReContext/data_eval/narrativeqa/…

ReContext/data_eval/infbench/…

9.3 模型准备

支持三个 backbone(HuggingFace Hub 自动下载):

Qwen/Qwen3-4BQwen/Qwen3-8Bmeta-llama/Llama-3.1-8B(需有访问权)

9.4 运行单次评测

最简命令(Qwen3-4B 在 KILT-NQ 上跑 ReContext):

CUDA_VISIBLE_DEVICES=0 python run_eval.py –seed 42 –generation_seed 23 –dataset kilt_nq –test_size -1 –model Qwen/Qwen3-4B –max_tokens 20 –temperature 0.0 –top_p 1.0 –max_model_len 131072 –enable_yarn –output_dir results –decoding_method ReContext –recontext_cfgs_path recontext_cfgs/qwen3_4b.yaml –recontext_top_p 0 –recontext_top_k 32 –recontext_strength 2.0 –recontext_decay_factor 0.75 –recontext_ctx_warmup 8 –recontext_interv_warmup auto –recontext_replay_rounds 1 –recontext_selection_scope context –recontext_dedup_inserted_sentences 1 –recontext_wrap_sentence_txt 0 –recontext_replay_position before_question –recontext_use_extension_inputs 1 –stop_on_newline

关键参数释义

--max_model_len 131072:128K 上下文窗口 – --enable_yarn:启用 YaRN RoPE 缩放(Qwen3 原生 32K/128K,跑 128K 需此) – --stop_on_newline:遇换行停止(QA 类短答案任务用,避免模型啰嗦) – --test_size -1:跑全量(100 条);设正数 N 则取前 N 条 – --max_tokens 20:QA 类只需短答案;多选题(infbench_choice)需 4096(要 CoT)

9.5 运行复现脚本

仓库提供了 Qwen3-4B 全数据集的扫参脚本:

先 dry-run 看看会跑哪些命令

DRY_RUN=1 bash reproduce_scripts/reproduce_recontext_qwen3_4b.sh

确认无误后正式跑

CUDA_VISIBLE_DEVICES=0 bash reproduce_scripts/reproduce_recontext_qwen3_4b.sh

脚本内含 8 个数据集的配置,每个数据集的 top_k / replay_rounds / selection_scope 各异(已调好):

数据集max_tokensstop_on_newlinetop_kroundsscope
kilt_nq20321context
kilt_triviaqa2083context
kilt_hotpotqa2083context
kilt_popqa_320321context
narrativeqa10082full_prompt
infbench_qa1082full_prompt
infbench_choice4096162context
clipper51282context

可见:短答案 QA 用单轮 + 小 top_k;需推理的(多跳、多选、真伪)用多轮 + 中等 top_k;叙事类用 full_prompt scope(允许从问题部分也选 token)。

9.6 结果文件

结果存于 results/ReContext/Qwen-Qwen3-4B/ 下,文件名编码了全部超参,例如:

kilt_nq_modlen131072_max20t0.0p1.0_dynamicrescaleheadk32p0.0s2.0df0.75 _ctxwarm8intwarm_wrapsent0_useext1_thinkFalse_42and23_testsz-1.json

同目录还会有 *.scores.json,仅含平均指标,便于批量比较。--auto_skip 可跳过已完成的(文件已存在则跳过)。

9.7 跑基线对比

--decoding_method 改为对应值即可:

Flash 基线

–decoding_method flash

DySCO 基线(逐 token 在线干预)

–decoding_method dysco

AttnSharp 基线

–decoding_method attnsharp –attention_logits_temperature 0.5

DAC 基线(需先准备 data_eval_compressed/)

–decoding_method DAC –dac_compression_rate 0.5

十、实战示例与调参指南

10.1 调参心法

ReContext 的超参虽多,然其要者不过四端:

一曰”看多远”——context_warmup_steps

回看窗口大小。太小则注意力估计噪声大;太大则计算开销增。默认 8,已为 sweet spot。一般不需调。

二曰”选多少”——top_k / top_p

top_k 小(8~16):证据精炼,聚焦最相关。适合短答案 QA、单跳任务。 – top_k 大(32+):证据宽泛,覆盖多跳。适合多跳 QA、需综合多处证据的任务。 – top_p:核采样,自适应数量。但复现脚本中多关闭(设 0),纯用 top_k 更可控。

三曰”几轮”——replay_rounds

– 1 轮:快,适合简单任务(单跳 QA)。 – 2~3 轮:递归精炼,适合多跳、需深挖的任务(多选、真伪判别)。 – 超过 3 轮:收益递减,且可能引入噪声。

四曰”选哪里”——selection_scope

context:只在 context 部分选 token。最常见,避免选出问题本身的 token。 – full_prompt:全 prompt 皆可选。适合叙事类(narrativeqa)——此类任务的问题本身也可能含线索。

10.2 常见问题排查

Q1:跑 128K 上下文时 OOM?

– 确认装了 FlashAttention 2(pip show flash_attn 应有 +cu12 之类后缀) – 确认 --max_model_len 不超模型上限 – Qwen3 跑 128K 须 --enable_yarn – 8B 模型在单卡 24G 上跑 128K + ReContext 可能吃紧,ReContext 多轮需额外存 context KV cache 快照(CPU),显存压力主要在 forward 时

Q2:FlashAttention 报错”does not support output_attentions”?

正常。ReContext 算注意力时自动回退到 eager attention,慢一些但能跑。此为设计内行为,见 LlamaFlashAttention2.forward 的回退逻辑。

Q3:句子切分报错 / 不准?

确保装了 nltk 且下载了 punkt:

import nltk nltk.download(‘punkt’) nltk.download(‘punkt_tab’)

若 nltk 不可用,代码会回退到正则切句(_SENTENCE_BOUNDARY_RE),效果略差但不报错。

Q4:CLIPPER 评估报”pair mismatch”?

CLIPPER 数据须严格成对(TRUE 在偶数位,FALSE 在奇数位,且 false_idx = true_idx + 1000)。若用 --test_size 截断,须保证截断后仍成对(即 test_size 为偶数)。脚本默认 test_size=-1(全量)无此患。

Q5:结果文件名太长?

文件名编码了全部超参以便追溯,确实长。可用 --auto_skip 跳过已完成的,或写脚本批量读 *.scores.json 汇总。

10.3 调试:查看选了哪些句子

ReContext 在运行时会打印每轮选中的句子:

[ReContext replay round 1] selected sentences before dedup: The capital of France is Paris. … [ReContext replay round 1] inserted sentences after dedup: The capital of France is Paris. …

若想更深入分析,可用 --recontext_importance_dump_dir ./dumps,每条样本会存一个 .pt 文件,含:

input_token_ids / input_tokens:原 prompt 的 token – context_token_length:context 边界 – importance_details:每步的注意力权重 + 衰减后分数 – generation_logging:选中的句子、位置、轮次信息

加载分析:

import torch data = torch.load(“dumps/kilt_nq_sample000_topk32_rounds1_ctxwarm8.pt”) print(data[“generation_logging”][“round_inserted_sentences”])

十一、设计哲学与局限

11.1 设计哲学

ReContext 之设计,可归纳为四条原则:

一曰”借模型之智,非代模型之劳”

ReContext 不外接检索器,不另训模型。它借模型自身的注意力——这本就是模型”心之所向”——作为相关性信号。模型擅长什么,ReContext 就放大什么。此为”顺势而为”,非”另起炉灶”。

二曰”显式胜于隐式”

DySCO 直接改 attention logits,是”隐式”干预——模型不知自己被改了。ReContext 把证据物化为可读句子,插入 prompt,是”显式”提示——模型”看见”自己重视什么。后者更接近人类阅读时的”划重点”行为,也更易被模型利用。

三曰”保留胜于裁剪”

RAG 丢弃上下文,DAC 压缩上下文,皆有信息损失。ReContext 原文全留,证据是”追加”而非”替换”。即使证据选错了,模型仍可见全文,不至于一错再错。此为”宁多勿少”。

四曰”递归胜于一次”

单次选证据难免有偏。多轮重放,每轮在新 prompt 上重算注意力,能发现被遮蔽的次级证据。此为”步步精炼”,如琢如磨。

11.2 局限与展望

局限一:依赖”好的检索头”

selected_heads 是预设的,因模型而异。换新模型须重新筛选头——此筛选过程不在本仓库代码中(论文另有述)。对未覆盖的模型,用户须自行分析注意力行为选头。

局限二:batch size 限为 1

代码中多处 assert batch_size == 1。ReContext 的逐样本 prompt 重构、KV cache 快照等机制,与批量推理不兼容。大规模评测须靠多卡数据并行。

局限三:仅支持 decoder-only 模型

代码中明确 if self.config.is_encoder_decoder: raise ValueError。 encoder-decoder 架构(如 T5)不支持。

局限四:句子切分依赖 nltk

对英文效果好,对中文等其他语言可能不准。若用于中文长上下文,建议替换 _collect_sentence_char_spans 中的切句逻辑。

局限五:多轮重放有计算开销

每轮需重跑一次 context cache 恢复 + evidence+question 前向。replay_rounds=3=1 慢约 2 倍(context 部分已缓存,主要开销在 evidence 增长后的前向)。

展望

– 检索头的自动筛选工具(降低新模型适配成本) – 批量推理支持(提升评测吞吐) – 多语言句子切分(扩展应用范围) – 与 RAG 结合(ReContext 作 reranker,RAG 作初筛)

附录:术语表

术语释义
Retrieval Head(检索头)注意力机制中善于把 query 与相关 context 对应的 (layer, head) 对。ReContext 用其注意力作为相关性信号。
Attention Readout(注意力读取)从选定头的注意力分布中提取 token 相关性分数的过程。
Evidence Materialization(证据物化)把选定的 token 映射回其所在句子,形成可读证据池的过程。
Recursive Replay(递归重放)多轮重复"算注意力 → 选 token → 物化句子 → 插入 prompt"的过程,每轮在前一轮的扩展 prompt 上重算。
Replay Position(重放位置)证据插入 prompt 的位置:before_question / after_question / after_question_user_side
Intervention Vector(干预向量)dysco 基线中加到 attention logits 上的向量,用于放大选定 token 的注意力。
Context Warmup(上下文预热)回看窗口大小,决定用多少个 query token 的注意力来估计上下文相关性。
Decay Factor(衰减因子)历史重要性权重在累积时的衰减系数,控制"记忆"长度。
YaRN一种 RoPE 缩放方法,用于扩展模型的原生上下文长度。Qwen3 跑 128K 须启用。
DySCOReContext 的前身方法,逐 token 在线干预 attention logits,不物化为句子。
DACDynamic Attention Compression,基于 PPL + 注意力融合的 prompt 压缩方法,作基线。
HELMET一套长上下文评测基准,含 KILT QA、NarrativeQA、InfiniteBench 等。
CLIPPER真伪判别任务,要求模型判断陈述是否被上下文支持,配对评估。
MRCRMulti-Retrieval and Context Reasoning,多针合成检索任务。

本文档基于 ReContext 仓库源码(commit 截至 2026 年)编撰,旨在解析架构与设计思想,并提供可操作的使用教程。如与论文表述有出入,以论文为准。

发表回复

人生梦想 - 关注前沿的计算机技术 acejoy.com 🐾 步子哥の博客 🐾 背多分论坛 🐾 借一步网 🐾 智柴网 沪ICP备2024052574号-1