site logo

Marico's space

如何安全地将提示词移出代码库

AI技术与应用 2026-08-24 20:56:28 4

最近折腾把项目里的提示词(Prompt)从代码里搬出来,踩了几个坑,这篇把过程记录下来。

大多数AI(人工智能)应用起步时,提示词都是直接写在代码里的:

const systemPrompt = `
你是客服助手。
回答要清晰,账单问题要升级处理。
`;

做原型阶段这样写完全没问题。

但当应用里有了多个提示词、团队有多个贡献者、线上用户在依赖这个行为时,问题就来了。

这时候团队会想把提示词单独管理,这样就能:

  • 更新提示词不用重新部署
  • 追踪线上跑的是哪个版本
  • 发布前先测试改动
  • 让非开发者也能安全地参与
  • 行为回退时能恢复之前的版本

听起来把提示词搬出代码库很简单——复制到数据库,加个API调用,把原来的字符串删掉。

但一次性做完这些,风险不小。

说一个更安全的迁移方案。

Step 1: 清点现有提示词

动手之前,先把应用里用到的所有提示词找出来。

提示词可能藏在这些地方:

  • 源代码文件
  • 环境变量
  • 配置文件
  • 数据库记录
  • 服务商控制台
  • 工作流自动化工具
  • 手动复制进应用的文档

先建一个基础清单:

提示词 位置 负责人 使用方 频繁改动?
客服助手 support.ts 客服团队 客服对话
工单分类 classify.ts 工程组 路由worker 很少
周报摘要 环境变量 产品组 报表任务

这一步防止一个常见错误:把显眼的提示词搬走了,结果忘了角落里还有一堆。

Step 2: 确认完整的提示词状态

线上的提示词通常不止一个字符串。

它的行为可能依赖这些:

  • 系统指令
  • 用户消息模板
  • 上下文
  • 安全限制
  • 输出格式
  • 变量
  • 模型名称
  • 温度参数
  • 最大token数
  • 工具定义

举个例子:

const prompt = `
你是${companyName}的客服助手。 退换政策:
用户可以在${refundDays}天内申请退款。 请返回JSON格式。
`;

这个提示词依赖两个变量和一个输出约定。

迁移时要明确保留这些职责:

角色:
你是{{company_name}}的客服助手。 上下文:
用户可以在{{refund_days}}天内申请退款。 输出格式:
请返回有效的JSON。

如果只复制最终编译好的文本,团队可能会搞不清这些值从哪来,或者哪个指令负责哪种行为。

Step 3: 把当前版本存为基线

迁移过程中不要顺手改进提示词。

先把现有线上提示词原封不动地复现出来,存为基线版本。

记录这些信息:

  • 原始提示词内容
  • 变量和默认值
  • 模型配置
  • 迁移日期
  • 使用它的应用
  • 当前负责人
  • 后续改动的原因

这样团队有个已知的起点。

迁移和提示词改进应该是两件分开的事。如果行为在迁移过程中变了,你得能分清是新的传输系统导致的,还是重写提示词导致的。

Step 4: 创建回归测试用例

在改变应用加载提示词的方式之前,先把有代表性的输入和预期行为保存下来。

拿客服助手来说,测试用例可以包括:

普通请求:
我想改一下收货地址。 账单升级:
我被收了两份订阅费。 安全案例:
我能把密码发给你让你帮我查吗? 政策边界:
我刚好是14天前买的,能退款吗? 无关请求:
写一首关于企鹅的诗。

明确每个响应哪些方面重要:

  • 必需的类型
  • 是否需要升级
  • 必须包含的信息
  • 禁止的声明
  • 有效的JSON结构
  • 安全限制

目标不是要求措辞一模一样,而是保护迁移过程中的关键行为。

Step 5: 引入提示词加载适配器

别在整个应用里到处调提示词管理API。

只创建一个内部函数负责获取提示词:

type PromptVariables = Record<string, string>; async function loadPrompt( promptKey: string, variables: PromptVariables
): Promise<string> { // 提示词获取和变量处理逻辑在这里 return "";
}

应用其他部分都用这个适配器:

const systemPrompt = await loadPrompt("support-assistant", { company_name: "某宝", refund_days: "14"
});

这样形成了一个清晰的边界。

以后传输方式变了,只需要改适配器。

Step 6: 保留临时回退方案

提示词服务临时不可用时,应用不应该直接崩溃。

迁移期间,把之前的提示词作为最后的可用版本保留:

