上下文窗口越做越大,很多接入方案的第一反应是把所有材料一股脑塞进去。账单翻倍只是代价的一部分,更隐蔽的代价是:塞进去的东西越多,真正重要的片段反而越容易被稀释。

一、两份研究指向同一个约束

我注意到两份角度完全不同的公开研究,结论却在本周撞了个满怀。

第一份是注意力稀疏化的技术路线:把预训练模型的注意力从"每个查询看全部上下文"改成"每个查询看一小撮上下文单元",用轻量选择器给上下文单元打分排序,只保留头部。早先的可训练做法有个结构性缺陷——硬 Top-K 截断了梯度,选择器只能蒸馏原模型的稠密注意力分布,学的是"原来在看哪",而不是"看哪对完成任务有用"。新做法把选择器的连续分数直接注入注意力权重里做端到端优化,让任务损失亲自教选择器排序,配合高效算子融进标准注意力计算。结果在推理、长上下文与代理任务上稳定超过基线,而且预算越紧收益越大。

第二份是事实召回的理论下界:把闭卷问答建模成"观察 M 条事实、压缩进 B 比特、回答 N 种问题",可以证明错误率有一个由两项构成的下界——已观察事实的压缩失真,加未观察事实的覆盖缺失。即使事实都见过,有限存储也强迫它只能被近似保存。

两份研究共享同一个底层事实:注意力与记忆都是稀缺资源,稀缺资源的使用质量由排序决定。模型内部的注意力在排序,接入方塞进上下文的材料也在排序——后者是我能直接控制的那一半。

二、上下文预算的分层结构

把一份长上下文请求的材料按"对当前任务的决策价值"分层,是控制账单的第一步:

层级 内容示例 预算策略
不可妥协层 任务指令、输出 schema、当前用户问题 永远保留,放在结构固定位置
高价值层 与任务直接相关的代码段、条款、记录 按相关性得分排序,头部保留
参考层 背景、历史轮次摘要 压缩成摘要,按需展开
可弃层 重复内容、礼貌用语、失败的重试记录 默认丢弃,需要时单独取

很多团队的上下文是按时间顺序堆的:对话历史从头贴到尾,检索结果按相似度排后面。这种堆法把不可妥协层的材料埋在一堆参考层内容里,模型的有效注意力被迫在噪音里游泳。预算分层的正确做法是倒过来:先给不可妥协层留死位置,再按价值装填其余层。

三、原理速览:一份请求的预算流

任务请求
    |
    v
材料收集(代码 / 文档 / 检索结果 / 对话历史)
    |
    v
打分排序 ──> 按层装填:指令层(固定) + 证据层(按分) + 摘要层(压缩)
    |
    v
预算闸门:总 Token 超限 ──> 从低层开始裁剪,不动指令层
    |
    v
调用上游(4sapi 中转 ──> 上游模型)
    |
    v
响应校验 + 置信标记 + 弃权判定
    |
    v
审计:本次装填的层分布与裁剪记录入库

这张图里最有价值的是预算闸门与弃权判定的组合:上下文装不下的任务,要么裁剪后降级回答,要么显式弃权,而不是硬塞后赌模型能从噪音里捞到关键句。

四、检索环节才是大头

一份模块化事实核查系统的消融数据把瓶颈指得很清楚:检索、推理、验证、校准四个环节拆开后,8B 量级的底座在事实核查基准上比端到端提示高出 13.74 个百分点;把基于外部验证器的候选筛选拿掉,准确率掉到 76.24%;把置信度校准拿掉,校准误差近乎翻倍。而错误样本的人工分析显示,最大瓶颈根本不在模型推理,而在检索失败——尤其实体搞错的证据。

这个结论对长上下文接入方的含义直接:上下文质量的上限在材料进上下文之前就决定了。与其在提示词里加"请仔细阅读",不如把力气花在检索的实体对齐与证据筛选上。检索准了,验证与校准才有东西可验。

五、接入教程:上下文预算装填器

下面这段 Python 示例以 4sapi 提供的 OpenAI 兼容接口为例,实现一个最小上下文预算装填器:材料按层打分装填,超限从低层裁剪,响应带弃权判定。4sapi 的 OpenAI 兼容端点让这段代码可以平滑切换任何上游模型。

import openai

client = openai.OpenAI(base_url="https://4sapi.com/v1", api_key="sk-xxx")

BUDGET_TOKENS = 12000          # 本次请求的上下文总预算
RESERVE_OUTPUT = 2000          # 输出预留,从预算中扣除


