编码智能体离开IDE:基于Azure KARS的多运行时基础设施实战
编码智能体真正开始产生价值,是在它离开代码库以后。我们团队最早把AI接进开发流程时,大家的预期很朴素:让它在IDE里帮忙补全代码、生成单测就够了。但真把项目跑起来才发现,那种玩法只是“人机结对编程”,离自动化还差得远。后来我们把编码智能体部署成一套基于Azure KARS的多运行时AI智能体基础设施,让它自己去接收PR事件、克隆仓库、跑测试、分析日志、写回issue评论——这个过程不再依赖任何IDE窗口,Agent本身成了后端服务。这篇文章就把我踩过的坑和搭建思路完整写出来,给正在规划AI工程化落地的同学一个参考。
这套方案适合谁?如果你手里已经有一个或几个编码智能体应用,但发现每次调用都是“无状态的问答”,没法让它主动响应代码库变化;如果你想让AI在PR review、issue分类、文档维护、故障诊断这些场景里持续干活;或者你只是好奇“多运行时AI智能体基础设施”这个概念怎么落地——这篇文章都能给你一个可以直接开跑的起点。我会从为什么要做开始讲,一路讲到底层设计、部署流程、典型场景,再到我在真实环境里遇到的排障记录。
1. 离开代码库之后,编码智能体的“成人礼”
1.1 困在IDE里的Agent,只能算半个工具
把编码智能体当成“对话框里的助手”来用,看起来很方便,但实际上会很快撞到天花板。最典型的表现有三个:第一,它没有事件驱动能力,必须有人手动把代码喂给它,它才知道现在该干什么;第二,它没有独立的运行空间,不能安全地执行Shell命令、装依赖、跑测试、访问内部服务,所以很多真正有价值的事它做不了;第三,它没有持久记忆,每次对话都要重新加载上下文,跨任务协作等于没有。
我团队里有个早期项目,把智能体直接嵌在VS Code扩展里,让它做“一键review当前文件”。刚上的时候看着很炫,用了一周大家发现,它只能针对单个文件发表点格式化意见,稍微涉及跨文件的分析就歇菜。原因不是模型不行,而是它在IDE的进程里被绑死了:没有自己的沙箱、没有代码库的完整克隆、没有运行测试的能力。后来我把同一个模型搬到独立运行时里,它立刻就能完成“拉分支→跑全量测试→定位失败点→生成修复补丁”这个完整链路。差距不在模型,在环境。
所以我的结论是:编码智能体想真正变成生产力,第一件事是“离开代码库”。这里说的离开,不是不再碰代码,而是不能再以IDE插件的形式寄生在开发者的桌面上。它需要被部署成一个常驻的、事件驱动的、拥有独立运行时和权限边界的服务。只有到了这个阶段,它才从工具变成“同事”。
1.2 缺的不是模型,是一套“能干活”的运行时
当编码智能体离开代码库之后,新的问题立刻浮现:它到底跑在哪里?很多人第一反应是“直接调LLM API不就行了”。对,模型推理只解决“思考”,但智能体的工作里至少有80%是“手和脚”:要git clone,要执行pip install,要跑pytest,要调用GitHub API回评论,要连数据库查索引,要抓取内部服务日志。这些操作每一种都是一个不同的Runtime。
我做过一次粗略统计,一个合格的编码智能体在一个标准PR review任务里,会用到至少五类运行资源:
- 代码执行运行时:用来跑测试、运行脚本、静态分析;
- 原生包管理运行时:安装Node/Python依赖,执行npm/uv操作;
- API调用运行时:访问GitHub、GitLab、内部工单系统;
- 数据库访问运行时:执行只读SQL,分析慢查询;
- 浏览器/交互运行时:操作内部系统的Web界面,或者渲染前端页面做视觉回归。
如果这些能力全部揉在一个单体进程里,很快会遇到两个麻烦:一是安全上没法隔离,Agent拿到数据库密码就能碰所有表;二是资源上没法扩缩容,一个跑测试的Python进程和一个做浏览器操作的无头浏览器进程,它们的CPU和内存需求完全不同,塞在一起就是互相拖累。于是“多运行时”就成了必然选择。多运行时不是多部署几个容器这么简单,而是把不同环境、不同工具链、不同权限配置抽象成一套可以按需调度的资源单元。KARS解决的问题,正是这套资源单元从搭建到调度、再到事件触发的全过程。
2. 拆解Azure KARS:把智能体当成正经后端服务
2.1 KARS到底是什么?一个不成熟但足够清晰的定位
KARS这个缩写在Azure生态里还没有一个让我满意的官方中文表述,我自己更愿意把它解读成Kubernetes Agent Runtime Services,也就是“跑在Kubernetes上的智能体运行时服务”。坦白说,KARS现阶段还不是一个像AKS那样文档极其完整的商业产品,更像是一套正在收敛的架构理念和一组基础服务组件。但这反而给了我们这样早早上车的团队一个好处:可以按自己的工程场景把它拆开重组,而不必被一套固定API锁死。
我理解KARS的核心视角,是彻底把智能体当成工作负载(workload)来管理。以前我们看待Agent的方式是“一个大模型API加上一些工具函数”,而KARS的方式是“Agent是若干个Pod/Job,它们有镜像、有资源配额、有调度策略、有生命周期”。这个转换非常重要。因为一旦Agent变成工作负载,Kubernetes那套成熟的能力——弹性伸缩、滚动更新、故障恢复、多集群调度、可观测性——就全部能用到Agent身上。你不再需要自己写一套“AI任务调度系统”,只需要把Agent任务映射成Kubernetes资源。
我在内部还经常把它类比成“把Agent变成一个微服务团队”。每个Agent服务有自己的环境,自己的依赖,自己的权限,自己的并发上限。代码库只是一个事件来源,不是Agent的家。KARS在这个模型里承担的角色,就是事件路由、运行时分配、身份下发和生命周期管理。
2.2 四种运行时形态:不是所有任务都该进容器
说“多运行时”的时候,很多人的第一反应是“说白了不就是多个容器镜像吗”。刚开始我也这么想,后来发现不够。KARS在具体落地时,至少需要区分四种运行时形态:
第一种是工作负载型运行时(Workload Runtime)。它负责真正执行Agent的业务代码,通常是Python、Node.js或者Go的容器,内部会有编码智能体运行时的SDK,比如代码解析、git操作、LLM调用封装。这是最像“普通后端服务”的形态。
第二种是沙箱执行型运行时(Sandbox Runtime)。它用来跑不可信代码或者不可控命令。比如Agent要执行AI自己生成的Shell脚本,或者运行来自外部的临时代码。这种运行时必须有严格网络隔离、磁盘隔离、进程隔离,通常会用Kubernetes的RuntimeClass配合安全容器实现,绝不能和主服务共用一个进程空间。
第三种是工具类运行时(Tool Runtime)。它提供的是外部能力对接,比如无头浏览器、数据库客户端、API网关代理。它们会被编排成“Agent的双手双腿”,供上层Agent通过标准协议调用。无头浏览器这个场景特别容易忽略,但做前端项目自动review的时候就离不开它。
第四种是事件驱动型运行时(Event Runtime)。它不是用来执行任务的,而是用来接收和标准化外部事件的。例如GitHub的webhook、GitLab的system hook、Azure DevOps的服务钩子、内部工单系统的事件流。这些事件进入系统后统一翻译成KARS自己的任务对象,再交给调度器。
这四类运行时在KARS中并不是割裂的,而是通过一个核心编排层串起来。事件运行时负责“听到”,工作负载运行时负责“思考”,沙箱运行时负责“动手”,工具运行时负责“接入”。这个分层,是我觉得KARS最值得借鉴的设计思路。它让每个环节都能独立伸缩和独立加固,而不必在Agent主脑里什么都做。
2.3 三个核心设计:RuntimeClass、AgentRuntime、Sidecar身份
我实际搭建时发现,KARS虽然组件不少,但抽象出三个对象之后,整个系统就清晰了。
第一个对象是RuntimeClass。这是Kubernetes自带的原生能力,用来标记一组Pod使用哪种容器运行时。KARS会在这里注册不同的运行时类型,比如kars-sandbox、kars-python、kars-browser。声明了RuntimeClass之后,Pod在调度时就会自动跑到支持对应RuntimeClass的节点上。它是多运行时这套体系的地基。
第二个对象是AgentRuntime,这是KARS自定义资源里最核心的一个。它描述的是一个智能体运行时的模板,包括基础镜像、资源限额、允许访问的网络白名单、可用工具列表、容器启动参数。你可以把它理解成“岗位说明”:这个岗位用什么样的环境,给多少资源,允许接触哪些系统和信息。AgentRuntime写得好不好,直接决定了Agent跑起来是顺畅还是处处撞墙。
第三个对象是AgentIdentity,配合Sidecar容器工作。智能体最大的风险之一,是密钥泄漏和权限越界。KARS的做法是,每个Agent任务调度的时候,自动注入一个identity sidecar,负责从云上的密钥托管服务读取临时凭证,再通过内存管道共享给主容器,而不是直接把Secret写成环境变量。凭证有短时效,任务结束自动失效。这个设计一开始看觉得重,但用下来就明白,它救了很多次命。后面排障部分我会详细讲。
如果非要用一句话总结这三个对象的关系:RuntimeClass决定“能跑在哪一类节点上”,AgentRuntime决定“跑成一个什么样子的环境”,AgentIdentity决定“它有没有合法身份去访问外部系统”。
3. 从零搭建:在AKS上落一套最小可用的KARS基础设施
3.1 前置准备:集群配置与节点规划
我不会写那种“第一步,创建一个订阅,点击创建”的手把手教程,直接讲怎么用命令把集群拉起来,并且解释每个参数为什么这么配。以下是我在真实项目里用的创建命令:
az group create --name rg-agent-kars --location eastus az aks create \ --resource-group rg-agent-kars \ --name aks-agent-kars \ --node-count 3 \ --node-vm-size Standard_D4s_v3 \ --nodepool-name agentpool \ --enable-cluster-autoscaler \ --min-count 3 \ --max-count 8选Standard_D4s_v3,4核CPU、16GB内存,是为了兼顾“跑静态分析”和“跑测试任务”两种负载。如果只做纯语言模型API调用,2核8GB就够;但编码智能体经常要临时clone仓库、安装依赖、跑编译,2核会非常痛苦。开启cluster autoscaler之后,当Agent并发上来,集群能自动加节点,但这里我建议min-count不要低于3,否则很多系统组件和事件入口会挤在一起,调度失败排查起来很头疼。
拿到集群凭证之后,顺手验证一下节点状态:
az aks get-credentials --resource-group rg-agent-kars --name aks-agent-kars kubectl get nodes接下来要安装KARS的控制平面。这一层包含事件网关、调度器、资源控制器这几个组件。我使用Helm chart安装,命令大概长这样(不同版本chart名称可能会有变化,以实际release为准):
helm repo add kars https://azure.github.io/kars-charts helm repo update helm install kars kars/kars-controller \ --namespace kars-system \ --create-namespace装完之后确认CRD已经注册:
kubectl get crd | grep kars正常情况下会看到类似agentruntimes.runtime.kars.azure.io、runtimegates.runtime.kars.azure.io这样的CRD。如果一条都看不到,先别急着排查网络,多半是Helm chart里CRD目录没写对,或者权限不够。这个问题后面排障表里再展开。
3.2 安装注意事项:命名空间和RBAC一次想清楚
KARS控制平面装进kars-system这个独立命名空间,而不是直接丢到default,这是个我认为必须坚持的做法。原因很实际:Agent任务本身会频繁创建和销毁Pod,如果都堆在default里,日志会混杂得让你怀疑人生。独立命名空间能让“基础设施”和“工作负载”清晰分开,排障时一眼就知道问题出在哪一层。
RBAC也是容易忽视的点。KARS调度Agent时,需要权限去创建Pod、读取ConfigMap、管理Secret、绑定ServiceAccount。我一开始图省事,给控制平面配了cluster-admin,结果安全团队检查时直接亮了红灯。后来我改成一个最小权限的ServiceAccount,只允许它操作agent-runtime这个命名空间里的指定资源。实际运行下来,完全够用,而且整个基础设施的风险面小了很多。
这里有个经验:KARS的安装默认会带一个AgentTask自定义资源,建议你提前规划好AgentTask的命名空间配额,给它设置LimitRange。否则一旦某个Agent任务因为代码死循环产生大量请求,控制平面会尝试创建海量Pod,把整个节点池打满。
3.3 声明第一个AgentRuntime与沙箱资源
控制平面就绪后,第一步就是定义我们的第一个AgentRuntime。我会给一个参考配置。再次强调,以下YAML是我按照KARS设计思路整理的可用示例,不是某个官方版本的逐字段复制,你拿到自己环境里时,字段名要以实际安装的CRD schema为准。
apiVersion: runtime.kars.azure.io/v1alpha1 kind: AgentRuntime metadata: name: python-code-reviewer namespace: agent-runtime spec: baseImage: mcr.microsoft.com/kars/agent-python:3.11-slim runtimeClassName: kars-sandbox resources: requests: cpu: 500m memory: 1Gi limits: cpu: "1.0" memory: 2Gi network: allowDomains: - api.github.com - pypi.org - github.com denyDomains: - "169.254.169.254" # 禁止访问云元数据接口 tools: allowed: - git - python - pip - pytest securityContext: runAsNonRoot: true readOnlyRootFilesystem: true ttlAfterFinished: 300这段配置里几个容易理解错的地方:
- runtimeClassName写成kars-sandbox,意味着这个AgentRuntime里的任务Pod都会使用沙箱运行时。为什么第一次就上沙箱?因为编码智能体要clone外部代码仓库、安装依赖、执行别人提交的代码,这些操作都有潜在风险,一开始就放在隔离环境里,之后才不用补课。
- network.allowDomains里的白名单,不是KARS自己实现的,最后会翻译成Kubernetes的NetworkPolicy。要注意的是,云厂商的NetworkPolicy插件不一定默认启用。我们用的是Azure Network Policy Manager,如果你没开这个能力,光在YAML里写上allowDomains是没有效果的。
- securityContext里的readOnlyRootFilesystem,很多Agent第一次在这个配置下跑挂掉,因为它需要临时目录写缓存。解决办法不是去掉只读,而是给emptyDir卷挂载到/tmp和/var/tmp。这一点直接在下面沙箱Pod模板里体现:
apiVersion: v1 kind: Pod metadata: labels: kars-task: pr-review spec: runtimeClassName: kars-sandbox containers: - name: agent image: mcr.microsoft.com/kars/agent-python:3.11-slim volumeMounts: - name: tmp mountPath: /tmp - name: tmp mountPath: /var/tmp volumes: - name: tmp emptyDir: {}3.4 接入代码库事件:让GitHub的PR变成Agent任务
有了运行时,下一步是让事件能“流进来”。我用的是GitHub App加webhook的方式。GitHub Enterprise或者Azure DevOps也类似,只要最终能发出标准事件即可。
KARS里处理事件入口的组件,叫RuntimeGate。我在这里定义了一个gate,监听pull_request事件:
apiVersion: runtime.kars.azure.io/v1alpha1 kind: RuntimeGate metadata: name: pr-event-gate namespace: agent-runtime spec: eventSources: - type: github appId: 123456 installationId: 789012 filters: - event: pull_request actions: - opened - synchronize targetBranches: - main workloads: - agentRuntimeRef: python-code-reviewer taskTemplate: | command: ["/agent/scripts/review.sh"] env: - name: REPO_URL valueFromEvent: repository.clone_url - name: PR_NUMBER valueFromEvent: pull_request.number这段配置的意思是:只要main分支上的PR被打开或更新,KARS就会自动创建一个AgentTask,找到python-code-reviewer这个AgentRuntime,把仓库地址和PR编号传进去,然后启动一个沙箱Pod去执行review脚本。
第一次配这个gate时我犯了个低级错误:只监听了opened,没监听synchronize。结果作者在review过程中更新代码,Agent完全没反应。这事的教训是,事件过滤器一定要列全生命周期的关键动作。GitHub PR一般至少要监听opened、reopened、synchronize、ready_for_review这几个。
把webhook从GitHub指向KARS系统还有一个关键点:GitHub webhook的URL必须是公网可访问的HTTPS端点。我在AKS前面用Ingress暴露事件网关,然后挂了一个Azure提供的免费证书。这里不要直接用IP裸奔,也不要关掉TLS校验。GitHub现在强制验证TLS证书,关闭校验后请求会自动失败。
3.5 完整调度链路验证
配置完以上所有资源,可以先不接入真实仓库,用一条测试事件验证整条链路。我常用两个办法:
一个是在本地用curl模拟GitHub webhook请求:
curl -X POST https://kars-gate.example.com/github-webhook \ -H "Content-Type: application/json" \ -H "X-GitHub-Event: pull_request" \ -d '{ "action": "opened", "repository": {"clone_url": "https://github.com/demo/repo.git"}, "pull_request": {"number": 1} }'这是让事件快速通过的第一步。如果这一步都通不过,多半是Ingress路径或webhook header校验的问题,先别看调度,先把gate日志调通。
另一个是直接创建一个AgentTask对象,绕过事件源,测试调度能力:
cat <<EOF | kubectl apply -f - apiVersion: runtime.kars.azure.io/v1alpha1 kind: AgentTask metadata: name: manual-review-test namespace: agent-runtime spec: agentRuntimeRef: python-code-reviewer task: command: ["/agent/scripts/review.sh"] env: - name: REPO_URL value: "https://github.com/demo/repo.git" - name: PR_NUMBER value: "1" EOF创建之后观察:
kubectl get agenttasks -n agent-runtime kubectl get pods -n agent-runtime -l kars-task=manual-review-test如果Pod起来之后马上退出了,先看日志:
kubectl logs -n agent-runtime -l kars-task=manual-review-test --tail=200这个验证流程非常关键。我每次新增一个AgentRuntime类型,都会先用手工AgentTask测一遍,再接入真实事件。否则一旦webhook和调度两条链路同时出问题,你会陷入“不知道是没收到事件,还是收到了但是调度失败”的尴尬。
在整个最小链路中,我最推荐的做法是把“事件接入”和“任务执行”彻底解耦,事件网关只负责写入内部队列,调度器独立消费队列。KARS本身也支持这种模式,但不少人图省事直接把事件和调度串起来,最后GitHub一次webhook风暴就让整个集群打喷嚏。解耦之后,事件的突发只会让队列变长,不会把运行时池打崩。
4. 四个离库场景:编码智能体真正开始创造价值
4.1 自动Code Review:别只让它点赞,要让它挑出真问题
代码库接入KARS之后,我们做的第一件事是自动Code Review。这个场景看似普通,但做深了价值很大。Common的玩法是“打开PR,让AI看看有没有bug”,但这种零散玩法没用,因为review首先要依赖完整的代码库上下文,要让Agent自己clone分支、切换base、跑git diff,再把变更文件列表带上,最后再调用LLM。
我的review脚本核心就几步:
- Agent clone PR的head分支,并且fetch目标分支,使用
git diff origin/main...HEAD拿到变更; - 对变更文件做静态分析,比如Python用ruff,TypeScript用eslint;
- 把生成的lint报告和diff一起丢给LLM,要求只关注“逻辑错误、潜在并发、安全权限、异常处理”这些高价值问题;
- 将评论通过GitHub API写到PR对应代码行;
- 如果发现严重问题,再启动一个沙箱任务,跑一遍受影响模块的测试。
这里要特别注意,不要让AI在一个review评论里写几十条意见,那会被开发者忽略。我们在prompt里设定了“只输出最有信心的三个问题”,每条必须带行号和修改建议。实测下来,采用率比“全面审查”模式高非常多。原因也好理解,人看到10条评论会心理上划水,看到3条精准评论才会认真对待。
4.2 Issue分流与修复建议
仓库里大量issue进进出出时,人工分流非常累。让编码智能体来做这件事,核心不是让模型“读一下issue然后猜个标签”,而是要让Agent真正去仓库里捣鼓。
现在的流程是:新issue到达后,KARS触发一个AgentTask,这个Agent会:
- 拉取仓库当前代码;
- 用
git log看最近涉及相关模块的提交记录; - 搜代码库,找issue中提到的报错字符串、函数名、类名是否还存在;
- 如果issue包含堆栈信息,它会尝试在沙箱里最小复现;
- 最后把结论写回issue:属于哪个模块、概率性的原因、建议指派给哪位负责该模块的维护者、是否要打bug标签。
这个流程听起来复杂,但整个任务在一个AgentRuntime里30分钟内能跑完。跑完之后,人类维护者只需要确认结论,而不是从零开始读冒烟。我特别推荐对历史issue多的一线项目做这个场景,它把维护者的“排查启动成本”压到了最低。
4.3 文档保鲜:让AI主动维护自己的“操作手册”
代码文档和API说明容易过期,这是所有项目的通病。编码智能体离开代码库之后,我在KARS里加了一个周期性任务,每天凌晨对repo的README和docs目录做一次“保鲜检查”。
Agent会做的动作包括:扫描所有文档里引用的文件路径,检查是否真实存在;检查文档里的命令示例,在沙箱里执行一遍看会不会报错;对比最近两周代码改动涉及的核心函数,看文档里是否缺失新API说明。一旦发现问题,它不直接改,而是创建一个文档修复PR,把“文档行号、过期原因、修改建议”全部写清楚。
这个场景的价值是细水长流型的。刚接入第一周感觉没什么,跑了一个月以后,新同学做环境搭建踩的坑肉眼可见变少了。因为AI会把README里的过期配置自动揪出来。我后来看这套流程就一个评价:它干的是“没人愿意干、但又必须有人干”的活。
4.4 生产环境问题预演
我们最重的一个场景,是让智能体在生产环境出现故障时进沙箱跑预演(dry-run)。生产环境本身不直接让Agent随意连,而是先通过KARS工具运行时给它授予一个“只读诊断身份”,这个身份只能查询状态、读日志、执行时间受限的只读SQL。
例如有一次线上接口超时,旧流程是人肉去翻日志。现在我们的做法是:告警事件直接触发KARS调度一个诊断Agent,它拿到只读身份后,会自动连到日志服务拉取最近10分钟的trace,分析耗时分布,再查数据库慢查询日志,把候选原因按概率排序输出给值班群。这个过程全程有审计,Agent的每个操作都能trace。
这类场景的收益不能只用“省了多久”衡量,更重要的是降低响应时间。以前大型故障光定位可能要40分钟,现在诊断Agent可以在几分钟内把怀疑范围缩小到几个模块。值班人员直接从“盲人摸象”变成“按图索骥”。
5. 排障实录与配置速查
5.1 RuntimeClass 没生效导致Pod调度失败
我们第一次部署沙箱运行时,遇到非常典型的调度失败:Pod一直Pending,事件里写着cannot find runtime "kars-sandbox"。排查发现,控制平面虽然装了RuntimeClass CRD,但集群里对应的runtime handler根本没有在节点上注册。这就像你写好了“这台机器要跑KVM”,但KVM根本没装,调度器当然拒绝。
解决办法是把RuntimeClass真正安装到AKS集群:
apiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: name: kars-sandbox handler: kata同时确保节点池里的虚机支持嵌套虚拟化。AKS的标准D系列节点默认是可以的,但如果你用低配的B系列或者某些受限制的VM,Kata容器起不来。我这里踩到的坑是:RuntimeClass管理员在本地环境验证没问题,但部署到AKS的一个旧节点池就卡住。后来换了Standard_D4s_v3新节点池,问题直接消失。所以建议第一次玩KARS,别在资源受限的旧节点池上浪费时间。
5.2 任务OOM和重启策略
编码智能体任务很容易内存超限,尤其是当Agent在沙箱里安装Python依赖、加载代码索引的时候。我们遇到过Pod反复OOMKilled,然后Kubernetes默认重启Pod,但它“重启完又重新跑同样的任务”,形成死循环。
后来我们做了三个调整:
第一,给AgentRuntime设置明确的limit,不要无限放量。2Gi内存不够就分析,不会让任务无休止膨胀;
第二,给AgentTask设置restartPolicy: Never,任务失败就进入失败状态,不要自动重启。因为编码智能体任务大多是幂等任务,重试由上层队列控制,而不是靠Pod重启重复消耗资源;
第三,在脚本层做“进度标记”。Agent在跑长任务时,把自己的阶段写到一个持久卷,比如状态从cloning到testing到analyzing。如果Pod中途被杀,下一个重试任务直接读取标记,跳过已完成阶段。这个细节用起来非常香,把任务整体耗时缩短了30%以上。
5.3 密钥与权限隔离
刚开始我把GitHub Token直接放在AgentRuntime的env里,后来发现这是最大的安全隐患。因为Agent可能会执行仓库里被污染的代码,比如安装一个恶意Python包,包里的setup.py完全可以在构建时把环境变量dump出去。好在沙箱网络白名单已经挡住了外传,但我还是惊出一身汗。
之后我彻底改用AgentIdentity Sidecar的方案。主容器里只挂一个临时身份,通过Unix socket访问短期凭证。具体到配置上,AgentRuntime不再包含任何Secret字段,而是声明identityRef。比如:
spec: identityRef: name: github-review-identity type: azure-workload-identity这个identityRef在调度时会被翻译成workload identity,由AKS上的托管身份把临时令牌投射给Pod。任务结束,令牌自动失效。现在即使沙箱被攻破,攻击者能拿到的也只是几分钟内有效的只读令牌,风险可控得多。
还有一个容易被忽略的权限点:数据库只读用户。一定要在数据库端单独创建只读账号给Agent用,而不是复用DBA账号。哪怕Agent业务逻辑写得再小心,权限最小化永远是多运行时基础设施的底线。
5.4 并发风暴:从限量到排队
GitHub webhook有个特点,有时候一分钟会涌进来几十个事件,比如有人分支上同时提交了多个PR的代码,或者有人rebase触发大量synchronize。如果每个事件都立刻创建一个AgentTask,集群会被瞬间打满。
KARS的RuntimeGate支持并发限制。我用的是两个参数配合:
maxConcurrentTasks:控制在同一个RuntimeSlot上同时跑多少任务;- 内部队列:当并发超过限制时,事件先进入队列而不是马上创建Pod。
这套机制的实际效果是,集群资源任何时候都在设定的水位内运行,事件只会晚处理,不会把系统压崩。刚开始我不信队列,总觉得实时性更重要。后来一次PR风暴直接把节点池打扩到max,账单翻了两倍,我才老实配上队列。现在队列还有附带好处:同一个PR的连续更新事件可以自动合并,Agent只处理最后一次提交,避免重复劳动。
5.5 排障速查表
最后整理一张我在实际运维中高频使用的速查表,方便大家直接对照:
| 现象 | 大概率原因 | 快速检查方式 |
|---|---|---|
| Pod一直Pending,报cannot find runtime | RuntimeClass handler没有在节点注册 | kubectl get runtimeclass确认对象存在,去节点池确认是否支持Kata等沙箱运行时 |
| Webhook进来但AgentTask没创建 | RuntimeGate过滤器或TLS校验失败 | 看事件网关日志,检查Gate的action列表是否遗漏synchronize |
| Pod启动后秒退 | readOnlyRootFilesystem导致无法写/tmp | 加emptyDir卷挂载到/tmp和/var/tmp |
| 任务一直OOMKilled | AgentRuntime内存limit过小或脚本无进度标记 | 调大limit,或让脚本先写自身阶段,失败后跳过已完成步骤 |
| 评论没有写到PR上 | 身份令牌权限不足或没有安装对应GitHub App | 检查identity侧边栏日志,确认GitHub App的Permissions里Pull requests write |
| 集群突然扩到max节点 | 事件并发无限制串到Pod | 给RuntimeGate配置maxConcurrentTasks,并启用队列合并 |
这张表未必覆盖所有问题,但覆盖了多数人第一次踩坑的前六个坑。如果你遇到的现象不在表里,建议先抓住一条主线:事件从GitHub进来以后,到底卡在“网关没有收到”“收到后没创建任务”“创建了Pod没起来”“Pod起来了没跑对”这四段中的哪一段。分段排除永远比整条链路猜测高效。
结尾:几句实在话
把这套基于Azure KARS的多运行时AI智能体基础设施跑起来之后,我最大的感受是:真正难的从来不是把模型接进去,而是给模型搭一个可以安全、稳定、弹性干活的环境。代码库只是Agent的起点,不是它的终点;让它离开代码库不是让它不碰代码,而是让它在更广阔的工作流里承担更多。
如果你也在规划类似的东西,我的建议是从最小闭环开始:一个沙箱运行时,一个事件入口,一个自动Code Review场景,跑通之后再慢慢加其他运行时和场景。千万别一上来就设计一套“全知全能的多智能体系统”,那只会让排障变成地狱。先把一个Agent在KARS上跑稳,比什么都重要。
最后再分享一个小细节:给你的每个AgentRuntime命名时,把用途写清楚,比如python-code-reviewer、node-issue-triage、browser-e2e-check,别用agent-1、agent-2这种名字。等集群里有十几个运行时之后,你会发现,名称本身就是最好的文档。