跳到主要内容

系统提示词作为代码、配置或数据:影响全局的架构决策

· 阅读需 13 分钟
Tian Pan
Software Engineer

上个季度我交流过的一个团队发布了一个客户支持智能体,其系统提示词存储在 Postgres 的一行中,每个租户一行。这个方案听起来很合理:企业客户要求定制语气,“让提示词可编辑”是实现这一目标最廉价的方式。六个月后,发生了三件事。评估套件从 200 个案例膨胀到了 11,000 个,因为每个租户的提示词现在都需要自己的回归测试集。提示词更新工作流悄然变成了一个没有审核的写入路径,因为产品负责人被赋予了对表的直接访问权限。此外,由于部署流水线根本不知道提示词发生了变化,一个韩国租户提示词中损坏的 UTF-8 字符导致该租户的聊天机器人下线了两天,却没人察觉。

这些结果都不是需求强制导致的。它们是由一个无人刻意做出的架构决策所强制导致的:系统提示词存放在哪里?在代码中?在配置文件中?还是在数据库行中?团队选择了“数据库”,因为这是实现功能最快的路径,而后果在接下来的几个月里级联影响到了每一个相邻系统。

这是早期 AI 产品中最被低估的架构决策。它看起来像是个部署琐事——“我们把这个字符串放哪儿?”——但答案决定了谁能更改提示词、适用什么样的审核流程、回滚如何工作、你的评估矩阵长什么样,以及当出现问题时,是由你的平台团队还是产品团队负责可靠性。这个决策的扭转成本也很高。将提示词从“数据库中”迁移回“代码中”耗时六个月并不罕见。做对的团队会在 MVP 之前,利用框架深思熟虑地进行选择。做错的团队则是偶然做出的选择,并为此支付数年的代价。

三种存储类别及其真实用途

在你做出选择之前,必须清晰地审视这三个选项。它们不是可以互换的。

提示词即代码 (Prompt as code) 意味着提示词字符串存在于你的应用代码库中,在 pull request 中接受审查,随部署产物发布,并绑定到特定的应用版本。更改需要运行 CI、代码审查和部署。提示词与使用它的代码共享发布周期。回滚就是 git revert。更改的成本很高——从几分钟到几小时不等——而且审计追踪是自动的,因为 Git 已经记录了谁在何时出于何种原因进行了更改。

提示词即配置 (Prompt as config) 意味着提示词存在于独立于代码发布的配置产物中:配置服务中的 JSON 或 YAML 文件、LaunchDarkly 或 Statsig 中的远程配置项、应用在短 TTL 内重新加载的“提示词”表中的一行,或者是产品团队拥有的 CMS 条目。应用在运行时读取提示词——有时是按请求读取,有时是缓存。更改不需要部署。更改的成本很低——从几秒钟到几分钟不等——而审查过程完全取决于你为配置存储封装了什么工具。如果没有明确的纪律,就没有审查。

提示词即数据 (Prompt as data) 意味着提示词是按行、按租户、按用户或按对话存储的,与业务数据库中的其他租户或用户状态并存。提示词不是带有版本的单一全局字符串;它是一个多值字段,每个租户都有自己的提示词。更改发生在客户的时间线上,而不是你的。评估问题会随着你持有的不同提示词实例的数量而成倍增加。审计追踪取决于你的数据层已有的任何审计功能,而这通常比你想要的要少。

这些映射到了不同的心理模型。代码用于不变量 (invariants)。配置用于随环境和时间变化的参数。数据用于随产品服务的实体而变化的东西。系统提示词可以理所当然地属于这三者中的任何一种,而这种模糊性正是团队选错的原因。

“提示词即代码”的隐藏成本

对于大多数处于产品市场匹配 (PMF) 之前的 AI 产品来说,将提示词放在仓库中是正确的默认选择,这也是工程团队在不检查其产品是否真正需要的情况下反射性做出的选择。好处是实实在在的:每一次更改都经过审核、可审计,且与依赖它的代码共用相同的 CI 流水线,回滚操作也与你处理代码回归的操作完全一致。共享的发布周期意味着你可以在同一个 PR 中重构提示词和解析其结构化输出的解析器,并通过单次评估运行验证这一对组合。

