Daemon 任务执行
说明 Daemon 管理的 Agent 任务如何调度、超时、上报、重试和恢复。
Daemon 任务执行
本文说明 Daemon 接收到任务派发通知后的单个 Agent 任务生命周期。Daemon 的安装、运行时注册和 CLI 生命周期命令请参见什么是守护进程。
生命周期与上报
Daemon 会在每个关键状态边界完成持久化后再继续:
- Poller 预留一个 Daemon 并发槽位,并为对应 Runtime 认领排队任务。
- 调度器准备任务工作目录以及所需的仓库状态。
- Daemon 将已认领任务标记为运行中,再启动一个 Provider CLI 进程。
- Provider 输出、工具活动、用量检查点和最终结果都会先在本地持久化,再上报到服务端。
- 未投递记录会在重连、启动和后续认领前重试;只有任务结算完成后才会释放本地槽位。
WebSocket 通知只负责快速唤醒调度器,并不是事实来源。Daemon 重连后会重新加载未投递的任务交互并回放持久化事件。因此,服务端的临时错误本身不会把已在本地完成的任务改写为普通失败。
并发
两个限制会同时生效,任务必须同时获得两层容量才能执行。
| 层级 | 配置项 | 默认值 | 作用范围 |
|---|---|---|---|
| Agent | maxConcurrentAgentTasks | 6 | 单个 Agent |
| Daemon | MOPHEUS_DAEMON_MAX_CONCURRENT_AGENT_TASKS | 20 | 单个 Daemon 管理的全部 Runtime |
服务端阻止 Agent 超过自身并发上限。Daemon 还会为其全部 Runtime 使用同一个全局槽位池,因此繁忙的 Provider 或工作空间不能超过主机级限制。任一限制已满时,任务继续留在队列,稍后再认领。
调度与评论合并
Daemon poller 只负责预留并发槽位,并为某个 Runtime 调用任务调度器。调度器随后认领任务、准备本地工作目录和仓库、在服务端启动任务、为该任务运行一个 Provider 进程,并在任务结束时释放本地资源。
Provider 输出、工具记录、用量检查点,以及最终完成或失败意图,都会先写入本地持久队列,再发送到服务端。服务端暂时不可用时,Daemon 会在重连、启动以及下一次认领前重试这些记录。任务已经在本地完成时,不能仅因最终 HTTP 请求失败而被改写为普通失败任务。
运行中任务的消息同样是持久化的。WebSocket 通知只用于快速唤醒调度器;重连或重试后,调度器仍会从服务端重新加载未投递消息。消息未投递或未撤回前,任务完成会继续等待。
任务开始运行前,服务端会先合并同一智能体和工单上的兼容评论触发。没有子评论的多个顶层评论会合并为一个待处理任务。成员在存在活跃任务的评论串内追加回复时,即使智能体尚未回复也会触发派发:活跃任务已经运行时,该回复会创建排队后继任务,后续回复再合并到该后继任务。其中任一根评论出现子评论后,这个根评论池会绑定到该评论串,后续新的顶层评论不会再混入。合并始终保留最高优先级。任务进入运行状态后,Daemon 使用已确定的评论快照执行;随后到达的评论会排入后继任务,同一智能体和工单不会被同时认领执行。
超时
设置了 Agent 的 timeoutSeconds 时,它是两层共用的任务截止时间:Daemon 在本地执行时强制执行,服务端清扫器也独立检查该截止时间。
| 条件 | Daemon 行为 | 服务端行为 |
|---|---|---|
已设置 Agent timeoutSeconds | 使用任务值作为执行截止时间。 | 保存相同值并据此清扫。 |
未设置 Agent timeoutSeconds | 使用 MOPHEUS_AGENT_TIMEOUT;0 禁用 Daemon 截止时间。 | 使用 agent_task_timeout,默认 24 小时。 |
服务端还会检测中间状态卡住的任务:排队任务受队列 TTL 限制,已派发任务必须在派发超时内进入运行中。MOPHEUS_AGENT_IDLE_WATCHDOG 与总执行截止时间独立,用于停止在配置时间内没有任何输出的 Provider;设置为 0 可禁用。
任务超时有两条并行解析线路,唯一的中间交集是智能体的 timeoutSeconds。Daemon 线路从智能体超时回退到 MOPHEUS_AGENT_TIMEOUT;服务端线路从同一个智能体超时回退到系统参数 agent_task_timeout。智能体设置了超时时,两条线路使用同一个值;未设置时,两边的回退参数相互独立。服务端清扫器约每 1 分钟运行一次,用于兜底处理 Daemon 没有清理的任务;排队和已派发任务仍分别使用 2 小时和 5 分钟的独立滞留窗口。
失败与重试
自动重试会消耗任务重试次数,且仅在 attempt < max_attempts 时发生。只有超时、重启或 Runtime 恢复相关失败会自动重试。
| 失败原因 | 含义 | 自动重试 |
|---|---|---|
run_timeout | 任务执行截止时间已到。 | 是 |
dispatch_timeout | 已认领任务未能及时进入运行中。 | 是 |
daemon_shutdown | Daemon 在处理任务时被强制停止或重启。 | 是 |
runtime_offline、runtime_recovery | Runtime 不可用,服务端正在恢复其工作。 | 是 |
task_cancel | 用户或服务端取消请求。 | 否 |
idle_watchdog、guard_tripped | 空闲或资源保护终止任务。 | 否 |
queue_ttl_exceeded | 任务在队列中等待过久。 | 否 |
setup、execute、failed | 准备、Provider 或普通执行失败。 | 否 |
Daemon 上报失败时会记录任务 ID、错误、失败原因和最终状态。这些字段是排查任务未重试时的首要依据。
停止与重启
mopheus daemon stop 与 mopheus daemon restart 会先停止认领新任务,再让运行中任务完成。排空期间,Daemon 仍会继续上报持久化事件、用量和最终状态。
使用 --force 时,剩余执行会被取消,并以 daemon_shutdown 结算。该原因可重试,因此重试次数尚未耗尽的任务会被服务端重新入队。Daemon 重启后,服务端还会恢复遗留在 dispatched 或 running 的任务;支持会话复用的 Provider 可以沿用之前的会话和工作目录。
如果任务在认领后、启动前已变为过期,启动请求会以状态冲突被拒绝,而不是被上报为新的 Provider 失败。已重入队或已结算的任务随后按其正常生命周期处理。
等待任务完成期间,mopheus daemon status 和本地健康检查端点会显示剩余活跃任务数。drain buffer 只是 Daemon 用来完成最后状态上报的内部收尾窗口。设置 timeoutSeconds 时,Daemon 的执行超时和服务端清扫是同一个截止时间的两种发现路径;未设置时,两层分别使用 MOPHEUS_AGENT_TIMEOUT 和 agent_task_timeout。