提示词版本管理:不到 10 秒上线修改,无需重新部署

将 LLM 提示词存入提示词库,通过 prompt_id 调用,无需在请求中内联提示词;上线时通过移动标签切换所用提示词。实测从移动标签到实际调用生效需要 8 到 9 秒,提示词用量为 114.5 个 token,与内联请求的 114.5 个相同,未增加提示词 token 消耗。

我们的推荐

实时价格
模型 结论 价格
GPT-6 Luna 提供了 support-answer 的 v1 和 v2 版本。通过提示词 ID 调用时,发送的提示词 token 数与内联发送渲染后请求时相同,均为 114.5(20 对请求的中位数)。 演示中回答提示词使用的模型 低至 $0.1/百万
DeepSeek V4.1 Flash 运行了 policy-check v1,使用存储在该版本上的输出模式:以 18 个输出 token 生成 JSON 回答,费用为 $0.000033,reasoning_effort 设为 none。 演示中 JSON 检查使用的模型 低至 $0.3/百万
目录
  1. 如何通过 ID 调用提示词?
  2. 按 ID 调用会比在代码中直接写提示词更贵吗?
  3. 移动标签后,多久会在调用中生效?
  4. 无法处理的调用会怎样?
  5. 除了文本,版本还能保存什么?
  6. 在哪里查看调用实际使用的版本?
  7. 本文没有展开哪些内容?
  8. 常见问题
  9. 什么是提示词版本管理?
  10. 按 ID 调用提示词会增加延迟或 token 吗?
  11. 提示词修改多久能在生产环境生效?
  12. 还能继续使用 OpenAI 或 Anthropic SDK 吗?
  13. 延伸阅读

提示词版本管理,就是把提示词从代码中独立出来,保存为带编号、不可修改的版本,通过 ID 调用,再用标签指定生产环境使用哪个版本。标签是一个命名指针,比如 production 或 staging,同一时间只指向一个版本。在 Synthorai 提示词库中,请求通过 prompt_id 指定提示词,网关负责填入该版本的消息、模型和参数。移动 production 标签,后续调用就会切换版本。我们的实测结果是:持续有流量时,标签移动后 8 到 9 秒在实际调用中生效;空闲一段时间后,第一条调用就能用上新版本。按 ID 调用发送的提示词 token 数,与在代码中直接构造同一请求完全一致。

TL;DR

  • 通过 prompt_id 调用 Synthorai 网关,在 20 组配对测试中,提示词 token 数的中位数为 114.5,与内联发送请求相同。
  • 持续有流量时,production 标签移动后 8.0 到 8.5 秒(中位数)在调用中生效;连续 15 秒没有调用后,下一条调用立即生效。
  • 在 20 次标签移动中,一旦有调用使用新版本,后续就没有调用再使用旧版本。
  • 无法处理的调用会在模型运行前返回 400 或 404,不计费。

本文将追踪 LLM 智能体中的客服智能体提示词移入提示词库,并演示网关支持的所有调用方式。代码位于 github.com/synthorai-io/use-cases 的 prompt-library/ 目录。运行需要 Python 3.10 或更高版本、一个推理密钥,以及一个从控制台获取、拥有 prompts.write 权限的管理密钥。

git clone https://github.com/synthorai-io/use-cases
cd use-cases
cp .env.example .env    # put both keys in it
cd prompt-library
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python setup.py         # saves the prompts and their versions; safe to run again
python call.py          # every way to call a prompt
python ship.py          # moves production and times it
python errors.py        # what a call that cannot be served answers

本文的测试运行于 2026 年 10 月 19 日。setup.py 保存了两个提示词。support-answer 用于智能体的回复步骤,共有四个版本,每个版本各做一项实际场景中常见的修改;policy-check 是一个返回 JSON 的子智能体检查步骤:

提示词版本修改内容模型
support-answerv1基线:说明订单号,绝不承诺退款日期GPT-6 Luna
support-answerv2缩短系统提示词,去掉必须说明订单号的规则GPT-6 Luna
support-answerv3使用 v1 的文本,更换模型DeepSeek V4.1 Flash
support-answerv4在 v1 中加入没有默认值的 {{tone}} 变量GPT-6 Luna
policy-checkv1按 schema 返回 JSON,包含主题和风险等级DeepSeek V4.1 Flash

