Kubernetes 上的生成式 AI——AI 驱动的应用

0 阅读1小时+

在前面的章节中,我们已经演示了如何在 Kubernetes 上部署像 vLLM 这样的模型服务、如何打包模型数据,以及如何在生产规模上运行推理。在这一基础之上,我们现在将把视角从“服务单个模型”转向“设计完整的 AI 驱动型应用”:在这类应用中,LLM 只是众多组件中的一个。

本章聚焦于应用架构:请求如何在系统中流动、上下文如何被检索或工具如何被调用,以及状态如何随着时间持续维护。我们会介绍几种流行的架构模式、AI 应用栈中的关键组件,以及把 LLM 集成到真实世界应用中时所面临的挑战。为了保持对架构总览的清晰聚焦,本章会有意维持在较高层次;更具体的技术实现细节,我们会在下一章深入展开。

LLM 最初以聊天机器人形态大举进入主流软件世界,其中最具代表性的当然是 ChatGPT。聊天依然是最主流的交互模式,但支撑它的软件体系已经成熟得多。现代 AI 应用会在 LLM 外面包裹一层应用逻辑:拉取业务上下文、调用内部系统、并写入状态。**LLM 推理服务本身是一个很强大的组件,但它并不会自己伸手去查数据库,也不会自己调用工具。**¹

真正掌控全局的是应用本身,而 LLM 被应用拿来做生成或推理。你会看到:在什么场景下应该使用检索来做 grounding,什么时候需要编排工具调用,以及如何在多轮交互中维护状态,同时又不失去对成本、延迟和质量的控制。

在下一节“架构模式(Architectural Patterns) ”中,我们会先看两种基础形态,用于把这类 AI 驱动型应用嵌入到更广泛的运行环境中。

在完成这个架构总览之后,我们还会进一步解释构建 AI 驱动型应用时两个非常重要的概念,也就是“检索增强生成(Retrieval-Augmented Generation) ”和“Agentic Workflows(智能体工作流) ”。

到本章结束时,你将会对 AI 驱动型应用的主要类别,以及生成式 AI 工作负载如何集成进更大的系统之中,形成一个相当完整的理解。

现在,让我们正式进入:AI 驱动型应用在 Kubernetes 上的总体架构和部署拓扑。

架构模式

在深入 AI 应用的典型架构之前,我们先快速回顾一下最重要的几类 Kubernetes 工作负载类型,这样才能把它们和后面提到的架构组件对应起来。

把每一项职责映射到合适的 Kubernetes 原语上,可以让系统中的各个部分拥有解耦的生命周期不同的发布节奏。例如,LLM 服务实例的更新时间表,很可能与应用逻辑部署的更新时间表并不相同。这样的拆分使你可以在不干扰其他部分的情况下,对某一个部分单独升级或扩缩,同时这也与如今被应用到 LLM 中心型应用里的微服务最佳实践高度一致。

注意(NOTE)

虽然本章主要聚焦于把所有组件都部署在 Kubernetes 内部,但 LLM 服务也完全可以运行在另一个集群里,或者直接作为云托管服务存在,例如 OpenAI、Anthropic,或者 Google 的 Vertex AI。考虑到 GPU 资源限制,这种解耦在生产环境里非常常见,而且也带来了很大的灵活性:你的 orchestrator 和应用逻辑依然运行在 Kubernetes 中,而推理能力则以“服务”的方式独立扩展。正如第 4 章所说,AI gateway 可以同时为自托管模型和云模型提供统一接口,从而让你无需修改应用代码,就能在不同后端之间切换。

Kubernetes 工作负载类型

下面我们来看一看几种关键 Kubernetes 原语,以及它们在 AI 应用中对应的角色。每一种类型在 Kubernetes Patterns 一书中都有更详细的说明,这里会用斜体标出对应模式。

Deployment

用于无状态服务,也就是那些持续运行的服务,例如主应用后端,或者一个事件驱动的 orchestrator。Deployment 负责管理这些长生命周期组件的滚动更新、扩缩容和故障重启。
在 AI 应用中,AI Orchestrator 负责处理请求或事件,而 LLM 推理服务(通常会申请 GPU 资源)负责真正执行推理。两者都很适合运行在 Deployment 中,这样它们就可以彼此独立扩缩。参见 Declarative Deployment 模式。

StatefulSet

用于有状态服务,也就是那些需要稳定身份或持久化存储的组件,例如数据库、缓存或向量存储,它们用来保存 embedding 和上下文。StatefulSet 能确保这些组件即使在重启之后,其数据仍然完好无损。参见 Stateful Service 模式。

Job / CronJob

用于一次性任务定时任务,例如离线数据摄取、报告生成或周期性维护。CronJob 负责按计划触发 Job,而 Job 会运行直到完成,随后释放资源。参见 Batch JobPeriodic Job 模式。

Ingress / Gateway

作为客户端请求进入集群的入口。标准 Kubernetes Ingress Gateway 会把外部请求路由到对应服务。至于 AI 感知型路由和调度策略,可参见“LLM-Aware Routing”。其底层思想则对应 Service Discovery 模式。

通过为每项功能分配合适的 Kubernetes 原语,你实际上是在系统中建立生命周期边界。Deployment 中的无状态逻辑可以独立更新和扩缩;StatefulSet 中的有状态数据组件可以维持连续性;而临时的或定时执行的工作则放在 Job 中,只在需要的时候产生成本。
这些边界同时也暗示着不同的 SLA 与成本画像。例如,对话式 API 往往要求低延迟和高可用;而一个每晚运行一次的摘要任务,则可以接受更宽松的时效要求。

把这些工作负载类型记在脑子里,会帮助你把接下来提到的架构组件更自然地映射到 Kubernetes 部署形态上。

下面我们先从最流行的一类 AI 应用开始:面向 UI 的聊天应用,比如 ChatGPT。

聊天应用(Chat Applications)

图 8-1 展示的第一个例子,是一个面向聊天界面的应用。用户通过 Web 或移动端 UI 与系统对话,而 UI 会去调用一个对话式后端。
这个后端负责整个流程编排:它会从存储中检索相关上下文,在需要时调用领域工具或 API,拼装 prompt,然后调用 LLM 服务生成回答。拿到模型输出之后,后端还会对结果做后处理、更新每个用户的记忆或其他状态,最后把响应返回给客户端。
这种拆分方式能让 LLM 专注于生成本身,而由应用掌控数据访问、副作用以及策略执行

image.png

图 8-1 一个典型的聊天型应用

这种由请求-响应驱动的系统——以聊天应用为典型代表——呈现的是一种线性的交互流。用户发起请求,然后等待响应。从用户视角看,这个流程是同步的,即便在后台其实经历了多个步骤。

在这种面向聊天的 AI 应用里,LLM 只是请求路径中的一个组件,而整体对话逻辑仍然由后端服务负责。

一个典型的单轮用户查询流程,大致如下。这里我们也会把各个组件对应到合适的 Kubernetes 工作负载类型上:

  • Gateway 或 Ingress 把用户请求路由到后端服务(Ingress / Gateway)。
  • AI Orchestrator 接收请求并控制整个聊天逻辑。它可能会检索相关上下文(用于 RAG)、组装 prompt,并调用 LLM 服务获取 completion。对于 agentic workflow,orchestrator 还可能根据 LLM 的输出去调用工具,这就会形成一个需要多次调用 LLM 的循环。这个组件就是聊天应用的“大脑”——例如,你可以在其中使用 LangChain 这样的框架,来管理 prompt、memory 和 tool usage。后面的“Retrieval-Augmented Generation”和“Agentic Workflows”两节,我们会重点讨论这个 orchestrator 组件,以及它是如何设计才能真正发挥价值的(Deployment)。
  • LLM 服务 执行推理并返回模型输出。orchestrator 会把准备好的 prompt 发送给这个内部服务(或者也可能是外部 API,比如 OpenAI),然后等待结果。把 LLM 单独隔离成一个服务,可以让我们在不影响应用逻辑的前提下独立扩缩模型,或者升级模型。Kubernetes 管理员可以把这部分部署到 GPU 节点上,并使用 autoscaling 来应对负载波动。第 1 章已经讲过如何部署和扩展模型服务;这里我们看到的是它们如何被真正整合进应用中(Deployment)。
  • 状态管理组件 提供会话记忆或检索索引,例如数据库、缓存或向量存储。这些状态可以存放在数据库或缓存里,比如 Redis 或 Postgres 用于保存聊天历史,或者一个向量库用于保存 embedding。在 Kubernetes 上,这些有状态组件通常会以 StatefulSet + PersistentVolume 的方式运行。orchestrator 会读取历史消息或已存储向量,把它们加入上下文(即“retrieval”);而当 LLM 回答完之后,它也可能把最新的用户提问和 LLM 答案写回这个存储。按照我们这里的 Kubernetes 映射,这个数据库或向量索引就是一个长生命周期组件,需要按需扩容、备份,并在需要时做副本来提供高可用(StatefulSet)。
  • 返回客户端的响应,在最终发送给用户之前,还可能会经过后处理,或者触发最后一次工具调用。

这种模式的关键在于:让 LLM 专注于生成或推理,而应用自己掌控数据访问、工具使用和副作用。
整个链路都运行在一个同步请求周期内,因此低延迟是首要目标。为了满足 SLO(例如单轮响应控制在亚秒级到几秒级),Kubernetes 部署通常会保持 orchestrator 和 LLM Pod 处于预热就绪状态。你可能会使用 autoscaling 来处理流量突发,但不会在每个请求到来时再从零启动这些组件,因为那样启动时间会完全不可接受。

这种面向用户的应用架构还有一个很现实的好处:它很容易参与诸如 OAuth2 这样的分布式认证流程,因为这类流程会涉及浏览器重定向到认证服务器。相比后面“Backend AI Services”里描述的纯后端服务,这种安全配置通常更直接、更简单。

从架构设计上看,这种模式很好地实现了关注点分离:
对话逻辑(它通常会随着 prompt 和 tool integration 的演进频繁变化)与 LLM 模型服务(它可能只有在新模型或新版本可用时才会改变)是解耦的。数据库则可以被当作一个变化频率更低的外部依赖。
这种解耦意味着:各部分都可以独立升级。例如,你可以部署一个新的 orchestrator 版本,以改进 prompt 处理逻辑,而完全不去碰 LLM deployment,也不会影响任何已经保存的数据。
同时,它还允许你针对不同组件的实际使用情况分别扩容。例如,如果有大量聊天会话,最吃资源的通常是 LLM 和数据库,那就优先扩这两部分;而 orchestrator 代码本身可能很轻,计算负载也相对小,副本数就不必很多。

后端 AI 服务(Backend AI Services)

第二种模式,是一种更“互联”的微服务架构:一个由 LLM 驱动的服务,作为更大系统的一部分运行,但它本身并不直接面向用户界面,也不是由“用户直接请求 LLM”触发的。相反,LLM 逻辑是由平台中的其他服务通过事件或调用来触发的。

