跳到主要内容

棕地 AI:如何在不重写的情况下将 LLM 功能集成到遗留代码库

· 阅读需 12 分钟
Tian Pan
Software Engineer

每个 AI 演示都从绿地起步:一个干净的代码仓库、一个全新的 API key,不到一小时就能跑通流式聊天响应。然后有人问:"我们能把这个加到真正的产品里吗?"——那个运行在十年老单体系统上、存有无数未记录存储过程的产品,那个部署流程需要三个团队外加一个变更顾问委员会的产品,那个数据模型比 JSON 还古老的产品。

大多数 AI 集成工作都死在这里。不是因为 LLM 不好用,而是因为周围的系统根本不肯配合。超过 75% 的企业 AI 项目都卡在集成边界上——无法将 AI 能力与真正持有所需数据的系统连接起来。

本能反应是计划重写。别这样做。那些真正将 LLM 功能推进生产遗留系统的团队,都是以渐进的方式完成的——通过将现有代码库视为具有已知接口的黑盒的适配器模式。以下是具体做法。

为什么绿地演示无法在单体系统中存活

AI 原型与生产集成之间的鸿沟,并不在于模型本身,而在于模型周围的一切。遗留系统呈现出一组绿地演示从未遇到过的结构性问题:

  • 没有干净的数据访问层。 业务逻辑与数据访问相互交织。你无法直接"查询数据库",因为一半重要状态是通过应用层转换派生出来的,而这些转换根本没有文档记录。
  • 孤岛式架构。 AI 富化所需的数据分散在多个具有不兼容 schema 的系统中。"客户"在 ERP 里是一种含义,在 CRM 里是另一种,在计费系统里又完全不同。
  • 延迟不匹配。 你的 LLM 能在 2 秒内生成响应,但它依赖的遗留批处理任务要每晚才跑一次。或者遗留 API 的响应时间是 50ms,而你插入的 LLM 调用会给用户期望瞬间完成的请求路径增加 3 秒延迟。
  • 发布节奏不匹配。 AI 功能需要快速迭代——调整提示词、更换模型、调节阈值。而遗留系统每季度才部署一次,还要附上一份 40 页的变更申请。

核心洞察在于:你不需要对遗留系统进行现代化改造来添加 AI 能力。你需要建立一个隔离边界,让 AI 层能够按自己的节奏演进,同时遗留系统继续做它一直在做的事情。

边车模式:不触碰生产代码的 AI 集成

最无侵入性的集成模式直接借鉴自服务网格领域。一个边车容器与遗留应用并行运行,在 AI 层与现有 API 接口之间进行转译——而无需修改任何一行生产代码。

实际运作方式如下:两个容器共享同一个网络命名空间(在 Kubernetes 中,它们位于同一个 Pod 内)。边车暴露一个对 AI 友好的接口——MCP server、GraphQL 端点,或你的 agent 框架所期望的任何接口。在内部,它通过 localhost 调用遗留 REST API。

遗留应用完全不知道边车的存在。它收到的是和以往一样的 HTTP 请求。边车负责处理所有转译工作:将 AI 工具调用映射到 REST 端点、在数据格式之间转换,以及处理模型预期与遗留系统提供之间的阻抗失配。

这种模式具备三个对棕地集成至关重要的特性:

  1. 独立部署。 你可以在不经过遗留系统发布流程的情况下更新边车(新的提示词模板、额外的工具映射、不同的模型提供商)。
  2. 安全隔离。 遗留 API 接口保持不对外暴露。只有边车的端点对 AI 层可见,为你提供了访问控制和审计日志的单一入口。
  3. 故障隔离。 如果 AI 层宕机或行为异常,遗留系统不受影响。边车中的熔断器可以防止级联故障。

对于无法运行 Kubernetes 的团队,同样的原则也适用于反向代理、坐在遗留 API 前面的 Lambda 函数,甚至是运行在同一主机上的简单 Node.js 服务。模式的核心是隔离边界,而不是编排层。

异步富化队列:将 AI 从热路径中解耦

