1. 从 HN 吵翻天的那个帖子说起:AX 到底想解决什么问题
前几天技术圈最热闹的事,莫过于 Google 开源了一个叫 AX 的项目,定位是"声明式编排数十亿 agent 的运行时"。帖子在 Hacker News 上挂了一整天,评论区从架构设计吵到工程哲学,从 Kubernetes 类比吵到 agent 到底该不该有"编排层"。我翻完了整栋楼,发现吵得最凶的其实不是技术细节,而是一个更根本的问题:当 agent 的数量从几个变成几十亿个,我们到底该怎么管它们?
这个问题听起来很夸张,但如果你最近半年在折腾 agent 开发,应该能感受到那种隐隐的焦虑。现在大家写 agent,基本还是"一个脚本一个 agent"的路子——用 Python 起个进程,挂个 LLM 调用,加几个 tool,跑起来就完事。几个、几十个 agent 这么搞没问题,可一旦你要做的是"每个用户配一个专属 agent""每个设备跑一个本地 agent""每个数据流触发一个处理 agent",数量级瞬间就上去了。这时候你面对的就不是"怎么写 agent"的问题,而是"怎么调度、怎么编排、怎么保证它们不互相踩脚"的问题。
AX 想干的,就是把这件事从"手工作坊"拉到"工业化生产"。它的核心思路非常 Kubernetes:你用 YAML 声明你想要什么,运行时负责把它变成现实。你不需要关心 agent 跑在哪台机器上、怎么扩容、挂了怎么重启,你只需要描述"我要一个能处理用户退款请求的 agent,它需要访问订单数据库和支付网关",剩下的交给 AX。
这个类比一出来,HN 上立刻分成两派。一派觉得"终于有人把 K8s 那套成熟经验搬到 agent 领域了,这是必然趋势";另一派则质疑"agent 不是无状态容器,它有记忆、有上下文、有不确定的行为,你拿编排容器的方式编排 agent,是不是刻舟求剑?"这场争论其实非常有价值,因为它逼着我们去想清楚:agent 编排和传统服务编排,到底哪里像、哪里不像?
我自己的判断是:AX 的价值不在于它现在有多完善,而在于它把"agent 运行时"这个概念正式推到了台面上。以前大家聊 agent,聊的是 prompt 怎么写、tool 怎么接、memory 怎么存,这些都是"单个 agent 内部"的事。AX 把视角拉高了一层,开始聊"agent 群体"的事——调度、编排、生命周期管理、资源隔离。这个视角的切换,才是它真正值得关注的地方。
接下来的内容,我会从几个角度把 AX 这件事拆开讲:它到底怎么工作的、YAML 声明式编排在 agent 场景下意味着什么、和 Kubernetes 的类比哪些成立哪些不成立、实际落地时会踩哪些坑、以及如果你现在就想动手试,该怎么起步。不管你是刚接触 agent 开发的新手,还是已经在做多 agent 系统的老手,应该都能从中找到对自己有用的东西。
2. 声明式编排的内核:你描述"要什么",运行时负责"怎么做到"
2.1 命令式 vs 声明式:一个生活化的类比
要理解 AX 的设计哲学,得先搞清楚"声明式"和"命令式"的区别。这两个词听起来很学术,但其实生活里到处都是例子。
命令式就像你给厨师写菜谱:"先热锅,倒油,油温七成热下葱姜蒜,爆香后放肉丝,翻炒三分钟,加酱油,再炒两分钟,出锅。"你每一步都要说清楚,厨师照着做。如果中间哪个环节出了问题——比如火太大肉糊了——你得自己想办法补救。
声明式则像你直接说:"我要一盘鱼香肉丝,微辣,不要胡萝卜。"你只描述最终状态,至于厨师怎么切、怎么炒、用哪个锅,那是他的事。如果第一遍做咸了,他会自己调整重做,直到端出来的菜符合你的描述。
AX 走的是后一条路。你写一个 YAML 文件,描述"我要一个 agent,它的职责是处理退款,它需要访问订单服务和支付服务,它最多同时处理 100 个请求",然后 AX 的运行时负责把这个描述变成实际运行的 agent 实例。如果某个实例挂了,运行时自动拉起新的;如果请求量涨了,运行时自动扩容;如果依赖的服务地址变了,运行时自动重新配置。
这种模式最大的好处是解耦。写 YAML 的人不需要知道底层跑在什么机器上、用什么容器运行时、网络怎么配。这些脏活累活全被运行时屏蔽掉了。对于 agent 这种本身就够复杂的东西来说,少操一份心就是多一份生产力。
2.2 AX 的 YAML 长什么样:核心字段拆解
虽然 AX 刚开源不久,文档还在完善中,但从它公开的示例和设计文档来看,一个典型的 AX 配置文件大概包含这几个核心部分:
apiVersion: ax.io/v1 kind: Agent metadata: name: refund-handler namespace: customer-service spec: runtime: python3.11 entrypoint: agent.py replicas: 10 resources: cpu: "500m" memory: "512Mi" dependencies: - name: order-service type: http endpoint: http://order-svc:8080 - name: payment-gateway type: grpc endpoint: payment-svc:9090 scaling: minReplicas: 2 maxReplicas: 100 targetConcurrency: 50 memory: type: vector-store backend: local ttl: 3600这个文件里,每一块都在回答一个问题:
metadata回答"这个 agent 叫什么、属于哪个业务域"runtime和entrypoint回答"用什么跑、从哪开始跑"replicas和scaling回答"要几个实例、什么时候扩缩容"resources回答"给它多少 CPU 和内存"dependencies回答"它依赖哪些外部服务"memory回答"它的记忆存哪、存多久"
你看,这里面没有一行是"怎么启动进程""怎么注册服务发现""怎么配负载均衡"——这些全是运行时的事。你只负责描述"这个 agent 应该是什么样",运行时负责让它变成那样。
2.3 为什么 agent 特别需要声明式编排
有人可能会问:传统微服务早就有声明式编排了(K8s 就是),agent 有什么特别的?
特别之处在于agent 的行为是不确定的。一个普通的 HTTP 服务,你给它同样的输入,它大概率给你同样的输出。但 agent 不一样,它背后是 LLM,同样的输入可能因为温度参数、上下文长度、模型版本的不同而产生完全不同的行为。这种不确定性带来两个后果:
第一,你不能用固定的健康检查来判断 agent 是否正常。一个 HTTP 服务返回 200 就是健康,但一个 agent 返回了结果,你怎麼知道它返回的是对的?它可能一本正经地胡说八道。所以 AX 这类运行时需要更复杂的健康评估机制,比如基于输出质量的评分、基于用户反馈的闭环。
第二,agent 的状态更难管理。传统服务可以设计成无状态的,但 agent 天然有记忆——它记得之前跟用户聊过什么、做过什么决策。这些记忆存在哪、怎么同步、怎么在实例之间迁移,都是传统编排没遇到过的问题。AX 在 YAML 里专门有个memory字段,就是在回应这个需求。
所以 AX 不是简单地把 K8s 那套照搬过来,它是在 K8s 的基础上,针对 agent 的特性做了扩展。这个扩展做得好不好,直接决定了它能不能真正支撑"数十亿 agent"这个量级。
3. 和 Kubernetes 的类比:哪些成立,哪些是刻舟求剑
3.1 成立的部分:调度、扩缩容、服务发现
HN 上吵得最凶的就是这个类比。我的看法是:在基础设施层面,类比完全成立;在行为层面,类比会误导人。
先说成立的部分。AX 从 K8s 继承的最核心能力有三个:
调度。数十亿 agent 不可能都跑在一台机器上,必然要分散到成千上万台机器。哪个 agent 跑在哪、怎么平衡负载、怎么处理故障转移,这些是 K8s 已经解决了十几年的问题。AX 直接复用这套调度逻辑,是最务实的选择。你不需要重新发明轮子,只需要把"容器"换成"agent 实例"。
扩缩容。agent 的负载波动可能比传统服务还大。比如一个电商客服 agent,平时可能只有几十个并发,大促期间瞬间涨到几万。AX 的scaling字段就是干这个的:你设一个目标并发数,运行时根据实际负载自动调整实例数量。这比手动扩容靠谱得多,也比预留大量闲置资源省钱。
服务发现。agent 之间需要互相调用——退款 agent 可能要调用风控 agent,风控 agent 可能要调用用户画像 agent。这些 agent 的地址是动态的,今天在这台机器,明天可能就漂到另一台。AX 自动维护服务注册表,agent 之间通过名字互相访问,不需要硬编码 IP。
这三块是 AX 和 K8s 最像的地方,也是它最稳的地方。因为这些问题的本质是"分布式系统的基础设施问题",跟上面跑的是容器还是 agent 没关系。
3.2 不成立的部分:agent 不是无状态容器
但一到行为层面,类比就开始出问题了。
问题一:agent 有记忆,容器没有。一个容器重启,状态全丢,这是特性不是 bug。但一个 agent 重启,如果记忆丢了,用户会疯掉——"我刚才跟你说的地址你怎么又忘了?"所以 AX 必须解决记忆的持久化和迁移问题。它 YAML 里的memory字段支持vector-store类型,说明它至少考虑到了向量记忆的存储。但记忆的同步、版本管理、跨实例共享,这些远比存储复杂。
问题二:agent 的行为不可预测,容器的是可预测的。你给一个 Nginx 容器发 100 个请求,它处理 100 个请求。你给一个 agent 发 100 个请求,它可能处理 80 个就卡住了,因为第 81 个请求触发了某个它没见过的场景,它在那里反复思考。这种"卡住"不是崩溃,进程还在,端口还通,但就是不干活了。传统的健康检查发现不了这种问题,你需要更智能的监控——比如检测输出延迟、检测 token 消耗速率、检测是否陷入循环。
问题三:agent 的"副本"不是完全等价的。在 K8s 里,10 个 Nginx 副本是完全一样的,请求发给谁都行。但 agent 不一样,如果每个 agent 有自己的记忆和上下文,那副本 A 和副本 B 就是两个不同的"人"。你把用户的请求随机发给 A 或 B,用户体验会非常割裂——A 记得他昨天说过什么,B 不记得。所以 AX 需要更复杂的路由策略,比如基于用户 ID 做一致性哈希,保证同一个用户总是打到同一个 agent 实例。
这三个问题,是 AX 这类 agent 运行时必须面对、但 K8s 没有现成答案的。HN 上那些质疑"刻舟求剑"的人,担心的就是这些。我觉得这个担心是合理的,但结论下得太早——AX 才刚开源,它有没有解决好这些问题,得看后续的迭代。
3.3 一个关键差异:agent 的"启动成本"高得多
还有一个容易被忽略的差异:启动一个容器可能只要几百毫秒,启动一个 agent 可能要几秒甚至几十秒。
为什么?因为 agent 启动时通常要做这些事:加载模型(如果是本地模型)、初始化向量数据库连接、拉取最新的 prompt 模板、预热缓存。这些操作加起来,冷启动时间轻松超过 5 秒。在数十亿 agent 的场景下,如果每次扩缩容都要等这么久,系统的响应速度会非常糟糕。
所以 AX 必须支持"热池"机制——预先启动一批待命的 agent 实例,需要时直接分配,而不是从零启动。这跟 K8s 的"预热 Pod"思路类似,但 agent 的预热更复杂,因为你还得考虑每个 agent 的个性化配置(比如不同用户可能有不同的 prompt 模板)。
这个差异也解释了为什么 AX 强调"声明式"——如果每次都要手动配置 agent 的启动参数,热池根本没法管理。只有声明式地描述"我要 100 个退款 agent 待命",运行时才能自动维护这个池子。
4. 数十亿 agent 的调度难题:AX 的架构猜想与工程现实
4.1 数十亿是什么概念:先算一笔账
"数十亿 agent"这个说法听起来很唬人,但咱们得算算账,看看它到底意味着什么。
假设你有 10 亿个 agent,每个 agent 平均占用 100MB 内存(这已经是很乐观的估计了,LLM 相关的 agent 通常远不止这个数)。那么总内存需求是 10 亿 × 100MB = 100PB。100PB 内存是什么概念?目前全球最大的超级计算机之一,内存总量也就几十 PB 级别。也就是说,你不可能同时运行 10 亿个 agent。
那 AX 说的"编排数十亿 agent"是什么意思?我理解它指的是管理数十亿个 agent 的定义和生命周期,而不是同时运行数十亿个实例。就像 K8s 可以管理数百万个 Deployment 定义,但同一时刻运行的 Pod 数量远小于这个数。大部分 agent 定义是"休眠"的,只有在需要时才被激活。
这个理解很重要,因为它决定了 AX 的架构重点:不是怎么同时跑 10 亿个 agent,而是怎么高效地管理 10 亿个 agent 定义,并在需要时快速激活其中的一小部分。
4.2 冷热分离:AX 最可能的架构选择
基于上面的分析,AX 的架构大概率是"冷热分离"的:
冷层存储所有 agent 的定义(YAML 文件)、配置、记忆快照。这一层可以用对象存储或分布式数据库,成本低、容量大。10 亿个 YAML 文件,每个平均 10KB,总共也就 10TB,完全存得下。
热层运行当前活跃的 agent 实例。这一层的规模取决于实际并发需求,可能只有几万到几百万个实例。热层的资源是昂贵的(CPU、内存、GPU),所以需要精细的调度和快速的扩缩容。
温层是介于两者之间的"待命池"。这些 agent 已经加载了基本配置,但还没绑定具体用户或任务。当请求来时,从温层直接分配,省去冷启动时间。
这个三层架构的关键挑战是状态迁移:当一个 agent 从冷层被激活到热层时,它的记忆、上下文、配置怎么快速加载?当一个 agent 从热层被释放回冷层时,它的状态怎么持久化?这些迁移操作如果太慢,整个系统的响应就会卡顿。
AX 的 YAML 里那个memory字段,很可能就是为这个设计的。type: vector-store说明记忆是以向量形式存储的,backend: local说明可以存在本地,ttl: 3600说明有生存时间。这些参数组合起来,就是在控制记忆的加载和释放策略。
4.3 调度器的核心挑战:不只是找台机器放进去
传统 K8s 调度器的任务是"找一个满足资源需求的节点,把 Pod 放上去"。AX 的调度器要复杂得多,因为它要考虑的维度更多:
维度一:记忆亲和性。如果一个 agent 的记忆存在节点 A 的本地存储上,那下次调度这个 agent 时,最好还调度到节点 A,否则就要跨网络传输记忆,慢且贵。这跟 K8s 的"本地卷亲和性"类似,但 agent 的记忆可能更大、更频繁地变化。
维度二:模型亲和性。如果多个 agent 共用同一个本地模型,那把它们调度到同一台机器上可以共享模型的内存占用。这需要调度器知道哪些 agent 用哪个模型,并做"打包"优化。
维度三:用户亲和性。前面说过,同一个用户的请求最好打到同一个 agent 实例。这要求调度器在做路由时考虑用户 ID,而不是简单地轮询。
维度四:成本亲和性。不同节点的成本不一样(比如 GPU 节点比 CPU 节点贵得多)。调度器需要在满足性能要求的前提下,尽量把 agent 放到便宜的节点上。
这四个维度叠加起来,调度问题就从"二维装箱"变成了"多维装箱",复杂度指数级上升。AX 能不能解决好这个问题,是它能不能支撑"数十亿"这个量级的关键。
4.4 工程现实:先别想数十亿,想想一万个怎么管
虽然 AX 的愿景是数十亿,但我觉得对绝大多数团队来说,更现实的问题是:怎么管好一万个 agent?
一万个 agent 已经足够让手工管理崩溃了。你不可能手动记录每个 agent 跑在哪、什么配置、什么状态。你需要一个系统来自动化这些。AX 提供的声明式接口,至少让你可以用 Git 来管理 agent 定义——每个 agent 一个 YAML 文件,提交到代码仓库,CI/CD 流水线自动部署。这个工作流跟管理 K8s 配置是一样的,成熟且可靠。
所以我的建议是:别被"数十亿"吓到,先从"一百个"开始用 AX 的思路管理你的 agent。当你发现手工管理一百个 agent 已经力不从心时,AX 这类工具的价值就体现出来了。至于数十亿,那是 Google 该操心的事,不是你我该操心的。
5. 落地实操:从零开始用声明式思路管理你的 agent
5.1 环境准备:你不需要真的装 AX
AX 刚开源,文档不全,直接上手可能会踩很多坑。但好消息是,你可以用 K8s 加一些自定义资源来模拟 AX 的核心思路。这样你既能学到声明式编排的精髓,又不用等 AX 成熟。
我的建议是:如果你已经有 K8s 环境,直接用它;如果没有,用kind或minikube起一个本地集群就行。然后你需要装一个能跑 agent 的容器镜像——最简单的做法是写一个 Python 脚本,里面调用 LLM API,把它打包成 Docker 镜像。
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY agent.py . CMD ["python", "agent.py"]agent.py里就是一个简单的循环:从环境变量读取任务,调用 LLM,输出结果。这个 agent 本身很简单,重点不在于它多智能,而在于它怎么被编排。
5.2 用 K8s 自定义资源模拟 AX 的 Agent 定义
K8s 允许你定义自定义资源(CRD)。我们可以定义一个Agent资源,字段跟 AX 的 YAML 类似:
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: agents.example.com spec: group: example.com versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: replicas: type: integer memoryTTL: type: integer dependencies: type: array items: type: string scope: Namespaced names: plural: agents singular: agent kind: Agent然后你就可以像这样定义一个 agent:
apiVersion: example.com/v1 kind: Agent metadata: name: refund-handler spec: replicas: 5 memoryTTL: 3600 dependencies: - order-service - payment-gateway这个 YAML 本身不会做任何事,你需要写一个控制器(Controller)来监听Agent资源的创建和更新,然后相应地创建 Deployment 和 Service。这个控制器就是"运行时"的核心。
5.3 写一个最简控制器:把声明变成现实
控制器用 Python 的kopf框架写起来最方便。核心逻辑就是:监听到Agent资源变化时,创建或更新对应的 Deployment。
import kopf import kubernetes @kopf.on.create('example.com', 'v1', 'agents') def create_agent(spec, name, namespace, **kwargs): apps_v1 = kubernetes.client.AppsV1Api() deployment = kubernetes.client.V1Deployment( metadata=kubernetes.client.V1ObjectMeta(name=f"{name}-deploy"), spec=kubernetes.client.V1DeploymentSpec( replicas=spec.get('replicas', 1), selector={'matchLabels': {'app': name}}, template=kubernetes.client.V1PodTemplateSpec( metadata=kubernetes.client.V1ObjectMeta(labels={'app': name}), spec=kubernetes.client.V1PodSpec( containers=[kubernetes.client.V1Container( name='agent', image='my-agent:latest', env=[ kubernetes.client.V1EnvVar(name='MEMORY_TTL', value=str(spec.get('memoryTTL', 3600))), kubernetes.client.V1EnvVar(name='DEPENDENCIES', value=','.join(spec.get('dependencies', []))) ] )] ) ) ) ) apps_v1.create_namespaced_deployment(namespace=namespace, body=deployment)这段代码虽然简单,但它完整地演示了声明式编排的核心循环:监听声明 → 对比现状 → 执行变更。AX 内部做的事情本质上跟这个一样,只是它处理的问题更复杂、优化的维度更多。
5.4 实测中的坑:我踩过的三个雷
我在用这套思路管理 agent 时,踩过几个坑,分享出来帮你省时间:
坑一:agent 的启动时间远超预期。我一开始设的initialDelaySeconds是 5 秒,结果 agent 因为要加载向量数据库,实际启动要 20 秒。K8s 以为它挂了,反复重启,陷入死循环。后来我把initialDelaySeconds调到 30 秒,并加了startupProbe专门处理启动阶段,才稳定下来。
坑二:记忆的持久化没做好,重启就丢。我一开始把 agent 的记忆存在容器本地文件系统里,结果容器一重启,记忆全没了。后来改成挂载 PersistentVolume,但又有新问题——多个副本同时读写同一个卷会冲突。最后的方案是每个副本用自己的卷,然后用一个后台任务定期把记忆同步到中心存储。
坑三:扩缩容指标选错了。我一开始用 CPU 使用率作为扩缩容指标,结果发现 agent 大部分时间在等 LLM API 返回,CPU 使用率很低,但实际并发已经很高了。后来改成用"正在处理中的请求数"作为指标,扩缩容才准确。
这三个坑的共同点是:传统服务的经验不能直接套用到 agent 上。agent 的启动、状态、负载特征都跟普通服务不一样,你需要重新思考每一个配置项。
6. 争议背后:agent 运行时该由谁定义
6.1 HN 争论的焦点:抽象层级对不对
回到 HN 那场争论,我觉得最有价值的一条评论是:"AX 把抽象层级提得太高了,高到它假设了太多关于 agent 应该怎么工作的前提。"
这句话点到了要害。AX 的 YAML 里,replicas、scaling、dependencies这些字段,都隐含了一个假设:agent 是可以被水平扩展的、无状态的、通过标准接口通信的。但现实中的 agent 可能完全不是这样——它可能是有状态的、不能简单复制的、通过自然语言而不是 HTTP 通信的。
如果 AX 强行把 agent 塞进 K8s 的抽象模型里,那它可能只适合某一类 agent(比如工具调用型的、无状态的),而不适合另一类(比如有长期记忆的、个性化的)。这个局限性,是 HN 上很多人担心的。
但换个角度想,任何抽象都有适用范围。K8s 也不是万能的,它适合无状态微服务,不适合数据库这类有状态服务(所以才有 StatefulSet)。AX 可能也会演化出不同的"agent 类型",对应不同的编排策略。现在下结论说它"抽象错了"或"抽象对了",都太早。
6.2 我的判断:AX 的真正价值在"定义问题"而非"解决问题"
说实话,AX 现在这个版本,离"能编排数十亿 agent"还差得远。它的文档不全、生态没起来、实际案例几乎没有。如果你现在就想用它上生产,大概率会失望。
但我觉得它的价值不在于它现在能做什么,而在于它把"agent 运行时"这个问题正式提出来了。在 AX 之前,大家聊 agent 都是在聊"单个 agent 怎么写得更好";AX 之后,大家开始聊"一群 agent 怎么管得更好"。这个视角的切换,会催生一批新的工具、新的模式、新的最佳实践。
就像当年 Docker 刚出来时,也没人觉得它能改变世界。但它把"应用打包"这个问题定义清楚了,后面的 K8s、Istio、Prometheus 才有机会在此基础上生长。AX 可能也在扮演类似的角色——它不一定笑到最后,但它定义的这个赛道,会有人跑出来。
6.3 给不同阶段团队的建议
最后,给不同阶段的团队一些实在的建议:
如果你还在写第一个 agent,别碰 AX,也别碰 K8s。用一个 Python 脚本,把 agent 跑起来,把业务逻辑跑通,比什么都重要。这个阶段你的核心问题是"agent 能不能用",不是"agent 怎么管"。
如果你已经有几十个 agent 在跑,开始感到管理吃力了,可以看看 AX 的设计思路,但不一定要用它的代码。你可以先用 Git 管理 agent 配置,用 CI/CD 自动部署,用简单的脚本做健康检查。这些"土办法"在几十个 agent 的规模下完全够用。
如果你已经有几百上千个 agent,那 AX 这类工具就值得认真评估了。但评估的重点不是"它能不能跑",而是"它的抽象模型跟你的 agent 匹不匹配"。如果你的 agent 是无状态的、可水平扩展的,那 AX 的思路很适合你;如果你的 agent 是有状态的、个性化的,那你可能需要等它演化出更合适的模式,或者自己基于 K8s 做定制。
如果你在做 agent 平台,那 AX 的源码值得细读。它怎么设计调度器、怎么管理记忆、怎么做扩缩容,这些决策背后的权衡,对你自己做平台很有参考价值。哪怕你最后不用它的代码,它的架构思路也能帮你少走弯路。
我在实际折腾 agent 编排的这段时间,最大的体会是:别被"数十亿"这种数字迷惑,先解决你眼前那一百个 agent 的管理问题。大部分团队一辈子也到不了数十亿的规模,但一百个 agent 的管理痛点,是实实在在的。AX 提供的声明式思路,哪怕你只用它来管理十个 agent,也能让你的工作流清晰很多。至于它能不能真的支撑数十亿,那是 Google 的工程能力问题,不是我们该操心的。我们该操心的,是怎么用它的思路,把自己手头那摊事管得更明白。