图 8-2 用这个模式展示了一个例子:在电商平台中,一个 AI 驱动的后端服务负责做订单风险分析,多个服务和多个数据存储协同工作。
在这个例子里,一个应用 orchestrator 会接收来自 Orders、Payments、Catalog 等服务的订单事件(例如,查看商品库存变化或价格变动是否暗示可疑行为)。然后它会去调用一个 AI 风险分析器:这个分析器会使用 LLM、一个保存策略文本和历史案例的向量存储,以及过去案件记录来评估订单。它还可能调用领域工具,例如规则引擎或反欺诈 API。最后,orchestrator 把分析结论写入风险数据库,并向下游服务(例如 Fulfillment)发出事件。
这种后端服务既可以采用同步 service call 的方式实现,也可以基于 event bus 做成异步形态。

image.png

图 8-2 事件驱动型 AI 服务

在这种事件驱动架构中,AI 应用会订阅事件、用 LLM 做多步分析,然后把结果发给其他服务:

  • 消息代理(message broker) 接收来自业务服务的事件,例如 OrderPlacedPaymentProcessed。我们的 AI orchestrator 服务会订阅其中相关的事件。在 Kubernetes 中,这可以通过集群内运行的事件流平台来实现,也可以接入外部消息总线。虽然 Kubernetes 自身并不原生提供消息队列,但很多 cloud-native 系统本身就运行在 K8s 上;或者你也可以使用 Knative Eventing 或 Dapr 的 pub-sub 组件。关键在于:这个 AI 服务是被异步触发的,而不是由一个 HTTP 请求直接拉起来的(StatefulSet)。
  • AI Orchestrator 会在相关事件到来时被唤醒并执行反应。举例来说,当收到 OrderPlaced 事件时,这个服务可能会从多个来源收集数据:拉取订单详情、支付历史,以及相关商品信息,然后再调用一个 AI 风险分析组件来评估欺诈风险。这个风险分析器本身可能就包含了一个带上下文的 LLM 调用。在这个例子里,这个服务会使用 LLM,再结合策略文档和历史欺诈案例的向量存储,去评估订单。同时,它也可能会调用非 AI 工具——例如规则引擎,或者第三方反欺诈 API——把它们作为分析流程的一部分。这个 orchestration 可以被视作一种 agent workflow(即 LLM 决定行动并调用工具),但它被封装在一个单独的微服务里(Deployment)。
  • LLM 与工具服务 运行在独立的 deployment 中,或者直接通过外部 API 提供能力。和聊天应用模式一样,LLM 通常也会被隔离成一个独立 deployment(或者是外部 API),由 orchestrator 在生成或推理步骤中调用。向量数据库(例如存储欺诈案例或策略文本)则通常是一个 StatefulSet + PVC,它作为知识库为 LLM 决策提供 grounding。任何领域工具(比如规则引擎)也都可能是独立服务,通过自己的 API 被访问。在某些情况下,工具甚至可以通过触发 Kubernetes Job 来实现——例如,如果你有一个计算量很大的数据处理步骤,它可以异步地跑,那么 orchestrator 就可以创建一个 Job,然后稍后再收集结果。不过,更常见的情况还是这些工具只是其他微服务提供的 HTTP / gRPC 接口。与“Chat Applications”中那种线性模式最大的区别在于:这里的这些调用并不是由用户直接发起的,而是作为后端流程的一部分存在(Deployment)。
  • 状态输出组件 会把分析结论持久化到数据存储中,并向下游服务发出新的事件。例如,在风险分析例子中,结果(批准订单 / 标记订单、风险评分等)会被写入风险数据库(通常又是另一个 StatefulSet,负责持久状态),然后可能会发出诸如 OrderFlagged 这样的事件,让 Fulfillment、Notifications 等下游服务继续处理。这样一来,AI 的决策就真正嵌进了整个系统的事件驱动架构中。
  • 下游服务 根据这些结果采取行动,例如暂停发货,或者触发人工审核。从 Kubernetes 角度看,这些下游消费者其实也只是其他 deployment 或 job,它们接着消费事件并继续执行逻辑。

这种架构遵循的是一种“短 think-act-observe 循环”模式。AI 服务收到一个输入事件,利用 LLM 进行规划、可能采取某些动作,更新状态,然后等待下一个输入。
从某种意义上说,它其实就是微服务生态中的一个自治 agent,只不过它运行在明确 guardrail 约束之内,并且产生的结果是可审计的。
如果事件负载很高,AI Orchestrator deployment 也可以横向扩容;当然,如果事件需要按顺序处理,就还得额外处理协调问题。

对这一模式特别值得注意的一点,是幂等性(idempotency)与可靠性。由于它是事件驱动的,所以你通常希望 AI 服务能够优雅地处理重复事件或失败重试。
在这种场景下,Kubernetes Job 可以很适合用来承载那些可重试任务;而如果我们的 AI Orchestrator deployment 崩溃了,或者正处于升级过程中,那么消息队列就可以把事件暂存起来,直到新的 Pod 准备好。
这种模式往往会用“稍高一点的延迟”(因为事件带来轻微延迟和最终一致性)去换取更松耦合的系统设计,以及更好的吞吐扩展能力。
同时,它通常还更具成本效益:只有在事件真正到来时,AI Orchestrator 才会做工作;甚至还可以借助 KEDAKnative 把它缩到 0 个副本

从发布角度看,这种模式往往涉及很多可以独立更新的部分,与聊天应用模式其实类似。
orchestrator 代码可能随着业务逻辑或策略更新而变化;LLM 服务栈则可能在新模型上线,或者引入更优化的 serving 方案时发生变化(例如,从一个模型切换到更大的模型,或者为了扩展而采用分布式 serving 架构)。
数据存储和消息代理也都有各自独立的升级路径。
因此,在这些组件之间设计清晰的接口(例如事件 schema、API contract)就变得非常关键:只有这样,你才能在升级某一个服务时,不把整条流水线都搞坏——这其实正是传统微服务最佳实践在 LLM 中心型功能中的自然延伸。

这种 AI 后端微服务架构,也存在几种不一定直接由外部事件立即触发的变体。
有些“无头(headless)服务”也可以异步地自行运行,或者成为更大后台工作流的一部分。
这些变体包括:定时任务、长时间运行的 agent 循环,以及按需触发的批处理任务

定时批处理任务(Scheduled batch jobs)

如果你要做数据摄取、夜间摘要、或周期性 fine-tuning,那么最自然的方式就是使用 CronJob,去更新向量存储或其他衍生工件。图 8-3 展示了一个很简单的结构:通过 CronJob 启动一个 ingestion job。

我们在“Document Ingestion”里将要描述的 RAG 文档摄取阶段,就是一个典型例子:你完全可以让一个 CronJob 周期性地处理新文档、生成 embedding,并更新向量存储,而不是把这一过程放在用户请求路径里。
这样做会显著提升效率,也能让面向用户的部分保持足够快。

image.png

图 8-3 定时批处理任务

一个典型的 batch job 流程大致如下:

  • 外部数据源 提供数据集或工作清单,由 CronJob 拉取进来。这可以是对象存储里新出现的文件、带有新数据的 API endpoint,或者单纯的时间触发。
  • CronJob 按固定间隔调度批处理逻辑(例如每晚、每小时)。CronJob 资源会在指定时间创建一个个 Job 实例。每个 Job 都是独立运行的,并且可以配置重试策略和资源限制(CronJob)。
  • 批处理 worker job 执行具体处理逻辑。它可以运行一个批处理脚本:加载数据、调用 LLM(可能是通过模型 API,也可能是本地跑一个较小模型)、写入结果(例如写文件或写数据库),然后退出。因为没有用户在等待,所以你完全可以把这类任务调度到低峰时段,或者放到低优先级节点上,以节约成本。任务完成后就会释放资源(Job)。
  • LLM 服务 按照批处理 worker 的请求执行推理。这个 Job 会调用系统其他部分也在使用的同一个 LLM deployment,从而保持模型服务的一致性(Deployment)。
  • 向量存储 保存 batch job 更新的 embedding 或已索引数据。例如,文档摄取任务生成 embedding 并把它们写到这里,以便后续查询时再进行检索(StatefulSet + PVC)。
  • 结果数据库 保存批处理输出的结果。这些结果可能是摘要、分类、或其他下游系统会查询的衍生数据(StatefulSet + PVC)。
  • 外部对象存储 还可以接收 batch job 生成的报告、工件或文件。这些输出随后可以被其他系统消费,或者供用户下载。

对于这类定时或触发式 batch job,Kubernetes 的 CronJobJob 是最自然的工作负载原语。它们提供失败重试、每次运行的日志,以及按运行维度隔离资源的能力。
例如,你完全可以为某个夜间任务临时分配更多内存或 GPU,而不需要把这些资源整天预留着。

持续控制循环(Continuous control loops)

与基于时间的触发方式相对,另一类模式是持续运行的控制循环。这类循环会一直运行,持续观察某些条件,或者反复迭代处理某个任务。图 8-4 展示了一个这样的异步轮询型结构。

举个例子,可以想象一个 “Ambient Agent”(我们会在“Ambient Agents”里展开):它持续监控某条数据流(例如日志、社交媒体、IoT 传感器读数),每当发现异常或某个关键词时,就调用 LLM 做分析,必要时触发告警。
与图 8-2 中事件驱动微服务那种“由外部推送事件触发”的方式不同,这种 agent 可能是通过轮询一个数据源或等待回调来持续获取工作,而不是订阅 pushed event。

image.png

图 8-4 异步智能体

这种持续控制循环的架构通常是这样工作的:

  • 外部数据流 提供 agent 要监控的数据或事件。这可以是变更流、API endpoint、日志流,或者任何 agent 会轮询的新信息来源。
  • 异步 agent 循环 作为一个 deployment 持续运行,轮询输入流,并决定什么时候采取行动。它可能以单副本 deployment 的形式运行,逻辑上就是一个循环:检查输入;如果发现感兴趣的东西,就调用 LLM 或工具;产出结果;然后重复。它在概念上很像一个 Kubernetes controller(持续 reconciliation loop),只不过这里做决策的是带有 LLM 的“controller”。这种 agent 也可以以“被动”的方式面向用户,例如一个始终在线的 Slack bot,只要用户 @ 它,它就会响应(Deployment)。
  • LLM 服务 在 agent 需要分析或生成内容时提供推理能力。agent 会根据它观察到的数据,在需要时调用 LLM(Deployment)。
  • 向量存储 保存 embedding 或参考数据,例如策略文档、历史案例或知识库文章,agent 可以查询它来做 grounding(StatefulSet + PVC)。
  • 结果数据库 保存 agent 的决策、行动或观察结果,从而提供审计轨迹,也让其他系统能查询 agent 做过什么(StatefulSet + PVC)。
  • 外部输出 可以包括 agent 生成的报告、文件或其他产物,它们可以被写到对象存储中以供后续使用。
  • 通知或 webhook 则使 agent 在采取行动时能够通知外部系统,例如把告警发到监控系统,或触发其他下游工作流。

