LLM 应用的 CI/CD:为什么部署 Prompt 与部署代码完全不同
你的代码通过一个流程发布:特性分支 (feature branch) → 合并请求 (pull request) → 自动化测试 → 预发布 (staging) → 生产环境 (production)。每一步都有门槛。如果没有通过你定义的检查,任何东西都无法到达用户手中。这种“枯燥”正是它最好的地方。
现在想象你需要更新一个系统提示词 (system prompt)。你在仪表盘中编辑字符串,点击保存,更改立即生效 —— 没有测试,没有预发布,版本控制中没有 diff,除了手动改回去之外没有回滚的方法。这就是大多数团队的运作方式,也是提示词更改成为 LLM 应用非预期生产事故主要原因的原因。
挑战不在于团队粗心大意。而在于持续交付 (continuous delivery) 的规范是为确定性系统构建的,而 LLM 并非确定性的。整个思维模型需要从头重建。
核心问题:Prompt 是未经测试的代码
在传统软件中,Bug 通常是可以检测到的。错误的值会抛出异常,缺失的字段会返回 404,损坏的查询会返回零结果。系统会以“大声”的方式失败。
LLM 应用的失败是无声无息的。你的 API 返回 HTTP 200,延迟看起来正常,Token 使用量也正常 —— 但模型现在给出的答案却有细微的错误,幻觉出了上周还没有出现的细节,或者忽略了你认为已经明确指定的约束。用户看到的是质量下降的输出,而你的仪表盘却什么也看不出来。
这就是为什么 Prompt 更改如此危险。从系统提示词中删除一个词、重新排列一条指令、为了节省成本而修剪几个 Token —— 其中任何一项操作都可能改变模型的行为,而这些改变在初看时可能没什么问题,只有在整体汇总时才会显现。对 1,200 多个生产环境 LLM 部署的分析发现,Prompt 更新是导致非预期生产行为的首要原因,排在模型版本更改和基础设施故障之前。
显而易见的解决办法是将 Prompt 视为代码 —— 对其进行版本化、测试,并根据通过标准来设置部署门槛。但这立即引出了更深层的问题:你如何测试一个非确定性的东西?
对 LLM 而言,“通过 CI”意味着什么
在传统的 CI 流程中,测试要么通过,要么失败。函数的输出要么与预期值匹配,要么不匹配。
LLM 评估从根本上是不同的。你定义指标,根据这些指标对输出进行评分,并设置阈值。当评估分数超过你定义的最小值时,测试就算“通 过” —— 而不是当输出完全匹配时。
一个 LLM CI 门槛的组成部分:
评估数据集 (Evaluation datasets) —— 一组经过策划的具有预期行为的输入。不是预期的精确输出(每次运行都会变化),而是预期的属性:“这应该包含免责声明”、“这不应推荐竞争对手”、“这应该在三句话内回答”。
评估器 (Evaluators) —— 对输出进行评分的函数。这些函数的范围从简单的字符串匹配(“输出是否包含‘我不知道’?”)到使用独立模型评估质量、相关性或政策合规性的 LLM-as-judge 评估器。
阈值和门槛 (Thresholds and gates) —— 定义“足够好”含义的数字目标。准确率指标可能要求 85% 的通过率。安全性指标可能要求 100% —— 不允许任何失败。
基准对比 (Baseline comparison) —— 使用与当前生产版本相同的数据集对新版本进行评分。如果新版本的评分明显变差,即使它通过了绝对阈值,也被视为回归 (regression)。
像 Braintrust 这样的工具会直接将评估结果发布到合并请求 (pull request) 中,如果分数下降则阻止合并。LangSmith 提供数据集管理和自动评估器运行。DeepEval 将类似 pytest 的评估引入到现有的 Python 测试套件中。这些平台已经从“线下实验的可选配置”转变为“安全部署的必需基础设施”。
关键的洞察在于门槛不是二元的 —— 它是概率性的。这改变了你对发布 (rollout) 的看法。
影子测试:部署新 Prompt 的正确方式
影子模式 (Shadow mode) 是 LLM 变更中置信度最高的部署模式。其核心理念非常直接:当新的 Prompt 版本准备好投入生产时,你将其与在线系统并行运行,为其提供相同的输入,但在输出到达用户之前将其丢弃。
每一个真实的生产请求都在加深你对新版本行为方式的理解。你不再依赖于可能无法捕捉到真实用户查询分布的合成评估数据集。你是在针对真实流量运行候选版本,收集真实输出,并进行离线评分。
运作机制:
- 在请求到达 LLM 之前,在应用层复制传入的请求
- 将一份副本路由到当前的生产环境 Prompt,另一份路由到候选版本
- 仅将生产环境的响应返回给用户
- 记录两个响应以进行异步评估
- 在夜间运行你的评估器;次日早晨查看汇总结果
与 A/B 测试的关键区别在于,影子模式完全不会让用户接触到候选版本。A/B 测试会分流,这意味着在测试期间,一半的用户可能会得到质量下降的输出。影子测试则完全推迟了这种风险,直到你对新版本有了统计学上的信心。
局限性在于成本 —— 你正在运行两倍的 LLM 调用。对于高流量应用来说,这笔开销可能非常显著。一些团队通过对采样百分比的流量进行影子测试,而不是全部流量,来缓解这一问题。
金丝雀发布与何时停止
一旦影子测试让你有了信心,下一步就是受控流量曝光。LLM 的金丝雀部署遵循与微服务相同的模式——将一小部分真实流量路由到新 版本——但推进或停止的标准完全不同。
对于微服务,你会观察错误率和延迟。对于 LLM,你需要持续运行在线评估:近乎实时地对输出进行评分的轻量级自动化检查。这是一个更难的基础设施问题,因为 LLM 评估本身既昂贵又缓慢。
实际做法:
- 针对每个金丝雀响应运行快速、廉价的评估器(基于规则或基于嵌入的相似度检查)
- 在抽样后的子集上运行较慢的 LLM 作为裁判(LLM-as-judge)评估器
- 每 15 分钟聚合一次分数,并与生产基准进行对比
- 提前定义停止标准:如果安全评分下降超过 X%,则自动停止
“推进”决策——从 2% 移动到 25% 再到 100%——也应该是自动化且由标准驱动的,而不是基于工程师手动查看仪表盘并判定它“看起来不错”。
蓝绿部署实现即时回滚
金丝雀发布擅长及早发现问题。蓝绿部署则擅长让回滚变得迅速。
在蓝绿部署中,你维护两个完整的生产环境:当前的稳定版本(蓝)和你正在推广的新版本(绿)。当满足推广标准时,流量会原子化地切换——一次性全部切换——从蓝色转向绿色。
如果切换后出现问题,回滚是即时的:将流量切回蓝色。无需重新部署,无需等待金丝雀流量排空,也没有部分回滚的复杂性。
对于 LLM,“环境”不仅包括应用程序代码,还包括提示词版本、模型版本、任何 RAG 索引快照以及评估基准。所有这些都需要锁定在一起。在不将新提示词锁定到与之测试过的模型的情况下推广它,会导致细微的不兼容性,而这些不兼容性往往在后期才会显现。
这意味着 LLM 应用的部署产物不仅仅是一个 Docker 镜像。它是一个清单(manifest):prompt_version: v4.2 | model: claude-sonnet-4-6 | rag_index: 2026-04-08。
行为漂移:无声的回归
最隐蔽的失败模式不是糟糕的部署,而是在没有任何部署事件触发的情况下积累的漂移。
模型提供商会持续更新他们的模型。今天的 claude-sonnet-4-6 API 端点表现可能与三个月前不同。这些更新可能是有益的(更好的推理能力、更少的幻觉),也可能悄悄地破坏你的应用程序所依赖的假设(改变的 JSON 格式化行为、不同的拒绝阈值、偏移的指令遵循倾向)。
对生产环境中 LLM 响应的分析显示,即使没有提示词更改,行为漂移也是可衡量的:相同运行下的响应长度方差可能超过 20%,静默模型更新后指令遵循度可能会偏移 30%,而 JSON 序列化行为——特别是在转义和嵌套方面——在模型版本之间的变化会破坏下游解析。
实际的防御手段是运行独立于部署流水线的持续行为监控:
- 保留一组固定的“金丝雀提示词”——具有已知预期行为的输入——并按计划(每小时或每天)针对你的生产端点运行它们
- 跟踪输出属性随时间的变化:长度、结构、必需元素的出现、与参考输出的语义相似度
- 当指标偏移超过阈值时发出警报,即使没有发生任何部署
- 保持在有特定模型快照可用时锁定到该快照的能力
这使不可见的问题变得可观察。GetOnStack 事件——由于行为逐渐漂移到无限对话循环,一个多智能体系统的成本在四个星期内从 127 美元/周飙升至 47,000 美元——正是持续行为监控可以及早发现的那种故障。
JSON 序列化:一个被低估的故障类别
一个没有得到足够关注的故障模式:模型版本之间的 JSON 处理不一致。
LLM 应用程序如果使用结构化输出或函数调用,就必须依赖模型生成有效的、可解析的 JSON。不同的模型版本和提供商处理边缘情况的方式不同——他们如何转义特殊字符,如何格式化嵌套对象,以及当 Schema 演进时如何处理工具调用参数。
在生产环境中观察到的常见故障:
- 双重序列化——当工具结果返回预序列化的 JSON 字符串,而应用程序再次对其调用
json.dumps()时,会产生后续模型调用无法解析的转义乱码 - Schema 演进破坏——在对话进行中升级工具的输入 Schema,导致模型尝试针对旧的 Schema 进行调用
- 提供商特定的特性——同样的提示词在 Claude、GPT 和 Gemini 上产生微妙不同的 JSON 格式,而你的解析器只正确处理其中一种变体
这些故障特别危险,因为它们会产生连锁反应。10 步智能体工作流中第 3 步的 JSON 解析失败可能直到第 8 步才会显现,到那时诊断根本原因需要详细的分布式链路追踪。
解决办法是结构性的:在序列化和反 序列化边界验证所有 JSON,明确地对你的工具 Schema 进行版本控制,并在你的评估套件中包含 JSON 往返测试——不仅仅是“模型是否响应”,而是“响应是否能被实际消费它的代码解析”。
总结:一个最小化的 LLM CI/CD 流水线
一个生产级别的 LLM 部署流水线至少需要:
- 提示词版本化 (Prompt versioning) —— 将每一个提示词都视为带有唯一 ID 的不可变产物。禁止对已部署的提示词进行就地编辑。
- 评估数据集纳入版本控制 —— 你的测试用例与代码存放在同一个仓库中,共同演进,并在每次 PR 时运行。
- 自动化评估门禁 —— 导致评估分数下降的 PR 将被禁止合并,就像单元测试失败会阻止合并一样。
- 针对高风险变更的阴影测试 (Shadow testing) —— 在接触任何用户之前,新的模型版本和重大的提示词结构调整都需经过阴影模式验证。
- 带在线评估的分阶段发布 —— 结合持续评分和自动化停止标准的金丝雀部署。
- 独立于部署的行为监控 —— 定时运行金丝雀提示词,以捕获两次发布之间模型提供商发生的漂移。
- 部署清单 (Deployment manifests) —— 将提示词 + 模型 + RAG 快照捆绑为版本锁定的单元,以便统一进行晋级或回滚。
那些成功交付了大规模可靠 LLM 应用的团队都趋向于采用这种模式。而那些将提示词视为配置而非代码的团队,则在累积无形的技术债,这些债务最终会演变成生产事故。
现在的工具链已经足够成熟,上述任何一项都不需要从零开始构建。真正的差距在于纪律 —— 即以过去几十年软件部署所积累的严谨态度来对待 LLM 部署。如果不这样做,代价将是那些你无法解释的事故和无法复现的性能退化。
- https://bhavishyapandit9.substack.com/p/cicd-for-llm-apps-how-to-deploy-without
- https://agenta.ai/blog/cicd-for-llm-prompts
- https://arize.com/blog/how-to-add-llm-evaluations-to-ci-cd-pipelines/
- https://www.braintrust.dev/articles/best-ai-evals-tools-cicd-2025
- https://www.codeant.ai/blogs/llm-shadow-traffic-ab-testing
- https://alexgude.com/blog/machine-learning-deployment-shadow-mode/
- https://earezki.com/ai-news/2026-03-12-we-built-a-service-that-catches-llm-drift-before-your-users-do/
- https://www.braintrust.dev/articles/llm-evaluation-guide
- https://www.zenml.io/blog/what-1200-production-deployments-reveal-about-llmops-in-2025
- https://www.zenml.io/blog/llmops-in-production-457-case-studies-of-what-actually-works
- https://dasroot.net/posts/2026/02/prompt-versioning-devops-ai-driven-operations/
- https://www.getmaxim.ai/articles/prompt-versioning-and-its-best-practices-2025/
- https://apxml.com/courses/langchain-production-llm/chapter-7-deployment-strategies-production/blue-green-canary-deployments
- https://www.promptfoo.dev/docs/integrations/ci-cd/