提示词页面:support-answer 有 4 个版本,production 指向 v1,staging 指向 v2;policy-check 的 production 指向 v1;页面还显示了工作区中的另外两个提示词

production 指向 v1,staging 指向 v2。后续两篇文章分别使用 v2 和 v3:上线前测试版本和向部分生产流量发布版本。控制台会展示每个版本相对于上一版本的差异:

support-answer 的 v2 与 v1 对比:删除较长的系统提示词,加入较短的版本,不再包含订单号规则;用户消息保持不变

如何通过 ID 调用提示词?

向 /v1/chat/completions、/v1/responses 或 /v1/messages 发送普通请求,在请求体中加上 prompt_id 和 prompt_variables 即可。变量是版本文本中的 {{name}} 占位符,prompt_variables 为这些占位符提供本次调用的值。SDK 要求必须传入模型和消息,因此可以填写任意模型名称和一条空的用户消息:网关会使用版本中保存的模型,并丢弃空消息。使用 OpenAI Python SDK 时,这些扩展字段放在 extra_body 中:

import os
from openai import OpenAI

client = OpenAI(base_url="https://synthorai.io/v1", api_key=os.environ["SYNTHORAI_API_KEY"])

raw = client.chat.completions.with_raw_response.create(
    model="any",                                   # the version's model is used
    messages=[{"role": "user", "content": ""}],    # required by the SDK; dropped
    extra_body={
        "prompt_id": "support-answer",
        "prompt_variables": {
            "customer_message": "Where is order A-50977? It has been a week.",
            "order_status": "in transit, shipped on 2026-10-01",
        },
    },
)
print(raw.headers["x-synthorai-prompt-version"])   # which version answered
print(raw.parse().choices[0].message.content)

调用中没有传 product,因为版本为它设置了默认值(“home electronics”)。响应头 X-Synthorai-Prompt-Version 会标明实际使用的版本。按标签调用时,这是调用方判断实际版本的唯一依据。

不指定版本时,调用使用 production 指向的版本。下面两个字段可以指定其他版本,但一次调用只能传其中一个,不能同时传:

字段示例演示中实际使用的版本
不传v1(production)
prompt_label"staging"v2
prompt_version1v1

同样的按 ID 调用在三个端点上都能正常工作:通过 OpenAI SDK 调用 Chat Completions 和 Responses,通过 Anthropic SDK 调用 Messages,使用相同的 extra_body。三个端点都使用 v1,模型均为 GPT-6 Luna。在 Responses 中,版本里的消息会放入 input;提示词库指南列出了各字段在不同端点上的映射关系。

按 ID 调用会比在代码中直接写提示词更贵吗?

费用相同,因为网关发给模型提供商的仍然是普通请求。我们做了 20 组配对测试。每组先渲染提示词,再分别以内联方式发送渲染后的请求,以及按 ID 发送相同的变量值。两种方式交替先后顺序,并使用相同的 salt(在消息中加入一小段随机标记,确保不同组不是同一个请求):

提示词 token 数,中位数客户端延迟,中位数
通过 prompt_id114.54.1 秒
内联114.54.6 秒

token 数完全一致。按 ID 调用反而更快,这不可能是额外开销造成的,因此我们将差异视为 GPT-6 Luna 各次调用之间的波动,并得出结论:按 ID 调用没有增加可测得的耗时。我们没有对延迟差异做统计检验。

如果想查看调用实际会发送什么,又不想调用模型,可以先渲染。POST /v1/prompts/{id}/render 接收相同的变量,返回请求内容以及解析后使用的版本:

curl https://synthorai.io/v1/prompts/support-answer/render \
  -H "Authorization: Bearer $SYNTHORAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format": "chat", "prompt_variables": {"customer_message": "Where is order A-50977?", "order_status": "in transit"}}'

渲染不计费,也不会出现在日志中:演示执行渲染后,日志中没有该会话 ID 对应的请求。但它会占用密钥的速率限额。

移动标签后,多久会在调用中生效?

不到 10 秒:实测在持续调用时需要 8 到 9 秒,空闲一段时间后则立即生效。发布版本只需用管理密钥调用一次 API,将标签指向指定版本号:

import os
import httpx

admin = httpx.Client(base_url="https://synthorai.io/api/v1",
                     headers={"Authorization": f"Bearer {os.environ['SYNTHORAI_PROMPTS_ADMIN_KEY']}"})