对于这种“始终在线”的 agent,最合适的 Kubernetes 工作负载通常是 Deployment
你可能只需要 1 个副本,但你依然能享受到自动重启等好处。
如果一个 agent 不能同时运行多个副本,那你可以加入 leader election,或者使用 singleton 模式。
一个最简单的方法就是把它做成一个 replicas: 1 的 deployment,并确保没有 autoscaling。
另一种方式是用 StatefulSet 且大小为 1,不过对这类 agent 来说,StatefulSet 最主要的好处——稳定网络标识——通常并不是必须的。
关于 singleton,你可以在 Kubernetes Patterns 中找到更多策略。

多步工具自动化(Multistep tool automation)

异步工作流还可以用于在无人干预的情况下完成复杂的多步目标。图 8-5 展示了一个会规划并执行一系列动作的多步 agent。

例如,可以想象一个 agent 被赋予任务:生成并发送每周摘要邮件。
当这个任务被触发时,它会计划并执行这样一串步骤:

  • 查询数据库
  • 调用 LLM 提炼关键点
  • 也许用绘图库生成一张图表
  • 然后通过 SMTP 服务把邮件发出去

image.png

图 8-5 多步工具自动化

这种多步自动化序列通常如下:

  • 触发器 启动整个多步工作流。它可以是一个 CronJob(用于定时任务,例如每周报告),也可以是一个 API 调用(用于按需执行)。触发器负责真正启动 agent。
  • 多步 agent 通过一种 plan-act-observe 循环来编排整个工作流。这个 agent 会维护一个内部状态,用来表示自己当前在计划中的执行位置。它会不断决定下一步动作、执行该动作、观察结果,然后更新自己的计划。这可以实现为一个把所有步骤都封装在内部的 Job(其中 agent 自己通过内部循环做规划);也可以实现为一个只在任务执行期间存在的临时 Deployment,不过对于“run-to-completion”逻辑来说,Job 通常会更简单(Job 或 Deployment)。
  • LLM 服务 提供推理与生成功能。agent 在执行过程中可能多次调用 LLM:例如生成初始计划、生成摘要内容、或根据新观察到的信息决定下一步(Deployment)。
  • 知识库(向量存储) 提供上下文与参考信息。agent 可能查询它,以对自己的决策做 grounding,或检索相关知识(StatefulSet + PVC)。
  • 外部工具 让 agent 能与外部世界交互。它们可能包括数据库查询接口、绘图库、SMTP 服务,或任何 agent 完成目标所需要的 API。我们在“Agentic Workflows”里会提到的 ReAct 模式以及多智能体协作,也都属于这一层——它们是运行在 orchestration service 内部的应用级控制流。
  • 任务状态数据库 用于记录 agent 的执行进度和决策,从而形成审计轨迹,也让 agent 在需要重启时具备恢复能力(StatefulSet + PVC)。
  • 通知 / 外部消费者 接收最终输出或状态更新。例如,工作流完成时,或者 agent 途中遇到问题时,它可以通知外部系统。

这里的关键特征在于:这些 agent 并不会直接对应某一种专门的 Kubernetes 资源类型——它们通常还是运行在 Deployment 或 Job 中。
从架构层面,你必须决定:每个多步流程究竟是同步执行(也就是整个过程中一直占着用户请求),还是异步执行(完全脱离请求路径)。
在大多数情况下,长时间运行的多步 agent 更安全的选择是异步执行,完成后再通知用户或系统。

异步模式还能让你在资源使用上更灵活。
如果事情不紧急,你完全可以把它安排到低优先级资源上,或者等集群有空闲能力时再跑。
比如,如果你在使用 spot 实例或“剩余算力”,就可以把一些非关键的 LLM 任务调度过去。
相反,如果某个 ambient agent 非常关键(例如它负责监控安全入侵),那你就必须像对待关键服务一样对待它:保证它有足够高可用,且足够快,这往往意味着你要为它长期保留一个 Pod,让 LLM 模型常驻内存。
始终在线的 agent 会带来持续成本,因为 Pod 永远在跑;而事件触发型 Job 只在用到时才产生成本。这本质上就是成本效率与响应速度之间的经典权衡
因此,我们必须有意识地划定这些生命周期边界:哪些流程可以按需拉起(省钱),哪些流程必须预热等待(满足延迟目标)。

现在,我们已经覆盖了设计 AI 增强型应用时最常见的一些架构选择。接下来,我们把焦点转向任何 AI 应用架构中都居于核心地位的那个组件:AI Orchestrator
为此,我们将重新审视几个流行概念,从一种让 LLM 立足于你的领域数据之上的技术开始。

检索增强生成(Retrieval-Augmented Generation)

Retrieval-Augmented Generation(RAG) 是一种设计模式,它通过在推理时动态获取相关外部信息并把它们插入 prompt 中,使 LLM 的输出能够立足于外部数据。
换句话说,模型不再只依赖自己固定训练集中的知识,而是在回答问题时被赋予了一本“开放的参考书”。
结果就是:幻觉更少,回答也能反映最新的、特定领域的知识,即便基础模型的训练数据已经过时。
我们在第 6 章里曾简要提过 RAG,把它作为模型定制的一种替代方案;而这里,我们将更深入地讨论它在 Kubernetes 上的实现方式。

把 RAG 和我们在第 6 章介绍过的 fine-tuning 对比起来,会很有帮助。
Fine-tuning 是通过更新模型权重,把新信息真正教给模型,因此它非常适合那些你希望“烤进模型里”的内容,比如:

  • 风格
  • 语气
  • 稳定不变的领域知识模式

但 fine-tuning 既耗资源,又耗时间,而且每次有新数据时你都得重新训练。
RAG 则完全绕开了这一点:它通过在查询时注入知识,来避免重训练。
你只需要把新文档写进向量数据库,下一次用户提问时,系统就能立刻把这些信息检索出来并用上。
也正因如此,RAG 对于动态知识库经常变化的内容格外灵活,这也是它在企业中如此流行的重要原因。

RAG 和模型调优并不是非此即彼的关系,而是互补的。
如果你有一些核心知识几乎不会变化,那么可以用一个经过 fine-tune 的小模型把这些“基础知识”内化进去,从而减少 prompt 大小;与此同时,RAG 则负责补充那些动态变化的、或者与某个用户相关的具体信息。
正如第 5 章所说,较大的上下文窗口会带来显存和延迟成本,因此尽量缩小 prompt 是有意义的。
在实践中,很多团队都会把两者结合起来:把长期不变的知识烤进模型里,而把动态的、用户特定的事实交给 RAG。
通过这种方式——静态知识由调优承载,动态知识由 RAG 按需拉取——你可以在准确性与效率之间取得平衡。

关键在于:RAG 能作用于任何基础模型,不管它是原始模型还是已经 fine-tuned 的模型,因为它完全是通过 prompt 接口工作的。
所有基于 prompt 的技术——例如 RAG、tool usage——都天然可以和 fine-tuned 模型兼容。

RAG 有两个截然不同的阶段,如图 8-6 所示:

  • 文档摄取(Document ingestion)
    负责为检索做好知识准备:解析文档、切块、生成 embedding,并写入向量数据库。详见“Document Ingestion”。
  • 用户查询处理(User query processing)
    负责把用户 prompt embedding 化,检索相似块,必要时进行 rerank,然后组装最终 prompt 发送给 LLM。详见“User Query Processing”。

image.png

图 8-6 RAG 的两个阶段:文档摄取与用户查询处理

一个 RAG 系统由多个相互协作的组件构成。在讨论这些组件如何部署到 Kubernetes 之前,我们先看清楚它们各自的职责。

RAG 组件(RAG Components)

一个典型的 RAG 架构,通常由一组边界清晰的服务组成,它们非常适合映射到微服务边界中。
图 6-2 和图 8-6 已经展示了 RAG 流水线以及核心构件的总览。这里我们再单独把这些组件拆开来看;然后在“RAG on Kubernetes”中,再把它们映射到具体的 Kubernetes 工作负载类型。

一个 RAG 系统通常包括以下组件:

向量数据库(Vector database)

一种专门用于存储高维向量(也就是 embedding)的数据存储,也常被称为 vector store
向量数据库以向量形式保存你的知识库,并支持高效的最近邻检索。
它会返回那些与给定查询向量最相似的文档或文档片段。
“Vector Databases in a Nutshell” 会更详细地介绍这类数据库及其典型产品。
在 RAG 中,向量数据库就是那个被用来查询相关上下文的“记忆体”。

Embedding 模型(Embedding model)

负责在数据摄取阶段和查询阶段,把文本(或其他模态)转换为 embedding 向量的模型。
在文档摄取阶段,它会把每个文档或文档块转换成数值向量,再写入向量数据库。
在查询阶段,同一个 embedding 模型又会把用户问题转成向量,这样系统才能在向量空间中查找语义上相近的文档。

embedding 质量会直接影响检索相关性。这里所谓的“质量”,指的是:语义相近的文档,在高维向量空间中能否被映射到彼此接近的位置。
你可以使用开源 sentence transformer,也可以调用专有 API(例如 OpenAI embeddings),甚至在有条件时使用 LLM 自身的 embedding 能力。
关键点在于:索引阶段和查询阶段必须使用同一个 embedding 模型。
还要注意的是,embedding 模型不必与用于生成回答的 LLM 是同一个模型;很多生产级 RAG 系统都会使用一个专门的小型 embedding 模型负责检索,而另一个通常更大的 LLM 负责生成回答。
一致性才是最重要的:如果你更换或升级了 embedding 模型,通常就必须重新对文档做 embedding,否则搜索准确性会被破坏。

Reranker

这是一个可选组件,用于提高检索结果的相关性。
典型做法是:先让向量搜索返回一批初始候选结果,然后交给 reranker 做二阶段排序或过滤,决定哪些内容最值得进入最终 prompt。
最简单的方式可以只是基于 similarity score 排序,或者利用文档元数据(例如时效性、来源可信度)做加权。
更高级的做法则会使用一个 cross-encoder 模型,甚至让 LLM 本身在问题上下文中重新给每个候选片段打分。
引入 reranker 往往能明显提升回答质量,因为它能确保最终送进 prompt 的,只是最相关的那几段信息。
但代价也很明显:系统复杂度和延迟都会增加,因此是否使用 reranker,取决于具体应用的要求。

AI Orchestrator

AI Orchestrator 是整个 RAG 系统的粘合层。正如我们在“Architectural Patterns”里已经看到的,这个角色在所有 AI 驱动型应用中都非常常见。
它负责处理整个查询工作流。
当用户请求到来时,AI Orchestrator 负责:

  • 调用 embedding 模型,把 query embedding 化
  • 在向量数据库中做相似度搜索
  • 必要时调用 reranker 进一步精炼结果
  • 把检索得到的文本组装成增强后的 prompt
  • 调用 LLM 服务获取答案
  • 对最终结果做后处理并返回

这个 orchestrator 可以是你自己实现的 REST API 服务,也可以是一层像 Llama Stack 这样的 API 层,或者是一个本地 AI 框架,比如 LangChain,由它来管理多次调用链。
它往往还会实现很多应用特定规则或 guardrail,例如:

  • 在没有检索到相关文档时如何处理
  • 强制要求某些数据源必须被纳入回答依据

