跳到主要内容
我那十一个 prompt,被一篇 stat.ME 的论文点名了

我那十一个 prompt,被一篇 stat.ME 的论文点名了

阿舟
阿舟

· 阅读约 6 分钟

上周四早上刷 arXiv:2610.30562,标题里 lifetime-valid 这个词让我停了一下。本来想划过去——主分类 stat.ME,cs.AI、cs.LG、stat.ML 各挂一个,一看就是统计那边的人跨界来管我们这些乱来的人。

摘要第二段,0.183。

产品 A/B 测试,一个会持续迭代的 AI 分析师,不上一套“持续验证”的机制,第一类错误率会升到 0.183。0.05 的线,翻了接近四倍。

我没接着往下读,先在心里盘了一遍自己手上那点东西。

先认了

我这大半年一直在干一件不太体面的事,只是没给它起名字。

分析类的任务——看漏斗、算留存分层、判断某组指标是不是异常——我会攒一堆 prompt 变体。真的,攒到十一个。然后写个循环全跑一遍,输出摊开对比:

for v in prompts/analyst_v*.md; do
  python run_analyst.py --prompt "$v" --data eval/2026q3.parquet \
    --out "out/$(basename "$v" .md).json"
done
python score.py out/*.json --expect eval/expected.json

看分数,挑最高的那版上线。当时我管这叫快速试错。

现在它有了名字:在评估集上做版本选择。行话叫 p-hacking,只不过被 hacking 的对象从一个 p 值变成了一排 prompt。

论文列的第二个风险几乎是照着我的剧本写的——反复拿评估反馈挑版本,会过拟合。第一个更隐蔽:版本一直在更新,每个版本自己带一点随机性,只要上线的版本够多,“曾经上线过一个错误率超标版本”这件事的累积概率就往上爬。单看一个版本,人人都是 0.05;看一辈子,就没人知道是多少了。

我从没算过这个数。我猜大部分人也没算过。

它提的东西不复杂

每个候选版本上线之前,拿原假设下生成的模拟数据考它一遍,考不过不许上。

也就是造一批肯定没有效应的数据——效应量为零,但该脏的地方全脏着,长尾、批次效应、流量结构漂移——然后看这个分析师会不会照样斩钉截铁地说“显著”。

def can_deploy(candidate, scenarios, alpha=0.05, margin=0.01):
    rej = [candidate.run(s.simulate_null())["p"] < alpha for s in scenarios]
    return mean(rej) <= alpha - margin

粗暴,但有效。

比听起来结实的地方在“终身”两个字。论文给的界是:不管你用什么更新策略,不管你提多少个候选版本,只要每个版本上线前真过了那道门,“曾经上线过一个超标版本”的概率就被压在一个有界范围里。你可以随便筛、随便挑、拿反馈往死里优化——它没要求你戒掉“再试一版”的瘾,它承认你会一直试,然后在这个前提下把账算平。

我服气的就是这点。大多数方法论都是先要求你变成另一个人,这篇没有。

两个案例:生物等效性那边 0.098 压到 ≤0.045,A/B 那边 0.183 压到 ≤0.044,功效保留了 82% 和 96%。

画外音:0.183 比 0.098 高这么多,我猜是因为 A/B 场景里“演进”得更勤、版本更多、挑得更狠——纯猜,论文没这么说。

不放心的地方

一、整个保证的地基不在数学里。

模拟场景生成器是这套机制唯一的物理基础。你的模拟器不认识的脏东西,门禁就查不出来,而界照样漂亮地成立。论文里有一句轻描淡写的话:场景可以随机化,并随时间扩充。这句承载了全部重量,也是落地时第一个被砍掉的工序——因为它不产出任何可见的指标。

药企那边还好,场景是监管认可的。业务分析这边,谁能保证我造出来的 null 分布跟我真实流水的怪癖对得上?我造的时候脑子里想的是“正态加一点噪声”,而真实数据里那个每逢大促就变形的分流逻辑,我根本没想过要模拟。

二、缓存会把门禁变成目标函数。

这条是我自己的前科。跑一遍几千个模拟数据集很贵,贵到你第二天一定会想存盘。下次候选版本来了直接读缓存,省一大笔。

然后一排候选版本开始对着同一批 sims 优化。这时候那道门还是门吗?我的读法是:不再是了。你能过门,是因为你被门筛过很多次,而不是因为你干净。论文的理论结果里,界成立的前提是每个版本面对的是它没见过的场景。缓存一上,终身保证就变成终身幻觉。

这不是论文的问题,论文明确要求场景扩充。但工程上,缓存是默认行为,扩充不是。

三、门禁本身也是个统计检验,也得花钱。

import numpy as np
# 用 M 个模拟数据集估拒绝率,标准误约 sqrt(p(1-p)/M)
for M in (500, 1000, 3000, 5000):
    se = np.sqrt(0.04 * 0.96 / M)
    print(M, round(se, 4), "2σ 余量", round(4 * se, 4))

M=1000 的时候标准误 0.0062,两倍标准误的余量 0.0125——你想把门槛卡在 0.05,实际上分不清 0.04 和 0.055。要分得清,M 得上到三五千。每个候选版本三五千次 agent 跑批,我那个十一版的循环乘出来是五万多次。

没人替你付这笔账。论文里 82% / 96% 那对功效数字,说白了就是这笔账的定价:生物等效性掉 18% 的功效,意味着样本量得往上加。A/B 那边掉 4% 无所谓,生物等效性那边 18% 是真金白银。两个案例的数据恰好横跨了“可以接受”和“回去再想想”。

我打算怎么办

评估集只用一次。选 prompt 的那份数据,看完封存,要再选就重新生成。这条跟论文没关系,是我早该守的规矩。

模拟场景单独一个目录,带 seed,每个候选版本现场生成,禁读缓存。慢,我认。

再记一份 deployed.log,每次上线一行,版本号加门禁结果。理由很实在——论文的界是关于“终身”的,我要是连自己这辈子部署过几个版本都数不清,那个界跟我一点关系都没有。

0.183 这个数我第一遍看的时候觉得是别人的问题。生物统计、药企、监管,跟我一个写业务脚本的有什么关系。

第二遍才反应过来:我那个十一版 prompt 的循环,跑的就是 A/B 测试的分析。我在用挑 prompt 的方式挑结论,只是中间隔了一层,看起来像是在优化工程。

这不是我第一次拿评估集当调参集。估计也不是最后一次——规矩写在纸上和手上做到,中间那截路我走过太多回了。

但至少现在它有名字了,还有个能算的界。

本期缴税:三个月,十一版 prompt,一份从头用到尾的评估集。

阿舟
阿舟

写代码写到一半开始怀疑人生,靠 AI 工具续命,顺手把踩过的坑都记下来。

查看主页 →