Skip to main content

第8章:调度、委托与执行后端 —— 从一次对话到无人值守工作流

一、Cron 不是“定时 print 一句文本”​

Hermes 的 cron 任务要完成完整的 Agent 运行:读取配置、构建 prompt、选择模型和工具、执行脚本或 Agent、持久化输出,再把结果投递到指定平台。cron/jobs.py:2753 的 run_job 和 :4099 的 tick 是核心入口。

一个 job 的生命周期可以画成:

二、为什么需要 claim 和 heartbeat​

多个 Gateway 或多个 scheduler worker 可能同时看到同一个 due job。claim_dispatch、heartbeat_run_claim 和 execution records 用来防止重复执行;长任务还需要 heartbeat 证明 worker 仍然活着。

这和 session turn lease 是同一种思想:把“我正在负责这个副作用”写成可过期、可恢复的所有权记录。

三、Delivery target 不是一个字符串​

cron/jobs.py 的 delivery 逻辑会解析 platform、chat、thread、home target、routing token、mirror 和 adapter capability。一个 job 的 deliver 值可能指向:

  • 当前来源平台;
  • 某个已配置的 home chat;
  • 多个平台目标;
  • profile 或 thread;
  • 媒体/文本不同的发送路径。

先解析为结构化 target,再交给平台 adapter,可以避免 cron 模块直接理解每种平台 API。

四、Delegate:把一个大任务拆成隔离子任务​

tools/delegate_tool.py:2778 的 delegate_task 与 agent/subagent_lifecycle.py 共同实现子 Agent。一个子任务至少要有:

  • parent task/session identity;
  • spawn depth 和并发上限;
  • 独立或受限的 context;
  • 工具/权限/credential 边界;
  • 取消、超时、结果回收和 live log;
  • 返回 parent 的结构化结果。

子 Agent 不是“再调用一次 AIAgent”这么简单。它必须防止:无限递归、凭证跨 profile 泄露、父任务取消后子任务孤儿化、并发 worker 把输出写到错误 session。

五、Delegation 的上下文传递​

agent/delegation_context.py 使用 ContextVar/环境变量把 parent identity、session source 和运行上下文传递给子线程或子进程;subagent_lifecycle.py 的 registry 负责注册、状态、完成和清理。

可把它理解成“结构化的调用栈”:

parent session
└─ child task
└─ child tool call
└─ execution environment

每一级都可以看到必要的父信息,但不能随意反向修改父级所有状态。

六、Execution Environment:同一个工具,七种落点​

tools/environments/base.py 的 BaseEnvironment 之下,源码提供 local、Docker、SSH、Singularity、Modal、Daytona、Vercel Sandbox 等实现。它们的共同接口通常围绕:

  • 创建/复用环境;
  • cwd 和文件同步;
  • 执行命令、读 stdout/stderr;
  • 超时、终止和进程回收;
  • 网络/凭证/环境变量隔离;
  • 清理和持久化。

这里体现“能力与落点分离”:模型只知道有 terminal,不需要知道底层是 Docker 还是远程 SSH;环境层决定真实的信任边界。

七、Relay:把调用生命周期集中到一个边界​

agent/relay_runtime.py 的 RelayHostRegistry 和 session coordinator 相关代码,把 provider codec、逻辑调用、生命周期、共享指标和进程内 managed call 连接起来。Relay 的价值在于:

  • 对模型调用进行统一观测;
  • 管理异步/同步和跨线程调用;
  • 记录一次 logical call 的开始、结束、失败、取消;
  • 让共享指标只接收有界 projection,而不把原始敏感数据到处复制。

它是可选边界,不应改变 Agent Loop 的语义;当 relay 不可用时,核心运行时仍需要有明确的降级路径。

八、Batch 与 trajectory:研究工作负载的另一侧​

batch_runner.py 和 trajectory_compressor.py 说明 Hermes 不只服务交互用户,也服务数据生成和训练前处理。研究负载需要:

  • 固定模型/工具/环境配置;
  • 多任务并行与重试;
  • 保存完整 trajectory;
  • 对工具调用轨迹做压缩、采样和清洗;
  • 不把交互式 UI/Gateway 状态带入离线任务。

这再次证明 AIAgent 作为可复用运行时容器的价值:CLI、Gateway、Cron、Batch 都能共享核心 Loop,但拥有不同的 session source 和生命周期。

九、运维层的四个边界​

  1. 调度边界:何时执行、谁 claim、下一次何时运行。
  2. 委托边界:谁创建子任务、谁拥有并发和深度配额。
  3. 执行边界:命令在哪运行、文件和网络如何隔离。
  4. 投递边界:结果发送给谁、重复发送如何避免。

拆开这四个边界,才能让“无人值守”不等于“无人负责”。