此外,AI Orchestrator 还常常负责实现来源追踪:记录最终回答使用了哪些文档块,并把这些块对应的链接或 ID 一起返回给用户。
这种 citation tracking 可以提升透明度,也能让用户回到原始文档中核对信息。
实践中,这通常是通过在 retrieval 和 reranking 的整个过程中一直保留 chunk 元数据(URL、标题、时间戳等),并在最后把它们以脚注、行内引用或 references list 的方式返回出来。

LLM 服务(LLM service)

最后,真正面向终端用户生成回答的,就是 LLM 本身。
LLM 会接收 orchestrator 组装好的 prompt(其中既有用户问题,也有检索到的上下文),然后生成 completion。
在 RAG 架构中,LLM 的职责被限定为“生成”:它不需要自己内建所有知识,而是依赖被提供进来的上下文来获得事实。
这个组件既可以是你集群中部署的模型服务(正如第 1 章所介绍的那样),也可以是对 OpenAI 之类外部 API 的调用。
无论哪种方式,LLM 服务都应该被当作一个普通的依赖服务来看待:你向它发送请求,它返回结果,而 orchestrator 再决定如何把这个结果交给最终用户——通常还会先做一些格式化或校验。

向量数据库速览(VECTOR DATABASES IN A NUTSHELL)

向量数据库(也称 vector store)专门用于做高效相似度搜索,从而让 RAG 管道能够检索出与用户问题最接近的文档块,再把它们传给 LLM。
文档和查询都会通过 embedding 模型被映射成高维空间中的一个向量,语义相近的内容在这个空间里也会彼此接近。常见的相似度度量方式是 cosine similarity(余弦相似度)

余弦相似度衡量的是两个向量在方向上的接近程度。
如果用二维空间来直观理解,可以想象从原点出发的两根箭头,它们形成一个夹角 θ,余弦相似度就是这个角度的 cos 值:

  • 同方向时为 1.0
  • 垂直时为 0.0
  • 完全相反方向时为 –1.0

这种“更关注方向而非长度”的特性,对文本 embedding 很有价值,因为放大或缩小一个向量通常不会改变语义,而向量的方向更能保留语义信息。

Hybrid search(混合检索) 则会把 dense vector 匹配与像 BM25 这样的 lexical ranking 结合起来,从而同时兼顾:

  • 语义相关性
  • 精确 token 信号

许多引擎会通过分数融合,或者两阶段流水线(再接一个 reranker)来实现 hybrid search。
这在处理稀有术语、标识符、精确短语时尤其有帮助,同时还能保持较高的语义召回率。

现代系统还会借助 ANN(approximate nearest neighbor)索引 来加速搜索,并增加过滤、持久化和分布式能力,以满足生产环境 SLA。
其实,向量搜索早在 LLM 之前就已经广泛用于推荐系统和多媒体去重;而像 HNSW 图FAISS 这样的技术突破,则使得十亿级别相似度搜索真正变得可行。
这类算法通常是用“近似正确”来换取速度,而不是保证返回绝对精确的最近邻。

目前流行的向量数据库包括:

  • 开源:Milvus、Weaviate、Qdrant
  • 托管服务:Pinecone
  • PostgreSQL 生态:pgvector
  • Elasticsearch 生态:dense vectors

这些组件共同协作,构成了 RAG。
而且,它们天然适合映射到微服务边界上——这对我们后面把它们部署到 Kubernetes 上非常有帮助。
例如,向量数据库可以是一个服务,LLM 是另一个服务,等等,这样每个部分都能独立扩缩、独立管理。
在进入部署层面之前,我们先把 RAG 的两个核心流程——文档摄取用户查询处理——完整走一遍。理解这两条流,会让你更容易真正构建和运维一个 RAG 系统。

文档摄取(Document Ingestion)

文档摄取是一个离线过程,负责把外部数据预处理好,使它们能够被用于后续检索。
在这个阶段,我们会把原始文档转化为 embedding,并把它们存入向量数据库。
你可以把它看作是为应用构建知识索引——之后用户查询时,系统就是在这个索引上做检索。
这个过程既可以在系统上线前预先执行一次,例如先把企业内部大量文档全部建好索引;也可以随着新数据不断到来而持续进行。

摄取流水线是异步运行的,它与用户查询处理之间没有阻塞关系。
换句话说,用户完全可以在 ingestion job 正在运行时继续查询向量数据库;而新文档也会在其 embedding 被写入数据库之后立刻变得可检索,而不需要等整批摄取任务全部完成。
在 Kubernetes 中,你通常会把 ingestion 实现为 Job 或 CronJob,按批次处理文档。
在单个 ingestion job 内部,处理步骤(解析、切块、embedding、写入存储)可以为了简单起见放在一个容器里顺序执行;如果吞吐要求高,也可以借助消息队列把这些步骤拆分给多个 worker pod 并行执行。
整个 pipeline 会以增量方式把结果写入向量数据库,因此一批文档里的新内容可以在整批完成之前就被搜索到。

一个典型的 ingestion pipeline 包含几个步骤,如图 8-7 所示。

image.png

图 8-7 RAG 文档摄取流程

下面我们逐一展开:

收集并解析文档(Collect and parse documents)

第一步,是收集你希望让 LLM 可用的源数据。
它们可以是:

  • 文本文件
  • PDF
  • 数据库记录
  • 网页
  • 转录文本

然后你要把每份文档解析成纯文本。
通常,这一步会依赖一些自定义代码或专门库,用于从不同格式中提取文本。
一个很值得关注的工具是 Docling:这是一个面向 AI 工作流的开源文档解析框架,能够处理异构来源,例如 PDF、Word 文件、HTML,甚至扫描图像,并把它们转换为结构化、机器可读的文本,同时还保留页码、标题等元数据。
对于 RAG 摄取来说,越一致、越结构化的输出,就意味着后续预处理工作越简单。

切块与预处理(Chunk and preprocess)

很少有场景适合把整篇文档作为一个整体做 embedding,因为文档往往很长,或者同时包含多个主题。
因此,文档通常会被拆成若干大小适中的 chunk,让每个 chunk 尽可能只围绕一个清晰主题,并能在 LLM prompt 中和问题一起被装下。

切块方式有很多种:

  • 按段落切
  • 按标题切
  • 更高级一点,可以按语义边界或句子边界切,避免把上下文在半句话中间截断

如果你使用 Docling,你甚至可以直接基于文档结构来生成 chunk,而不用依赖任意滑动窗口。因为 Docling 会把句子、段落、章节标题、表格、图注等都当作一等对象暴露出来。
Docling 支持多种 chunker:

  • 按句子
  • 按段落
  • 按与标题对齐的章节
  • 按语义分组

它还支持 overlap 与最大尺寸控制,让你能在召回率和 prompt 预算之间做权衡。
此外,它会为每个 chunk 生成稳定 ID 和整洁的 metadata schema,例如:

  • source
  • timestamp
  • version
  • section
  • page

这些信息既能保留 provenance,也能支持在查询阶段进行精确过滤。
不同的切块策略适合不同场景:例如 FAQ 式问答通常更适合更小、更句子级别的 chunk;而政策文档或产品手册这类内容,则更适合更大的、按标题对齐的片段,因为它们需要更完整的上下文。
同时,你还必须考虑文档生命周期管理:当文档发生变化时,必须触发失效或重新 embedding,以保持向量存储一致。
metadata 可以包含:

  • 源文档标题
  • 日期
  • 作者
  • 章节标题
  • 或者其他未来可能用于过滤、或供模型识别来源的标签

为 chunk 生成 embedding(Embed the chunks)

接下来,每个文本 chunk 都会通过 embedding 模型转换成数值向量,通常是一个高维向量。
例如,你可能使用 sentence-transformers 的 all-MiniLM-L6-v2,也可能调用托管 API,例如 OpenAI 的 text-embedding-ada-002
这一阶段的结果,就是每个文本块都有了一个对应的向量表示。

正如在“RAG Components”中强调的那样:索引和查询必须使用同一个 embedding 模型。
在工程实践中,这一步通常还会按 batch 进行,例如一次 embedding 1000 个 chunk,这样可以借助 GPU 或并行化来提高效率。
更多关于 embedding,以及它们如何把语义表示成向量的背景,可参考“Embeddings”。

把向量写入数据库(Store vectors in the database)

最后,把每个向量、它的标识符以及元数据,一起写入向量数据库。
这个标识符会把向量映射回原始文档或 chunk,以便未来能把源文本取出来,或者展示给用户。
元数据中既可以直接包含 chunk 的原文,也可以只保存一个引用,在需要时再从内容存储里取文本。
有些设计只存 ID,回答时按需回源拉文本;也有些系统直接把文本 payload 存进向量数据库里,以换取更快检索。选择哪一种,要根据你的延迟和存储权衡来决定。
完成这一步之后,向量数据库里就已经填满了代表你的知识库的向量,能够开始响应相似度查询。

为了更具体一点,我们来想象一个电商平台的客服机器人。
它的数据源可能包括:

  • FAQ 页面
  • 产品手册
  • 退货政策
  • 故障排查指南

在 ingestion 阶段,你会把 PDF 和 HTML 转成文本,按章节切块,为每个 chunk 做 embedding,并把这些向量连同元数据(例如 source: Product Manual Xsection: 2.1 Installation)一起写入向量存储。
摄取完成后,你的向量库里可能已经有了成千上万个向量,每一个都代表文档知识库中的一个片段。

还需要强调一点:ingestion 不是“一次性动作”
在 RAG 系统中,你必须有一套策略,确保向量索引始终保持最新。
例如,Kubernetes CronJob 可以定期拉取新增或变更文档,生成 embedding,并执行 upsert;你也可以在文档发生变更时,直接触发事件驱动式重新索引。
从运维角度看,最重要的结论是:向量数据库里的内容不是静态不变的,它必须随着你的数据演化,而平台本身也必须具备相应的 job 或流程来支撑这种演化。

现在,文档 chunk 已经进了向量数据库,接下来我们来看:系统究竟是如何通过相似度查询,把这些 chunk 取出来,并组装成回答所需的 RAG 上下文的。

用户查询处理(User Query Processing)

当向量数据库已经加载了知识之后,RAG 系统就可以开始处理用户查询。
查询处理流水线会在每一次新问题或新请求到来时运行。
它的目标是:找出最相关的知识片段,把它们拼进 LLM 的 prompt 中,然后返回模型生成的答案。
下面我们把这一条推理时流水线拆开来看。

图 8-8 展示了用户查询处理流水线的组成部分。

image.png

图 8-8 RAG 用户查询处理流水线

用户查询处理流水线通常包含以下步骤:

用户查询到达(User query arrives)

用户,或者一个上游服务,会发来一个需要由 LLM 处理的问题或请求,例如:
How do I reset my device?
这个查询会先打到应用的 API,由 orchestrator 组件接管后续处理。

对查询做 embedding(Embed the query)

orchestrator 会使用和 ingestion 阶段相同的 embedding 模型,把用户查询编码成一个向量。
这通常是一个很快的操作,最终得到的 query embedding 会处在和文档 embedding 相同的向量空间中。

向量搜索相关文档(Vector search for relevant docs)