第二种模式解决了棕地 AI 集成中最常见的错误:在同步请求路径中放置 LLM 调用。你的遗留结账流程耗时 200ms。加入一个需要 3 秒的 LLM 欺诈分析步骤,这不是增强——这是退化。

异步富化队列模式将 AI 处理与关键路径完全解耦:

  1. 遗留系统将领域事件发布到消息队列(RabbitMQ、SQS、Kafka——你已经在运行的任何一种)。
  2. AI worker 服务消费事件,执行 LLM 操作(分类、摘要、富化),并将结果写回共享数据存储。
  3. 遗留系统按自己的节奏读取富化后的数据——下一次页面加载、下一次批处理运行、下一次 API 调用。

关键洞察在于:大多数 AI 功能实际上并不需要同步执行。客服工单可以在创建之后再进行分类和路由,而不必在创建过程中完成。产品描述可以在后台进行富化。风险评分可以在用户到达决策点之前预先计算。

这种模式也能优雅地吸收流量峰值。如果同时涌入 1000 个请求,API 保持响应,队列缓冲工作,AI worker 以可持续的速率处理积压。你获得了天然的背压机制,而无需将其工程化到遗留系统中。

对于确实需要实时 AI 响应的场景,可以考虑混合方式:立即返回一个快速的默认值(遗留系统的现有行为),然后在 AI 结果就绪时异步更新。渐进式增强,应用于智能。

绞杀者模式:无大爆炸风险的渐进迁移

绞杀者模式——得名于那种逐渐包裹宿主的热带树木——是对于最终希望实现 AI 原生功能但又承担不起重写风险的团队来说最安全的路径。

你无需替换遗留组件,而是在遗留系统前面放置一个路由层(API 网关或门面)。最初,所有流量不变地透传到遗留系统。然后,一次一个端点,将特定路由重定向到新的 AI 驱动实现。

这对棕地 AI 集成之所以有效,原因如下:

  • 可以渐进式验证。 将 5% 的流量路由到 AI 驱动的版本,与遗留系统的输出进行比较,只有当 AI 版本匹配或超越遗留行为时才完全切换。
  • 回滚极为简单。 如果 AI 驱动的端点出现问题,几秒钟内就能将路由切回遗留实现。
  • 边走边积累规格覆盖。 每个迁移的端点都迫使你正式规定遗留行为——输入、输出、边界情况。这项规格化工作会产生复利效应:下一次迁移会更容易,因为你更了解相邻组件了。

这种渐进式规格驱动方法与 Michael Feathers 的遗留代码测试策略如出一辙:不要试图一次性理解整个系统。将规格化工作聚焦在你即将修改的组件上。随着时间推移,经常被触碰的区域会积累足够多的规格,开始像绿地区域一样运作,而很少被触碰的遗留代码则安全地隐藏在它的门面后面。

没有 API 时的数据提取

许多遗留系统早于 API 时代就已存在。AI 功能所需的数据被锁在没有访问层的数据库中、批处理生成的平面文件中,或者没人想碰的 SOAP 端点里。以下是按侵入性从低到高排列的实用提取模式:

  • 变更数据捕获(CDC)。 Debezium 等工具读取数据库事务日志并将变更发布为事件。你的遗留应用完全不知道自己被观察着。这是非侵入式数据提取的黄金标准——你可以实时访问数据变更,而无需给遗留数据库增加负载。
  • 复制的 AI 就绪数据存储。 CDC 的数据流入一个针对 AI 工作负载优化的独立数据存储——用于 RAG 的向量数据库、用于分析的数据仓库、用于富化的文档存储。这将 AI 查询模式与操作数据库隔离开来,防止 AI 层影响遗留系统性能。
  • 带语义规范化的 ETL。 当 CDC 不可行时(不暴露事务日志的遗留数据库),定时 ETL 任务负责提取和转换数据。关键步骤是语义规范化:建立规范的实体定义,使得无论记录来源于哪个遗留系统,"客户"都代表相同的含义。
  • 屏幕抓取作为最后手段。 对于真正古老、没有数据库访问也没有 API 的系统,无头浏览器自动化或终端抓取可以提取数据。这种方式脆弱且缓慢,但有时是不需要修改遗留系统的唯一选择。

