同一个 RAG 功能,两个团队各自实现:都检索 top-8 片段、都用同一个模型、prompt 长度一样。A 组准确率 78 %,B 组 61 %。差异只有一处——片段在 prompt 里的排列顺序:A 组把最相关的放最前、次相关的放最后,B 组按分数从高到低依次排下去。
这不是偶然。长上下文模型对"位置"有明显的敏感度:开头和结尾的信息被利用得最好,中间那一段最容易被忽略。所以"把资料按相关度排序塞进去"恰好把第 4–6 名的片段放进了最不被关注的位置,而它们往往才是补充关键约束的那几条。
1. 位置偏见是稳定的,不是随机噪声
| 片段位置 | 被正确引用的概率 | 说明 |
|---|---|---|
| 最前 1–2 段 | 高 | 与指令最近,注意力集中 |
| 中间(第 4–6 段) | 最低 | 最易被跳过 |
| 最后 1–2 段 | 高 | 与"现在回答"最近 |
| 超长上下文的中间段 | 更低 | 距离越远越明显 |
// 一次实测的位置-引用率曲线(8 段,同一批 500 条问题)
位置 1-2: 引用率 71%
位置 3-4: 引用率 54%
位置 5-6: 引用率 43% ← 谷底
位置 7-8: 引用率 66%
// 两端的"高地"与中间的"洼地"相差近 30 个百分点
第二行的落差在实验里通常有 10–20 个百分点——足以让同一个功能从"能用"掉到"不能用"。而它在监控上完全不可见:token 数、延迟、检索分数都没变。
// 同一批 8 个片段,两种排列的典型结果
按分数降序: [1][2][3][4][5][6][7][8] ← 3–6 落在中段
两端布局: [1][3][5][7][8][6][4][2] ← 高分在两端,低分在中间
// 后者把"次相关但含关键约束"的 3/5 挪到了前段,准确率因此上升
2. 三种可用的排列策略
| 策略 | 做法 | 适用 |
|---|---|---|
| 两端布局 | 高分放头尾,低分堆中间 | 通用,默认首选 |
| 分数缩放 | 给每段加"相关度标记",让模型自己判断 | 片段质量差异大 |
| 分层 | 强相关单独一段区块 + 弱相关折叠成摘要 | 片段数 > 10 |
第二行常被忽略:在每段前加一行"相关度:高/中",比调整顺序更直接——它把"位置信号"换成了"显式标注信号"。代价是每段多几个 token,收益是模型不必靠位置猜。而在片段质量参差(有的来自制度文档、有的来自论坛回帖)时,这个标注还能顺带把"可信度"传进去。,比调整顺序更直接——它把"位置信号"换成了"显式标注信号"。代价是每段多几个 token,收益是模型不必靠位置猜。
3. 片段数不是越多越好
| 片段数 | 典型收益 | 代价 |
|---|---|---|
| 3–5 | 准确率最高的区间 | 召回不足时缺信息 |
| 8–12 | 召回覆盖好 | 中段被忽略 |
| 20+ | 边际收益为负 | 噪声、成本、位置偏见叠加 |
// 过度补偿的典型形状:召回越多,准确率越低
recall@20 全塞: 准确率 58%,输入 9.8k token
截断到 12 条: 准确率 69%,输入 6.1k token
截断到 8 条: 准确率 76%,输入 4.3k token
// 少喂一半资料,反而答得更准,还省了一半钱
第三行是反直觉但可复现的:加入更多片段会让准确率下降,因为新增的低分片段既占位置又引入矛盾信息。所以"召回 20 条全塞进去"是一种常见的过度补偿。
// 一个可用的取舍:先按分数截断,再按位置重排
候选 20 条 → 分数截断到 8 条 → 两端布局 → 送入模型
// 判据:第 8 到第 12 条的分数落差 < 5% 时,截断位置就该往前挪
4. 指令位置:放在前还是后
| 指令位置 | 效果 |
|---|---|
| 仅在开头 | 长上下文时容易被"淹没" |
| 仅在结尾 | 与"现在回答"最近,遵循度高 |
| 开头 + 结尾各一次(简短复述) | 最稳,多花 30–60 token |
第三行是性价比很高的一招:开头给完整规则,结尾用一句话复述"只依据上面资料、找不到就说找不到"。它不改变上下文长度量级,但显著降低"忽略指令"的概率——因为长上下文里,离"现在要回答"最近的那几十个 token,权重天然更高。它不改变上下文长度量级,但显著降低"忽略指令"的概率。
// 结构模板(顺序固定,不要随意调换)
[system: 角色 + 规则]
[资料区块:两端布局的 8 段,每段带相关度标记]
[结尾复述:只依据资料回答;不足则说明缺什么]
[user: 问题]
// 注意:问题放最后,而不是放资料前面
5. 怎么验证"顺序真的有效"
| 做法 | 能证明什么 |
|---|---|
| 固定同一批片段,只换排列 | 顺序的净效应 |
| 按位置分层统计引用率 | 偏见的具体形状 |
| 两组各 500 条以上 | 差异是否显著 |
| 同时看"答错类型" | 是漏信息还是被矛盾信息带偏 |
第四行是关键区分:如果错答都是"漏掉某条约束",那是位置问题;如果错答是"引用了错误片段",那是检索问题。两者的修法完全不同——前者调排列,后者调召回与切分——而只看总准确率区分不出来,必须把错答按类型打标。两者的修法完全不同,而只看总准确率区分不出来。
// 一次只改一个变量的对照实验(脚本骨架)
$ for layout in score_desc two_ends; do
python3 eval.py --layout $layout --n 500 | grep -E "acc|missing_constraint"
done
// 判据:two_ends 的 acc 高且 missing_constraint 明显下降,才说明是位置效应
6. 排错:五种"资料都在但答错"
| 现象 | 八成原因 | 处理方向 |
|---|---|---|
| 关键约束被忽略 | 该片段落在中段 | 两端布局 |
| 答对一半、漏一半 | 片段过多,中段被跳 | 截断到 8 条以内 |
| 引用旧版本内容 | 同主题片段重复且互相矛盾 | 去重 + 版本优先 |
| 指令不遵循 | 指令只在开头且上下文很长 | 结尾复述一次 |
| 换模型后突然变好/变差 | 不同模型的位置敏感度不同 | 重跑排列实验 |
第五行提示一个实践要点:位置偏见是模型特性,换模型要重测。不能把 A 模型的排列策略直接搬到 B 模型上当成定论。
7. 边界:什么时候顺序不重要
| 场景 | 为什么 | 说明 |
|---|---|---|
| 片段 ≤ 3 条 | 都在"两端" | 无需重排 |
| 单段长文(整篇塞入) | 没有多段可比 | 关注切分而非顺序 |
| 抽取类任务(找字段) | 模型做的是扫描 | 顺序影响小 |
| 上下文远小于窗口 | 注意力未衰减 | 影响可忽略 |
判断口径一句话:顺序只在"多段竞争注意力"时才有影响——片段少、上下文短、任务是扫描而非推理,这三种情况都不必优化排列。反过来,只要满足"≥5 段 + 接近窗口上限",就该把两端布局当成默认。
8. 自测
- 位置偏见的形状是什么?为什么中段最容易被忽略?
- "按分数降序"为什么恰好把关键片段放进了最差的位置?
- 片段数超过多少时边际收益转负?判断截断位置用什么信号?
- 指令为什么要在开头和结尾各出现一次?代价是多少?
- 如何区分"位置问题"和"检索问题"?看哪个指标?
- 什么情况下顺序优化没有意义?
9. 一句话总结
长上下文里,位置就是权重:开头与结尾被利用得最好,中段最容易被跳过——所以"按分数降序塞进去"会把第 4–6 名的关键约束放进盲区。默认策略是两端布局 + 每段相关度标记 + 结尾复述指令,片段数控制在 8 条以内(20 条以上边际收益为负)。判据:错答是"漏约束"就是位置问题,是"引错片段"才是检索问题;而位置偏见随模型变化,换模型必须重测。