有了 query embedding 之后,orchestrator 就会在向量数据库中执行相似度搜索。
向量库会返回 top-k 个最近邻,通常按照 cosine similarity 或距离来排序,同时还会附带 metadata,以及在某些配置下直接返回已存储的文本 payload。
在这一步,你还可以叠加 metadata filter,或者使用 hybrid retrieval,把 lexical scorer 加进来,这样既能抓住语义相近内容,也能兼顾稀有术语、标识符和精确短语。

对结果 rerank 或过滤(Rerank or filter results)

如果你有 reranker,这一步就会让它根据“查询上下文下每个 chunk 的实际帮助程度”给候选结果重新打分,只保留最值得占用 prompt budget 的那几条。
即便不用复杂模型,简单启发式方法也常常很有效,例如:

  • 最低相似度阈值
  • 根据新近性做加权

如果延迟预算很紧,很多系统其实只用向量搜索,也已经足够好。
经过这一步之后,你最终会得到一个较小的上下文片段集合,准备用来增强 prompt。

用检索到的上下文构造 prompt(Construct the prompt with retrieved context)

接下来,orchestrator 会把最终 prompt 拼装出来,把检索得到的文本和用户问题一起填进一个 prompt 模板中。示例 8-1 就是一个这样的模板。
具体 prompt 的措辞,以及上下文以什么形式呈现,都可以根据实际需要进行调优。
关键在于:我们通过显式给模型提供事实材料,让它“有依据地回答”。
在我们的例子里,这段上下文可能会包含产品手册中关于如何 reset 设备的一段说明。

示例 8-1 用 RAG 文档构造 prompt 的示例模板

Use the following context to answer the question.
If the context doesn't have the answer,
say you don't know.

Context:
{retrieved_text}   

Question: {user_question}   
Answer:

这里:

  • {retrieved_text} 是从向量存储中检索出来的文档内容占位符
  • {user_question} 会被替换成用户的真实问题

LLM 生成答案(LLM generates an answer)

然后,orchestrator 会把这个最终拼装好的 prompt 发送给 LLM 服务,也就是通过 API 调用模型推理服务。
LLM 会处理 prompt,并生成 completion。
在我们的例子里,它理想情况下会输出一个“如何 reset 设备”的分步说明,而且答案内容会来自刚才提供进去的上下文。
因为 relevant snippet 已经在 prompt 中了,模型不需要自己凭空发明事实;它只需要把这些事实组织成自然语言答案。
于是,这个例子的输出可能会类似于:

To reset your device, first hold down the power button for 10 seconds until the LED blinks

这句话本质上是在复述文档中的步骤,只不过是由 LLM 组织成更自然、更适合作答的语言。

后处理并返回响应(Post-process and return the response)

在把模型输出返回给调用方之前,orchestrator 通常还会做一些后处理。典型步骤包括:

  • 格式化
  • 附加来源引用(利用 metadata)
  • 执行 guardrail
  • 截断到长度限制之内

处理完成后,最终答案再通过 API 或 UI 返回给用户。

从用户角度看,这整个流水线是不可见的:他们只会感觉系统给了一个有帮助、而且引用了正确信息的答案。
向量搜索通常已经足够快,因此在整体延迟中,真正占大头的往往还是 LLM 生成本身。所以,一个实现得当的 RAG 流水线通常仍然能够给人“几乎实时”的感觉。
如果系统没能找到足够强的上下文,AI Orchestrator 应该优雅地选择不回答,而不是冒险生成一个幻觉答案。
当这些步骤都到位之后,LLM 的回答就真正被你的知识库“锚定”住了,并能持续对齐最新事实。

现在,RAG 系统的所有“配料”我们都已经摆出来了。接下来就看:这些独立的 RAG 组件,在 Kubernetes 上究竟该如何映射与部署。

Kubernetes 上的 RAG(RAG on Kubernetes)

让我们把“RAG Components”里的各个组件映射到 Kubernetes 上,看看如何把它们作为一个生产级系统来运行。
一个真正的生产级 RAG 栈,本质上是一组互相协作、拥有不同生命周期和不同 SLO 的服务,而这些组件非常自然地可以落在 Deployments、StatefulSets、Services 和 Jobs 上。
Kubernetes 让你能够独立扩缩各个部分、做安全发布,并在不同环境里统一配置与安全策略。

表 8-1 对 RAG 相关组件、它们对应的 K8s workload 类型,以及典型资源需求做了一个快速概览。

表 8-1 Kubernetes 上 RAG 组件概览

Vector database
K8s 原语:StatefulSet + PVC
类型:有状态
资源:高 RAM / CPU,快速卷
存储:持久卷

Embedding
K8s 原语:Deployment / Sidecar / In-process
类型:无状态
资源:轻量模型用 CPU;可选 GPU
存储:无

Orchestrator / API
K8s 原语:Deployment + Service(+ Ingress)
类型:无状态
资源:CPU 与适量 RAM
存储:无

Ingestion
K8s 原语:CronJob / Job
类型:批处理
资源:CPU,可选 GPU
存储:读写向量存储

接下来,我们分别更深入地看这些组件在 Kubernetes 上的典型落地方式,并在适合的时候,关联到 Kubernetes Patterns 中的对应模式。

向量数据库(Vector database)

向量存储是检索阶段的核心,因此在 Kubernetes 上,它理应运行在 StatefulSet + PersistentVolumeClaim 中,让每个 shard 都拥有稳定身份,并且在重启之后数据仍然能被保留。
如果某个向量数据库厂商提供了 operator,那么你应优先考虑使用它,因为 operator 能把集群初始化、升级等复杂逻辑封装起来。
同时,你需要为它配置足够内存,以便让热点索引常驻内存;在规模变大时,也应启用 ANN(approximate nearest neighbor)索引
通常,它会通过一个仅集群内部可见的 Service 暴露出来,并配合 NetworkPolicy 限制哪些 Pod 可以连接它。
在运维方式上,你应像对待任何关键数据库一样对待它:要有快照策略、并且定期验证 restore 流程。
这正对应 Stateful ServiceService Discovery 模式。

Embedding 服务(Embedding service)

Embedding 模型在 Kubernetes 上有几种不同的部署方式,取决于你的性能要求和运维偏好。
最常见的生产实践,是把 embedding 模型做成一个轻量级模型服务,以 Deployment 形式运行;这样你就能独立扩缩它,并根据需要给它分配 CPU 或 GPU。

对于小而高效的模型,也有一种更简单的方式:直接把 embedding 逻辑内嵌在 orchestrator 进程里。这样可以省掉一次网络 hop,并把延迟压到更低。
再折中一点的方式,则是把 embedding 模型做成 orchestrator Pod 中的 sidecar,这样既能共享命运共同体,又能让模型本身独立版本化,如图 8-9 所示。
无论采用哪种模式,最重要的是:ingestion 阶段和 query 阶段必须使用同一个 embedding 模型和相同配置。
而 sidecar 模式尤其适合“需要一个本地 helper、但又不想把它和主应用构建彻底绑死”的场景。

还有一些数据库本身也能在写入或查询时直接生成 embedding。
例如:

  • Weaviate 的 vectorizer module 可以在写入和查询时自动 embedding
  • Postgres + pgvector 则可以借助 SQL trigger 和扩展实现自动 embedding

这种方式的好处是:ingestion 和 retrieval 天然使用同一套 encoder,不会不一致。
代价则是:数据库与模型的耦合会更强。
无论选哪一种部署方式,都必须强制保证:用于 ingestion 和 query-time encoding 的 embedding 模型与配置是一致的。

image.png

图 8-9 Embedding 服务的多种部署方式

AI Orchestrator

Orchestrator 是整个系统的“大脑”:它负责对 query 做 embedding、做检索、可选地 rerank、构造 prompt,并调用 LLM。
因此,最合理的方式通常是:把它作为一个无状态 Deployment 来运行,并通过 ClusterIP Service 暴露;如果它直接面向最终用户,则再通过 Ingress 或 API gateway 对外提供入口。

由于 orchestrator 要协调多个依赖,因此它必须具备良好的可观测性:
你应该把 trace context 在各个调用链路中继续传递,这样才能测量端到端延迟,并快速定位瓶颈。
像 prompt template、threshold、retrieval 参数这类配置,应尽量放在 ConfigMap 中,以便你无需修改代码就能动态调优。
而任何凭证类信息则应通过 Secret 挂载。
关于如何加固敏感配置,可参考 Secure Configuration 模式。

在扩缩方面,你可以使用 Horizontal Pod Autoscaler 来应对稳定流量;而如果你的负载是更明显的事件驱动波动,则 KnativeKEDA 会更合适,甚至能在空闲时做到 scale-to-zero
更多关于 Knative / KEDA 的内容,以及它们如何支持从 0 到高峰的弹性扩缩,可参见 Elastic Scale 模式。

Reranker(可选)

如果你的系统非常关注精确率,那么你可以把 cross-encoder 或更重型的 reranker 独立部署成一个单独服务,并只在高价值查询中选择性调用它,以在成本和延迟之间取得平衡。
如果只是一些简单启发式规则,则完全可以继续留在 orchestrator 内部;但把 reranker 单独拆出来的好处是:你可以独立为它调整资源配置和发布节奏,这与 Stateless Service 模式完全一致。
保持 reranker 的“可选性”非常重要,这样你就能针对不同路由,在“成本优先”和“质量优先”之间切换。

批量摄取任务(Batch ingestion jobs)

文档摄取并不是一次性的动作,而是一个持续性过程,而 Kubernetes 非常适合把这种工作作为后台任务长期运行。
你可以通过 CronJob 周期性地启动摄取任务:拉取新增或更新的数据源、解析、切块、生成 embedding,并把结果 upsert 到向量数据库。
如果你追求接近实时的更新,那么也可以把这条流水线做成事件驱动:例如,文件上传、数据库更新时就直接触发 job。
文档摄取甚至也可以很好地被建模为 Event Mesh 中的一个 endpoint,这方面 Knative Eventing 提供了很好的支持。

同时,要记得给这类 ingestion workload 配置好 resource request / limit,避免它们挤占面向用户服务的资源。
如果你需要更严格的隔离,还可以把 ingestion 与在线路径分到不同的 namespace,甚至不同 node pool。
把 ingestion 当成一个“一等工作负载”来看待——为它配置监控、重试和运维规则——才能保证向量数据库始终新鲜,从而让你的 RAG 系统反映领域知识的最新状态。
相关技术细节可以参考 Batch JobPeriodic Job 模式。

通过 RAG,我们已经看到了:如何让 LLM 的回答立足于可信知识,并如何把相关支撑组件部署到 Kubernetes 上。
接下来,我们将进入 agentic workflow:在这种模式中,模型不只是消费上下文,还会规划动作、选择工具,并在短小的 think-act-observe 循环中不断迭代,朝着目标前进。

Agentic Workflows

Agentic 应用会在模型推理调用外层再包一层小型控制循环,使系统能够:

  • 规划
  • 调用工具
  • 观察结果
  • 再继续迭代,直到达到目标

总体上,这个控制循环如图 8-10 所示,包含以下步骤:

感知(Perceive)

读取新的信号:用户输入、工具输出,以及对话状态。

