本节摘要:5.1 的判定卡需要六个字段的数据,本节交付取数的流水线。核心是行为指纹的思路:同一份权重在固定探针集、固定采样参数下的输出分布是稳定的,权重被更换、量化被下调、训练被微调都会让分布移动——2026 年的公开研究已经证明行为指纹对微调、合并、量化等常见改动保持稳健(arXiv,2026 研究),端点稳定性监控也已有用能量距离加置换检验做分布漂移检验的成套做法(ACM,2026);单 token 指纹(低温采样下"下一个 token"的选词分布)是更细的信号点(前半参考来源已列)。本书不要求复现论文,只交付选择者够用的三层信号框架——元数据层(模型标识与版本口径)、行为层(探针集输出比对)、质量层(锚点分漂移)——加上两张清单(接入期留档八项、常态期复测六项)与一个最小比对脚本(含真实运行输出)。边界也说清楚:指纹能证明"变了",很难证明"变成了谁";它是概率证据,不是数学证明。
阅读完本节,你应当能够:
直觉版原理一句话:把模型当成一个函数,往里灌同一组输入,看输出的"习惯"是否还是原来那套。权重决定了模型选词、句长、口头禅、格式偏好的分布;权重变了,分布就有可测量的移动。操作三要素:
公开研究底账(供深入,非本书依赖):行为指纹对微调、合并、量化等改动稳健——也就是说换了"近亲"权重也查得出(arXiv,2026 研究);端点监控方向有成套的统计检验方案(跨 prompt 的分布距离 + 置换检验出 p 值,ACM,2026);单 token 指纹用低温下首 token 的选词分布做更细粒度的信号点(2026 研究,前半参考来源已列)。这些方法你在生产里大概率用不上全套——但它们证明了一件事:黑盒 API 的行为漂移是可以被严肃检验的,不必停留在"感觉"层面。
⚠️ 指纹是概率证据:同一权重不同部署( batching、浮点非确定性)也会有轻微输出差异;结论永远以"分布漂移是否超出基线噪声带"的形式给出,不给"一定换了"的断言。
| 层 | 取什么 | 强度 | 局限 |
|---|---|---|---|
| 元数据层 | 响应里的 model 字段、版本号、服务方公告 | 变了就是硬证据 | 不变不能证明没变(多数服务不回传权重指纹) |
| 行为层 | 探针集输出的逐字一致率、相似度、长度分布 | 漂移的主要信号 | 受采样参数、部署非确定性干扰 |
| 质量层 | 锚点题(从盲测 50 题挑的高区分度子集)均分 | 直连业务损失 | 题量小时噪声大,须配区间(2.2 思维) |
三层合用是关键:元数据变了 + 行为漂了 + 质量掉了,三者同向,判定卡才有分量;只有一层异常,先怀疑自己(Invalid)。
接入期(一次性,约半小时)——留档八项:
baseline.json;常态期(每月一轮,约十分钟)——复测六项:
current_时间戳.json;# fingerprint_diff.py(纯标准库,可直接运行) """最小指纹比对:接入期基线快照 vs 常态期当前快照,输出一致率与越限清单。""" from difflib import SequenceMatcher BASE = { # 示意:接入期留档的探针基线(10 条节选,含元数据) "q01": {"answer": "退款已受理,预计1-3个工作日原路退回。", "meta": "fam-x-7b"}, "q02": {"answer": "订单已发货,物流单号可在详情页查看。", "meta": "fam-x-7b"}, "q03": {"answer": "很抱歉给您带来不便,我们先核实订单状态。", "meta": "fam-x-7b"}, "q04": {"answer": "该问题需要人工客服跟进,已为您创建工单。", "meta": "fam-x-7b"}, "q05": {"answer": "会员积分可在下单后24小时内到账。", "meta": "fam-x-7b"}, } CUR = { # 示意:一个月后的当前快照(q03/q05 出现漂移,q05 元数据也变了) "q01": {"answer": "退款已受理,预计1-3个工作日原路退回。", "meta": "fam-x-7b"}, "q02": {"answer": "订单已发货,物流单号可在详情页查看。", "meta": "fam-x-7b"}, "q03": {"answer": "抱歉。请提供订单号,这边核实。", "meta": "fam-x-7b"}, "q04": {"answer": "该问题需要人工客服跟进,已为您创建工单。", "meta": "fam-x-7b"}, "q05": {"answer": "积分一般会到账的,稍等即可。", "meta": "fam-x-lite"}, } def compare(base, cur): rows = [] for qid in base: a, b = base[qid]["answer"], cur[qid]["answer"] rows.append((qid, a == b, SequenceMatcher(None, a, b).ratio(), len(b) / max(len(a), 1), base[qid]["meta"], cur[qid]["meta"])) return rows if __name__ == "__main__": rows = compare(BASE, CUR) exact = sum(1 for r in rows if r[1]) mean_sim = sum(r[2] for r in rows) / len(rows) meta_diff = [r[0] for r in rows if r[4] != r[5]] print(f"探针 {len(rows)} 条:逐字一致 {exact} 条,平均相似度 {mean_sim:.2f},元数据变化 {meta_diff or '无'}") for qid, eq, sim, ratio, m1, m2 in rows: if not eq: print(f" 越限 {qid}:相似度 {sim:.2f},长度比 {ratio:.2f},meta {m1} -> {m2}") if mean_sim >= 0.9 and not meta_diff: print("读法:整体一致(Match 倾向)") else: print("读法:出现漂移信号 -> 走 5.1 判定卡流程定类型")
一次运行的真实输出(实测,示意输入):
探针 5 条:逐字一致 3 条,平均相似度 0.73,元数据变化 ['q05'] 越限 q03:相似度 0.34,长度比 0.75,meta fam-x-7b -> fam-x-7b 越限 q05:相似度 0.32,长度比 0.82,meta fam-x-7b -> fam-x-lite 读法:出现漂移信号 -> 走 5.1 判定卡流程定类型
读法(示意数据的教学含义):q03 风格变了但元数据没变(行为层异常);q05 连元数据都变了(fam-x-lite——名字里就带 lite,教育读者别指望现实里这么好心)。两处越限 + 元数据变化,六字段凑出大半,剩下的(锚点分、双簇)补齐后进判定卡。真实使用时把 BASE/CUR 换成你的留档文件(json.load 读入即可),判定阈值按第三部分清单里自校准的噪声带来定。
💡 低温(temperature=0)下仍出现的差异最有价值:随机性被排掉之后,剩下的差异才更可能来自部署侧。反之,高温探针的比对噪声会让你什么都看不出来。
三条边界,写在使用说明里:
升级路径:个人/小团队,本节的脚本 + 月度节奏够用;要做成常态化工程(回归集、线上监控、告警治理),转《Evals 实战》第 3 章(回归集工程)——那是建设者的装备。第 9 章会把本节流水线接上定时调度与告警,做成漂移监控的最小自动化。
身份的疑心到此装备完毕。但无论服务方换没换,你手里最硬的证据永远应该来自自己的考场——第 6 章,五十条盲测,两小时,把选择权从榜单手里拿回来。