1. 云端智能体的基础设施困局:从一个报错说起
上周帮一个朋友排查他们线上智能体集群的问题,现象很典型:单个智能体跑得好好的,一旦并发上到两百以上,整个集群开始出现莫名其妙的超时,日志里夹杂着各种运行时错误,有的任务卡在沙箱里出不来,有的容器直接被OOM Killer干掉,还有几个Pod反复重启把节点资源吃得干干净净。他们用的是Kubernetes做编排,每个智能体跑在独立容器里,看起来架构没问题,但就是扛不住真实流量。
这个场景我太熟悉了。过去一年,我参与过三个不同规模的智能体平台建设,从几十个并发的小型客服系统,到日处理百万级任务的多智能体协同平台,踩过的坑几乎覆盖了云端智能体基础设施的每一个层面。今天想借这个话题,把智能体运行时、沙箱隔离、Kubernetes编排这几个核心环节拆开聊透,说说为什么传统的云原生基础设施在面对智能体负载时会遇到瓶颈,以及我在实际项目中验证过的应对方案。
如果你正在做智能体开发、平台搭建,或者负责AI基础设施的运维,这篇文章应该能帮你少走一些弯路。我会从架构设计讲到具体参数配置,从沙箱选型讲到Kubernetes调度策略,尽量把每个决策背后的逻辑说清楚。
2. 智能体负载到底特殊在哪里
2.1 传统微服务与智能体运行时的本质差异
很多人第一反应是:智能体不就是个服务吗?用Kubernetes部署不就行了?我一开始也这么想,直到被现实反复教育。
传统微服务的负载特征很明确:请求-响应模式,处理时间可预测,资源消耗相对稳定。一个HTTP接口,CPU跑满也就几百毫秒,内存占用基本在启动后就稳定了。但智能体完全不是这个路数。
智能体的执行过程是"思考-行动-观察"的循环。以ReAct模式为例,一个任务可能需要多轮LLM调用,每轮调用之间还要执行工具函数、访问外部API、处理返回结果。这意味着单个智能体实例的生命周期内,资源消耗是剧烈波动的:LLM推理时CPU和内存飙升,等待API返回时几乎不占资源,执行代码沙箱时又需要隔离的计算环境。
我实测过一组数据:一个基于ReAct的智能体处理一个中等复杂度的任务,平均需要4.7轮LLM调用,每轮调用耗时2-8秒不等,峰值内存占用是空闲时的12倍。这种"脉冲式"的资源需求,用传统的Kubernetes HPA(Horizontal Pod Autoscaler)根本来不及响应——等指标触发扩容,任务早就超时了。
更麻烦的是状态问题。传统微服务提倡无状态,但智能体在执行过程中需要维护对话历史、工具调用结果、中间推理步骤。这些状态如果放在内存里,Pod一重启就全丢了;如果放外部存储,每次读写又增加延迟。这个矛盾在单机环境下不明显,一旦上到Kubernetes集群,就变成了架构设计的核心难题。
2.2 沙箱隔离:安全与性能的钢丝绳
智能体执行代码这件事,本质上是在运行不可信代码。用户让智能体"帮我写个脚本处理数据",这个脚本可能是Python、JavaScript,甚至Shell。如果直接在智能体进程里执行,一个死循环就能把整个实例拖垮,一个文件删除操作就可能危及宿主机。
所以沙箱是必须的。但沙箱方案的选择,直接决定了整个基础设施的复杂度和性能上限。
我试过三种主流方案,各有各的坑:
进程级隔离:用subprocess加资源限制(rlimit)跑代码。实现最简单,启动最快,但隔离性最差。我遇到过用户代码里调用fork炸弹,虽然rlimit限制了单进程资源,但子进程数量爆炸直接把节点拖死。后来加了cgroup限制才勉强能用,但安全性依然堪忧。
容器级隔离:每个代码执行请求起一个Docker容器。隔离性好,但启动慢——冷启动一个容器至少200ms起步,如果智能体一轮要执行多个代码片段,这个延迟累积起来很可观。而且频繁创建销毁容器对Docker daemon压力很大,我们最高峰时每秒要起300个容器,Docker daemon直接卡死。
微虚拟机隔离:用Firecracker或gVisor这类轻量级虚拟化技术。启动速度介于进程和容器之间(约50-100ms),隔离性接近虚拟机。但引入这套东西意味着运维复杂度直线上升,而且和Kubernetes的集成需要额外适配。
最后我们的方案是混合策略:短小的代码片段用进程级隔离加严格cgroup限制,复杂或不可信的代码走容器级隔离,对安全性要求极高的场景才上微虚拟机。这个决策不是拍脑袋定的,而是根据实际业务中代码片段的长度分布、执行频率、安全等级要求做的权衡。
2.3 Kubernetes在智能体场景下的水土不服
Kubernetes是为微服务设计的,它的很多默认行为在智能体场景下反而成了障碍。
首先是调度粒度问题。Kubernetes调度的基本单位是Pod,一个Pod里可以跑多个容器。但智能体的资源需求是动态的,同一个Pod里,LLM推理容器可能需要8核CPU,而工具执行容器只需要0.5核。如果按峰值配置,大部分时间资源是浪费的;如果按均值配置,峰值时又不够用。Kubernetes的Request/Limit机制在这里显得很僵硬。
其次是生命周期管理。智能体任务可能运行几毫秒(简单查询),也可能运行几小时(复杂数据分析)。Kubernetes的Pod默认没有执行超时,一个卡死的智能体可能永远占着资源。我们后来强制给所有智能体Pod加了activeDeadlineSeconds,但这就引出了下一个问题:超时后Pod被杀死,任务状态怎么恢复?
还有网络问题。智能体需要访问外部API、数据库、消息队列,Kubernetes的Service和Ingress提供了服务发现和负载均衡,但智能体的出站流量模式很特殊——它可能同时访问几十个不同的外部服务,每个服务的QPS要求还不一样。用NetworkPolicy做细粒度控制很麻烦,不做又担心安全问题。
我现在的做法是:不把Kubernetes当万能药,而是把它当作资源池和基础调度层,在它之上再构建一层智能体专用的调度逻辑。这层逻辑负责决定任务该路由到哪个节点、用什么隔离级别、超时怎么处理、状态怎么持久化。Kubernetes只管把Pod跑起来,剩下的交给上层。
3. 智能体运行时的核心组件拆解
3.1 任务队列与调度器的设计要点
智能体平台的第一道关卡是任务队列。用户提交的任务不能直接扔给执行器,需要经过排队、优先级排序、资源匹配。
我们用的是Redis Stream做任务队列,原因很简单:支持消费者组,天然适合多执行器竞争消费;支持消息确认,任务失败可以重新入队;性能足够,单队列每秒处理万级任务没问题。
但Redis Stream有个坑:消息积压时内存增长很快。我们设置过maxlen做容量限制,结果发现被裁剪的消息直接丢了,没有死信队列。后来改成用Redis的List加BRPOP,配合单独的失败重试队列,才解决了这个问题。
调度器的核心逻辑是资源匹配。每个执行器节点定期上报自己的可用资源(CPU、内存、当前运行任务数),调度器根据任务的需求标签(需要GPU、需要大内存、需要特定工具链)做匹配。这里的关键是不要追求完美匹配,而是保证高优先级任务优先获得资源,低优先级任务可以等待或降级执行。
我设计过一个简单的优先级公式,实际用下来效果不错:
优先级得分 = 用户等级权重 × 0.4 + 任务紧急度 × 0.3 + 等待时长 × 0.2 + 资源匹配度 × 0.1用户等级权重是预设的(免费用户0.3,付费用户0.7,企业用户1.0),任务紧急度由提交时指定,等待时长是动态计算的(每等待1分钟加0.01),资源匹配度是当前节点资源与任务需求的契合程度。这个公式不完美,但比单纯的FIFO或固定优先级灵活得多。
3.2 沙箱运行时的性能优化实践
沙箱的性能瓶颈主要在启动速度和文件系统I/O上。
启动速度方面,我们做过一轮详细的基准测试:
| 隔离方案 | 冷启动耗时 | 热启动耗时 | 内存开销 | 隔离强度 |
|---|---|---|---|---|
| 进程+rlimit | 5ms | 1ms | 极低 | 弱 |
| Docker容器 | 220ms | 80ms | 中等 | 中 |
| gVisor | 150ms | 60ms | 较高 | 强 |
| Firecracker | 90ms | 40ms | 高 | 极强 |
热启动是指复用已初始化的运行时环境。我们发现,对于Python代码执行,预加载常用库(numpy、pandas等)可以把热启动时间再降低30%。具体做法是维护一个运行时池,每个池里的实例已经完成了Python解释器初始化和常用库导入,新任务来了直接从池里取,执行完清理状态后放回。
文件系统方面,沙箱内的代码经常需要读写临时文件。如果用容器默认的overlayfs,大量小文件读写性能很差。我们后来挂载了tmpfs到沙箱的/tmp目录,内存盘的速度比磁盘快两个数量级。但要注意限制tmpfs的大小,否则一个写满/tmp的恶意代码就能把节点内存吃光。
还有一个容易被忽视的点:网络隔离。沙箱内的代码如果需要访问外部网络,必须经过代理,否则可能被用来做内网扫描。我们在沙箱网络命名空间里设置了iptables规则,只允许访问白名单内的地址,其他流量全部DROP。这个白名单是动态的,根据任务类型自动生成。
3.3 状态管理与持久化的取舍
智能体的状态分三类:会话状态(对话历史)、执行状态(当前跑到哪一步)、结果状态(最终输出)。
会话状态我建议放外部存储,比如Redis或PostgreSQL。原因很简单:智能体可能被调度到任何节点,状态必须全局可访问。但要注意序列化开销——一个长对话的token数量可能上万,每次读写都序列化反序列化很浪费。我们的做法是只存增量,完整历史按需重建。
执行状态比较棘手。如果智能体执行到一半Pod挂了,恢复时怎么知道之前跑到哪了?我们的方案是checkpoint机制:每完成一个"思考-行动"循环,就把当前状态写到一个持久化队列里。恢复时从最后一个checkpoint继续。这要求智能体的执行逻辑是幂等的,或者至少工具调用是可重放的。
结果状态最简单,任务完成后写入对象存储,返回URL给用户。但要注意清理策略,否则存储成本会失控。我们设置的生命周期规则是:免费用户结果保留7天,付费用户30天,企业用户可配置。
这里有个经验教训:不要试图把所有状态都塞进数据库。我们早期版本把每个中间步骤都写PostgreSQL,结果数据库连接数不够用,写入延迟成了整个系统的瓶颈。后来改成Redis做热存储、PostgreSQL做冷存储、对象存储做归档,才把压力分散开。
4. 基于Kubernetes的智能体集群实操
4.1 节点池规划与资源隔离
在Kubernetes上跑智能体,第一件事是规划节点池。不要把智能体和普通微服务混在一个节点池里,它们的资源特征差异太大。
我通常分三个节点池:
推理节点池:跑LLM推理服务,需要GPU或高性能CPU,内存要大。这类节点通常用裸金属或高性能云主机,不打其他负载。污点设置为workload=inference:NoSchedule,只有推理Pod能调度上去。
执行节点池:跑智能体主进程和沙箱,CPU密集,内存中等。这类节点可以用普通云主机,但要注意CPU型号尽量统一,否则不同节点的执行性能差异会影响任务调度的公平性。
工具节点池:跑各种工具服务(代码执行器、浏览器自动化、文件处理等),资源需求多样。这类节点可以混部,但要用ResourceQuota限制每个命名空间的资源上限。
节点池之间的网络要打通,但安全策略要隔离。推理节点不应该能直接访问执行节点的沙箱网络,工具节点访问外部API要经过统一的出口网关。
4.2 智能体Pod的配置模板
下面是一个我实际在用的智能体Pod配置模板,去掉了业务相关的部分:
apiVersion: v1 kind: Pod metadata: name: agent-worker labels: app: agent-worker workload: execution spec: runtimeClassName: gvisor # 使用gVisor运行时增强隔离 activeDeadlineSeconds: 3600 # 硬性超时1小时 containers: - name: agent-main image: agent-runtime:v2.3.1 resources: requests: cpu: "500m" memory: "1Gi" limits: cpu: "2" memory: "4Gi" env: - name: SANDBOX_TYPE value: "container" - name: MAX_TOOL_CALLS value: "50" - name: LLM_TIMEOUT_MS value: "30000" volumeMounts: - name: tmpfs mountPath: /tmp - name: state-cache mountPath: /var/agent/state - name: sandbox-sidecar image: sandbox-runtime:v1.8.0 resources: requests: cpu: "200m" memory: "512Mi" limits: cpu: "1" memory: "2Gi" securityContext: privileged: false readOnlyRootFilesystem: true allowPrivilegeEscalation: false volumes: - name: tmpfs emptyDir: medium: Memory sizeLimit: "512Mi" - name: state-cache emptyDir: {} restartPolicy: Never几个关键点解释一下:
runtimeClassName: gvisor让Pod跑在gVisor沙箱里,比普通容器多一层隔离。代价是性能损失约15%,但对智能体这种本身就有网络延迟的负载来说可以接受。
activeDeadlineSeconds: 3600是硬性超时,防止任务卡死。注意这个超时是从Pod创建开始算的,不是从任务开始执行算的。如果Pod调度等待时间长,实际执行时间会被压缩。我们的做法是调度器在创建Pod时根据任务预估时长动态设置这个值。
MAX_TOOL_CALLS限制工具调用次数,防止智能体陷入无限循环。这个值需要根据业务调整,太小会导致复杂任务无法完成,太大又浪费资源。我们统计过,90%的正常任务工具调用次数在20次以内,所以设50次作为上限。
tmpfs挂载到/tmp,给沙箱内的临时文件读写加速。sizeLimit必须设,否则内存会被写爆。
restartPolicy: Never很重要。智能体任务不应该自动重启,失败了就失败了,由上层调度器决定是否重试。如果设成Always,一个崩溃的Pod会无限重启,把节点资源耗尽。
4.3 自动扩缩容的实战参数
Kubernetes原生的HPA对智能体负载响应太慢,我们用的是自定义指标加KEDA(Kubernetes Event-driven Autoscaling)。
核心指标是任务队列长度。当队列中等待任务数超过阈值时触发扩容,低于阈值时缩容。但这里有个坑:扩容太快会导致节点资源不足,新Pod一直Pending;扩容太慢又会导致任务积压。
我们最终用的参数:
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: agent-worker-scaler spec: scaleTargetRef: name: agent-worker-deployment minReplicaCount: 2 maxReplicaCount: 200 cooldownPeriod: 300 pollingInterval: 15 triggers: - type: redis metadata: address: redis-cluster:6379 listName: agent:tasks listLength: "50" # 队列长度超过50开始扩容pollingInterval: 15是每15秒检查一次队列长度。太短会增加Redis压力,太长响应不及时。
cooldownPeriod: 300是缩容冷却时间,5分钟内没有新任务才缩容。这个值设大一点,避免任务波动导致频繁扩缩容。
listLength: "50"是触发扩容的队列长度阈值。这个值需要根据平均任务处理时间和可接受等待时间计算。假设平均任务处理时间30秒,希望新任务等待不超过10秒,那么队列长度阈值 = 并发处理能力 × (等待时间/处理时间)。我们有20个worker时,阈值设为50意味着队列积压到50个任务时开始扩容,扩到100个worker时队列消化速度翻倍。
但KEDA只能控制Deployment的副本数,不能控制节点数量。如果集群节点不够,新Pod会Pending。所以还需要配合Cluster Autoscaler,当有Pod因资源不足无法调度时自动加节点。Cluster Autoscaler的扩容速度取决于云厂商,一般需要1-3分钟,这段时间任务会积压。我们的做法是保持一定的资源缓冲,节点池的日常利用率控制在70%左右,留30%给突发流量。
5. 那些只有踩过才知道的坑
5.1 沙箱逃逸与安全加固
智能体沙箱最怕的是逃逸。我遇到过几次惊险的情况,虽然没造成实际损失,但暴露了隔离方案的薄弱点。
一次是用户提交的Python代码里用了ctypes直接调用系统调用,绕过了Python层面的限制。我们的进程级沙箱只限制了Python层面的资源,对底层系统调用没有拦截。后来加了seccomp过滤器,禁止了ptrace、mount、reboot等高危系统调用。
另一次是容器沙箱里的代码通过/proc文件系统读取了宿主机的信息。虽然容器有独立的PID命名空间,但/proc默认还是能看到一些宿主机信息。解决方案是挂载/proc时加上hidepid=2选项,并且用readOnlyRootFilesystem限制写入。
还有一次是网络层面的。沙箱内的代码尝试连接内网地址,虽然我们设了iptables规则,但规则是在容器启动后才应用的,存在时间窗口。后来改成在CNI插件层面做网络策略,容器网络创建时就应用规则,消除了这个窗口。
安全这件事没有终点。我的建议是:假设沙箱一定会被突破,在沙箱之外再加一层防护。比如沙箱节点不存储敏感数据,沙箱网络只能访问必要的服务,沙箱的文件系统是临时的。这样即使沙箱被突破,损失也可控。
5.2 资源泄漏的排查与预防
智能体平台最常见的故障是资源泄漏。表现是:运行一段时间后,节点内存逐渐被吃满,新任务无法调度,老任务越来越慢。
泄漏的来源通常有三个:
文件描述符泄漏:智能体频繁访问外部API,如果HTTP连接没有正确关闭,fd会持续增长。我们用lsof监控每个进程的fd数量,超过阈值就告警。修复方法是在代码里确保每个HTTP请求都有超时和连接释放。
内存泄漏:Python的循环引用、缓存没有淘汰策略、大对象没有及时释放。我们用tracemalloc定期采样内存分配,找出增长最快的对象类型。有一次发现是对话历史缓存没有设置上限,每个会话的历史都保留在内存里,时间长了就爆了。
僵尸进程:沙箱里执行的代码如果fork了子进程但没有wait,子进程会变成僵尸。虽然僵尸进程不占CPU和内存,但会占用PID。PID耗尽后新进程无法创建。我们在沙箱启动脚本里加了trap 'wait' EXIT,确保退出时回收所有子进程。
预防资源泄漏的最好方法是定期重启。我们给每个执行器节点设置了最大运行时长(比如24小时),到期后优雅退出,由Kubernetes重新调度新Pod。这听起来很粗暴,但实际效果很好——大部分泄漏在24小时内不会造成严重影响,定期重启相当于定期清理。
5.3 超时与重试的策略设计
智能体的超时处理比传统服务复杂得多。一个任务可能包含多个步骤,每个步骤的超时要求不同。
LLM调用超时:一般设30秒。超过30秒还没返回,要么是模型负载太高,要么是网络问题,重试通常能解决。但要注意重试次数,我们设的是最多2次,总耗时不超过90秒。
工具调用超时:取决于工具类型。代码执行设60秒,API调用设10秒,文件处理设120秒。工具调用失败后是否重试要看工具是否幂等。查询类工具可以重试,写入类工具重试可能导致重复写入。
整个任务超时:设1小时。超过1小时的任务强制终止,返回部分结果。这里有个设计决策:是返回失败还是返回部分结果?我们选择返回部分结果,因为智能体任务往往是有中间产出的,用户可能只需要其中一部分。
重试策略我们用的是指数退避:第一次失败后等1秒重试,第二次等2秒,第三次等4秒,最多重试3次。但LLM调用不适用这个策略,因为LLM的失败往往是瞬时的,立即重试反而成功率更高。所以LLM调用用的是固定间隔重试,间隔500毫秒。
还有一个容易被忽视的点:重试时的状态恢复。如果任务执行到第5步失败了,重试时是从第1步重新开始,还是从第5步继续?我们的做法是尽量从失败点继续,但这要求每个步骤都是幂等的。对于不幂等的步骤,只能从头开始,但会跳过已经成功且结果已持久化的步骤。
6. 智能体基础设施的未来演进方向
6.1 从通用Kubernetes到智能体专用编排
Kubernetes的通用性在智能体场景下反而成了负担。我预计未来会出现专门为智能体设计的编排系统,或者Kubernetes之上会出现更厚的智能体编排层。
这个编排层需要具备的能力包括:感知任务语义的调度(不只是资源匹配,还要考虑任务之间的依赖关系)、动态隔离级别调整(根据代码风险等级自动选择沙箱类型)、跨节点的状态迁移(任务可以从一个节点迁移到另一个节点继续执行)、细粒度的成本核算(每个任务消耗了多少资源,用于计费和优化)。
现在已经有一些开源项目在往这个方向走,但成熟度还不够。我的建议是:如果你的智能体平台规模不大(日任务量万级以下),用Kubernetes加自定义调度器就够了;如果规模更大,可以考虑自研编排层,但要做好长期投入的准备。
6.2 沙箱技术的演进趋势
沙箱技术正在往两个极端发展:一端是更轻量的进程级隔离,通过eBPF和seccomp实现细粒度的系统调用控制;另一端是更安全的微虚拟机,通过硬件虚拟化提供接近物理隔离的安全性。
我比较看好的是WebAssembly(WASM)作为沙箱方案。WASM天然具备内存安全、启动快(微秒级)、跨平台的特点。虽然目前WASM的工具链还不够完善,执行Python等语言还需要编译成WASM,性能也有损失,但随着WASM运行时的成熟,这可能是未来智能体沙箱的理想选择。
另一个趋势是硬件辅助的隔离。Intel的SGX、AMD的SEV等技术可以在硬件层面隔离内存,即使操作系统被攻破,沙箱内的数据也不会泄露。目前这些技术的使用成本还比较高,但在对安全性要求极高的场景(如金融、医疗智能体)中,可能会逐渐普及。
6.3 成本优化:从资源浪费到精细运营
智能体平台的成本大头是GPU和内存。我见过太多平台因为资源管理粗放,成本是合理水平的3-5倍。
成本优化的第一步是资源计量。每个任务消耗了多少CPU时间、多少内存、多少GPU时间,必须精确记录。我们用的是cgroup的统计接口,每个Pod的资源使用量精确到秒级。
第二步是资源回收。任务完成后,Pod占用的资源要立即释放。我们遇到过Pod已经执行完但没退出的情况,原因是智能体主进程在等待一个永远不会到来的信号。后来加了看门狗进程,主进程超过预期时间没退出就强制杀掉。
第三步是资源复用。沙箱运行时池、LLM连接池、工具实例池,这些复用机制能显著降低启动开销和资源占用。但要注意池的大小要动态调整,高峰期扩大,低谷期缩小。
第四步是调度优化。把任务调度到资源利用率高的节点上,提高整体利用率。但要注意不要过度追求高利用率,留一定的缓冲应对突发流量。我们的目标是节点平均利用率70%,峰值不超过85%。
成本优化是个持续的过程,没有一劳永逸的方案。我的经验是每个月做一次成本审计,看看哪些环节还有优化空间。往往能发现一些意想不到的浪费,比如某个工具的镜像特别大导致拉取时间长、某个API的调用频率过高可以加缓存、某个节点的配置过高可以降配。
7. 一些实操中的小技巧
关于智能体基础设施,最后分享几个我在实际项目中总结的小技巧,都是那种文档里不会写但很实用的东西。
日志采集要区分类型。智能体的日志分三类:系统日志(Pod事件、调度信息)、执行日志(智能体的思考过程、工具调用记录)、用户日志(用户代码的输出)。这三类日志的采集频率、保留时间、存储介质都不同。混在一起采集会导致存储成本失控,查询效率低下。我们用的是Fluent Bit做采集,按日志类型打不同标签,分别路由到不同的存储后端。
监控指标要加业务维度。CPU、内存、网络这些基础指标当然要监控,但更重要的是业务指标:任务成功率、平均执行时长、工具调用失败率、LLM调用延迟。这些指标能更早地发现系统异常。我们有一次发现任务成功率下降,但基础指标都正常,最后查出来是某个外部API的响应变慢了,导致工具调用超时率上升。
压测要模拟真实负载。用JMeter或Locust做压测时,不要只发简单的请求。智能体的负载特征是:请求大小差异大(有的任务只有几个token,有的上万)、执行时间差异大(有的毫秒级,有的小时级)、资源需求差异大(有的只需要CPU,有的需要GPU)。压测数据要覆盖这些分布,否则压测结果没有参考价值。
灰度发布要控制爆炸半径。智能体平台的更新很频繁,新版本可能有bug。我们的做法是:先在一个节点上部署新版本,把1%的流量导过去,观察24小时。如果没有异常,扩大到10%的节点,再观察24小时。然后50%,最后全量。整个过程大约一周。虽然慢,但能避免大规模故障。
文档要写清楚"为什么"。基础设施的配置参数很多,每个参数都有默认值。但默认值不一定适合你的场景。写文档时不要只写"这个参数设成X",要写"这个参数设成X是因为我们的负载特征是Y,如果负载特征不同,可能需要调整"。这样后来的人才知道什么时候该改,什么时候不该改。
这些技巧看起来琐碎,但每一个都是踩过坑之后总结出来的。智能体基础设施这个领域还在快速演进,今天的最佳实践明天可能就过时了。保持学习,保持实验,保持对生产环境的敬畏,大概就是这个领域从业者的常态。