一句话结论:一个"随时在线"的聊天 Agent,真正难的地方不是模型,而是把一次对话做成一个可打断、可崩溃、可恢复、还不烧钱的分布式系统。你发给 Grok Bot 的每条消息,先落本地并带一个唯一 ID(连接抖动重发不会变成两条),进该会话的队列;主循环是Temporal 上的持久化工作流,每次模型调用、每次工具调用都是一个已落库的步骤——所以你连发一百条把它打断一百次,它也不会重做任何已经完成的工作。
披露:本文编译自 Peter Piekarczyk《What actually happens when you message Grok Bot》(2026-10-14,11 分钟阅读)。原文主要依据 Lee Robinson 关于 Grok Bot 内部实现的公开分享,作者自己补了一部分分布式系统经验。必须提醒:文中有大量"我猜/据我记忆(IIRC)/最可能是这样"的推测(数据库选型、Firecracker 是否为同款、批次窗口、helper 是否可再起 helper 等),作者本人在文末列了 11 个待确认问题。本文忠实保留这些不确定性标注;未做独立验证。
一、按下发送的那一瞬间
界面立刻显示你发的消息,它不等服务器。App 在本地保存这条消息,并生成一个唯一 ID。
这个 ID 在服务端有实际作用:服务器会丢弃任何它已经见过的消息 ID。所以当你在地铁里网断了一下、客户端自动重发时,你不会收获两条一样的消息——一个很小的乐观更新设计。
然后这条消息进入你这个会话的队列。
二、每条会话一条队列:谁能插队,谁只能排队
每个会话都有一个队列,任何想引起 Agent 注意的东西都往这里放。但不是所有消息享受同等待遇:
来源
行为
你直接发的消息
打断。它正在跑一个回合,你发新消息,它就停下接你这条
helper(子代理)跑完回报
排队,不打断
Slack 里被 @
排队,不打断
定时工作流触发
排队,不打断
理由很直白:你是那个在主线程上等着的人——字面意义上的"等"。其余都是后台事件,没有人在屏幕前盯着。
还有个细节和体感有关:如果你飞快地连发三条,它们很可能不会变成三个独立回合。作者推测存在一个很短的时间窗,把挨得很近的消息合并成一个回合处理。
用的人能立刻得出的第一条结论:别把聊天框当命令行连打。你以为在"补充信息",它可能在反复重启一个回合。
三、"被打断"和"白干了"是两件事
主 Agent 的循环是一个跑在服务器上的持久化工作流(durable workflow),底层是 Temporal。每一次模型调用、每一次工具调用都是一个 step,每一步一完成就把结果存下来;对话历史则单独存在自己的数据库里。
先解释一下 Temporal:一个开源引擎,它在代码运行过程中把每一步的结果存下来。服务器崩了,另一台机器从断点接着跑,不会重做已完成的工作;它还负责重试,并允许工作流"睡到被某个事件叫醒"。
于是有两个容易被混为一谈的性质,其实是独立的:
- 可打断(interruptible):你的新消息能中断正在进行的回合;
- 持久化(durable):已做完的工作不会被丢掉,下一个回合从存下的步骤继续。
合起来的效果就是:你可以发一百条消息,它被打断一百次,但一次都不会重做已经干完的活。同一套机制也让它扛得住崩溃——服务器挂了,工作流从最后一个已保存的步骤恢复,而不是从头发车。
四、这个循环到底怎么跑(作者的推测)
原文作者说,Lee 只讲了"用了 Temporal",没讲循环具体怎么挂在上面,所以这段是他的推断,而我觉得推断得挺合理:
- 服务器是常驻的 worker 进程,所有用户共享;两次回合之间,你没有任何东西在跑;
- 循环就是一段写一次的普通代码,类似
agentTurn(conversationId); - 你发消息 → 前门把消息存下并给 Temporal 一个信号 → Temporal 建一个任务 → 某个空闲 worker 领走 → 该 worker 从数据库读出你的对话历史,跑一个回合。
所以就一个回合而言,它很像你熟悉的无状态 API 请求:
workflow agentTurn(conversation): messages = loadContext(conversation) // saved step loop: reply = callModel(messages) // saved step if no tool calls: break for toolCall in reply: result = runTool(toolCall) // saved step messages.append(result) saveTurn() waitForSignal() // sleepTemporal 在这里同时是调度器和记账员:一步只交给一个 worker,结果当场记账。worker 中途崩了,它对这一步的"认领"会超时,另一个 worker 从已存步骤重放工作流。"每次模型与工具调用都写库"这件事,正是重放能成立的原因。
回合结束后,工作流停在"等待信号"上:内存里没东西,也没有进程在跑,它只是数据库里的几行记录。原文说 Agent 在空闲约两分钟后被报告为睡眠——他猜这是 worker 在保温,以防你马上回一句。
这就是"一直在"却几乎不耗钱的做法:睡眠很便宜,因为状态根本不在 worker 上。
五、小活自己干,重活交给 helper
消息到了 Agent 手里,它怎么处理?前门的 router 先做触发去重、塞进会话队列;然后主 Agent 用提示词里的一条经验法则自行决定:先回你一句"收到",接着要么自己干,要么转出去。
- 小活留在主循环:改一个 Notion 页面、更新一条 GitHub 评论,算"快",丢到后台反而更贵更慢。
- 主 Agent 也能跑 shell,但据作者记忆,shell 跑在 VM 上,所以即使一条"很快"的命令也会把 VM 唤醒;凡是 shell 密集的活,都算长任务,转给 helper。
- 长任务交给四类 helper:general(通用)、computer use(操作电脑)、video(视频)、cloud coding(云端写码)。每个 helper 是自己的一条持久化工作流,有自己的 harness 和工具集,全程在后台跑。跑完把结果投回队列——也就是前面那条"不打断你"的消息。
这里有个使用上的反直觉结论:inline 干的活,你的打断是直接打在它身上的。你一边催一边发消息,那条 GitHub 评论就改得更慢,因为循环被反复切断。原文说他相信 Meta 的 Muse 也是这样。
所以:想让事情并行,就让它开 helper,而不是在主线里刷屏。
六、VM:把"你的东西"和"跑循环的机器"分开
Agent 循环跑在共享服务器上;VM 是另一台机器,是"你的 Agent 自己的电脑",循环需要时用一个工具把它叫起来。
这套分层里,"谁是真相源"说得非常清楚:
层
存什么
特点
服务器(数据库)
会话、例行任务(routines)、工作流步骤
真相源(作者假设是 Postgres 或 SQLite)
对象存储
文件、记忆、技能
备份(作者假设是 S3 或 R2)
VM
它正在处理的文件副本 + 工作文件(下载的视频、SEC 文件、clone 的仓库)
可丢弃;同步到对象存储
VM 睡眠时,内存和磁盘会被快照,所以你的登录态和其它状态都能留着。反过来说,VM 是可弃的:Grok Bot 团队要为 0-day 打补丁时,直接换掉就行,什么都不丢——因为真相源在服务器,文件都在对象存储里备份着。
作者说这些是Firecracker microVM(据他所知和 AWS Lambda、Fly.io 用的是同一种):很小、大约125 ms就能启动、从快照恢复也快,所以"唤醒 VM"感觉是瞬时的。
技能和记忆走的是同一条思路:内置技能与插件技能来自平台本身;你的自定义技能是 VM 上的 markdown 文件(就跟你存在笔记本上一样),并备份到对象存储;记忆也是,Agent 用grep去搜它。
七、“多个机器人,一台电脑”
这条对使用方式影响很大,也最容易被忽略:
你可以建任意多个 Grok Bot,但它们不会各拿一台 VM——你所有的 bot 共用一台电脑。
这意味着什么:
- 共享同一批文件、同一套已安装的工具、同一批浏览器登录态。一个 bot 装了某个东西、登录了某个站点,其它 bot 都能用。
- 每个 bot 有的是自己的桌面:自己的屏幕、自己的浏览器窗口。一个 bot 看不见、也开不了另一个 bot 的屏幕,所以两个 bot 可以同时用浏览器而不抢鼠标。
- 作者的原话比喻:很像几个人共用一台笔记本——每人有自己的用户会话,但硬盘是共享的。每个 bot 的会话、记忆、例行任务存在服务器上,共用那台电脑只是工作区。
使用含义:隔离是"桌面级"而不是"机器级"。你做实验时装的包、登的账号会串到别的 bot 上。要干净,就自己负责清理。
八、安全:审的是"动作",不是你的提示词
和 ChatGPT / Claude 的自动审查类似,Grok Bot 有一个独立的模型在动作执行前做审查,主要针对 shell 命令。它审的是Agent 准备做什么,不是你的提示词。
另外两条规则:
- 来自外部来源的内容(邮件、网页)永远被当作"数据",不被当作"指令"——这是防提示词注入的基本姿态;
- 不可逆的动作(发邮件、往 Slack 发帖)会变成草稿回到你这里等批准。作者括号里补了一句略带调侃的话:大概是在某位 CEO 的银行账户信息被泄露之后加上的。
九、一个出口:SendToUser
不管活是怎么干完的,结果都从同一个口子回来:有一个全局工具叫SendToUser。你在桌面端、手机端还是 Slack 里,Agent 都只调它,各客户端自己决定怎么呈现。
这个设计值得抄:"怎么通知人"不写在 Agent 的逻辑里,而是收敛成一个工具 + 客户端渲染。
十、上下文的四道防线
一个"永远在线、什么都记得"的 Agent,很容易变得又慢又贵还没用。原文列了四种对抗手段,我按"对使用者有什么影响"重排:
1)稳定的提示词前缀(缓存)模型供应商从第一个 token 开始缓存。只要开头(系统提示词 + 工具列表)每回合都一样,就能被便宜地复用;变化的东西(新消息)放最后。这件事比听起来重要:每回合都要重发整段对话,缓存命中就是它能既快又便宜的原因。开头一变,后面全部失效,成本和延迟一起跳。
作者在这里认了个错,值得抄:他之前推算某人每月在 AI 上花$11M,后来发现算错了——忘了提示词缓存;把缓存算进去,真实数字接近$1M/月。
2)工具与技能懒加载提示词里只放技能的名字和一句短描述,完整技能在真正需要时才加载。这正是 Agent Skills 标准的工作方式(他说 Claude、Codex、Pi 都遵循)。所以——
一句技能描述要写"什么时候该用它",而不是"里面有什么"。因为模型决定要不要加载时,能看到的只有这句描述。
技能不应该自动加载。他提到pstack里有个布尔值disableModelInvocation:开启后模型没法自己加载这个技能,你只能显式调用(比如/hello-world)。
3)主线上下文保持小重活都丢给 helper,主线程的上下文自然就小。让子代理各自去拉它们需要的东西。
4)上下文压缩(两级触发)
触发条件
行为
为什么这样设计
上下文超过100K token,且你离开 10–60 分钟
后台压缩
压缩要把整段对话重新喂一遍模型;趁缓存还热做,便宜
撞到上下文窗口上限(Grok Bot 约200K;Codex/Claude 约 1M 但可配)
立即压缩,阻塞主线程
没法延后,只能停下来做
重要的一点:你的对话历史永远不会被删,可以像翻 iMessage 一样往回滚。被压的只是模型的工作上下文。
使用含义:别等它撞窗口。你离开的那 10–60 分钟,正是它便宜地整理上下文的最佳窗口。
十一、它怎么越用越好:两个循环
- 内环,按天:团队找 bug,改 harness、提示词或推理栈,跑几个 eval,然后发给你。
- 外环,按月或几个月:拿收集到的失败样本训练新模型。
作者猜测,像"这次该自己干还是转给 helper"这类判断,会有可观测量喂进外环。长期看,模型是围绕这个产品训练出来的,两者一起变好;原本住在提示词和 harness 里的经验,会被搬进模型本身。
这也是为什么有人说:你那些精心维护的AGENTS.md和自定义技能,可以扔了——下一代模型可能已经被训练成会自己这么干。
十二、读完就能改的六个用法
以下是我们根据这套机制整理的落地建议,不是原文观点:
- 要补充信息,攒成一条发。连着发会反复打断正在进行的回合,inline 的活只会更慢。
- 要并行,让它开 helper,别在主线里催;helper 的结果走队列,不会打断你。
- 不可逆动作先看草稿再批。这是你唯一的人工闸门,别养成"无脑点同意"的习惯。
- 多 bot 共用一台机器:装包、登录账号会互相影响;实验前想清楚隔离边界。
- 技能描述写"何时用",否则模型根本不会想到加载它;需要确定性的技能,就禁掉自动调用、走显式命令。
- 长会话主动在离开时让它压缩(>100K 且闲置 10–60 分钟),别等阻塞式压缩。
十三、作者自己还没搞清的问题(选译)
原文结尾列了 11 个问题,挑几个最能说明"这是外部观察"的:
- 主循环的每一步是独立工作流,还是整个会话一条长工作流?(他倾向前者)
- 打断一个回合时,正在跑的工具调用是直接被丢弃吗?
- "启动 helper"是不是一个"起子工作流、立刻返回 ID"的工具?helper 能不能再起 helper?
- 不同 helper 是否跑在不同模型/不同设置上?
- 快照恢复到底多快?内部有没有 VM 管理器、怎么选宿主机?
- 压缩是"替换整个上下文",还是只替换上次摘要之后的部分?
十四、边界
- 原文为外部观察 + 一次公开分享的整理,作者多处明说"这是我的推断";数据库、对象存储、microVM 型号、批次窗口都是推测。
- 数字(100K / 200K / 1M token、125 ms、2 分钟空闲、$1M/月)来自原文或作者引用,本文未独立验证,产品也可能随时调整。
- 这套机制是 Grok Bot 的实现,不等于"所有 Agent 都该这么做";但"队列 + 持久化步骤 + 懒加载 + 稳定前缀"这套骨架,已经被几家前沿产品反复验证。
- 关于 Firecracker 的 125 ms 启动,可以对照我们写过的另一种路线:Hyperlight 把启动压到 1–2 ms,代价是只能跑专门编译的客体。
原文链接:Grok Bot 用法拆解:一条消息之后,服务端发生了什么 - 汇智网