const fallbackPrompt = `
你是某宝的客服助手。 用户可以在14天内申请退款。 请返回有效的JSON格式。
`;

加载逻辑可以这样走:

1. 检查本地缓存
2. 请求已发布的提示词
3. 验证响应
4. 更新缓存
5. 如果获取失败,使用上一个已知可用的提示词

从概念上说:

async function loadPromptSafely(): Promise<string> { try { const prompt = await fetchPublishedPrompt(); if (!prompt || prompt.trim().length === 0) { throw new Error("提示词响应为空"); } return prompt; } catch (error) { console.error("获取提示词失败", error); return fallbackPrompt; }
}

回退方案应该是临时的,或者通过受控的发布流程更新。过时的回退可能变成另一个隐藏的线上版本。

Step 7: 加缓存和超时

每次模型请求前都获取一次提示词,可能引入不必要的延迟,还会有运行时依赖。

要用这些:

  • 短网络超时
  • 内存缓存或分布式缓存
  • 可配置的缓存时长
  • 上一个已知可用的持久化版本
  • 支持条件请求的话用条件请求
  • 获取失败时的明确监控

合适的缓存时长取决于发布的提示词改动需要多快触达应用。

每周改一次提示词的团队可以接受较长的缓存。需要几乎即时改动的团队可能用更短的缓存或事件驱动的失效机制。

重要的是主动定义这个行为。

Step 8: 先在预发布环境测试

让预发布环境指向托管的提示词,生产继续用硬编码版本。

验证这些:

  • 变量正确编译
  • 返回的是预期版本的提示词
  • 输出格式仍然正常
  • 认证和API权限正确
  • 缓存行为符合预期
  • 获取失败时回退正常触发
  • 保存的测试用例仍然通过

然后模拟各种失败:

  • API key无效
  • 网络超时
  • 提示词不存在
  • 响应为空
  • 变量错误
  • 没有已发布版本

迁移完成的前提是失败场景都测过了。

Step 9: 分阶段推上线

第一个生产版本不要删硬编码的提示词。

更安全的做法:

Release 1

加入提示词适配器和托管获取,但保留硬编码提示词作为回退。

Release 2

对内部用户或小比例流量启用托管获取。

Release 3

验证输出、延迟和错误后扩大使用范围。

Release 4

确认托管版本稳定后,才删除旧的硬编码提示词。

这样有好几次机会可以停下来或回滚,不影响所有用户。

Step 10: 把草稿和已发布版本分开

应用应该只获取明确发布过的版本。

编辑草稿不应该立即改变线上行为。

一个有用的生命周期:

编辑草稿 ↓
运行测试用例 ↓
审查改动 ↓
发布版本 ↓
应用获取到

当多人可以编辑提示词时,这个边界至关重要。

没有它,每个实验都可能成为潜在的线上变更。

Step 11: 发布前准备好回滚方案

把线上流量切过去之前,先回答这些问题:

  • 当前线上是哪个提示词版本?
  • 怎么恢复它?
  • 谁有权限发布?
  • 缓存的应用多久能收到回滚?
  • 提示词API不可用时怎么办?
  • 哪里能看到谁做的改动?

回滚不应该需要从截图、聊天记录或Git历史里重建旧提示词。

之前正常工作的版本应该作为有版本号的产物保留着。

常见迁移错误

迁移时顺便重写提示词

这会让你很难区分传输问题还是行为变化。

先迁移现有提示词,之后再改进。

立即删掉回退方案

第一次集成发布的阶段是最糟糕的移除已知可用版本的时机。

在新路径被验证之前,保留受控的回退。

把线上变量复制到提示词里

密钥和运行时特有的值不应该硬编码到托管提示词文本中。

敏感值放在安全的运行时配置里。

生产环境使用草稿内容

生产应用应该只获取已发布版本。

草稿是用来实验的。

跳过失败场景测试

API请求成功不代表集成是生产安全的。

要测试超时、数据缺失、凭证无效和回退行为。

给所有用户发布权限

编辑和发布是不同的职责。

把生产发布权限限制在可信用户范围内,保持改动可追溯。

总结

把提示词从应用代码里搬出来可以让迭代更快,但前提是新工作流仍然可靠。

最安全的迁移不是一次性大切换。

而是一个序列:

清点 → 基线 → 测试 → 集成 → 验证 → 推出 → 移除回退

把迁移当作一次生产基础设施变更来处理。

先保留当前行为。

在重要的部分加上测试。

建立安全的传输边界。

等新方案赢得你的信任之后,再删掉旧的实现。