一把 key 背后的提示词版本登记表:每次调用到底把哪一版发给了哪个后端
用本地小登记表配合提示词哈希,解决多模型聚合调用时“哪一版提示词发给了哪个后端”的追溯问题:登记表存模板与哈希,调用日志只记哈希和后端标识,事后按哈希还原。
为什么需要提示词版本追溯
当一个 API key 同时能调多个模型时,问题会悄悄变复杂:同一份业务代码,可能对 Claude 用一套系统提示词,对 DeepSeek 用另一套,对 Qwen 又做了裁剪。上线几周后,你看到一个效果不佳的请求日志,却无法确定当时到底用的是哪一版模板。
传统做法是在代码里给提示词写版本号,但版本号是人维护的,容易漏改、错改。更可靠的做法是以内容为准:对提示词正文算哈希,让哈希成为版本的唯一身份。
核心设计:登记表 + 调用日志
思路拆成两个独立部分:
- 提示词登记表:保存在本地,记录
哈希 → 模板正文 + 元信息,只增不改。 - 调用日志:每次请求只记
哈希 + 后端标识 + 时间,不重复存正文。
日志行保持轻量,正文集中在登记表里,事后用哈希做一次 join 即可复原。
登记表的字段建议
一个够用的最小结构:
hash:对规范化后的提示词正文计算的哈希body:模板原始文本label:人类可读的名字,如system-zh-v3created_at:首次登记时间note:可选,记录这次改动的意图
关键约定是:同一个哈希只登记一次。如果内容没变,哈希就不变,登记表自然去重;内容一变就是新哈希,自动成为新版本,不需要人手动升版本号。
规范化再算哈希
算哈希前先做规范化,否则空白差异会制造大量“伪新版本”:
- 统一换行符
- 去掉行尾空白
- 决定是否折叠连续空行
- 明确模板中的变量占位符写法(如
{{question}})并保持稳定
规范化规则一旦确定就不要再改,改了会让历史哈希失去可比性。如果必须改,就在登记表里保留旧规则时期的记录,别重算。
调用时记录什么
每次请求把以下信息写进日志:
- 提示词哈希(系统提示词、必要的用户模板各一个)
- 后端/模型标识
- 请求时间与请求 ID
- 可选:参数摘要、token 用量
注意日志里不要存提示词正文,避免日志膨胀和敏感内容扩散。正文留在登记表,靠哈希关联。
对于聚合网关场景,建议在请求头或元数据里带上一个自建追踪 ID,日志与网关侧记录用同一个 ID 对齐,排查时两边能对上号。
事后复原的三步查询
拿到一条效果异常的日志后:
- 从日志取出哈希与后端标识。
- 用哈希去登记表查正文和
label。 - 若哈希查不到,说明这次调用用的模板没经过登记流程——这本身就是一条值得追的信号。
反过来也能回答“这个模板都发给了哪些后端”:用哈希反查日志,按后端分组统计即可。
把登记做进调用路径
最省心的方式是让登记成为发送前的必经步骤:
- 组装好最终提示词字符串
- 规范化后算哈希
- 查登记表,不存在则插入
- 用同一个哈希写调用日志并发请求
这样登记不再依赖开发者自觉,而是流程的一部分。任何绕过该流程的调用都会在事后查不到哈希,暴露出来。
常见坑
- 在提示词里拼时间戳或随机数:哈希每次都变,版本体系失效。需要动态内容就放用户消息,别混进模板。
- 哈希算法中途更换:新旧哈希不可比。要换就加算法标识字段,新旧并存。
- 只记哈希不记后端:无法回答“发给了谁”,追溯价值减半。
- 日志里塞正文:体积和敏感性问题都会出现,违背了分离设计的初衷。
和聚合调用的配合
多模型聚合的好处是一个 key 打通多个后端,代价是调用路径变长、变量变多。提示词版本登记表把其中一个变量固定下来:无论请求最终落到 Claude、GPT、DeepSeek、Qwen、GLM 还是 Kimi,你都能凭一条日志哈希,精确说出那次请求用的是哪一版模板。这种可追溯性不改变计费与调用方式,只是让调试和回归有据可依。