Justin Huang

Back

SiMaster Stateless Agent Runtime

static page

把依赖进程内存的 Agent session、sandbox 和工具调用状态,迁移到更适合水平扩容和恢复的分布式链路中。

Agent Infra 2025 done
#agent-runtime#redis#sandbox#distributed-systems#mcp#observability

Context#

SiMaster 是一个面向科研场景的 Agent 产品。它涉及主 Agent、子 Agent、工具调用、沙箱执行、任务状态管理和长链路消息流转。

我参与的一项核心工作,是把原先更偏 demo / 单进程形态的 Agent runtime,改造成更适合生产环境的分布式执行链路。

The problem#

早期实现里,很多 session、sandbox、子 Agent 状态和工具调用状态都维护在单个进程的内存中。这样在小规模实验里能跑,但一旦进入生产环境,就会遇到一组很具体的问题:

  • 服务无法水平扩容;
  • K8S 动态调度困难;
  • 负载均衡后请求可能找不到原来的 session;
  • worker 重启后任务状态丢失;
  • 多 Agent 和工具调用的嵌套顺序难以恢复;
  • 异步消息容易乱序;
  • retry 可能带来重复消费和状态不一致。

这类问题很容易在 demo 阶段被忽略,但线上产品一定会碰到。

What I did#

我的工作主要围绕 Agent runtime 的无状态化改造:

  1. 将 session / sandbox / tool-call 状态从进程内存外置到 Redis-backed 链路;
  2. 通过 session_id、run_id、step_id、tool_call_id 等标识建模任务过程;
  3. 解耦 Agent 后端、工具执行器和子 Agent 调用;
  4. 改造消息入站 / 出站结构,使它能表达嵌套 Agent 和工具调用关系;
  5. 处理分布式环境下消息乱序、重复消费、延迟返回和上下文恢复问题。

Message ordering#

Agent 执行过程不是线性的。一个主 Agent 可能调用子 Agent,子 Agent 继续调用工具,工具异步返回后还需要重新写入上下文。

我当时比较关心的是:如何让这些消息在分布式链路里仍然保持可理解的拓扑顺序。

一个可解释的抽象是把执行过程看成事件流和调用栈的组合:

  • parent_id 表示父子调用关系;
  • sequence_id 表示同一链路内的顺序;
  • run_id 表示一次任务运行;
  • tool_call_id 表示一次工具调用;
  • session_id 表示用户任务上下文。

这样做以后,系统能更清楚地知道:当前消息属于哪个任务、哪个 Agent、哪个工具、哪一步,以及它应该被恢复到哪个上下文中。

What I learned#

这段经历让我意识到,Agent runtime 的难点不是“能不能调一次模型”,而是系统能不能接住 Agent 运行过程中的中间状态。

真正的 Agent 产品里,runtime 需要处理状态、顺序、失败、恢复、观测和多 worker 并发。这些东西不显眼,但会决定产品能不能真的上线。