
“Daily Paper”这个系列我已经连续更新了快两百期。标题里的日期停在2026-09-24这个晚上我本来只想快速翻三篇论文找找选题灵感结果被一个埋在实验里很久的线上问题拽住了注意力用户丢进来一份两百多页的技术合同问答系统回答“是否有知识产权担保条款”时抽出来的两段证据自相矛盾最后模型给了一个含糊其辞的结果。问题不在模型身上而在我们检索链路上。今天的Daily Paper我不打算聊某一篇具体的论文而是把这一整天围绕“长文本场景下如何做精准信息定位”做过的文献阅读、方案对比和落地验证完整记录下来。如果你也在做知识库问答、合同审查或者长文档信息抽取这篇内容应该能直接帮你少踩几个坑。我能在一开始就给出的结论是长上下文模型并不能解决检索质量问题真正决定回答上限的仍然是证据链路的构造方式。下面按我今天的完整推进过程来讲。1. 今天这个选题其实是被一个线上问题逼出来的1.1 先交代一下翻车现场我们的系统是一个面向法律文书和合同文本的问答服务底层用了一个参数量比较大的开源模型上下文窗口开到128K。按道理两百多页合同裁成片段后全部塞进上下文也勉强装得下早期我们是这么设想的既然模型能看很长的文本那就把整篇合同按章节切块全部拼在一起让模型自己去定位答案。结果就是前面说的那个翻车场景。用户问的是A条款系统召回的片断里有一段是A条款的正文另一段却是合同尾部“废除与变更”里对A条款的补充修订。两段内容在字面上互相矛盾模型无法判断哪一段才是当前有效约定只能给出模棱两可的回答。复盘之后我意识到这一类问题的根源在于我们只做了“切块”和“塞入”没有做“证据顺序”和“效力层级”的建模。长上下文模型确实记住了两段内容但它不负责判断哪段应该覆盖哪段。1.2 把线上故障翻译成文献检索问题遇到这种故障我的习惯是先不着急改代码把具体问题抽象成一个可以检索文献的技术命题。这个命题就是——在长文档问答场景中检索阶段如何平衡召回率与上下文重构质量进一步说当候选证据之间存在冲突关系时系统应该依据什么信号来选择最终上下文。带着这个问题我今天的阅读范围固定在三类方向上第一类是传统向量检索与重排序的优化第二类是“上下文压缩”思路第三类是长文本模型本身对证据内部一致性的感知能力。这三类方向各有各的适用边界我后面会详细拆。2. 纸上谈兵没意思先搞清三类方案各自的底层逻辑2.1 老派做法向量召回 重排序问题出在重排只看相关性最开始想到的当然是最经典的“向量召回 Reranker”组合。向量召回负责把相关片段捞回来Reranker负责把最相关的排在前面。这套架构在通用问答上表现很稳但有一个隐蔽的问题绝大多数Reranker打分时只衡量候选片段和query之间的语义相关性不会去衡量候选片段之间的逻辑关系。这意味着哪怕某个片段是A条款的补充修订版只要它与问题的语义足够接近它仍然会被排到高位。两个片段都进上下文之后模型看到的是两个语义都相关、内容却互相打架的证据回答质量就只能靠模型自己悟。这不是模型的错而是我们的排序目标与任务目标不对齐。2.2 偏激进的做法上下文压缩关注信息密度第二类文献是关于上下文压缩的。这类算法的核心思路不是让模型看到更多文本而是在送入模型之前把不重要的内容去掉只保留信息密度最高的部分。实际效果通常很惊艳对于三千字左右的候选文本压缩到五六百字之后模型回答质量不仅没有下降部分场景还有提升。原因是压缩过程相当于做了一次“二次抽取”把真正和问题相关的实体、数值、限定条件单独拎出来模型注意力更容易聚焦。代价也很明显——压缩本身依赖一个额外的生成式模型会带来额外延迟而且遇上表格、列举这类结构密集的内容时容易把关键对应关系压缩丢。2.3 我们最终选择的方向把文档结构变成上下文顺序在对比完前两类文献之后我决定把今天的实验重心放在第三种思路上不压缩文本而是利用文档本身的结构信息去重建证据顺序。具体来说对于一份合同我们解析出章节层级、条款编号和修订关系在送入上下文之前把“基础条款”“补充条款”“修订条款”按效力顺序组织。后出现的修订内容放在基础内容之前并显式生成一行说明文字告诉模型后面的内容优先级高于前面。这个方法不需要改动模型结构工程实现成本可控逻辑上也更匹配法律文本的阅读习惯。3. 用现有工具链复现一遍看看效果到底能到什么程度3.1 复现环境的搭建思路今天的实验我没有从零写检索系统而是直接在我们生产环境的检索服务上做改造。技术栈大概是这样文档解析用Unstructured向量化用现成的Embedding接口召回环节用带过滤条件的向量检索重排环节用一个轻量级交叉编码器最后接一个128K窗口的生成模型。改造点主要在生成前的上下文组装模块。原来的逻辑是“召回片段按分数排序直接拼接”我把这个逻辑改成了“召回片段先分组再按文档结构和效力规则排序拼接”。排序规则写成了一个可配置的Python模块核心代码并不复杂def reorder_evidence(evidences: list[dict], section_structure: dict) - list[dict]: 按文档结构重建证据顺序修订 补充 基础条款 buckets {revision: [], supplement: [], base: [], unknown: []} for ev in evidences: ev_type classify_evidence_type(ev, section_structure) buckets[ev_type].append(ev) ordered buckets[revision] buckets[supplement] buckets[base] buckets[unknown] for rank, ev in enumerate(ordered): ev[reordered_rank] rank return ordered3.2 实验指标怎么定我设计了两组对比A组用原始“按相关性排序拼接”的方式B组用新的“结构感知排序”方式。评测集是从线上历史问题里抽出的120条法律文书问答每一条都标注了证据片段的来源章节和最终答案是否采用修订关系判定。评测指标没有只盯着准确率还额外看了三个值上下文冲突率、无效证据占比和单次请求的生成Token数。冲突率这个指标来自今天阅读文献时的一个启发——通过统计送入模型的证据片段中“语义成对矛盾但都被保留”的比例可以更早发现上下文组装环节的问题而不是等模型给出错误答案后再去反推。3.3 实测结果和我的解读跑出来的结果比预期更明显。A组在120条测试上的准确率是71.7%B组是84.2%提升超过12个百分点。冲突率方面A组有38条用例出现了至少一对互相矛盾的证据进入上下文B组下降到11条。无效证据占比也从27.4%降到9.1%。指标A组原排序B组结构感知排序变化答案准确率71.7%84.2%12.5%上下文冲突率31.7%9.2%-22.5%无效证据占比27.4%9.1%-18.3%平均生成Token数18611427-23.3%生成Token数下降23%是我没想到的额外收益。证据顺序合理之后模型不再需要在两段矛盾文本之间反复权衡解码时用的推理Token明显减少。这也从侧面说明一个结构清晰的上下文对模型来说省力很多。3.4 复现过程中踩到的两个坑第一个坑是章节结构解析。我们最初直接按“第X条”正则去切结果碰到“第X条第Y款”包含“第Z条”的情况正则把嵌套条款切碎了。这个问题的解法是引入括号深度计数只在一级标题位置切开避免把子条款截断。后面我把这个解析器单独抽成了函数跑完解析测试后才接进主流程。第二个坑是效力规则写得太死。最开始我把“修订必须覆盖基础条款”写成了全局规则导致一些本身就互相独立的并列条款被错误覆盖。后来改成了可配置的优先级列表并且加入一个兜底逻辑如果解析器无法判断条款关系就退回原始相关性排序。这个兜底很快救了一次线上发布——有一批老合同的条款编号不规范规则引擎全部判成了unknown没有兜底的话准确率直接跌破A组。4. 从“读了”到“能落地”我用一个固定模板倒逼产出4.1 我的Daily Paper阅读转化模板每日论文如果只是读过、收藏、划线意义会小很多。我给自己定了一个固定模板每篇文献读完必须填完四块内容问题定义、核心机制、适用边界、可落地点。问题定义作者到底在解决哪个环节的问题描述得是否清晰。核心机制方法的主路径是什么关键创新点在哪里。适用边界方法在什么数据分布上有效什么场景会退化。可落地点我当前系统的哪个模块可以直接借鉴这个思路。今天关于上下文冲突率的启发就是填“可落地点”时冒出来的。我原来只关注最终准确率很少直接度量上下文内部的一致性。读完一篇讨论长上下文注意力稀释的文献后我意识到可以把“是否互相矛盾”变成一个上线前的预检信号于是当天就新增了这个指标。4.2 指标口径比工具更重要做实验的时候我一度想把冲突率直接交给一个大模型去判断把所有证据丢给模型让它告诉我有没有矛盾。后来发现这么做成本太高而且不稳定。最终我实现了一个便宜得多的近似方案——检测证据句子中“保留原文表述的否定词和程度副词”以及“是否引用同一条款号但给出相反语义”用规则加少量模型打分来估算冲突概率。这个例子说明指标口径本身也需要迭代。第一次定义冲突率时我按“两段证据在语义上矛盾”来标注标注一致性只有一半后来把口径改成“在回答同一问题时两段证据无法同时为真”标注一致率立刻上去了。合理定义指标往往比找更先进的模型更有效。4.3 回归测试集要跟着业务走今天的120条测试用例是从线上日志里抽出来的抽完后我还做了一件事把过去三个月出现过的“答非所问”案例全部回填进测试集。这样每次改检索策略时都能看到是否把历史坏案例修好同时有没有引入新的退化。建议每个做检索问答的团队都维护这么一套回归集。测试集不用大但一定要包含真实用户问过的、模型答错过的Case。我自己用下来这套回归集比网上公开的那些问答基准更能暴露系统结构化问题。5. 坚持Daily Paper近一年的具体心得5.1 每天的阅读节奏是怎么固定的经常有人问我每天上班那么忙哪来的时间读论文。说实话我靠的不是毅力是固定流程。我会把阅读时间固定在早上到工位后的第一件事以及下午精神疲惫后的间隙。早上用来读需要动脑子的核心文献下午用来读工程实践类文章和开源代码。阅读时我会先花五分钟扫摘要和图表判断这篇值不值得精读。值得精读的才进我的模板流程不值得的直接进收藏夹仓库打上标签备用。过滤标准就一条会不会改变我现在系统的某个设计决定。不会就不必深度投入。5.2 我用什么工具维护这个系列笔记层面我用的是Markdown文件按“YYYY-MM-DD”命名配合Git进行版本管理。每天一篇文件名和这个系列完全对应。每个文件开头是模板四要素后面是摘录和代码草稿最后留一个“下次要验证的问题”小节。这个小节非常重要。它相当于给自己埋了一个持续迭代的锚点。今天的实验之所以这么快就是因为三天前我在一篇关于证据排序的笔记里留了“能否用结构信息做上下文重建”这个待验证问题今天直接顺着推进而不是从零开始。5.3 给也想长期读论文的人几句实在话不要一上来就追求每天精读一篇。更现实的节奏是每周精读两篇、泛读十篇持续三个月后再决定要不要加量。精读的要深到“能改自己代码”泛读的只需要知道“什么问题用什么思路解决”。也不要只读最新论文。很多经典方法在工程场景里反而更实用。今天的“结构感知排序”其实不是新思想搜索领域很早就有人讨论文档结构对排序的影响只是很少有人把它和长上下文问答组装放到一起。经典文献加上工程场景的再挖掘往往会产生比追热点更有价值的产出。我自己的另一个体会是分享本身会反向推动输入。每次把Daily Paper发出去评论区里总会有人指出某个我没想过的边界情况这些反馈又变成了下一轮阅读和实验的线索。你不需要有大量读者哪怕只有一个同行愿意定期和你讨论这个系列就已经进入了正向循环。