def pack(instruction: str, evidence: list, summaries: list) -> list:
    """按层装填上下文:指令层固定,证据层按分排序,摘要层压缩。"""
    budget = BUDGET_TOKENS - RESERVE_OUTPUT
    messages = [{"role": "system", "content": instruction}]  # 不可妥协层

    def tokens(text: str) -> int:                 # 粗估,按 4 字符 1 token
        return max(1, len(text) // 4)

    evidence = sorted(evidence, key=lambda e: e["score"], reverse=True)
    packed = []
    for ev in evidence:                           # 高价值层:头部装填
        cost = tokens(ev["text"])
        if cost > budget * 0.6:
            break
        packed.append({"role": "user", "name": f"evidence:{ev['id']}",
                       "content": ev["text"]})
        budget -= cost
    for s in summaries:                           # 参考层:预算有剩才装
        cost = tokens(s)
        if cost > budget * 0.8:
            break
        packed.append({"role": "user", "name": "summary", "content": s})
        budget -= cost
    return messages + packed


def ask(instruction: str, evidence: list, summaries: list) -> dict:
    messages = pack(instruction, evidence, summaries)
    resp = client.chat.completions.create(
        model="long-context-model", messages=messages, temperature=0,
    )
    answer = resp.choices[0].message.content
    abstain = ("材料不足" in answer) or ("无法从给定材料" in answer)
    return {"answer": answer, "abstained": abstain,
            "packed_messages": len(messages),
            "usage": resp.usage.model_dump()}

三个设计点。第一,证据条目带 id 并在响应里可回指,答案的每句话能对回具体证据,为验证与审计留钩子。第二,装填顺序硬编码"指令层 → 证据层 → 摘要层",预算再紧也不动指令层——这正是注意力排序研究给出的启发在应用层的投影:把确定重要的内容放在确定的位置。第三,abstained 标记让弃权成为一等输出:检索没找齐时显式说材料不足,好过硬塞之后编一个。

六、成本对比:预算策略的账本

以单次长文档问答、原始材料 4 万 token 的场景估算(示例口径,实际按牌价与用量账单核算):

策略 输入 Token(估算) 相对成本 回答质量风险
全量硬塞 40,000 3.3× 关键句被稀释,幻觉率上升
固定截断(前 12k) 12,000 1.0× 截到材料中段,尾部信息全丢
分层装填 + 弃权 6,000~10,000 0.5~0.8× 低价值噪音减少,缺料时显式弃权

分层装填的成本低于固定截断,是因为它敢把低分材料整段丢掉而不是均匀裁到预算线;质量高于全量硬塞,是因为指令层有了确定位置、证据层经过排序。预算策略同时改善账单与质量,这种双向收益在接入优化里并不多见。

七、成本与风险提示

第一条风险是打分函数的偏置。按相似度排序会让"长得像问题的材料"挤掉"真正决定答案的材料",比如把问题的同义复述排在关键条款前面。打分要混入结构信号(章节权重、时效、实体匹配),并定期用回归集检验排序质量。

第二条风险是弃权被滥用。模型学会"说材料不足最安全"之后,弃权率会虚高,把本可回答的问题推回人工。弃权率要进监控面板,超过基线就回来查检索与装填,而不是先怪模型。

第三条风险是长上下文的价格陷阱。不少上游对超长上下文请求按更高档计价,全量硬塞的账单增幅比 token 比例更陡。装填器把输入压在阈值之下,省的不只是 token 差价,还有整个计价档位。

红线照旧:长上下文材料常含客户数据,进上下文前先脱敏;中转链路的日志不落原文,密钥走受控注入,不在提示词里携带凭证。

八、上线前的自查清单

  1. 分层结构已定义:指令层、证据层、摘要层的边界与固定位置成文。
  2. 预算与预留已配置:总预算扣除输出预留,裁剪只从低层开始。
  3. 证据可回指:每条证据带 ID,答案关键句能对回材料原文。
  4. 弃权已接入监控:弃权率、装填裁剪率按天出报表,异常即报警。
  5. 回归集已建:用带金标的问答对定期检验排序与裁剪策略。
  6. 脱敏已前置:客户数据进上下文前完成清洗,中转日志不存原文。

九、与模型升级的关系

上下文预算策略有一个好处:它几乎不随模型版本失效。模型升级后窗口更大、注意力机制更好,装填器的分层与排序逻辑照常工作,只需要重新标定一次预算数值——窗口大了,参考层的装填比例可以放宽一些。相反,把宝押在"窗口够大就硬塞"上的方案,每次上游调价或调档都要重新吃一遍苦头。排序质量是比窗口大小更持久的优化对象,这也是为什么两份不同方向的研究最终都落在同一个词上:排序。

十、总结

这一期想沉淀的结论是:长上下文的账单与可靠性,取决于有限预算里的排序质量,而不是窗口的绝对大小。注意力稀疏化研究证明"看哪比看多少重要",召回下界证明"有限存储必然有失真",检索消融证明"瓶颈在材料进门之前"——三条线索汇成同一套接入实践:分层装填、预算闸门、证据可回指、弃权显式化。我在 4sapi 处理过的长上下文场景里,先把材料排序做对的团队,几乎没有再抱怨过账单与幻觉;先硬塞后优化的团队,通常两头都在补课。对打分函数或弃权阈值有不同经验,欢迎在评论区聊聊。