Skip to content

长期在线 Agent 自动化的工程手记

第二部 · Agent 实践 · 第14章

撰写日期:2026-07-03

结论

让一套 Agent 自动化从"演示能跑"变成"长期在线",中间隔着一层工程纪律。我在维护一套跑在本机的团队自动化(知识库镜像、每日记忆筛选、早晚报)的过程中攒下这份手记。核心体会:

长期在线系统的故障几乎都不是模型问题,而是网络抖动、状态管理、多机冲突和告警设计问题。AI 产品经理如果只设计"AI 做什么",不设计"AI 挂了怎么办",系统活不过一周。

1. 单机部署原则:一套自动化只能有一个运行机

这是我踩过的最隐蔽的坑。换新电脑时用迁移助理整机复制,旧机器上的定时任务和事件监听进程原样存活——于是两台机器同时在跑同一套自动化。后果有两个,一明一暗:

后果表现危害
明:告警轰炸旧机凭证失效,每 5 分钟发一条失败告警打扰,但至少看得见
暗:事件被抢两台机器用同一个应用凭证建事件长连接,事件被对方消费本机的实时同步静默失效了 4 个小时,没有任何报错

暗的那个才致命:系统看起来一切正常,只是"恰好最近没有事件"。约定必须写进文档红线:事件驱动的自动化只能单机部署,其他人只读产物,不装运行时。 换机要有显式的交接检查单(更新代码 → 重新授权 → 验证一轮 → 旧机退役),而不是依赖整机迁移"应该没问题"。

2. 告警设计:告警通道是会被训练废掉的

告警的敌人不是漏报,是用户学会忽略它。三条实践:

原则做法反例的代价
只报失败同步成功静默,git log 即审计每次成功都通知 = 通知等于没通知
空天静默无事可报的日子一个字不发"今日无事"的推送在训练用户划掉推送
带上来源告警文案前缀机器名多机场景下一样的文案完全无法溯源

第三条是这次多机事故里最实用的教训:两台机器发出的失败告警一字不差,排障时只能靠 API 查消息创建时间去反推来源。给告警加一个 [hostname] 前缀是一行改动,省掉的是整晚的排查。

3. 瞬时网络错误:重试要分阶段设计

跑在家用网络上的自动化,SSL 断连、连接重置是常态而非异常。加重试时有一个容易忽略的边界:

失败阶段请求是否已到达服务端重试策略
连接阶段(握手失败)任何方法都可以安全重试
读取阶段(响应中断)是,可能已处理只重试幂等方法

区别在消息类接口上会变成用户可感知的 bug:读取阶段无脑重试 POST,用户就会收到重复推送。在 HTTP 客户端层统一挂一次重试适配器(指数退避),比在每个调用点写 try/retry 干净得多——一处修改,抓取、同步、告警三条链路同时受益。

4. 排障方法:查事实源,不做剧情推理

这次告警风暴的排查,走对的每一步都是"放弃推理,去查事实":

阶段直觉推理(错)事实核查(对)
收到重复告警"大概是网络抖动的延迟送达"调消息 API 查每条告警的真实创建时间
发现每 5 分钟一条"本地日志有失败记录,应该就是它"对账:本地日志只有 4 次发送成功,实际发出 49 条
怀疑另有发送方"可能是某个残留脚本"全盘 grep 告警文案 + 核对 crontab 与消息时刻的秒级对齐
定位到第二台机器"大概是队友装的"本地事件流有 4 小时空洞 + 本机定时任务与告警时刻互斥

通用形式:当日志和现象对不上时,优先怀疑"还有一个你不知道的运行副本",并用发送端的权威记录(服务端 API、消息创建时间)对账,而不是用接收端的观感(客户端展示时间、通知到达顺序)推理。 客户端时间戳、迟到的推送、聚合展示都会说谎,服务端创建时间不会。

5. 运行成本要实测并公开

"跑一套 AI 自动化很贵"是默认印象,实测后往往相反。这套系统的边际成本:

实测
知识库镜像同步0 模型 token(纯 API 搬运 + git)
LLM 筛选每天 ≤ 2 次调用,走订阅套餐,无单独账单
常驻进程2 个 daemon,约 4 MB 内存
API 调用每天约 3k 次轮询级调用,远低于配额

把成本写进公开文档有两个作用:给团队一个"这东西不贵,放心跑"的确定性;也给自己一个基线——当调用量突然偏离基线,本身就是故障信号。

6. 面向读者的仓库文档结构

自动化的仓库文档很容易写成作者视角的流水账。有效的结构是先问"谁会打开这个 README",再决定放什么:

读者想知道对应内容
团队同事这是什么、怎么用、别碰哪里README:影响说明、使用表、红线清单
未来的维护者怎么部署、有哪些坑子目录 README + 运维笔记(已知坑逐条记录,含"已解决"状态)
排障时的自己日志在哪、先看什么排障入口一节,直接给路径

易变的细节(成本数字、坑列表)放运维笔记,稳定的契约(红线、语义约定)放 README——README 的更新频率应该远低于系统本身的迭代频率。

7. 降级路径纪律:可以晚,不能静默发次品

依赖 LLM 生成内容的推送管线,生成一定会失败(限流窗口、登录态、超时)。我踩过的版本是:生成失败就静默降级成"原文开头截取"照发——群里收到一张明显没消化过的次品卡,而日志里只有一个返回码,连失败原因都没留下。两个错要分开治:

错误治法
降级不留痕失败必须把子进程的 stdout/stderr 记进日志——限流和登录态失败的返回码一样,只有原始输出能区分
降级不可逆推送是发出去就收不回的动作。失败的正确动作是留队重试,不是就地降级

