Anthropic 应用 AI 团队发布「Building always-on agents」架构指南,说明如何在自己的产品里搭建始终在线的 Agent。文章把这类 Agent 拆成收件箱、计时器、记忆、权限和发件箱五类组件,并强调始终在线是一种使用体验,Agent 大部分时间休眠、不消耗 token。
文章把始终在线 Agent 拆成收件箱、计时器、记忆、权限、发件箱五类组件,并说明各自职责边界。
Claude 团队发布了一份可以照着搭的架构说明
收件箱、计时器、记忆、权限、发件箱单独看都不新,很多团队早已用队列加定时任务拼出过类似的东西
它真正有价值的地方,是把每个部件的职责划清楚,并明确一点:始终在线是一种使用体验,并不是让模型一直挂在线上消耗 token https://x.com/i/article/2121421287063179264
Anthropic 发布 7*24 在线 Agent 架构指南:用收件箱、计时器、记忆、权限和发件箱,无人值守也能持续工作
Claude 官网发布「uilding always-on agents」。作者是 Anthropic 应用 AI 团队的 William Hu,文章收录在 Claude Platform 最佳实践指南里。它回答的问题是:怎样在自己的产品里做出始终在线的 Agent。读者是想让现有 Agent 更主动的团队,也包括从零开始搭的团队。
文章对这类 Agent 的定义是既有状态、又会主动做事:它要在多次会话之间管理上下文,也要在没人盯着时自己采取行动。底层仍是和其他 Agent 一样的核心循环,这一点承接的是 Anthropic 之前的「Building effective agents」。真正不同的是外围架构,也就是一组组件,分别控制它什么时候醒来、哪些状态要跨运行保留、结果怎样交给用户。
对照着 SpacexSI 团队 Lee Robinson 在斯坦福 CS146S 的讲座「How do always-on, proactive agents like @Bot work?」看可以更客观的认知。
7*24 始终在线其实是一种使用体验
始终在线不是字面意义上一直开着,是对使用体验的描述。Agent 大部分时间都在休眠,隔一段时间被唤醒干活,休眠期间不消耗 token。
每一段工作叫做一次运行(run):收到一个事件,完成任务,再把发生过的事情存下来。运行发生在会话(session)里,会话就是消息和工具调用的记录。运行结束后 Agent 重新休眠,这个循环会一直重复下去。
为什么现在值得做?Claude 团队把它归功于模型进步。一是判断力更好,更能理解用户意图,也更清楚什么时候该停下来确认。二是智能越来越便宜,长周期任务受益最大。文中举例说,在一项长周期编程基准上,Opus 5.5 每完成一个任务的成本比 Opus 5 低 78%,在典型工作负载上低 40%,两个数字都是 Anthropic 自己报告的。配套的成本优化文档写明,这项基准是 SWE-bench Pro 的一个子集,两代模型的解题比例大致相当,子集成绩也不能和公开榜单直接比较。
事件收件箱:先排队,再唤醒
多数 Agent 只在用户发消息时运行。始终在线的 Agent 还会被 webhook、定时任务和后台作业触发。如果每个来源都直接调用 Agent,两个同时到达的事件可能启动两次互相冲突的运行,进程一崩溃事件也就丢了。
文章的做法是让所有来源先把事件送进收件箱,再由一个 worker 领取处理。最简单的版本由三部分组成:一份持久化的事件列表,通常是一张数据库表;一个负责写入的处理端点,通常是 HTTP 接口;一个从列表读取事件的 worker,通常是后台服务。收件箱要记录每条事件的状态,并用来源那边的唯一标识去重。文章建议先给每个用户各开一个收件箱,需要更高并行度时再拆分。
接续先前工作的事件,比如用户的回复,要回到原来的会话,其他事件则新开会话。worker 把事件交给 Agent 循环,等运行结束再把事件标记为已完成。
文中还给了几条实践建议:
- 处理端点要尽快确认收到,因为有些 webhook 会把慢响应当作失败。提示注入这类更深入的检查,放到运行过程中再做。
- 收件箱是唯一可信的事件来源。worker 被唤醒后先从收件箱拉取最新事件,这样它中途挂掉,新的 worker 也能重试同一条事件。
- 同一个收件箱同一时间只应有一个活跃的 worker,避免重复干活或互相覆盖。
- 运行中途来了新事件,可以推到下一轮处理、并入当前这轮,或者中断后重新开始。
- worker 应把待处理事件打包。比如用户连发几条消息,就合并成一次运行,只回复一次。
计时器:让 Agent 主动跟进
收件箱加上计时器,Agent 才显得主动。计时器大致有两种。一种是跟进(follow-up),也就是 Agent 给自己定的截止时间,比如到时候去看看对方有没有回复。另一种是日程(schedule),也就是按固定节奏重复的任务,比如每日简报。
计时器要单独存放在持久化存储里,通常是另一张表,因为收件箱里只应放已经可以处理的事件。跟进和日程的结构可以差不多,只是日程还要记录重复规则。
计时器自己不会启动运行,而是由调度器定时轮询到期的项目。每轮轮询时,调度器找出已到期的计时器:跟进类的标记为完成,日程类的把到期时间改到下一次,然后往收件箱里加一条事件并唤醒 worker。调度器可以自己写,也可以用现成的持久化执行引擎。
记忆:只存下次查不到的东西
始终在线的 Agent 要经历很多次互不相连的运行,前后的连续性靠记忆来维持。记忆也要有自己的存储。文章提到,Claude Code 的自动记忆(auto memory)和 Messages API 的 memory tool 都是用文件目录实现的。放到始终在线的 Agent 上,这些目录应对应到数据库或对象存储里,这样 worker 停掉了,记忆还在。
一条经验法则是,记忆里只放 Agent 以后查不到的东西。信息已经存在问题跟踪系统这类地方的,只存一个指向它的链接。文章把要保存的内容分成四类:
- 用户对 Agent 行为的指令:单独成文,每次运行开始时加载。用户不一定在场,指令必须在每次运行中都生效。
- 关于用户的笔记:比如角色、偏好和纠正过的地方。可以从一个文件开始,内容多了再拆成小文件,每次先加载索引,需要细节时再读具体文件。
- 已经告诉过用户的内容:保留下来避免重复,可以单独成文,也可以由 worker 载入最近几条。
- 进行中任务的进度:每项任务一个进度文件,写明做完了什么、还剩什么,新的运行照着接着做。
这些都是在运行过程中写入的记忆。文章还提到可以加一层叫 dreaming 的整理过程:回看过去的会话,清理过时的条目,找出只有跨多次运行才能看出来的规律。更详细的讨论在 11 月 6 日的文章「The components of agent memory」里。
每次运行开始前,Agent 都要加载系统提示、工具定义和记忆文件。这笔初始上下文开销每次都有,所以要尽量精简,并用提示缓存(prompt caching)控制成本。新会话只有这笔开销加上触发事件;已有会话还会带上之前运行的消息和工具调用。会话太长时,可以压缩(compaction)较早的消息,也可以新开一个会话。两种办法都会丢细节,所以必须保留的内容要在运行过程中先写进记忆。
权限:无人值守时风险最高
权限是始终在线 Agent 风险最大的部分。一次运行握着用户的访问权限,而它本来就是在没人看着的时候工作。每次调用工具,Agent 都要决定是自己继续、直接拦下,还是等人批准。
用到的组件和其他 Agent 没有区别,但文章特别强调了三种做法。
第一是凭证保管。有用的始终在线 Agent 经常需要密码或访问令牌。推荐的做法是在 Agent 的运行环境之外放一个代理,由代理把凭证注入要发出的请求,Agent 自己发出的请求里不带真实凭证。这和 Agent SDK 安全部署文档里的凭证管理建议一致。
第二是自动模式(auto mode)。始终在线的 Agent 不能每一步都等人点头。Claude Code 的 auto mode 用一个分类器审查风险较高的操作,拦下它认为危险的部分,让模型换一条更稳妥的路走,但最重要的决定仍要人来看。3 月 25 日的文章「How we built Claude Code auto mode」更完整地介绍了这套分类器方案。
第三是延后执行的工具调用。在 Claude Code 里,可以用 hook 先把某次工具调用搁下,等用户补充信息。始终在线的 Agent 同样需要在行动前澄清问题或拿到批准。调用被延后时,当前运行结束,会话停在这次调用的中途并保存下来。请求可以通过 MCP Apps 做成交互卡片展示给用户,用户的回答作为新事件回到收件箱,再启动一次运行,恢复保存的会话。等待期间 worker 照常处理其他事件,Agent 不会被卡住。
发件箱:交出结果,不推送全过程
普通 Agent 会把推理、工具调用和答案一路流式推给用户。始终在线的 Agent 大部分工作在后台完成,用户看到的是结果和少量细节,而不是每一步的过程。
一次运行有三种结局:往发件箱发一条消息;判断没什么需要发;或者用延后执行的工具调用去请求许可或补充信息。多数情况下,有一个直接给用户发消息的工具就够了。只有需要控制发送节奏,或者希望 Agent 能从发送失败中恢复时,才需要正式的发件箱。
发件箱是一张存放待发消息的表,作用和收件箱类似。另有一个分发进程(dispatcher),作用和 worker 类似,负责读取待发消息、决定发哪些、再交给用户。延后调用的请求可以经过发件箱,也可以直接显示在自己的界面里。
发送频率要单独调。发太多会打扰用户,发太少又会漏掉用户该知道的事。文章建议用评测来找平衡点,并认为更好的做法是先让 Agent 少说话,再一点点争取用户更多的注意力。
五类组件怎样连成一个循环
每次运行都从收件箱里的一个事件开始,不管它来自用户消息、webhook 还是计时器。worker 领取事件后,在新会话或已有会话里唤醒 Agent。Agent 先读记忆了解现状,在权限允许的范围内行动,再把新学到的东西写回记忆。运行结束时,它往发件箱写一条消息,由分发进程交给用户。
这些部件连起来就是一个循环:一次运行可以设一个计时器,过一阵再把自己叫醒;用户回了一条消息,回答作为事件回到收件箱。每次运行都很短,但循环可以一直转下去,这就是“始终在线”这种感觉的来源。
一点读后感
Claude 这次发布的是一份可以照着搭的架构说明。收件箱、计时器、记忆、权限、发件箱单独看都不新,很多团队早已用队列加定时任务拼出过类似的东西。它真正有价值的地方,是把每个部件的职责划清楚,并说明了一点:始终在线是一种使用体验,不是让模型一直挂在线上消耗 token。
从做 Agent 的角度看,最该先落地的是收件箱。以它为唯一可信来源、同一时间只放一个活跃 worker、把连发的消息合并成一次运行,这几条决定了系统能不能扛住 webhook 重试和并发事件。运行中途来了新事件,是放到下一轮、并入当前这轮还是中断重来,文章把选择留给了开发者。这个选择会影响产品给人的感觉,需要提前想清楚。
记忆那条经验法则值得写进团队约定:只存以后查不到的东西,能链接到外部系统的就不要再抄一份。用文件目录分别存放指令、索引、已告知内容和任务进度,也正好和 Claude Code、Messages API 的 memory tool 的做法一致。dreaming 在文中只是点到,具体做法要看那篇记忆配套文章。
权限才是上线前真正的门槛。凭证放在 Agent 环境之外、由代理注入,应当一开始就作为默认设计,而不是事后补上。延后执行的工具调用会结束当前运行、让 worker 去处理别的事,既保留了人工审批,又不卡住整个循环,比“整个 Agent 挂起、干等用户点击”实用得多。发件箱则是产品设计问题:先少说话,再慢慢赢得注意力。很多团队恰好反过来,一上线就推送得很频繁。
来源:meng shao · x.com