
一条我用错了地方的原则:该隔离的和该比较的
手上有一个文件夹,33 篇行人轨迹预测的论文,要给每篇做结构化总结:解决什么问题、用了什么方法、跑了哪些数据集、结果如何、跟我的课题有多相关。
第一个设计决定很清楚:每篇论文一个独立的模型请求,互相看不见。
这个决定是对的,理由也硬:
- 防串味。 一旦几篇论文同时在上下文里,后面的总结会朝前面漂——“与前一篇类似……“这种句子一出现,这条总结就没法单独用了。
- 防上下文爆炸。 论文全文会累积,而且这个开销在会话的每一轮都要重付。第 800 篇和第 1 篇的成本必须一样。
跑完 76 篇空战论文(另一个文件夹)验证:27 分钟,零失败,数字和数据集名全是英文原文照抄,可以直接拿去核对。这套隔离是有效的。
然后我把同一条原则用到了打分上。
第一次:零个满分
总结的最后一个字段是「相关性 1-5 分」。跑完一看分布:
5 分 0 篇
4 分 11 篇
3 分 13 篇
2 分 5 篇
1 分 2 篇
33 篇里一个满分都没有。 连这个领域的奠基文献——发在 Science 上、被引上千的那篇——也只拿了 4 分。
查理由,发现模型在用一把我没给它的尺子:这个文件夹里的论文横跨三支文献(信息论上界、人群仿真、深度学习预测),模型默认「覆盖得越全越相关」,覆盖一条给 3,两条给 4,于是没有 5。
而我的评分标准里明明写着三条线是并列的,做好一条就算高相关。问题在于:那份标准写在文件夹的 AGENTS.md 里,而脚本的命令行只有一个 --context "<研究主题>"——规则从来没有传进过 prompt。
这个 bug 有个讨厌的性质:它不报错,输出看起来完全正常。总结的事实部分照样准确,数据集名照抄,数字不差,“未说明”该写的地方都写了。只有打分这一栏静默地用了脚本内置的通用规则。如果不是我去数分布,这批数据会被当成好的直接用下去。
后来我加了个 --rules-file 参数,并且让脚本启动时打印一行 Rules from <路径>——没有那行就说明规则没传进去。一个静默失效的东西,至少要让它在正确工作时说一声。
第二次:二十一个满分
补上参数,把规则真的传进去,重跑。新的分布:
5 分 21 篇
4 分 1 篇
3 分 7 篇
2 分 2 篇
1 分 2 篇
从一个满分都没有,变成三分之二都是满分。 一样没法排序。
理由的措辞暴露了机制。21 个满分的理由是同一个模板:
直接属于 信息论/熵 分支……方法和结果可作为引用 直接属于 deep-learning pedestrian trajectory forecasting……可直接引用 直接属于 agent-based crowd simulation 分支……可作为该分支引用
每一条都是**「属于」判断**,不是**「多好」判断**。而这个文件夹是我按这三条线挑出来的——「在不在线上」这个问题,答案当然基本都是 yes。
更要命的是我亲手拿掉了唯一在起作用的区分器。第一次那把「覆盖了几条线」的尺子按我的标准是错的,但它至少在制造区分。我禁掉了它,没有任何东西补上它的位置,分数就全糊到顶。
还有一条:我的 5 分判据写的是「方法或结果会被引用」。这是关于我的论文的判断,不是关于这篇论文的判断。模型只看得到这一篇的总结,看不到我要写什么,所以它无法否定——默认就是 yes。
真正的原因
两次失败都指向同一件事,而它跟规则怎么写无关:
一次只看一篇,模型没有任何依据把 A 排在 B 前面。
它只能对着绝对标准判。而绝对标准里凡是模糊的、无法从这一篇的总结里证伪的条件,一律会被判成满足。
这不是 bug,是隔离的必然结果。要隔离,评分标准就必须写成「单看一篇总结就能核对」的形式——有没有提出新方法、实验对象是什么、是不是综述。这些是事实,不是判断。
那具体该怎么写?我把原来的判据逐条换成能从总结里查到的事实:
| 档 | 原来(判不出来) | 改成(查得到) |
|---|---|---|
| 5 | 方法或结果会被引用 | 提出新方法/新理论界,且实验对象是行人或人类移动本身 |
| 4 | 可作背景 | 综述、或对已有模型的校准验证,对象仍是行人 |
| 3 | 同技术不同问题 | 同技术,但实验对象是车辆/船舶/机器人 |
| 2 | 边缘 | 只共享底层训练技巧,任务完全不同 |
| 1 | 无关 | 三条线都不沾 |
右边每一条都对应总结里明确写着的信息——有没有提新方法、实验对象是什么、是不是综述。模型没有含糊的余地。
但即便判据都改成可核对的事实,还有一层解决不了:分档能做,排名做不了。 一个已经按主题筛过的文件夹,界内的高下需要比较才能出来。
修正后的原则
我原来的说法是「批处理要隔离」。太笼统了。准确的分界是:
| 输出是什么 | 该不该隔离 |
|---|---|
| 事实(数据集叫什么、ADE 是多少、发在哪儿) | 必须隔离。看了别的论文只会污染 |
| 排序(哪几篇更该读) | 必须比较。不比较就没有序 |
总结是前者,打分是后者。我用了同一套原则,所以一半是对的,一半从一开始就不可能成立。
重打分,但不重读论文
想通之后有个现实问题:重新打分要不要把 33 篇 PDF 再读一遍?
不用。总结有六个字段,前四个(问题/方法/数据集/结果)是从论文里抄事实,我核对过,都对;坏掉的只有后两个(发表分级、相关性),它们是判断。事实层可信,判断就不需要再看原文。
于是重打分的输入是已有的总结,不是论文全文:
| 每篇送多少 | 33 篇总量 | |
|---|---|---|
| 重跑总结 | 约 30000 token | 约 100 万 |
| 只重打分 | 约 1000 token | 约 3 万 |
三十分之一。 这条对 300 篇以上的库是决定性的——重跑一次全文总结是几百次请求的开销,而规则总会改。
顺带一提,第一次跑这个的时候 76 篇里有几次 502。脚本当时不重试,一个 500 就少一篇。补了指数退避(2 秒、8 秒、20 秒)之后,76 篇零失败——中转这种链路,重试比并发重要得多。
改完之后
把打分拆成单独一步,一批一起送进去做横向比较。同样 33 篇:
5 分 15 篇
4 分 7 篇
3 分 6 篇
2 分 3 篇
1 分 2 篇
层次出来了,而且边界判得准:自动驾驶车辆那几篇统一落到 3 分(同技术、不同对象),NLP 那两篇训练技巧留在 1 分。理由的措辞也变了——从「直接属于 X 分支」变成「优先阅读」「优先级低于核心上界论文」「可作相邻方法参考」,全是相对表述。
比较压力真的起作用了,不是碰巧分布好看。
代价,和一个新问题
比较不是免费的。批次的构成决定分数:同一篇论文放在一批弱的里面得 5,放在一批强的里面得 3。33 篇一次装得下时无所谓,几百篇分成十几批,就得到十几把互不相通的尺子——原来的问题,粒度从「篇」变成了「批」。
解法是锚点:每一批固定塞进同样几篇参照论文,分数事先定死,标明“这几篇的分数不要改,用来对齐尺度”。模型在每批里都看到同一把尺子,批间就对齐了。
锚点要选跨度大的,不要选边界模糊的:一篇明显的 5 分、一篇明显的 1 分,中间补两三篇。挑五篇都在 3 到 4 之间徘徊的,等于没有尺子。代价是每批多送几篇的 token,很便宜。
76 篇的那个文件夹刚好逼出了这个场景:一次送 76 篇服务端 504,只能分 4 批,锚点这才第一次真正派上用场。
还有个细节值得记:同一篇论文出现两次时(文件名带 Duplicate Copy),两次必须给同样的分数。这条得写进 prompt——不写的话,同一篇论文在同一批里可能拿到不同的分,那是最尴尬的自相矛盾。
还有个问题没解决:头部仍然拥挤。76 篇里 47 篇是 5 分(62%),比 33 篇那次的 45% 更差。比较能把“在圈内”和“在圈外”分开,分不出“奠基”和“一般”。真要分,得对 5 分那一档再跑一次比较——在同质的一批里,压力才够集中。
一个更一般的说法
这件事我觉得不只关于论文。任何“批量让模型判断一堆东西”的场景,都可以先分一下类:
- 抽取(这份合同的生效日期是哪天)——事实,隔离。
- 归类(这个工单属于哪个部门)——多数是事实,隔离;除非类别定义本身要靠样本间的对比才说得清。
- 打分/排序/挑选(哪些工单最紧急)——判断,必须比较。
第三类被当成第一类做,是很容易发生的:它长得跟前两类一模一样,都是“对每个对象输出一个值”,接口一致、代码结构一致,只有输出的语义不同。而这个差别不会体现在类型签名里,只会体现在结果没法用上。
它甚至不会报错——我这两次,一次全是 3 到 4 分,一次三分之二是 5 分,输出格式完全合法,字段一个不少。是分布告诉我出了问题,不是异常。 所以做这类流水线,值得专门看一眼输出的分布,而不只是抽查几条内容。
一句话
这一步的输出,是一个关于单个对象的事实,还是一个关于它在集合中位置的判断?
前者,越隔离越好。后者,隔离等于让它闭着眼睛排队。