宁可晚发五分钟,不发一张次品卡。降级路径一律要"留痕 + 可重试",静默兜底只配出现在有截止时间的最后一层。

8. 三层兜底:不丢、不哑、不烂尾

上一节的留队重试机制后来真的接住了一次两个半小时的认证故障——没发一张次品。但那次暴露了新缺口:卡了两个半小时,是负责人先发现的,不是系统上报的。 "人先于系统发现故障"本身就是故障。完整的防线是三层:

机制兜住什么
不丢失败任务留队,固定周期(如 5 分钟)自动重试一切瞬时故障,自愈无感
不哑卡队超过阈值(如 30 分钟)自动私聊负责人一次,附排障入口和手动放行命令持续故障必须有人知道;告警只发一次防刷屏
不烂尾超过截止(如 6 小时)允许降级兜底发出长期故障不至于把产物永久压在队列里

这套结构下"漏发"在机制上不可能:结局要么是正常发出,要么是迟发且有人早就知道。另一个细节:告警文案里的"处方"必须实测。我第一版告警写的恢复方法是凭推理写的,后来实测无效——排障文案里每一条建议都应该被验证过,否则它在最需要它的时刻误导人。

9. Headless 认证:会话凭证撑不起无人值守

订阅型 CLI 工具的登录态通常存在系统钥匙串里,绑定用户的 GUI 会话。交互使用时 token 过期会自动刷新,你永远无感;但无人值守的定时任务(cron 类调度器跑在非 GUI 上下文)在 token 过期后读不到也刷不了凭证,从此每一轮都报"未登录"。更迷惑的是:此时人工在终端跑一次完全正常——故障只存在于调度器的上下文里。

方案原理局限
换调度器上下文把定时任务从 cron 挪进用户会话级调度器(macOS 的 LaunchAgent 类),钥匙串访问与交互一致仍依赖登录态,账号在别处登出照样失效
长期凭证(推荐)用官方 headless 授权流程(如 claude setup-token)生成一年期 token,600 权限文件存放,运行时注入环境变量到期需人工轮换一次;走订阅额度,无额外账单
计费 API keykey 不过期,彻底无登录态从订阅池切到按量付费,调用量大时是真实账单

配套两条实操教训:headless 抓取交互式 CLI 的输出时,终端 UI 的重绘会吞字符、凭证和提示文字之间可能没有分隔符——提取出的凭证必须先验证可用再销毁现场,顺序反了就要重跑整个授权流程;代码层给凭证文件做兜底(文件不存在时回退默认认证),迁移过程不产生断点。

10. 静默分支审计:每个 continue 都是未来的漏发

推送管线的"漏发"复盘到最后,往往不是异常炸了——异常有 traceback、有告警,反而安全。真正漏掉东西的是那些当初看起来合理的静默跳过分支

静默分支当初的理由实际后果
元数据没找到 → 跳过"可能是脏数据"改名/同步间隙中的条目被永久丢弃
内容文件缺失 → 跳过"没内容播什么"上游镜像故障期间的条目被永久丢弃

第二条的实翻车值得记:两个同名文档在镜像里共享同一个文件路径,删除其中一个节点时文件被一起删掉,另一个活文档就成了"登记表里有、磁盘上没有"——推送管线读到空内容,走静默跳过分支,无声漏播。上游的一个边界 bug,靠下游的一个 continue 变成了用户可见的"怎么没推送"。

三条纪律:

  1. 白名单式静默:队列型管线里只允许语义明确的白名单跳过(如"自己发布的内容不重复播报"),其余一切异常一律留队 + 留痕,交给重试和告警兜底。
  2. 入出对账:入队数和播报数必须能对上,出现差值就说明有静默分支在漏——和第 4 节"查事实源"是同一个思路,对账优于推理。
  3. 走管线自愈,不走手工补发:修复方式是把该条目从状态登记表里摘掉、让同步把它当新条目重新走一遍全流程(重新导出 → 自动入队 → 正常播报)。手工补发绕过管线,修不了根因,还会污染"入出对账"的账本。

11. 从本机到云端:换的是位置,不是单机约束

常有人问这套长期在线的自动化"能不能搬到云端常驻"。能,而且往往更合适——会睡眠、会关机的个人电脑,本就不适合托管一条需要 7×24 保持的事件长连接;一台常开的云主机在这件事上天然更稳。但迁移时真正要守的不是"云比本地强",而是第 1 节那条单机约束在换了位置之后依然成立。

维度个人电脑常开云主机
事件长连接稳定性受睡眠/关机影响,随手合盖就断常开,明显更适合
单连接约束成立同样成立——新旧位置不能并存
无 GUI 环境的认证依赖钥匙串,踩第 9 节的坑无桌面钥匙串,反而绕开该坑,但要先验证长期凭证在目标环境可用

迁移三条纪律:

  1. 硬切换,不能双跑:有状态的单连接服务,迁移必须一刀切——旧位置的进程全部停掉,再在新位置起。新旧并存就是第 1 节的多机抢事件,而且这次是你亲手制造的。
  2. 分清可迁移与环境绑定:代码、凭证文件、状态数据可以带走;绑定局域网或特定硬件的部分(例如只在内网访问的展示屏)留在原地,云端帮不上也不该搬。
  3. 认证方式要能在目标环境复现:迁移前先在新环境跑通一次 headless 认证(见第 9 节),确认长期凭证可用,再做切换——别等切过去才发现"未登录"。

相关

MIT License