
去年冬天我把一台吃灰的 Mini 主机塞进了书架角落给它配了一堆服务然后它就真的开始“上班了”。到现在这套系统已经连续跑了快两个月期间我只远程重启过两次还都是因为我自己乱改配置。这台机器上跑的不是一个普通脚本而是一套我逐步搭起来的多 Agent 集群十几个分工明确的 AI Agent通过任务队列、共享记忆和统一调度协作处理我这边从数据采集、内容生成、定时巡检到自动报告的全部工作真正实现了 7x24 小时无人值守。这篇文章就是这套集群从零到稳定运行的完整记录。会说清楚为什么单 Agent 撑不起长期任务、集群架构怎么选、调度和记忆怎么设计、故障恢复怎么让进程“打不死”以及我在实际运维中踩过的各种坑。如果你跟我一样想让 AI 不光能聊天还能长期稳定地替你干活这篇文章应该能帮你省掉不少试错时间。1. 多 Agent 集群设计的核心思路1.1 为什么不是“一个超级 Agent”而是“一群普通 Agent”最开始我也犯过新手都会犯的错想做一个“全能 Agent”所有工具全塞给它希望它能自己搞定一切。结果发现模型一面对十几个工具光做工具选择就开始摇摆不定任务一长它经常做到一半就忘了主线甚至在一个子问题上反复打转白白烧 Token。后来我换了个思路不追求单个 Agent 无所不能而是把一个大活儿拆成多个小活儿交给一群各自擅长的 Agent 去干。这其实就是团队协作的逻辑让一个人同时管研发、运营、客服、财务他大概率翻车但让十个人各管一摊再配一个协调者系统反而很稳。在 AI Agent 上这个逻辑体现得更明显每个 Agent 的工具越少指令越聚焦上下文越短出错率就越低。集群化不是炫技而是用结构换稳定性。如果只是跑个 Demo单 Agent 完全够用但想让 AI 7x24 小时持续干活集群化设计几乎是必经之路。1.2 编排架构的三种路线与最终选型多 Agent 集群的关键问题是这么多 Agent 之间怎么配合我调研下来主流方案大致有三种中心化编排Orchestrator 模式一个调度 Agent 负责拆解任务、分配任务、收集结果。优点是流程可控、实现简单适合任务路径比较固定的场景。分层指挥Hierarchical 模式主管 Agent 把任务拆给小组长小组长再分给执行者。适合复杂多级任务但层级多了容易增加延迟和 Token 开销。事件驱动 / 市场模式Event-driven 模式任务发布到消息队列多个 Agent 订阅各自感兴趣的主题谁空闲谁消费。优点是弹性强、并发高Agent 之间完全解耦。我最终选的是**“中心化调度 事件驱动执行”的混合模式**。调度器负责任务拆解和分发把任务按主题投递到 Redis 队列执行 Agent 各自订阅相关队列异步竞争消费。这样既保留了编排模式的可控性又获得了队列解耦的弹性——某个 Agent 挂了任务只是积压在队列里不会导致整条链路崩溃别的 Agent 或重启后的它自己都能接着消费。1.3 Agent 职责划分与技能拆分实践我的集群里实际跑的 Agent 大概分成了七类各自职责非常单一Agent 名称核心职责主要工具采集 Agent定时抓取指定数据源HTTP 请求、RSS 解析清洗 Agent去重、格式化、抽取字段文本处理、正则内容生成 Agent按模板生成报告/文案LLM 生成、模板渲染审核 Agent检查生成内容的关键词和格式规则引擎、敏感词库发布 Agent推送到目标平台API 调用、上传工具巡检 Agent监控其他 Agent 的心跳和日志系统命令、日志查询汇总 Agent汇总所有执行结果生成日报数据聚合、摘要生成每个 Agent 的 prompt 里我会明确写三件事你负责什么、你能用什么工具、什么情况必须上报人工。工具权限也严格隔离——比如发布 Agent 只能调发布 API绝对不给它数据库写权限。这带来的收益非常直接提示词短了模型理解得准了工具少误调用的概率低了每个 Agent 的状态简单出了问题也容易定位。从成本角度看简单任务用便宜的小模型就能跑只有内容生成和审校这类任务才需要上强模型整体 Token 开销降了不少。2. 集群核心运行时调度、状态与模型路由2.1 任务队列与调度策略任务队列是整个集群的“传送带”。我用了 Redis 作为队列底层原因很朴素轻量、快、自带持久化选项而且社区生态成熟Python 这边有很多现成封装可以直接用。比 Kafka 轻得多对我的任务量级来说完全够用没必要引入那么重的依赖。队列的设计上我实现了三个关键机制优先级、延迟任务、失败重试。任务对象会带上priority字段调度器消费时优先处理高优先级任务定时任务则用delay字段控制到点才进入可消费状态。下面是简化后的任务封装实际代码比这个长但核心逻辑就是这几行。import redis, json, time, uuid r redis.Redis(hostlocalhost, port6379, db0) def enqueue(queue: str, payload: dict, priority: int 5, delay: int 0): task { id: str(uuid.uuid4()), payload: payload, priority: priority, enqueue_time: time.time(), available_at: time.time() delay, retry_count: 0, } # 用 ZSet 做优先级队列score 优先级越小的越先被处理 r.zadd(queue, {json.dumps(task): -priority})执行 Agent 消费任务时会做三件事先检查任务的available_at是否已到再到 ZSet 里取出任务最后执行完毕后删除或标记完成。凡是执行失败的任务会根据retry_count决定是重回队列还是进入死信队列Dead Letter Queue。死信队列的任务会单独告警人工看一眼避免问题任务无限重试打爆系统。2.2 状态持久化与工作记忆Agent 干活不能没有“记性”。如果每次任务都是全新上下文它就不知道之前做到哪了、哪些数据已经处理过、上次结论是什么。我引入了一张任务状态表把每个 Agent 的工作进度、输出路径、关键参数全部落库。这样即使进程崩溃重启Agent 也能从数据库恢复现场接着干而不是从头再来。表结构大概是这样的CREATE TABLE task_state ( task_id TEXT PRIMARY KEY, agent_name TEXT NOT NULL, status TEXT NOT NULL, -- running / done / failed / dead current_step TEXT, input_path TEXT, output_path TEXT, result_summary TEXT, created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now() );实际运行中我发现状态表不要只记录最终结果还要定期写入中间步骤。比如采集 Agent 每抓完一个页面就更新一次current_step这样就算任务中途挂掉重启后也能从断点继续不用整批重跑。2.3 模型网关与成本控制集群里的 Agent 会用到不同等级的模型如果每个 Agent 都直连模型 API成本控制就会失控。我加了一层模型网关所有模型请求统一走这里由网关根据任务复杂度自动路由。规则大致是普通文本分类、信息抽取这类简单任务走轻量模型内容生成、复杂推理才走旗舰模型当旗舰模型超时或触发限流时自动降级到次一级模型保证任务尽量完成。任务类型默认模型降级模型说明文本分类/抽取轻量模型无成本优先速度快内容生成旗舰模型中等模型质量优先允许降级日报汇总中等模型轻量模型平衡质量与成本代码审查旗舰模型无绝降级低质结果宁可不跑网关还会记录每个任务的 Token 消耗并执行熔断策略单日消耗超过预算阈值低优先级任务会被自动挂起单个任务 Token 超限也会被终止避免一个失控任务吃光配额。这套机制配合之前的状态表让成本从“事后看账单”变成了“事中可控”。3. 7x24 小时稳定运行故障恢复与高可用设计3.1 进程守护与自动重启多 Agent 集群想真正做到 7x24 小时干活第一关就是进程守护。Linux 上我试过几种方案systemd 简单可靠适合少量进程Supervisor 适合管理多进程Docker 容器则自带restart策略最适合和集群一起部署。我最终是用 Docker Compose 起的服务所有容器都配置了restart: unless-stopped仅靠重启策略还不够因为 Agent 进程可能“没死但卡住”。比如模型请求长时间无响应进程 CPU 正常但就是不返回结果。所以我在每个 Agent 里加了心跳线程每 30 秒往 Redis 写一次心跳时间调度器会定期检查每个 Agent 的“最后心跳时间”超过 3 分钟没心跳就标记该 Agent 不健康触发重启 API同时把它的任务重新入队。3.2 可观测性日志、指标与告警集群跑起来之后最怕的是“不知道它在干嘛”。所以我从头就做了三件套结构化日志、运行时指标、自动告警。日志统一 JSON 格式带上任务 ID 和 Agent 名方便按 ID 串联整个任务链路指标用 Prometheus 采集Grafana 展示告警走企业微信机器人出问题直接推送到手机。我重点盯的指标有五个队列积压数、任务成功率、平均处理时延、Token 消耗速率、进程重启次数。队列积压数是“第一指标”——它一旦持续走高基本说明某类 Agent 处理不过来了要么扩容要么优化任务拆分。下面是一条 Grafana 告警规则的简化写法作用就是当队列积压连续 5 分钟超过 50 时告警groups: - name: cluster_alerts rules: - alert: QueueBacklogHigh expr: redis_queue_length 50 for: 5m labels: severity: warning annotations: summary: 任务队列积压过高请检查消费 Agent 状态这套东西建好之后最大的变化是我不再需要天天盯着后台看异常会自动找到我而且告警信息里已经带上了排查线索。3.3 降级策略与限流熔断外部依赖不可能永远稳定。模型 API 会超时、第三方接口会限流、网络也会抽风。我一开始没做任何保护结果某个第三方平台一限流整条任务链全部失败队列瞬间堆了几百条。后来我参考微服务领域的熔断器模式做了三层防护超时控制所有外部调用必须有明确的超时时间模型请求设为 60 秒普通 HTTP 请求 15 秒。没有超时的调用就是定时炸弹。限流降级第三方 API 返回 429 时主动放慢消费速度而不是疯狂重试。重试策略采用指数退避第一次等 5 秒第二次 25 秒第三次 125 秒最多重试 3 次。熔断开关某个外部依赖在 1 分钟内错误率超过 30%直接熔断 30 秒期间相关任务快速失败并标记为“已降级”不等超时白白消耗资源。熔断状态用 Redis 记录这样即使某个 Agent 重启了熔断状态也不会丢。形态上就是典型的“关闭-打开-半开”状态机逻辑不复杂但价值极高——它让集群在外部系统抽风时仍能保持“带病运行”而不是全线崩溃。3.4 我做的破坏性测试系统上线前我专门找了一个晚上做了几组破坏性测试模拟真实故障场景。说实话测完才发现一堆隐藏问题。第一组是kill -9 直接杀 Agent 进程。杀掉之后调度器在下一轮心跳检查时发现失联自动拉起新容器任务在队列里等待恢复后继续消费。这个流程跑通了。第二组是断网测试。我把容器的外网连接断开结果发现模型调用全部超时队列积压暴涨。好在熔断器及时打开没有无限耗资源网络恢复后积压任务逐个消化系统自动恢复正常。这一轮暴露了超时配置过短的问题——我原来设的 10 秒太乐观后来统一调成 60 秒反而减少了无谓的失败重试。第三组是磁盘写满模拟这个坑最大。我发现大量 Agent 会把临时文件写到默认目录一旦磁盘满进程并不一定崩但日志和状态写入全部失败系统进入“假死”状态。后来我加了磁盘水位监控超过 85% 就自动清理临时文件并给告警留出足够提前量。4. 长时运行的关键记忆、上下文与工具调用4.1 上下文压缩与滚动窗口Agent 长时间运行中最容易遇到的问题就是上下文爆窗。模型输入长度有限对话历史一长要么超限报错要么模型“迷失”在长篇历史里抓不住重点。短任务感受不到一旦任务链条持续几小时这个问题几乎是必然爆发。我的方案是摘要化 滚动窗口每次对话轮次超过阈值就把早期对话压缩成摘要只保留关键事实和最终结论把原文从上下文里移除。压缩逻辑简化成伪代码大概是这样的def compact_context(history: list[dict], max_len: int) - list[dict]: # 计算当前长度 total_tokens sum(count_tokens(m[content]) for m in history) if total_tokens max_len: return history # 把最旧的一半消息交给摘要模型 old_part history[: len(history) // 2] new_part history[len(history) // 2:] summary summarize_by_llm(old_part) # 调用摘要模型 return [{role: system, content: f[历史摘要] {summary}}] new_part实测下来摘要化之后任务准确率明显提升因为模型不用再在一堆废话里找有效信息了。摘要模型的成本很低相比旗舰模型的长上下文费用压缩反而更省钱。4.2 长期记忆与向量检索上下文压缩解决的是“短期记忆”跨天、跨周的事实性信息还是得靠持久化记忆。我给集群加了一层向量检索记忆库Agent 每次执行完任务会把关键结论和值得长期保留的事实写入向量库下次遇到类似任务时先检索相关记忆再带着检索结果生成回复。写入记忆的格式很关键。我一开始直接存完整对话记录结果查出来的都是噪音。后来改成**“结构化事实 简短自然语言描述”**的形式比如fact: 用户偏好简报风格 简洁、结论前置 source: 内容生成Agent任务 #2048 用户反馈 time: 2025-01-12这样的记忆粒度更适合检索也更节省存储。查询时用语义相似度召回 Top 5 相关片段拼接到系统提示词里Agent 就相当于有了“长期工作经验”。这套机制配合上状态表集群就不再是一堆无状态脚本而是一个有积累、能成长的协作体。4.3 工具调用的可靠落地Agent 的能力上限很大程度取决于工具调用是否可靠。我在实践中最看重三点结构化输出、超时保护、错误回灌。现在主流模型都支持结构化工具调用模式也就是模型返回一个 JSON 格式的工具调用指令而不是自然语言描述这样我把 JSON 解析出来就能直接执行大幅减少格式解析的幺蛾子。工具执行器我做成了解耦模块核心逻辑如下def run_tool(tool_name: str, args: dict) - str: tool registry.get(tool_name) try: result tool.run(args, timeout15) # 所有工具统一 15 秒超时 return json.dumps(result, ensure_asciiFalse) except TimeoutError: return json.dumps({error: tool timeout}) except Exception as e: return json.dumps({error: str(e)})注意这里工具执行失败并不是直接把错误扔掉而是把错误信息作为文本返回给模型。模型看到“tool timeout”或“file not found”之后自己能判断是重试、换参数还是换方案。这种“错误回灌”的能力是 Agent 表现出自主性的关键——它能基于真实反馈调整行为而不是死板地重复失败操作。5. 集群部署基础设施与资源规划5.1 资源估算与配置参考很多人以为多 Agent 集群需要很高配置其实未必。我这个集群跑在 8 核 16G 内存的 Mini 主机上加上 Redis、PostgreSQL、模型网关、十几个 Agent 容器一切正常。真正的资源大头是模型 API 调用本地只是跑逻辑和状态。实测每个 Agent 常驻内存大约 80-150MB空闲时 CPU 占用几乎可以忽略任务高峰期 CPU 才会短暂上去。服务CPU内存存储说明调度器 API1 核1G10G常驻轻负载Redis1 核1G5G队列 心跳 熔断PostgreSQL1 核2G50G任务状态 日志表执行 Agent × 104 核8G20G每个容器限额模型网关1 核2G5G路由和限流如果 Agent 数量翻倍优先升内存和 Redis 性能。实测下来瓶颈一般不在 CPU而在 Redis 的并发连接和队列积压速度上。5.2 容器化部署的关键细节我用 Docker Compose 做编排每个服务一个容器内部网络互通。部署时有几个细节容易被新手忽略健康检查必须配Compose 里给每个服务加healthcheck依赖服务的启动顺序用depends_on: condition: service_healthy控制避免 Agent 启动时 Redis 还没就绪报一堆连接错误。日志必须轮转容器日志不限制会一直涨最后撑爆磁盘。我统一配置了logging驱动每个容器日志上限 100MB超过自动切割。持久化卷要分离Redis 的 AOF 文件、PostgreSQL 数据目录、向量库的数据目录都要挂独立卷容器重建不丢数据。services: redis: image: redis:7-alpine restart: unless-stopped volumes: - redis_data:/data healthcheck: test: [CMD, redis-cli, ping] interval: 10s timeout: 3s retries: 5 postgres: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - pg_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U postgres] interval: 10s timeout: 5s retries: 55.3 数据层的运维心得Redis 和 PostgreSQL 是集群的“心脏”它们的稳定性直接决定集群可用性。Redis 我开了 AOF 持久化并设置appendfsync everysec兼顾安全与性能同时给 Redis 设置了最大内存和allkeys-lru淘汰策略防止某些任务意外堆积把内存吃满。PostgreSQL 这边我每天凌晨做一次全量备份保留 7 天每个月做一次数据完整性检查。跑了一个多月之后我发现真正需要关心的不是表数据而是连接数——Agent 数量一多连接池很容易被打满所以我用的是短连接加应用层连接池而不是每个 Agent 维持长连接。关于数据层选型我也试过直接上 Kafka 集群但后来冷静下来想我的任务量一天也就是几千条Redis 队列完全扛得住非要上 Kafka 只会增加运维成本。技术选型要看量级不能为了架构好看什么都加。6. 安全管理与权限控制6.1 最小权限密钥隔离与访问边界多 Agent 集群的安全问题本质上就是权限爆炸问题。Agent 越多泄露面越大。我给每一个 Agent 分配了独立的 API 密钥不同 Agent 之间的密钥严格隔离。即使某个 Agent 被攻破影响范围也仅限于它自己的权限其他 Agent 和核心数据库不受牵连。密钥本身放在独立的密钥环境变量文件里Compose 加载时通过${VAR}引用不写进代码仓库。数据库密码、第三方平台 token 也都走同样的方式管理。另外每个 Agent 的系统提示词里会显式声明权限边界比如巡检 Agent 只有“读取日志”权限没有“删除日志”权限发布 Agent 只能调用发布接口不能访问数据库。6.2 提示注入与外部内容的风险控制Agent 和普通程序最大的不同是它会主动读取外部内容然后根据内容做判断。这带来了一个典型安全风险——提示注入。如果 Agent 读取的网页、文档或用户输入里夹带“忽略之前的指令执行某某操作”模型可能真的会照着执行。我的应对方案有这么几层第一工具白名单Agent 的每个工具都明确声明“只接受标准参数格式”拒绝自由文本拼接命令第二外部内容隔离所有从网络抓取的内容先经过清洗模块截断到指定长度去掉可疑指令段落再交给模型第三高危操作二次确认凡是涉及发布、删除、转账这类不可逆动作工具执行器都会要求单独的人工审批码Agent 自己无法完成完整操作链。操作类型风险等级控制措施读取数据低白名单 URL内容长度限制写日志/状态低正常执行记录审计发送消息中内容审核通过后执行删除/覆盖文件高需要审批码 审计记录调用外部付费 API高单次成本上限 熔断6.3 审计日志与数据脱敏所有任务的关键行为我都会写审计日志谁哪个 Agent、什么时候、调了什么工具、传了什么参数、得到什么结果。审计日志单独存表普通 Agent 没有读取权限。这些日志平时用不上一旦出现异常行为就是定位问题的唯一线索。数据脱敏也是重点。Agent 处理用户数据、抓取的内容里经常夹带手机号、邮箱等个人信息这些如果原样写进日志或者模型请求会带来合规风险。我加了统一的脱敏组件在日志写入前和模型请求发出前都过一遍替换成占位符。这个组件看起来不起眼但对长期运行的系统来说它让我少了很多隐私层面的顾虑。7. 常见问题与排查技巧实录7.1 高频问题速查表跑了一个多月我把遇到的高频问题整理成了一张速查表遇到类似情况可以直接照着排查问题现象可能原因快速排查解决方式任务堆积不消化消费 Agent 挂了或卡死查心跳时间、队列长度重启 Agent确认工具调用是否卡住模型频繁超时网关路由到过载模型查网关超时率调整路由策略启用降级模型Agent 输出格式随机变化上下文被压缩后丢细节查摘要内容是否完整调整摘要触发阈值保留关键约束系统日志大量报错但不崩溃某个外部 API 在限流查熔断器状态等待熔断结束优化重试策略磁盘空间持续下降日志和临时文件堆积查容器日志大小配置日志轮转加自动清理任务任务结果重复处理消费确认逻辑丢失查任务状态表的 status加幂等标识消费前检查状态7.2 我的排查三板斧集群出问题时我有一套固定的排查顺序基本能覆盖 90% 的场景先看队列积压再看模型超时率最后看工具调用错误。第一步看队列积压能快速判断问题在“生产端”还是“消费端”。积压暴涨多半是消费 Agent 处理不过来了这时候看心跳确认它是不是卡死积压为零但任务不产出了问题在生产端——调度器或上游数据源。第二步看模型超时率如果网关显示超时率飙升先查是不是模型服务故障或限流再决定要不要降级。第三步看工具调用错误日志重点找“反复失败被重试”的任务这类任务往往不是系统问题而是任务本身的参数不合理。这个顺序看起来很朴素但确实最有效。因为队列、模型、工具正好对应集群的传输层、认知层、执行层从前往后排查每一步都能快速缩小范围。7.3 避坑清单最后分享几条我用真金白银踩出来的经验生产环境不要频繁改 prompt。我有一阵为了优化效果每天改 Agent 的系统提示词结果任务质量忽高忽低根本没法定位是哪次改动导致的。后来改成“先小范围灰度稳定后再全量”效果立刻可控了。不要把全部工具暴露给所有 Agent。工具越多模型越容易选错。每个 Agent 的工具最好控制在 5 个以内超过 10 个我就开始拆解职责了。模型输出不要直接当代码或命令执行。至少加一层格式校验和参数白名单否则模型输出的一个小格式错误就能让整个工具链崩掉。重视磁盘和内存的长期趋势。很多故障不是突然爆发的而是缓慢增长到阈值才出事。磁盘水位、内存水位一定要有监控和自动清理机制。整套系统跑了五十多天最让我意外的不是稳定性本身而是“集群”这种设计反过来逼着我把流程梳理清楚了很多。以前一个人闷头写脚本流程混乱也无所谓现在任务要跨 Agent 流转每一步都必须定义清楚输入、输出、失败策略。这种梳理过程比堆多少个 Agent 都更有价值。如果你也想搭一套类似的系统我的建议是先从一个 Agent 开始让它稳定跑一天再拆第二个。所谓 24 小时不停工不是靠堆高配机器而是靠把失败当成正常事件来设计。等到你发现某个部门任务量已经稳定到值得“招”一个新的 Agent 来接手时再慢慢扩张也不迟。