admin.put("/prompts/support-answer/labels/production", json={"version": 3}).raise_for_status()

回滚也是同样的调用,只需改成旧版本号。业务代码继续调用同一个 ID,无需重新部署任何服务。只有管理员或工作区所有者能移动标签,每次移动都会记录在工作区审计日志中。

ship.py 测量了生效时间。它每隔 0.5 秒发送一次调用,通过 API 移动 production,持续调用,直到响应头显示新版本,再继续调用 10 秒。脚本从 v1 切到 v3 共 5 次,反向切回共 5 次,然后在每次切换前停止调用 15 秒,重复这 10 次移动:

场景最后一次由旧版本响应的调用发送时间第一次由新版本响应的调用发送时间
每 0.5 秒调用一次,v1 切到 v3移动后中位数为 8.02 秒(7.53 到 8.53)中位数为 8.52 秒(8.03 到 9.03)
每 0.5 秒调用一次,v3 切到 v1中位数为 7.52 秒(7.52 到 8.52)中位数为 8.02 秒(8.02 到 9.02)
停止调用 15 秒后,双向切换无10 次移动均为 0.0 秒

计时从移动标签的 API 调用返回时开始。文档说明,标签移动会在“大约 10 秒”内对调用生效。这两种场景的表现符合以下缓存机制:网关读取标签后,会将读取结果保留几秒;持续有流量时,要等这份缓存过期才能看到变化,暂停调用后再发出的第一条请求则会重新读取标签。20 次移动中,一旦有调用使用新版本,就没有后续调用再使用旧版本,因此生效过程中没有出现新旧版本来回切换。

无法处理的调用会怎样?

请求会在调用模型之前失败,返回 400 或 404,错误格式与所调用的端点一致,不产生费用。errors.py 发送了五种此类调用:

调用状态码错误码消息
调用 v4,但不传 tone400prompt_variable_missing“prompt_variables:缺少 tone(既未传值,也没有默认值)”
同时指定版本和标签400prompt_invalid“请传 prompt_version 或 prompt_label,不要同时传入”
使用不存在的 ID404prompt_not_found“prompt_id:此工作区中没有提示词 “support-answer-missing””
指定版本 99404prompt_not_found“提示词 “support-answer” 没有版本 99”
指定未指向任何版本的标签404prompt_not_found“提示词 “support-answer” 的 “development” 标签尚未指向任何版本”

这五次调用都没有计费记录。第一行展示了会破坏现有调用的修改:v4 新增了一个没有默认值的变量,因此所有不传 tone 的调用都会失败。控制台会在上线前发现这个问题。将 production 移到 v4 时,控制台会打开一个对话框,检查这次修改对现有调用方的影响,要求填写默认值,再将其保存为下一个版本。

将 production 移到 v4 的对话框:v4 新增了 tone,且没有默认值,现有调用会收到 400;输入框要求填写 tone 的默认值,按钮为“保存下一个版本并移动 production”

如果提示词库暂时无法读取,网关会继续使用最后一次读取的副本,最长 24 小时,并在响应中加入 X-Synthorai-Prompt-Stale: 1。如果没有该提示词的近期副本,则返回 503 prompt_unavailable,此时应重试。演示中没有遇到这两种情况。

除了文本,版本还能保存什么?

一个版本可以保存系统提示词、消息、模型、五个参数、输出 schema,以及函数工具。参数以调用方为准:只有请求未设置对应字段时,才使用版本中的 temperature、max_tokens、top_p、response_format 和 reasoning_effort。

policy-check 将 JSON schema 保存在版本中,因此调用方无需传入格式设置,就能得到符合 schema 的 JSON。通过 API 保存时,版本在 params 中指定格式,在 schema 中保存结构定义:

version = {
    "model": "deepseek-v4.1-flash",
    "system": "You review messages to an online electronics shop's support desk. ...",
    "messages": [{"role": "user", "content": "{{customer_message}}"}],
    "params": {"max_tokens": 300, "response_format": "json_schema"},
    "schema": {"name": "policy_check", "strict": True, "schema": {
        "type": "object",
        "properties": {"topics": {"type": "array", "items": {"type": "string"}},
                       "risk": {"type": "string", "enum": ["low", "medium", "high"]}},
        "required": ["topics", "risk"], "additionalProperties": False}},
}
admin.post("/prompts/policy-check/versions", json={"body": version, "note": "v1: JSON check"}).raise_for_status()

