prompt-template-registry · ZH · 2026-10-22

一把 key 背后的提示词版本登记表:每次调用到底把哪一版发给了哪个后端

用本地小登记表配合提示词哈希,解决多模型聚合调用时“哪一版提示词发给了哪个后端”的追溯问题:登记表存模板与哈希,调用日志只记哈希和后端标识,事后按哈希还原。

为什么需要提示词版本追溯

当一个 API key 同时能调多个模型时,问题会悄悄变复杂:同一份业务代码,可能对 Claude 用一套系统提示词,对 DeepSeek 用另一套,对 Qwen 又做了裁剪。上线几周后,你看到一个效果不佳的请求日志,却无法确定当时到底用的是哪一版模板。

传统做法是在代码里给提示词写版本号,但版本号是人维护的,容易漏改、错改。更可靠的做法是以内容为准:对提示词正文算哈希,让哈希成为版本的唯一身份。

核心设计:登记表 + 调用日志

思路拆成两个独立部分:

  • 提示词登记表:保存在本地,记录 哈希 → 模板正文 + 元信息,只增不改。
  • 调用日志:每次请求只记 哈希 + 后端标识 + 时间,不重复存正文。

日志行保持轻量,正文集中在登记表里,事后用哈希做一次 join 即可复原。

登记表的字段建议

一个够用的最小结构:

  • hash:对规范化后的提示词正文计算的哈希
  • body:模板原始文本
  • label:人类可读的名字,如 system-zh-v3
  • created_at:首次登记时间
  • note:可选,记录这次改动的意图

关键约定是:同一个哈希只登记一次。如果内容没变,哈希就不变,登记表自然去重;内容一变就是新哈希,自动成为新版本,不需要人手动升版本号。

规范化再算哈希

算哈希前先做规范化,否则空白差异会制造大量“伪新版本”:

  • 统一换行符
  • 去掉行尾空白
  • 决定是否折叠连续空行
  • 明确模板中的变量占位符写法(如 {{question}})并保持稳定

规范化规则一旦确定就不要再改,改了会让历史哈希失去可比性。如果必须改,就在登记表里保留旧规则时期的记录,别重算。

调用时记录什么

每次请求把以下信息写进日志:

  • 提示词哈希(系统提示词、必要的用户模板各一个)
  • 后端/模型标识
  • 请求时间与请求 ID
  • 可选:参数摘要、token 用量

注意日志里不要存提示词正文,避免日志膨胀和敏感内容扩散。正文留在登记表,靠哈希关联。

对于聚合网关场景,建议在请求头或元数据里带上一个自建追踪 ID,日志与网关侧记录用同一个 ID 对齐,排查时两边能对上号。

事后复原的三步查询

拿到一条效果异常的日志后:

  1. 从日志取出哈希与后端标识。
  2. 用哈希去登记表查正文和 label。
  3. 若哈希查不到,说明这次调用用的模板没经过登记流程——这本身就是一条值得追的信号。

反过来也能回答“这个模板都发给了哪些后端”:用哈希反查日志,按后端分组统计即可。

把登记做进调用路径

最省心的方式是让登记成为发送前的必经步骤:

  • 组装好最终提示词字符串
  • 规范化后算哈希
  • 查登记表,不存在则插入
  • 用同一个哈希写调用日志并发请求

这样登记不再依赖开发者自觉,而是流程的一部分。任何绕过该流程的调用都会在事后查不到哈希,暴露出来。

常见坑

  • 在提示词里拼时间戳或随机数:哈希每次都变,版本体系失效。需要动态内容就放用户消息,别混进模板。
  • 哈希算法中途更换:新旧哈希不可比。要换就加算法标识字段,新旧并存。
  • 只记哈希不记后端:无法回答“发给了谁”,追溯价值减半。
  • 日志里塞正文:体积和敏感性问题都会出现,违背了分离设计的初衷。

和聚合调用的配合

多模型聚合的好处是一个 key 打通多个后端,代价是调用路径变长、变量变多。提示词版本登记表把其中一个变量固定下来:无论请求最终落到 Claude、GPT、DeepSeek、Qwen、GLM 还是 Kimi,你都能凭一条日志哈希,精确说出那次请求用的是哪一版模板。这种可追溯性不改变计费与调用方式,只是让调试和回归有据可依。