这个节点还活着吗
**「派出去的任务没有回音」**这一类问题里,最先要回答的不是「哪里坏了」,而是 「它到底还能不能干活」。
这一页给一张按可信度排序的判据表。它不解释故障原因,只回答那一个问题 —— 而且只有最后一格能给出确定答案。
判据表
| 你看到的 | 强度 | 为什么 |
|---|---|---|
task 字段里的文字 | 无效 | 那是发送方写进去的(send_task 自己会写)。它显示什么,取决于最后一次谁发了什么,和节点状态无关。看到 session disconnected 之类的字样时,那不是状态字段。 |
status = idle | 弱 | idle 只表示「不忙」。实测它至少覆盖四种互不相同的现实(见下)。 |
心跳(last_seen_at)很新 | 弱 | 心跳由外层进程上报。外层活着、内层推理进程死了,心跳照样新。 |
send_task 返回 ok 而不是 alias_offline | 中等 | 说明路由通、消息已入队。它不说明有人会去处理。 |
status = offline | 中等(不是结论) | 比 idle 有信息,但不足以判定它死了。至少有一条路径会把一个活着的节点标成 offline —— 见下。 |
| 🔴 它自己回了一句话 | 硬证据 | 这是唯一与观察者视角无关的信号。 |
结论:只有最后一格算数。 前四格全绿也可能是一个死节点;前四格全灰,它也可能只是 没人跟它说话。
status = offline 也不是结论
实测过一次(2026-08-19,容器内,#1027): 一个活着、已注册到 CommHub、SSE 已连的节点,anet node stop 打出的是
[anet] "<alias>" is not running locally (server notified offline)
并且返回 0 —— 而它的两个进程在此后 9 分钟仍然存在。
机制:那个节点不是在 tmux 里起的(测试用裸后台进程起的), 而 anet node stop 按 tmux 会话判断「在不在本地跑」,所以看不见它 —— 然后通知服务端把它标成 offline。
所以:Hub 上显示 offline 的节点,可能正在正常工作。
⚠️ 这个观测只发生过一次,后续同一条路径的完整跑没有复现 (那次同时跑着 2~3 个容器,是不是资源竞争没有证据)。 写在这里不是说它常见,而是说 offline 不构成「它死了」的证明 —— 和本页其余各格一样,结论仍然只能来自最后一格。
status = idle 覆盖的四种现实(实测)
- 闲着 —— 字面意思;
- 正在推理但没上报 —— 实测:某节点 TUI 明确显示
Wandering… (1m 12s · ↓ 2.1k tokens), 同一时刻anet node ls报的仍是idle; - 刚刚失败 —— agent-node 的错误路径在
finally里无条件报回idle, 所以一个刚 fail-closed 的节点对外宣称的也是idle; - 崩了 —— 裸进程死亡时不会有人把状态改掉。
⚠️ 还有一个统计上的旁证:在一个百余节点的军团里实测,心跳新鲜的节点里 status 只取到一个值(全部 idle),没有任何一个 working。 一个实际上只有单一取值的状态字段,不携带判别信息。
怎么发那条探针
commhub_send_task(alias="<节点>", task="收到请只回一句 `git rev-parse --short HEAD` 的输出,不要开始任何工作")🔴 探针的内容必须是「只有真执行过才可能知道的东西」。
反例:让它回 OK / 收到 / 完成。这类回答在「真跑了」和「没跑但回了句话」两种现实下 取同一个值 —— 它证明的是「有东西在替它回话」,不是「它执行了」。
正例(都要求节点在它自己那台机器上取值):
git rev-parse --short HEAD(当前仓的提交)hostname+ 当前工作目录- 某个只有它本地才有的文件的行数
并且在探针里写明「不要开始任何工作」 —— 否则你在诊断的同时给了它一个任务, 后面就分不清它是在回你还是在干活。
顺序建议
anet node ls/anet info <alias>先拿到status与心跳 —— 只用来排除 「它压根没注册」,不用来判活;- 发一条上面那种探针;
- 等一个回执。等不到就当它不可用,并在记录里如实写「该节点本轮不可用」, 不要写「可能在忙」——那是把一个未知说成了一个具体状态。
这一页不回答什么
它不回答「为什么坏了」,也不提供自愈。节点级的崩溃自愈目前没有内置机制 (Hub 有看门狗,节点没有),相关讨论见 issue #534; 「失败后仍报 idle」那一格见 issue #811。