如果保存 schema 时没有设置 response_format: "json_schema",API 会返回 400,并说明原因。一次使用 DeepSeek V4.1 Flash 的 policy-check 调用返回了 {"topics": ["shipping", "refunds"], "risk": "medium"},输入为 151 个 token,输出为 18 个 token,费用为 $0.000033。这次调用由调用方传入 reasoning_effort: "none",因为演示中的版本保存时,还不支持在版本中保存该参数。现在可以将其存入版本的 params:我们保存了一个带有 reasoning_effort: "none" 的测试版本并渲染,确认 Chat Completions 请求中包含 reasoning_effort,Responses 请求中包含 reasoning.effort。Messages 不会传递版本中的这个值,因为 Claude 模型的思考设置采用另一种格式;使用该端点时,应由调用方传入。

工具调用和多轮对话的用法与不使用提示词库时相同。版本中的消息排在前面,调用方发送的对话接在后面。演示的工具循环中,模型在第 1 轮请求 lookup_order({"order_id": "A-50977"});第 2 轮仍按同一个 ID 调用,将模型的工具调用和工具结果作为普通消息发送,v1 随后根据结果作答。

在哪里查看调用实际使用的版本?

每次调用都会记录对应的提示词和版本,可以从三个地方查看:

  • 响应中:查看 X-Synthorai-Prompt-Version 响应头。演示会在结果中为每次调用记录这个值。
  • 日志中:使用 prompt = support-answer 和 prompt_version = 3 筛选,即可列出该版本的调用;请求详情中也会显示提示词和版本。GET /api/v1/requests 支持相同的 prompt_id 和 prompt_version 筛选条件。
  • 追踪中:瀑布图中的每条模型调用记录都带有“prompt vN”标记,调用面板中会列出提示词和版本。追踪 LLM 智能体中的追踪请求头可以原样用于按 ID 发起的调用。

演示中工具循环的追踪:瀑布图包含两次 gpt-6-luna 调用,都带有 support-answer v1 标记;第二次调用的面板在标识信息下列出 Prompt support-answer v1

提示词页面会按版本汇总最近 7、30 或 90 天的调用数、错误数、费用、token 数和延迟。灰度发布指南使用这个视图,对比两个版本在真实流量下的表现。

本文没有展开哪些内容?

  • 灰度发布和测试。将部分生产流量分配给候选版本,见对提示词修改进行 A/B 测试;使用固定输入集测试版本,见上线前测试提示词。
  • 公共片段。一段文本只保存一次,通过 {{>id}} 在多个提示词中引用;每个保存的版本都会固定其引用的片段版本。
  • 变更 webhook。提示词新增版本或标签移动时,发送带签名的 POST,可用于将提示词同步到 git 或聊天频道。演示中的 webhook.py 会验证签名,但我们没有收到实际投递。
  • 创建操作的幂等性。Idempotency-Key 请求头可确保重试安全:24 小时内,同一个 key 的重复请求会返回首次请求的结果,而不是重复创建提示词。即使提示词已删除,这条规则仍然有效,因此要重新创建已删除的提示词,必须使用新的 key。

常见问题

什么是提示词版本管理?

提示词版本管理是将每个提示词保存为带编号的版本,保存后不可修改,再通过标签选择生产环境使用的版本,而不是修改代码。回滚只需将标签指回上一个版本。

按 ID 调用提示词会增加延迟或 token 吗?

在我们的 20 组配对测试中,两者都没有增加:两种方式的提示词 token 数中位数均为 114.5,按 ID 调用也没有更慢。网关解析版本后,向模型提供商发送普通请求。

提示词修改多久能在生产环境生效?

大约 10 秒内。实测在持续有调用时,第一条由新版本响应的调用是在标签移动后 8.0 到 8.5 秒(中位数)发出的;停止调用 15 秒后,则立即生效。

还能继续使用 OpenAI 或 Anthropic SDK 吗?

两种 SDK 都无需修改:通过 extra_body 传递提示词字段,再填入任意模型名称和一条空的用户消息,满足 SDK 的必填参数要求即可。

延伸阅读

← 使用场景