每种提取模式都应该输入到一个具有明确血缘追踪的受治理数据层中。当你的 AI 功能产生错误结果时,你需要能够通过富化管道追溯到源系统。没有这一点,在棕地环境中调试 AI 问题将变得不可能。

集成边界的护栏

AI 层与遗留系统之间的集成边界是一个新的攻击面,需要明确的保护措施:

契约测试。 为每个集成点定义正式契约——请求/响应 schema、延迟预算、错误码。在 CI 中测试这些契约。当遗留系统修改其 API 时(它会的,而且不会通知你),你希望是契约测试失败,而不是生产集成崩溃。

延迟预算。 为请求路径中每个 AI 操作分配明确的时间预算。如果 LLM 在预算内没有响应,则返回遗留系统的默认行为。用户永远不应该因为 AI 功能超时而体验到性能下降。

熔断器和舱壁。 将 AI 流量与事务流量隔离。AI 相关请求的峰值绝不能消耗遗留系统核心操作所需的连接池或线程池。这不是理论问题——棕地 AI 最常见的故障之一,就是一个热情洋溢的 AI 功能意外对其所依赖的遗留系统发起了 DoS 攻击。

成本控制。 LLM 调用按 token 计费。一个无限重试的 bug 或一个急剧膨胀的提示词,可能在任何人注意到之前就产生巨大费用。为每个端点和每小时的 token 使用量设置硬性截止的预算,而不仅仅是告警。

审计日志。 记录每一个 AI 决策,包括输入、模型输出、置信度评分以及采取的行动。在受监管的行业中,这不是可选项。即使在非受监管的场景下,你也需要这些数据来调试集成问题,并随时间推移提升模型性能。

真正有效的迁移路径

在帮助团队将 AI 集成到遗留系统的过程中,始终奏效的模式是一种尊重现有系统约束的分阶段方法:

第 0 阶段:在沙箱中验证。 使用合成数据或匿名数据证明 AI 能力是可行的。暂时不要触碰遗留系统。这是你迭代提示词、评估模型质量、建立信心的阶段。

第 1 阶段:构建集成层。 部署边车、API 门面或异步队列——选择符合你约束条件的模式。建立监控、日志和契约测试。遗留系统仍然处理 100% 的流量。

第 2 阶段:影子模式。 让 AI 功能与遗留行为并行运行。比较输出结果,但暂时不根据 AI 结果采取行动。这一步能在影响用户之前发现语义不匹配、延迟问题和数据质量问题。

第 3 阶段:渐进式推出。 将一小部分流量路由到 AI 增强路径。监控错误率、延迟百分位数和业务指标。逐步增加流量。保持遗留回退路径活跃。

第 4 阶段:加固与扩展。 一旦 AI 增强路径可靠地处理生产流量,添加安全加固(针对提示词注入的红队测试、权限边界测试)、成本优化(缓存、批处理)和可观测性改进。

从第 0 阶段到第 4 阶段的完整路径,简单的富化功能可能需要几周,复杂的工作流可能需要几个月。重点在于,在任何阶段,你都没有将生产系统的赌注押在 AI 功能能否正确运行上。每个阶段都有安全的回退到上一状态的方案。

结语

AI 演示与 AI 生产之间的鸿沟,不是技术问题——而是集成架构问题。遗留代码库不是障碍;它是塑造解决方案的约束条件。

成功将 AI 功能交付到棕地系统的团队有一个共同特质:他们停止将遗留系统视为需要克服的对象,开始将其视为可以依托的稳定平台。边车模式、异步富化队列、绞杀者模式以及受治理的数据提取层,都遵循同一原则——建立隔离边界,让 AI 层独立演进,并渐进式迁移。

大爆炸式重写是个陷阱。先交付边车,证明其价值,然后让绞杀者慢慢完成它的工作。

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