思考(Think)

规划下一步,决定是否需要调用工具,并构造下一轮 prompt。

行动(Act)

执行一个动作:调用工具、运行代码、获取数据,或者起草一个候选答案。

观察(Observe)

把工具返回结果,或者用户后续输入,归一化成系统当前工作上下文的一部分。

反思(Reflect)

检查当前进展是否逼近目标,修正计划,并决定是停止还是继续。

记忆(Remember)

把短期 scratchpad 和记长期事实都保存到外部记忆中。

image.png

图 8-10 Agentic 控制循环

这个流程是对著名 ReAct loop 的进一步细化。后面的侧栏会专门解释 ReAct。
这种循环既适用于简单 agent,也构成了更复杂场景的基础,例如我们将在“Multiagent Systems”中讨论的多智能体系统,以及在“Ambient Agents”中讨论的环境型智能体。

ReAct 循环(THE REACT LOOP)

ReAct 模式把 chain-of-thought(CoT)推理tool action 交错在一起,让模型以一种紧凑的循环方式完成“思考—行动—观察—重复”。
它最初由 Yao 等人在论文 “ReAct: Synergizing Reasoning and Acting in Language Models” 中提出。论文表明:如果模型能够在推理与调用外部工具(例如搜索 API)之间来回切换,那么它会更少产生幻觉,也更容易提高任务成功率。

在一个 ReAct agent 中,LLM 会输出中间 thoughts;在需要时,它会给出一个 action 以及 JSON 参数;然后工具执行,observation 被追加到上下文里,模型继续思考,直到命中 stop condition。

在生产环境里,你通常不会把 CoT 直接暴露给用户——这些 trace 会保留在内部,或者被替换成摘要——但底层控制流本身是一样的。
一个重要的工程实践是:把每一个 thought、action、observation 都记录成结构化数据,这样你就能在以后调试 agent 行为,也能把成本与质量关联起来分析。

现在我们先聚焦在 agentic 控制循环中的 Act 这一环,因为它是最关键的一步:
它让系统能够使用不属于模型训练数据的信息——要么是因为这些信息太新,要么是因为它们是模型训练阶段根本拿不到的领域专有信息。

这些“动作”通常统称为 tools(工具) ,它们可以是:

  • 简单的 Web 搜索
  • API 调用
  • 企业内部后端服务调用

工具使用通常有两种执行路径,而这两种路径甚至可以在同一个工作流里混合使用:

客户端执行的函数工具(Client-executed function tools)

模型会输出一个函数调用,包括 JSON arguments;然后由你的客户端代码去真正执行这个动作,并把结果通过 call_id 关联回模型。
这是最可移植的基础做法,也为你提供了最细粒度的控制和审计能力。
示例 8-2 展示了这种 request-response 流:服务端返回一条响应,要求客户端在本地调用一个工具,并把工具执行结果再次发回服务端。
这种多步交互意味着:agentic 流程本身必须是有状态的,因为它需要保留整个对话历史。
客户端侧工具调用其实相当脆弱,因为每个 agent framework 对工具调用请求的格式要求都不完全一样。

服务端执行的工具(Server-executed tools)

在这种模式下,agent runtime(例如 LangChain、CrewAI 或其他类似框架)会代表你去执行工具,包括远程的 MCP(Model Context Protocol) 服务器。
我们会在“The Model Context Protocol”中专门深入讨论 MCP,因为它已经成为当前工具交互领域的事实标准。
现在你只需要知道:MCP 是一个大大简化了服务端工具调用与发现的协议。相比客户端函数调用,它能让领域知识更自然地融入 agent 流程。

示例 8-2 使用 OpenAI Responses API 的客户端函数调用

# Initial request including description of available tools
curl https://api.openai.com/v1/responses \   
  -d '{
    "input": [
      {"role": "user", "content": "Do I need an umbrella in Berlin today?"}
    ],
    "tools": [
      {
        "type": "function",   
        "name": "get_weather",
        "description": "Get the weather information for a city and ISO date.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string" },
            "date": { "type": "string", "format": "date" }
          },
          "required": ["city", "date"],
          "additionalProperties": false
        },
        "strict": true
      }
    ],
    "tool_choice": "auto"
  }'
# Part of the returned response, asking the client for a a tool call
{
  ...
  "output": [
    ... ,
    {
      "type": "function_call",   
      "call_id": "call_wx_1",   
      "name": "get_weather",
      "arguments": "{"city":"Berlin","date":"2025-09-20"}"
    }
  ],
  "status": "incomplete"
}

# Part of the second client request, holding the result of the tool call
curl https://api.openai.com/v1/responses \
  ...
  -d '{
    "input": [   
      ...,
      {
        "type": "function_call_output",
        "call_id": "call_wx_1",
        "output":
          "{"precipitation_chance":0.80,
            "summary":"Heavy rain expected in the afternoon."}"
      }
    ]
  }'

这里:

  • 第一段是初始用户请求
  • 中间定义了一个客户端函数工具
  • 服务器响应要求客户端去调用这个工具
  • call_id 用于把工具调用请求和返回结果关联起来
  • 第二次请求则把工具执行结果送回服务端

最初,客户端侧工具调用通常是由调用 agentic loop 的那一方亲自执行的,因此客户端要真正承担“调用工具”的责任。
当 LLM 在推理阶段判断出需要调用某个工具时,它会根据工具暴露出来的描述与元数据,把控制权交还给调用方,请求调用方执行这个工具,并在下一轮多轮会话中把结果再发回来。

本章接下来的内容都将在这个基础上展开。
在“OpenAI’s Responses API”中,我们会深入讨论 Responses 提供的状态、事件和审批机制。
在“Agentic Frameworks and Runtimes”中,我们会比较客户端库和服务端运行时。
在“Multiagent Systems”中,我们会把这个控制循环扩展到一组 agent 之间。
在“Ambient Agents”中,我们会把这个循环改造成运行在 Kubernetes 上的事件驱动形态。

Agentic Frameworks and Runtimes

从零开始手工搭建一个 agentic workflow 是很困难的,因此各种 framework 和 runtime 会大大降低工作量。
大体上,你会遇到两类东西:

  • 客户端 agentic 库:直接嵌入到你的代码中
  • 服务端 agentic runtime:作为一个服务对外暴露接口

这里我们不会展开太多实现细节,因为本章重点在于“如何在 Kubernetes 上运维 agentic 系统”。
但理解这些 framework 的分类方式非常重要。

客户端侧 agentic framework

这类库帮助你在自己的应用内部运行 agent loop。
它们的优点是:

  • 你拥有完全控制权
  • 调试相对容易

缺点则是:你自己要管理整个 orchestration 复杂度。

例如:

  • LangChain 提供了 prompts、memory 和 tool use 的抽象,支持 Python 和 JavaScript,并拥有丰富生态,可接 Web search、数据库和 REPL。
  • 对 Java 来说,LangChain4j 提供了类似能力,并且已经与 Quarkus 集成,支持原生 agentic workflow。
  • LangGraph 则把 agent 的步骤建模成一张图(graph),使分支和并发子任务显式且可观测。
  • CrewAI 更强调多 agent 协作:通过基于角色的 agent 相互通信、委派任务,这在 specialization 和并行化真的有收益时会非常有用。CrewAI 甚至使用自定义 REST endpoint 来承载 agent 之间的通信,因此它有时候会模糊“客户端编排”和“服务端编排”的边界。

由于这些库直接嵌在你的运行时中,所以你要自己负责那整个循环:

  • 调用 LLM
  • 执行工具
  • 把结果反馈回模型
  • 决定什么时候结束

这意味着你得到的是最大的灵活性,但也承担了更高的复杂度。
在生产环境里,即便这些被称作“客户端侧”的 framework,往往也仍然会被打包进运行在 Kubernetes 上的容器化微服务里,因此它们照样会受到和普通应用工作负载一样的打包、扩缩和可观测性约束。

服务端 agentic runtime

另一条路径,是用专门的后端服务把整个 loop 封装在一个 API 之后。
这样一来,客户端只需发出一个请求,而后端就会完成多轮推理和工具调用。

例如,OpenAI 的 Responses API 就提供了:

  • 状态化多轮交互
  • 集成工具使用
  • 结构化输出
  • 事件流
  • human-in-the-loop 的暂停 / 恢复机制

这样你就不必自己写整个 orchestration loop。
(下一节我们会专门继续讲 Responses API。)

Responses API 同时支持:

  • 服务端执行的工具,包括通过 MCP 调用远程工具
  • 客户端执行的函数工具,用于那些必须由你自己控制平面的动作

Llama Stack 则提供了一个开源、可自托管的 runtime,它既有 Agents API,也有 OpenAI-compatible endpoint,并且包含类似 Responses 的流程。
这意味着你可以把 agentic backend 真正运行在 Kubernetes 上,同时自己选择模型后端。
与之相比,vLLM 也正在朝同样方向发展:它在自己的 OpenAI-compatible server 中加入了 tool calling 和 structured output 支持。至于它与 Responses API 的功能对齐程度,还需要关注最新文档,因为这一领域发展非常快。

从实践角度看,这两类方案的真正区别在于:agent 的 orchestrator 究竟运行在哪里
服务端 runtime 把 loop 隐藏在网络 API 后面,因此:

  • 客户端代码更简单
  • scaling 与治理都被集中管理

而客户端 framework 则把逻辑保留在本地,以换取最大定制性与可组合性。
在实践中,这两种方式甚至可以混用:例如,在你自己的应用中使用 LangChain,但底层推理和服务端工具调用由 Llama Stack 来承载;又或者规划发生在服务端,但工具仍然保留在本地,由客户端执行。

OpenAI 的 Responses API

OpenAI 的 Responses API 是为 agentic workflow 设计的一种“单一、状态化 API 调用”接口。
它引入了一系列能够明显简化 agent 开发的能力,包括:

  • 自动维护多轮会话状态
  • 结构化输出
  • 集成的工具使用
  • 中间工具事件流
  • 内建错误处理

你只需要提供:

  • 用户输入
  • 一组工具目录(使用 JSON Schema 描述)

然后服务本身就可以自动编排一连串工具调用,把 observation 反馈给模型,并最终返回答案。
在一个工作流里,客户端执行工具和服务端执行工具这两种路径也可以并存。

对于服务端工具,它们会在 OpenAI 的 runtime 内执行,其中也包括通过 MCP 接入的远程工具。这样你无需在客户端实现 loop,就能获得流式事件和最终结果。
我们会在“The Model Context Protocol”中继续解释 MCP。
客户端执行函数工具则是另一条路径:模型输出工具名和 JSON 参数,由你的服务去真正执行动作,然后再把结果发回,让模型继续执行并完成整个任务。

更重要的是,Responses 接口正在迅速成为一个事实标准,并且已经通过 Open Responses 倡议被形式化为一个开放规范。
目前,已经有多个后端开始实现与之兼容的 endpoint。

