prompt-template-portability · ZH · 2026-10-25

在六后端间迁移 Jinja 风格的提示词模板:渲染在哪一步开始不一样

同一份 Jinja 风格提示词模板通过一个聚合 API key 发往不同后端(Claude、GPT、DeepSeek、Qwen、GLM、Kimi)时,渲染差异往往不在模板语法本身,而在后端对模板的接受方式与后处理阶段。本文从模板渲染的位置、角色标签的映射、空白与换行的归一化、停止词与后处理四个层面拆解差异来源,并给出在客户端做统一渲染与归一化的具体做法,帮助你在多后端之间保持提示词行为一致。

为什么“本地渲染”和“后端渲染”不是一回事

Jinja 风格模板在聚合站里通常有两种走法:一种是在你的客户端先用 Jinja 引擎把变量填好,再把纯文本发给后端;另一种是把模板字符串和变量一起发给后端,由后端决定怎么渲染。

差异就出在第二种:不同后端对模板的接受度不同,有的把模板语法当普通文本原样保留,有的会做自己的 chat template 包装,有的对 {{ }} 的空白处理敏感。结果是同一份模板,发出去之后渲染结果在换行、缩进、角色边界上开始分叉。

渲染差异真正开始的四个位置

1. 模板求值发生在哪里

  • 客户端求值:你本地渲染,后端只收到最终文本。行为最可控,推荐作为基线。
  • 后端求值:模板语法由后端处理。不同后端对 {% if %}、{% for %}、过滤器、默认值的支持范围不一致,未支持的语法可能被当作字面量输出。

2. 角色标签的映射

Jinja 模板里常见的 system、user、assistant 段落,在发到各后端时会被映射成各自的对话结构。映射规则不同会带来两种偏差:一是 system 段落可能被合并进第一条 user 消息,二是段落之间的分隔符(换行数、空行)被改写。

3. 空白与换行的归一化

这是最容易踩的坑。模板里为了可读性加的缩进和空行,在客户端渲染后是原样文本,但后端在拼装 chat template 时可能做 trim、折叠连续空行、或在末尾追加分隔符。同一份模板在两个后端上,最终喂给模型的 token 序列在空白处就不一样。

4. 停止词与后处理

停止词(stop sequences)的匹配时机不同:有的在后端解码时截断,有的在返回后由网关过滤。模板里如果用到类似 ###、<|end|> 这类结构标记,不同后端对它们的保留或剥离策略不同,会导致输出截断位置不一致。

归一化做法:把差异收敛到客户端

统一在客户端渲染模板

最稳的做法是把 Jinja 渲染放在客户端完成,发给后端的只有最终纯文本。这样模板语法、条件分支、循环都在你手里,后端只负责补全。聚合站的价值是“一把 key 调多个模型”,而不是替你做模板求值,所以把渲染收归客户端不会损失能力。

定义一套空白规范

渲染后统一做一遍空白归一化:

  • 去掉行尾空格;
  • 把连续 3 个以上换行折叠为 2 个;
  • 段落之间固定用空行分隔;
  • 模板缩进只用于可读性,渲染时用 trimblocks、lstripblocks 一类选项消除控制语句留下的空行。

这一步做完,再发往任何后端,空白层面的输入就是同一份。

角色标签用显式分隔符

不要把角色边界交给后端去猜。在渲染后的文本里用明确、稳定的标记分隔段落,例如固定的标题行或分隔线,并保证这些标记在模板里只出现一次、不被变量内容污染。这样无论后端如何包装 chat template,你的结构信息都保留在文本里。

停止词在客户端对齐

如果模板依赖结构标记来分段,把停止词定义在客户端配置里,而不是依赖各后端默认。发请求时显式传入同一组 stop 序列,并在收到结果后统一做一次后处理:解析结构标记、去掉尾部分隔符、再交给下游。

迁移检查清单

  • 同一份模板分别在两个后端上渲染,逐字符对比最终输入文本;
  • 检查 system 段落是否被合并或改写;
  • 检查连续空行、行尾空格、控制语句留下的空行;
  • 检查结构标记是否被后端剥离;
  • 检查停止词截断位置是否一致;
  • 把以上差异全部收敛到客户端归一化层,再切换后端。

计费与迁移的关系

聚合站按官方价 ×1.3 扣费,贡献 key 按官方价 ×1.1(优质 ×1.2)返 USDC,充值走 Base 上的 USDC、无 KYC。这些是账户层面的规则,与模板渲染无关;但迁移后端时要注意,不同后端的 token 计数方式不同,同一份归一化文本在不同后端上的计费 token 数可能有差异,做成本对比时应以实际账单为准。

小结

Jinja 风格模板在多后端之间的差异,主要不来自 Jinja 语法,而来自渲染位置、角色映射、空白归一化和停止词处理这四个阶段。把渲染和归一化收归客户端,用显式分隔符表达角色边界,显式配置停止词,就能让同一份模板在六种后端上表现一致。迁移时按清单逐项对比,比事后调提示词更省力。