成本会在后期以两种可预见的形式显现。第一种是迭代延迟。当你的提示词工程团队需要测试五个微小的措辞变化,而你的部署流水线每次更改需要 25 分钟时,你刚刚让提示词迭代的内环 (inner loop) 变得极其缓慢。处于这种情况的团队通常会发明一个侧向通道——一个覆盖代码内提示词的功能标志 (feature flag),或者一个绕过 CI 的“提示词草稿板”环境——而侧向通道会悄悄成为生产路径,而代码内的提示词则变成了没人更新的陈旧后备方案。

第二个成本是工程团队与真正理解提示词该说什么的人员之间的隔阂。领域专家和产品负责人如果不找工程师提交 PR,就无法发布提示词更改。当提示词每周更改一次时,这没问题。当每天都要更改时,这就会产生腐蚀。当这道墙开始产生束缚时,正确的做法是刻意将提示词移动到配置中,而不是发明一套假装提示词仍在代码中的影子工具。

为什么“提示词即配置”看起来免费,实则不然

将提示词移入配置服务是当代码内工作流变得太慢时最受欢迎的“捷径”。像 Langfuse 和 PromptLayer 这样的托管提示词管理工具正是为了让这种过渡变得平滑而存在的:它们为你提供 Git 风格的版本控制、预发布(staging)与生产环境(production)的标签,以及应用程序读取的 API。其核心卖点是“拥有类代码的规范而无需经历部署周期”,在小规模阶段,这个卖点确实有效。

陷阱在于这种规范是自选的(opt-in),而大多数团队从未真正选择加入。版本控制是自动的,但环境标签仍需要部署门禁,在将 staging 推送到 production 之前调用你的评估套件。提示词的变更仍需要等同于代码审查的工作流。回滚仍需要测试。这些都不是工具自带的免费功能。它们是你必须构建的流程,而且你必须在一个现在已经与应用程序发布管线解耦的系统之上构建它们。

另一种失败模式更为隐蔽。当提示词独立于代码发布时,提示词与消耗其输出的解析器之间的契约会发生无声的漂移。提示词工程师调整系统提示词以开始生成带有新字段的 JSON;部署在生产环境中的解析器不知道该字段;没有任何报错,但新字段被默默忽略,下游功能出现回归。这种漂移的检测滞后通常长达数天到数周,因为症状是“用户抱怨功能 X 变得不那么好用了”,而不是“日志中出现异常”。

缓解措施是在每次提示词配置更改时运行契约测试,并验证提示词的输出是否仍符合解析器的预期。大多数团队在吃过亏之前都不会构建这些。两行总结:一旦你拥有了规范,存储在配置中的提示词就是一种很好的架构;在那之前,它们是导致无声回归的快车道。

评估矩阵爆炸以及为什么“提示词即数据”很少是你想要的

按租户定制提示词很有诱惑力,因为客户的需求是真实的。企业买家希望他们的助手听起来像他们的品牌,拒绝讨论竞争对手,并使用他们的内部术语进行升级处理。对“让提示词可编辑”最廉价的理解是在管理面板中提供一个文本框,将其写入数据库行。六个月后,你拥有了 N 个租户、N 个提示词、N 套没人运行的评估套件,以及 N 个等待合适输入触发的潜在事故。

架构上的错误在于将提示词定制视为字符串替换问题,而它实际上是一个配置 API 问题。租户“提示词即数据”系统赋予了每个租户系统提示词的全部权限——这意味着他们也可以破坏拒绝校准、覆盖安全指令,或者粘贴与你随智能体提供的工具描述相矛盾的内容。你刚刚发布了一个面向公众的提示词注入向量,而客户就是攻击者,客户的评估责任现在变成了你的责任。