例如:

  • Meta 的 Llama Stack 已经提供了 OpenAI-compatible interface 和一个可用但仍在快速演进中的 Responses 实现,这使得你能在 Kubernetes 上运行自托管 agent runtime,而不必修改客户端代码。
  • vLLM 也已经在其 OpenAI-compatible server 中加入了 Responses 入口,并且截至 2026 年初,发展速度非常快,非常值得关注。
  • OpenAI 自己的 cookbook 和社区指南也已经提到:vLLM 正在提供与 Responses 兼容的 API,这进一步说明整个生态正在朝着这一协议收敛。
  • 同时,LiteLLM 也提供了一个 proxy,通过暴露 /responses endpoint,把请求路由给多个 provider,从而在不同服务端实现尚未完全成熟的阶段,给团队提供一层兼容适配层。

这其中最重要的结论是:可移植性(portability)
你可以把客户端代码统一建立在 Responses API 之上,而“agentic loop 究竟在哪儿跑”则可以随着运维需求变化灵活切换:

  • OpenAI 云端
  • 自托管的 Llama Stack
  • 你自己集群中的 vLLM 服务

都可以作为后端替换,而不必动客户端。

Human-in-the-loop 也非常自然地融入了这个流程。
你可以在模型请求执行某些动作时先暂停,让用户审批、补充输入,或升级给审核人,然后再继续执行。
对于敏感工具,你甚至可以显式设置审批门:在你确认之前,模型不能继续执行。
当你通过 MCP 使用远程 provider 时,API 还可以把审批请求本身显式地暴露出来,让你在真正发生任何副作用之前,得到一个可审计的确认点。

总之,Responses 提供的是一种“agentic reasoning as a service”:
它帮你提供 reasoning 和 loop,但你仍然能控制:

  • 系统拥有哪些工具
  • 哪些调用必须在你这边执行
  • 哪些动作需要人工审批

而随着越来越多兼容后端的出现,它正在成为一种非常务实的 agent 架构基座。

Kubernetes 上的 Agents(Agents on Kubernetes)

在“Agentic Frameworks and Runtimes”中,我们已经把实现 agent workflow 时常用的 framework 与 API 服务做了分类。
这一节里,我们将把重点转向:在 Kubernetes 上,如何部署和集成 agent-enabled application,也就是 agent 系统的实际部署模型,以及 Kubernetes-native 集成长什么样。
这里仍然保持在高层次,真正更深入的运维细节,请看第 9 章。

Kubernetes 天然非常适合承载 agentic system,因为它已经为你提供了可组合的基础构件:

  • orchestrator
  • tools
  • memory

接下来,我们再把视角转向更“Kubernetes-native”的一种集成方式:通过 CRD(Custom Resource Definition)和 controller,把 agent 纳入 Kubernetes 控制平面。
截至 2026 年初,这个领域仍然变化很快,不过相对成熟的项目之一是 Kagent
Kagent 最初由 Solo.io 发起,如今也在 CNCF 社区中逐渐成长。
它是一个 Kubernetes-native operator,允许你把:

  • agent
  • tool
  • 暴露方式(exposure mode)

都定义成自定义资源,然后由 operator 把它们 reconcile 成可运行的 Pod。
它在设计上非常强调协议兼容性:无论是工具侧,还是 agent-to-agent 交互,都可以通过标准协议接入。例如,你可以注册 MCP-compatible 工具服务,也可以暴露 A2A skill,而所有这些都仍然发生在 Kubernetes control plane 内。
从运维视角看,agent 就像 Deployment 或 Job 一样,可以用同样的 GitOps、安全与配置治理流程来管理。

示例 8-3 展示了它的“意图表达方式”:定义推理循环、通过 MCP 附加工具,并发布一个 A2A skill,而具体的 Pod、配置和状态管理都交给 operator。

示例 8-3 Kagent 中的 Agent 定义示例

apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
  name: k8s-a2a-agent
  namespace: kagent
spec:
  description: An example agent
  declarative:
    modelConfig: default-model-config   
    systemMessage: |
      You are a helpful Kubernetes agent.   
    tools:   
      - type: McpServer   
        mcpServer:
          name: kagent-tool-server
          kind: RemoteMCPServer
          toolNames:
            - k8s_get_resources
  a2aConfig:   
    skills:
      - id: get-resources
        name: Get Resources
        inputModes: ....
        outputModes: ....

这里:

  • modelConfig 引用 agent 配置
  • systemMessage 是系统提示词
  • tools 定义要使用的工具
  • mcpServer 引用另一个资源 RemoteMCPServer 中声明的 MCP 服务
  • a2aConfig 则是与 Google A2A 协议集成相关的配置

Kubernetes Agent 领域的前沿实验(EMERGING KUBERNETES AGENT EXPERIMENTS)

2025 年涌现出的两个方向,在迈向生产可用的过程中获得了越来越多关注。

首先,Kubernetes 社区中的 agent sandbox 正在探索一种新的 controller 与 custom resource,目标是提供一种具备更强边界的、隔离的、有状态的、singleton 风格 runtime,并支持:

  • 持久身份
  • hibernation / resume
  • 更强运行边界

它的目标,是支撑那些受信任边界更强、或具有交互性的 agent 工作负载:
既保持类似 VM 的隔离性,又依然能以“像 Pod 一样”的方式被 Kubernetes 管理。

第二个方向是 Kagenti,它把自己定位为框架无关的中间件层,通过 operator 和统一抽象面去承载 agent,目标是标准化:

  • 身份
  • 配置
  • 暴露方式

同时还能通过 MCP 与 A2A 之类协议桥接不同 agent 世界。
这两个项目目前都仍在积极开发中,因此在采用之前,必须仔细评估它们的 API 稳定性,以及它们是否真的适合你的运维环境。

并不是所有 Kubernetes agent integration 都会用 CRD 来定义 agentic workflow。
一个很典型的例子就是 Llama Stack:它本身是一个通用 API 层,支持 agentic flow,包括 tool calling 和多轮推理。
Llama Stack 当然也可以通过 operator 管理安装过程,但在配置 agentic 功能后端时,它主要还是依赖自定义配置文件,而不是通过 CRD 把 agent workflow 全部声明出来。

