资讯动态

多Agent集群实战:Codex与SSH自动化运维

发布时间:2026/10/26 8:08:12 来源:尧图企业网站定制
1. 从一个人写代码到一群Agent替我干活的转变去年有段时间我特别焦虑。手头同时压着三个项目一个需要持续跑数据清洗脚本一个要盯着接口联调结果还有一个得定期检查服务器上的日志异常。每天睁眼就是打开终端、连服务器、跑命令、看输出、改代码循环往复。最要命的是很多活儿其实是重复性的——比如每天凌晨检查一次数据管道的健康状态出问题就重启对应服务没出问题就记录一条日志。这种活儿人干久了会麻木但机器干刚刚好。于是我开始琢磨一件事能不能让多个Agent各管一摊7×24小时不间断地替我盯着这些事不是那种我发一条指令它回一条的被动模式而是真正意义上的多Agent集群——每个Agent有自己的职责边界、有自己的工具权限、能互相通信、能自主决策遇到搞不定的再上报给我。这个想法听起来有点大但拆开来看其实就三块Agent怎么定义职责、Agent之间怎么通信、Agent怎么安全地操作远程环境。前两块是架构问题第三块是工程问题——而第三块恰恰是最容易翻车的地方。你让一个Agent去连远程服务器跑命令SSH密钥怎么管权限怎么隔离连不上怎么办命令卡住了怎么超时这些问题不解决集群就是空中楼阁。这篇文章我会把这套东西的完整搭建过程拆开讲。包括我为什么选Codex作为Agent的核心执行引擎、SSH在整个架构里扮演什么角色、多Agent之间怎么协调、以及我在实际跑的过程中踩过的那些坑。如果你也在琢磨怎么把重复性开发运维工作自动化或者单纯对多Agent协作感兴趣这篇应该能给你一些可以直接抄的作业。2. 为什么是Codex加SSH这套组合拳2.1 Codex作为Agent执行引擎的定位先说清楚Codex在我这套架构里干什么。很多人第一次接触Codex会把它当成一个AI编程助手就是你在编辑器里写代码它给你补全那种。但在我的用法里Codex的角色更接近一个可编程的命令执行与推理引擎——我给它一个任务描述它自己决定跑什么命令、怎么解析输出、下一步做什么。这个定位很关键。因为多Agent集群的核心需求不是生成代码而是根据当前状态做出决策并执行。比如数据管道挂了Agent需要先跑systemctl status看服务状态再跑tail -n 100看日志尾部根据日志里的错误类型决定是重启服务还是清理临时文件还是上报。这一连串动作里Codex负责的是看日志判断错误类型这个推理环节而SSH负责的是把命令送到远程机器上执行这个传输环节。我试过几种方案。纯脚本加cron的方式太死板错误类型一多就写不下去了。纯LLM API调用的方式又太飘它没法直接操作远程环境。Codex加SSH的组合刚好卡在中间Codex提供推理能力SSH提供执行通道两者通过一个薄薄的调度层粘在一起。2.2 SSH不只是连服务器那么简单大部分人用SSH就是ssh userhost然后敲命令。但在Agent集群场景下SSH承担的责任要重得多身份认证每个Agent用独立的密钥对方便审计和权限回收通道复用多个Agent连同一台机器时通过ControlMaster复用连接避免频繁握手命令隔离不同Agent能执行的命令范围不同通过受限shell或包装脚本控制超时与重试Agent跑的命令可能卡住需要SSH层面设置超时批量操作一个Agent可能需要同时操作多台机器SSH的批量登录能力就派上用场了我一开始图省事所有Agent共用一个SSH密钥。结果有一次一个Agent误操作把测试环境的配置改了我查了半天才定位到是哪个Agent干的——因为日志里所有操作都来自同一个用户。后来改成每个Agent独立密钥并且在远程机器的authorized_keys里给每个密钥加command限制问题才解决。2.3 多Agent集群的职责划分逻辑我的集群目前有四个常驻Agent职责划分如下Agent名称职责操作频率权限范围Watcher监控服务健康状态每5分钟只读命令 重启服务Cleaner清理临时文件和过期日志每小时文件删除限定目录Deployer拉取代码并部署按需触发代码目录读写 服务重启Reporter汇总状态并生成报告每天一次只读命令这个划分不是拍脑袋定的。核心原则是最小权限加职责单一。Watcher只需要看状态和重启就不给它文件删除权限。Cleaner只需要删文件就不给它服务重启权限。这样即使某个Agent的推理出了问题它能造成的破坏也是有限的。另外还有一个协调者角色不直接操作远程环境只负责接收其他Agent的上报、决定是否触发Deployer、以及在我需要的时候把汇总信息推给我。这个协调者跑在本地通过一个简单的消息队列和其他Agent通信。3. 搭建过程中的关键步骤与配置细节3.1 SSH密钥体系的设计与落地第一步是把SSH密钥体系搭起来。我的做法是在本地生成四对密钥每对对应一个Agentssh-keygen -t ed25519 -f ~/.ssh/agent_watcher -C agent-watcher ssh-keygen -t ed25519 -f ~/.ssh/agent_cleaner -C agent-cleaner ssh-keygen -t ed25519 -f ~/.ssh/agent_deployer -C agent-deployer ssh-keygen -t ed25519 -f ~/.ssh/agent_reporter -C agent-reporter选ed25519而不是RSA原因是密钥更短、签名更快而且现代OpenSSH版本默认支持。生成之后把公钥分发到目标服务器的authorized_keys里。这里有个细节不要直接把公钥追加到authorized_keys就完事。我踩过的坑是如果多个Agent的公钥都追加进去远程机器上所有Agent看起来都是同一个用户审计日志分不清谁是谁。正确做法是在公钥前面加限制选项command/usr/local/bin/agent-wrapper.sh watcher,no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... agent-watcher这个command选项的意思是不管这个密钥连上来之后想跑什么命令实际执行的都是agent-wrapper.sh watcher。包装脚本里再根据传入的参数决定允许执行哪些命令。这样即使Agent的密钥泄露了攻击者也只能跑包装脚本里允许的命令。3.2 本地SSH配置的优化Agent频繁连远程机器如果每次都完整握手会很慢。我在~/.ssh/config里给每个目标机器配了连接复用Host prod-server HostName 10.11.225.193 User deploy IdentityFile ~/.ssh/agent_deployer ControlMaster auto ControlPath ~/.ssh/sockets/%r%h-%p ControlPersist 600 ServerAliveInterval 30 ServerAliveCountMax 3ControlMaster auto加ControlPersist 600的意思是第一个连接建立后后续600秒内的连接复用这个通道不再重新握手。实测下来Agent连续跑十条命令的时间从原来的8秒左右降到了1秒出头。ServerAliveInterval 30和ServerAliveCountMax 3是保活配置。Agent跑长任务时如果网络抖动没有这两个配置连接会断任务就丢了。加上之后30秒没数据就发一个保活包连续3次没响应才断开。3.3 Codex的安装与基础配置Codex的安装方式取决于你用哪个版本。我用的CLI版本安装过程不复杂但有几个配置项必须提前设好否则Agent跑起来会各种报错。安装完成后配置文件通常在~/.codex/config或者项目目录下的.codex文件里。我的配置核心是这几项[model] provider openai model codex [execution] timeout 120 max_retries 3 working_dir /home/agent/workspace [logging] level info path /var/log/agent/codex.logtimeout设120秒是因为有些命令比如日志分析确实需要跑一会儿设太短会误杀。max_retries设3是因为网络抖动导致的失败重试三次基本能覆盖。working_dir必须设成绝对路径否则Agent在不同上下文里跑的时候工作目录会乱。如果你遇到Codex登录不上的问题先检查网络连通性和API端点配置。国内使用的话需要确认你的网络环境能正常访问对应的服务端点。另外Codex CLI在Windows和macOS上的配置路径不同Windows一般在%USERPROFILE%\.codex\下面。3.4 Agent调度层的实现思路调度层我用Python写了一个轻量级的框架核心是一个事件循环加一个任务队列。每个Agent注册自己的触发条件定时触发或事件触发调度器根据条件把任务推给对应的Agent执行。class AgentScheduler: def __init__(self): self.agents {} self.task_queue queue.Queue() def register(self, name, agent, trigger): self.agents[name] { instance: agent, trigger: trigger } def run(self): while True: for name, cfg in self.agents.items(): if cfg[trigger].should_fire(): self.task_queue.put(name) # 从队列取任务执行 ...这个框架很粗糙但够用。关键点是每个Agent的执行是独立的一个Agent卡住了不影响其他Agent。我一开始用单线程顺序执行结果Cleaner在删大文件的时候卡了半分钟Watcher的监控就延迟了。改成多线程之后问题解决。4. 多Agent协作中那些文档不会告诉你的坑4.1 SSH连接数爆炸与文件描述符耗尽集群跑起来第三天Watcher突然报错说连不上服务器。我上机器一看ss -s显示大量TIME_WAIT状态的连接。原因是每个Agent每次执行任务都新建SSH连接虽然配了ControlMaster但有些Agent的代码里没走配置文件直接用了paramiko库新建连接。paramiko默认不复用连接每个SSHClient实例都是独立连接。Agent跑一轮下来建了几十个连接系统文件描述符很快就不够用了。解决方案有两个层面一是所有Agent统一走系统SSH命令加ControlMaster不用paramiko直接连二是如果非要用paramiko就维护一个连接池复用SSHClient实例。我最后选了第一种方案因为系统SSH的稳定性和可调试性都更好。注意如果你的Agent需要频繁连大量不同的机器ControlMaster的socket文件会堆积。记得定期清理~/.ssh/sockets/目录下的过期socket文件。4.2 命令超时与僵尸进程Agent跑的命令如果卡住比如tail -f忘了加-n限制或者某个服务重启卡在等待状态SSH连接会一直挂着。我遇到过最离谱的一次是Deployer跑了一个git clone仓库特别大跑了二十分钟还没完把整个调度队列堵死了。后来我在SSH命令层面加了超时timeout 60 ssh prod-server systemctl restart my-servicetimeout命令会在60秒后强制杀掉SSH客户端。但这里有个坑SSH客户端被杀了远程机器上的命令不一定被杀。远程命令可能还在跑变成僵尸进程。更稳妥的做法是在远程命令里也加超时或者用ssh -o ConnectTimeout10控制连接超时用远程的timeout控制命令超时。4.3 Agent之间的消息传递与状态同步四个Agent各跑各的但有些决策需要协调。比如Cleaner发现磁盘快满了需要通知Deployer暂停部署等清理完再继续。我一开始用文件锁做同步结果Cleaner持有锁的时候崩了锁没释放Deployer一直等。后来改成用一个简单的消息队列Redis的List结构Agent之间通过push和pop消息通信。Cleaner清理完push一条cleanup_doneDeployer收到后才继续。Redis挂了怎么办加一个本地文件作为降级方案Redis不可用时写本地文件恢复后再同步。这个方案不完美但比文件锁可靠。核心教训是Agent之间的同步不要用阻塞式原语要用消息传递因为Agent可能随时崩溃阻塞式原语一旦没释放就会导致整个集群卡死。4.4 Codex推理结果的不可预测性Codex再聪明也是概率模型同样的输入可能给出不同的输出。我遇到过Watcher看到日志里有error字样就决定重启服务但实际上那个error是历史日志里的当前服务是正常的。结果服务被反复重启反而搞出了问题。解决办法是给Agent的决策加确认机制。Watcher判断需要重启时不直接重启而是先跑一次健康检查确认服务确实不健康才重启。这个确认逻辑写在Agent的代码里不依赖Codex的判断。另外Codex的输出格式要严格约束。我在prompt里明确要求它输出JSON格式的决策结果比如{action: restart, reason: service_unhealthy, confidence: 0.9}然后在代码里解析这个JSON。如果解析失败或者confidence低于阈值就不执行动作直接上报给我。5. 集群稳定运行后的日常维护与调优5.1 日志体系与问题追溯集群跑起来之后最大的挑战不是让它跑而是出了问题能快速定位。我搭了一套简单的日志体系每个Agent的操作日志写到独立文件同时汇总一份到中央日志。日志格式统一成JSON方便检索。{ timestamp: 2025-01-15T03:22:11Z, agent: watcher, action: check_health, target: prod-server, result: unhealthy, decision: restart, duration_ms: 1240 }有了这个日志出问题的时候直接grep agent名字加时间范围几分钟就能定位。之前没有结构化日志的时候我翻了半小时才找到是哪个Agent在什么时候干了什么。5.2 资源占用的监控与限制四个Agent加一个调度器常驻内存占用大概在300MB左右CPU平时几乎不占。但Codex推理的时候会吃CPU和内存特别是同时有多个Agent触发推理的时候。我给每个Agent加了资源限制用systemd的MemoryMax和CPUQuota控制。Watcher和Reporter限制得紧一些因为它们的任务轻。Deployer和Cleaner放宽一些因为它们可能要处理大文件。另外Codex的推理调用我加了并发限制最多同时跑两个推理请求。超过的排队等待。这样避免了推理请求把机器资源吃满导致其他Agent饿死。5.3 定期演练与故障注入集群最怕的是平时看着好好的一出事全崩。我每个月会做一次故障演练手动杀掉一个Agent看其他Agent能不能正常接管手动断开SSH连接看Agent能不能重连手动往日志里写错误信息看Watcher会不会误判。有一次演练发现Watcher被杀掉后调度器没有重新拉起它因为我的调度器只负责触发任务不负责监控Agent存活。后来加了一个心跳机制每个Agent定期往Redis写心跳调度器发现心跳超时就重新拉起Agent。5.4 安全边界的持续收紧Agent集群跑久了权限容易越给越大。我每季度会review一次每个Agent的SSH密钥权限和远程命令白名单把不再需要的权限去掉。比如Deployer一开始有删除旧版本代码的权限后来发现这个操作可以手动做就从白名单里去掉了。另外所有Agent的操作都记审计日志包括执行的命令、时间、结果。这个日志单独存一份Agent自己不能删。这样即使Agent被恶意操控也能追溯到它干了什么。6. 关于这套集群我目前的一些真实体会这套东西跑了大半年最大的感受是多Agent集群的价值不在于智能而在于持续。单个Agent的决策能力其实有限Codex再强也有判断失误的时候。但四个Agent各管一摊、互相补位、7×24小时不间断地跑整体上确实把我从重复劳动里解放出来了。我现在每天只需要花十分钟看一下Reporter生成的汇总报告确认没有异常就行。另一个体会是SSH这个看似古老的技术在Agent集群场景下依然是不可替代的。它简单、可靠、到处都有、权限模型清晰。我试过用HTTP API替代SSH做远程执行结果发现要自己处理认证、加密、超时、重试工作量比直接用SSH大得多。SSH把这些都标准化了你只需要管好密钥和配置就行。最后说一个我最近在琢磨的扩展方向把Agent的决策逻辑从规则加Codex推理逐步过渡到Codex推理为主规则做兜底。目前规则占的比重还比较大因为规则可靠、可预测。但随着Codex对日志和状态的理解越来越准我打算让它承担更多判断工作规则只保留最核心的安全边界。这个过渡急不得得一步步来每放开一点就观察一段时间确认稳定了再继续。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