相反,行之有效的模式是结构化定制:一组有限的受限字段(语调、品牌名称、拒绝敏感度、领域词汇表),它们被插值到你控制的主提示词中。主提示词存在于代码或配置中;按租户定制的字段存在于数据中。评估矩阵现在是针对受限的定制字段,而不是针对任意的租户提示词。这很难卖给企业买家,因为它听起来不够灵活,但这是多租户提示词中唯一能通过安全审查和受监管行业检验的版本。

真正的“提示词即数据”有一个合理的案例:提示词本身就是产品的开发者平台产品。如果你在销售一个“构建你自己的智能体”工具,那么你客户的提示词根据定义就是数据——这是他们购买的产品。在这种情况下,评估矩阵的爆炸是客户的问题,而不是你的问题,你应该在架构平台时明确这一点。

在 MVP 之前你应该应用的决策框架

按照以下顺序回答四个问题,从而选择匹配的存储类别。

第一,相对于你的部署节奏,提示词变更的频率如何? 如果频率较低,选择提示词即代码。如果频率高出 10 倍或更多,选择提示词即配置。10 倍的阈值很重要,因为在此之下,代码审查的摩擦并不是瓶颈;在此之上,它会主导一切。

第二,谁需要能够在没有工程师的情况下更改提示词? 如果只有工程师,选择代码。如果产品负责人或领域专家,选择围绕配置存储构建了审查工作流的配置。如果是最终客户或租户,你不需要原始提示词——你需要受限的定制字段,而主提示词保留在代码或配置中。

第三,如果提示词变更在凌晨 3 点未经审查就发布了,会发生什么? 如果答案是“没什么,我们明天会发现它”,那么配置就可以。如果答案是“受监管行业,我们需要审计追踪和合规官的签核”,那么配置也可以,但必须有显式的审查工具——而且你应该诚实地面对你需要构建这些工具的事实,因为提示词管理 SaaS 并不免费提供这些。如果答案中包含“紧急开关(kill switch)”这个词,考虑一下提示词即代码是否更简单。

第四,你能负担得起维护多大的评估范围? 单个全局提示词有一套评估套件。少数 A/B 变体则会增加一小倍。按环境配置(预发布 vs. 生产)最多使矩阵翻倍。按租户定制提示词则会乘以 N,其中 N 是租户数量且单调增长。如果你无法承诺运行 N 套评估套件,就不要采用 N 个提示词的架构。

当你意识到选错后的迁移成本

一个令人不快的事实是,这三种迁移方式都各具成本,且这种成本是非对称的。

从代码迁移到配置是这三者中最便宜的。你建立起配置存储库,将代码中的 prompt 镜像到其中,发布一个应用程序版本(让其从配置中读取,并以代码内的值作为兜底),然后逐渐切换。难点不在于迁移,而是在新的配置库周围建立审核和评估(eval)规范,大多数团队在这方面会有长达半年的投入不足。

从配置迁移回代码虽然少见但确实会发生——通常是因为团队意识到存储在配置中的 prompt 在未经测试的情况下已经偏离(drift)数月,他们希望重新引入 CI 门禁。这种迁移是机械性的;真正的成本在于与已经习惯了快速迭代的产品负责人重新谈判,因为他们现在必须再次提交 PR。

从数据迁移到代码或配置则是最痛苦的。你必须查看每个租户的 prompt,对其进行分类(合法定制 vs. 意外复杂性 vs. 注入向量),设计一个能涵盖合法案例的结构化定制 API,编写迁移程序将非结构化 prompt 映射到结构化字段中,并告知那些 prompt 无法映射的客户:你正在取消他们正在使用的功能。这是一个耗时数个季度的项目,也是团队会拖延数年的迁移任务,因为每个季度的成本都在上升。

核心结论并不是说这些存储类别中哪一个在抽象层面是正确的。而是应该在产品第一个版本发布之前,明确回答上述四个问题,有目的地做出选择。能够做对的团队并不是那些选择了最灵活选项的团队,而是那些选择了与实际构建的产品约束相匹配的选项,并能在约束变化时重新审视选择的团队。

References:Let's stay in touch and Follow me for more thoughts and updates