虽然不同 agent 平台的实现路径并不相同,但大多数最终都会在 Kubernetes 中收敛到一些类似的概念:²

  • 在大多数 agent 平台中,custom resource 与 controller 会把 agent 声明式地纳入 Kubernetes 管理。像 KagentKagenti 就引入了建模 agent 的 CRD,因此你可以像管理 Deployment 一样,通过 GitOps 来管理 agent。Controller 会把这些资源 reconcile 成 Pod 和 Service,从而把 infrastructure-as-code 的优势带到 AI 系统上。(Controller, Operator, Declarative Deployment
  • 长生命周期的有状态 Pod 会跨交互持续维护会话上下文。与无状态服务不同,agent 往往会作为 singleton deployment 或单副本 StatefulSet 运行,以保存 session state。如果需要扩展,则平台通常会把 session 打散到多个 Pod 上,或者把状态外置,以支持 round-robin 负载均衡——这其实借用的正是有状态微服务的常见模式。(Singleton Service, Stateful Service
  • 批处理 Job 可以把一些离散任务从主 agent loop 中剥离出来。当 agent 需要生成一份报告,或处理一批大数据时,它可以提交一个 Kubernetes Job,然后等结果返回。这样做能自动获得重试与资源隔离能力,这和 ML pipeline 拆分工作方式很像。(Job, Periodic Job
  • 事件驱动架构(EDA) 则让 ambient agent 成为可能。在这类架构里,agent 会作为 listener 部署,对 Kafka topic、Kubernetes event 或 webhook 做出响应。再结合像 Knative Eventing 或 KEDA 这样的扩缩平台,agent 甚至可以在空闲时缩到 0,有事件来时再快速扩出来。此时 agent 被当作的是反应式微服务,而不是 request-response endpoint。关于 ambient agent,我们会在“Ambient Agents”中继续展开。(Elastic Scale
  • 对 agentic app 来说,工具集成(tool integration) 是必不可少的,因为 agent 能力本质上就是通过标准 API 方式暴露出来的。工具本身通常是独立 Deployment,并通过 ClusterIP Service 提供调用接口,agent 则通过 DNS 名称调用它们。很多平台都采用 MCP 来统一工具接口,这意味着:任何符合 MCP 的工具,都可以与兼容的 agent framework 协作。在“The Model Context Protocol”中,我们会更深入讨论如何在 Kubernetes 上运维 MCP 服务。(Service Discovery, Declarative Deployment
  • 持久化存储 让 agent 能跨重启保留记忆。长期记忆通常放在向量数据库里,以 StatefulSet 运行;对话历史则存放在带 PersistentVolume 的数据库中。通过把状态外置,哪怕 agent Pod 本身是短暂的,也依然可以支撑有状态的 AI 过程——这与普通依赖数据的微服务并无二致。(Stateful Service
  • Kubernetes 原生安全控制定义了 agent 能访问什么、能做什么。受限权限的 ServiceAccount、限制网络访问的 NetworkPolicy、按最小权限挂载凭证的 Secret,都是这里的基本能力。对于多租户部署,则可以通过独立 Namespace 来隔离 agent,再借助 admission controller 来执行配额和合规策略。(Process Containment, Secure Configuration, Access Control, Network Segmentation
  • 可观测性栈会把 agent 也当成可度量的服务。Agent 应用会暴露 token count、tool call 数量等指标,会输出详细 reasoning 日志供排障,也可以把重大动作写成 Kubernetes Event。当这些能力配置得当时,SRE 就可以用和普通服务同样的 dashboard 和告警体系来监控 agent。

在实践中,成功地在 Kubernetes 上部署 agent,往往意味着:
你不仅要把容器打包好,还要谨慎地处理状态、合理配置资源,并提供完整的可观测性。
此时,平台本身就成为了你的 agentic AI control plane:它管理生命周期与资源,而 agent 自己则专注于推理与工具编排。
无论你是在一个单容器里跑一个简单的 ReAct loop,还是像下一节要讲的那样,在多个 namespace 中协调一组 multiagent cohort,Kubernetes 都提供了那些让 agent 能以可靠方式大规模运行的核心原语:调度、网络和存储

多智能体系统(Multiagent Systems)

多智能体系统把多个专门化 agent 组合起来,让它们协同完成一个单个 agent 很难独立完成的更大目标。
每个 agent 都是一个自治服务,拥有:

  • 自己的 prompt
  • 自己的工具
  • 自己的 guardrail

并通过远程 API 使用一个或多个 LLM。

这种协作会带来一些很有价值的副作用:

  • agent 之间传递中间结果
  • 相互交叉校验工作
  • 并行化子任务

从而同时提升质量吞吐
在设计上,每个 agent 都应该被限制在一个相对独立的职责范围内,这样责任边界才清晰、耦合度也低。
你可以把它想象成一个软件团队:
一个 planner 先把工作拆成步骤,一个 coder 去写改动,一个 tester 验证行为,然后一个 reviewer 做最终把关。³
这种 specialization 让每个 agent 只专注于自己擅长的狭窄能力,而整个系统却能跑得更快、更稳。
这种架构的一个巨大优势是:每个 specialist agent 只需要维护一个远小于整体问题上下文的工作上下文。这样做能让 prompt 更聚焦、token 消耗更少、准确率更高。
这些 agent 会通过一个显式控制流进行协作,使得各自的部分结果最终能拼成一个连贯结果。

一个 multiagent system 的核心,其实是它的协调逻辑(coordination logic)
其中一种常见模式,是有一个中央 orchestrator,把任务分配给各个 role agent,并汇总结果;这正是很多 crew-style framework 的基本形态:
例如,一个 facilitator 把代码相关问题路由给 coding agent,把合规问题路由给 policy agent。
图 8-11 展示的就是这种形态:由一个中心 planner / coordinator 去调度多个 worker agent。

image.png

图 8-11 由协调者统一编排的多智能体

另一种模式则是 点对点协作(peer-to-peer coordination) :agent 彼此直接通信、动态发现能力、相互升级或委派,而不是依赖一个单一中心。图 8-12 展示了这种结构。
我们会在“Agent-to-Agent Protocol”中讨论的 Google A2A 协议,正是对这种风格的形式化:它把以下能力标准化了:

  • 能力发现
  • 通过 agent card 交换能力描述
  • task 生命周期
  • artifact 流转

这使得不同团队、不同厂商的 agent 之间也能互操作。
无论是哪一种模式,系统的成败都不在于某个单独的 prompt,而在于它的消息纪律:什么信息被共享、何时共享,以及以什么样的保证共享。

image.png

图 8-12 按需互相触发的多智能体

在 Kubernetes 上,这类系统通常会把每个 agent 建模成一个带 Service 的 Deployment,然后通过 HTTP / gRPC 做同步协作,或者通过 pub-sub 消息总线做异步协作。
当然,也可以把多个 agent 放在同一个 Pod 中,由某个 framework 在进程内协调它们的对话。
这种方式的优点是:

  • cross-agent 状态共享更简单
  • 延迟更低

但代价是:生命周期和扩缩被绑死,所有 agent 必须一起扩容,这通常会显著削弱弹性,因此除了很小规模或高度耦合的团队之外,通常并不合适。
在本节剩余部分中,我们都默认关注分布式设计——也就是 agent 通过网络协作。

在这种分布式设计里,共享记忆后端(shared memory backend) 是多智能体协作的粘合剂。
它可能是:

  • 向量存储
  • 文档存储
  • 黑板(blackboard)

agent 可以把发现、待办任务或 artifact 写进去,再由其他 agent 消费。
这种共享状态使整个系统能够跨 agent 边界“记住发生过什么”,同时又仍然把每个 agent 的运行时与资源配额隔离开。
与此同时,那些经典分布式系统问题也依然存在:

  • service discovery
  • retries
  • backoff
  • circuit breaker

因为 multiagent system 本质上就是一个“缩小版的分布式系统”。
在“Agent-to-Agent Protocol”中,我们还会把这些问题进一步和协议层联系起来,帮助你选择一种在版本演化和团队边界下仍然稳定的 message shape。

一个很具体的 multiagent system 场景,就是客户支持自动化
一个 agent 持续监控进入的 support ticket,然后把每张票派发给适当的 specialist agent——例如一个 NetworkTroubleshooter agent,或者一个 BillingInquiry agent——最后再由一个 Summary agent 汇总本次处理过程并生成报告。
这里的 coordination logic,决定了:

  • 哪一个 specialist agent 需要参与
  • 任务何时算完成

多智能体系统在这种场景下非常适合,但它也会引入新的复杂性:
你必须保证所有 agent 协同一致,而不是彼此“踩脚”。
因此,在角色设计、通信通道和故障兜底机制上必须做非常谨慎的设计。
例如:如果两个 agent 意见不一致怎么办?

在这里,一个非常有用的分布式系统模式是 Saga pattern
Saga 本来是用来处理长事务和失败补偿的;在 multiagent orchestration 中,你同样可以借鉴它:
如果某个 agent 在工作流中途失败,就可以由一个“补偿型 agent”去回滚或清理此前部分完成的工作。
这样一来,你就不会把 multiagent system 留在一个不一致状态,而是拥有清晰的 rollback path。

总结一下:
多智能体系统,本质上是一种协作型智能。
你把许多“小而锋利”的 agent 组合起来,再用一个协调层——可以是中心式,也可以是点对点——并辅以共享记忆,去保留上下文与证据。
如果设计得好,这就是真正意义上的“agent orchestration”:许多乐器、一份总谱、清晰的进出场提示。

在这个基础之上,我们接下来转向 ambient agent 和那些运行在后台、持续监听事件流、识别条件并触发工作流的服务,看看它们如何在 Kubernetes 上补充 multiagent 设计。

环境型智能体(Ambient Agents)

Ambient agent 会一直在后台运行,并对环境中的信号作出反应,而不是等着有人来给它一个交互式 prompt。
它们像是静静“栖居”在你的系统旁边,当触发条件满足时才采取行动:

  • 有新文件出现
  • 某一行数据发生变化
  • 某个传感器阈值被越过
  • 定时器触发

你可以把它们理解成一种“默认被动、但随时待命”的机制。它们不会主动开启对话,当然在需要时也可以先向人类确认,但这不是必须的。

一个很实际的例子,是一个 Kubernetes caretaker agent
它持续监控集群健康信号,例如 crash loop 或 CPU pressure;一旦发现异常,就立即展开调查,例如查询日志、对比近期指标。
如果调查结果与某个已知问题模式吻合,这个 agent 就会尝试执行一个定向修复动作,例如:

  • 重启某个 deployment
  • 回滚某个配置
  • 对某个服务扩容

只有当自动修复失败,或者策略把当前情况判定为高风险时,它才会升级给人类。
图 8-13 展示了这样一个 ambient agent 方案所涉及的组件。

image.png

图 8-13 监控 Kubernetes 事件的 ambient agent 示例

Ambient agent 建立在 事件驱动架构(EDA) 之上。
它们会订阅:

  • 队列
  • webhook
  • 文件监听器
  • 或定时触发器(如 CronJob)

然后更新自己的 working context,决定是否需要行动,再去调用工具。
例如,一个“每日规划 agent”可以在每天凌晨 2 点运行,分析前一天的活动,生成当天计划,然后通过邮件或通知渠道把结果发给人类审阅。

对于敏感操作,这类 agent 通常会加入 human-in-the-loop(HITL) 检查点:
agent 先起草一个计划,把它交给审批者,只有在显式得到 “go” 之后才真正执行。
你可以通过策略控制它的自治级别:

  • 仅建议(recommend only)
  • 批准后执行(approve to act)
  • 对低风险操作完全自动(auto for low risk)

同时,你还可以通过一个“确定性预算(determinism budget) ”来限制变化范围,让重试与回放行为更可预测。
而在输出侧,每一个动作都必须留下证据轨迹:

  • 输入是什么
  • 它是如何做出决策的
  • 产出了哪些 artifact

这样运维过程才具备审计性。

Human in the loop(HUMAN IN THE LOOP)

Human in the loop 是一种显式设计出来的检查点:
在 agent 继续执行之前,由一个真实的人类先审核其计划或结果,并明确批准下一步。

这种机制主要用于:

  • 高风险、不可逆操作
  • 组织策略要求人工监督的场景
  • 信号模糊、系统置信度不足的场景

典型例子包括:

  • 推送一个生产热修复
  • 回滚一个可能导致停机的配置
  • 批准一笔大额金融交易
  • 发出一条高覆盖率客户通知

反馈可以通过 Slack、Teams 这类消息系统完成:
agent 把提议计划发出去,然后等待“approve / reject”回复。
另一种更解耦的方式,是把一个 approval request 连同 correlation ID 发到消息总线上,再由某个独立 UI 或流程发回对应 decision event。

为了保证可审计性,agent 应该:

  • 附上它的 rationale 与 diff
  • 记录审批人和最终决策
  • 在执行后再把结果回发出去

这样一来,低风险场景下你可以保留自治,而真正关键的判断则交给人类去完成。

对于 ambient agent 来说,平台层关注点其实和普通分布式系统没有本质区别:

  • service discovery
  • retry 与 backoff
  • circuit breaker
  • 幂等 handler
  • 配置和 secret 的明确归属

在实践中,想要把 ambient agent 做好,通常需要同时做到三件事:

  1. 事件处理必须可靠,动作必须幂等
  2. 对不可逆操作设置清晰的人类审批点
  3. 在 Kubernetes 层面划清缩放与安全边界的 ownership

只有这样,ambient agent 才会像普通微服务一样可预测,同时又具备“在大规模上做主动式运维”的能力。

经验总结(Lessons Learned)

本章中,我们讨论了如何在 Kubernetes 上为完整的 AI 驱动型应用做架构设计:从聊天界面,到事件驱动的后端服务,再到 RAG 管道与 agentic workflow。

从架构上看,AI 应用主要可以归为两大模式,而它们的运维特征完全不同。
交互式聊天应用运行在同步请求路径上,因此最看重的是延迟:它们需要预热好的 LLM、轻量级 orchestrator,以及尽可能少的往返调用。
后端事件驱动服务则是异步运行在微服务网格中,更关注幂等性、缓冲能力和最终一致性,而不是单纯追求最快响应时间。
在这两种核心模式之外,还会有 batch job、持续控制循环、以及工具驱动的自动化流程,它们负责把那些不紧急的工作移出热路径,以换取更好的成本效率。

当 RAG 的 ingesting 流程与 query-time 流程保持一致且可观测时,它的效果最佳。
应当在两个阶段都使用同一个 embedding 模型,选择与内容类型匹配的 chunking 策略,并保存 provenance,以便可靠地做 citation 与 filtering。
向量数据库应运行在 StatefulSet 中,并具备 snapshot / restore 方案;而 ingestion 应作为 Job 或 CronJob 独立运行,避免与面向用户的流量争抢资源。
Reranker 可以在高价值查询中显著提升精度,但它应保持可选,以便按路由在成本与质量之间灵活切换。

Agentic workflow 则是在模型外面加上一层显式控制循环,把工具真正提升为一等公民。
对于高风险动作,human-in-the-loop 审批门不是“以后再补”的附加功能,而应该是体系设计的一部分。
每个步骤都应记录 rationale 和 artifact,使整个过程具备审计性;同时,尽量采用像 MCP 这样的开放标准,以提升 agent 架构的可移植性。

当你真正掌握了这些模式与边界之后,我们就可以进入“怎么做”的层面了。
下一章,我们会把这些高层设计转化为生产实践指南,讨论如何在 Kubernetes 上真正搭建 agentic application,并深入一些更棘手的问题,例如如何保护 MCP 和 A2A 通信的安全。


¹ 截至 2026 年初,越来越多的功能责任正在被交给推理平台本身。例如,vLLM 已开始集成 Responses API,其中包括 tool calling 与 MCP server calling——而这些能力在过去通常是由 Llama Stack 这类专门 orchestration 中间件负责的。

² 文中在适用时,已在括号中给出对应的 Kubernetes Patterns 模式。

³ 已经有若干项目直接支持这种多智能体协作式编码流程。截至 2026 年初,一个比较流行的选择是 Claude-Flow,它以 Claude 作为后端模型,构建了较成熟的多智能体协作体系。