AI Agent 实例跑起来之后,怎么暂停、恢复、重启?到期了是直接停用还是自动删除?改了模型配置之后要不要重建容器?本文用怡途Claw 的实例生命周期 API 把这些操作拆开讲清楚。
AI Agent 实例创建完成之后,不是说就扔在那里不管了。实际用起来会遇到几个很现实的问题:
- 买了个实例,改了大模型配置,怎么让它生效?
- 套餐到期了忘记续费,实例会被怎么处理?
- 续费之后,需不需要重新创建一个实例?
- 不想用的时候能不能先暂停,下次再恢复?
怡途Claw 的实例生命周期 API 就是处理这些事情的。本文不讲概念,直接从 API 接口和代码逻辑来看这几个操作各自做了什么。
先看实例当前状态:GET /api/app/instances
调用 GET /api/app/instances 返回当前用户的所有实例列表,每个实例带上 status 字段,值可能是 running、stopped、error、creating、pending 等。
返回的实例数据里,env 和 token 字段会被过滤掉,不会暴露给调用方。customLLMConfig 里的 apiKey 也会被替换成 [REDACTED]。
如果想看汇总数据,可以调 GET /api/app/instances/stats,返回 total、running、stopped 三个数字,外加按 type 分组的统计。这个接口还会额外返回 subscriptionStatus、packageStatus 和 suspendedInstances。suspendedInstances 是那些因为套餐过期被系统自动暂停的实例数量。
暂停实例:POST /api/app/instances/:id/stop
调用 stop 接口,系统会去 Docker 层面 stop 容器。
代码里有一个细节:如果 Docker 返回的 error message 包含 "304" 或 "already stopped" 或 "not running",系统不会报错,而是直接返回成功。也就是说 stop 是幂等的——你对着一个已经停掉的实例再调一次 stop,不会报 400。
停掉之后,instance.status 变成 stopped,lastStoppedAt 记录下时间。
注意:停掉实例不会删除数据。容器还在,只是进程停了。
恢复实例:POST /api/app/instances/:id/start
start 接口做的事情比 stop 多一些。
第一步,检查实例是否已经在 running 状态,如果是就直接报错。
第二步,检查 expiresAt。如果实例已经过期,直接返回 "实例已过期,请先续费后再启动"。
第三步,如果实例之前因为订阅过期被暂停(isSuspendedDueToSubscription 返回 true),start 会清除暂停状态——把 suspendReason、suspendedAt、scheduledDeleteAt 都置空。也就是说,续费之后只要调一次 start,实例就恢复正常了,不需要重新创建。
第四步,看有没有容器。如果 containerId 为空,说明之前没创建过容器,会调用 createContainer 重新创建。如果 instance.needsRecreate 为 true(说明 LLM 配置更新过),会先删掉旧容器,释放旧端口,再重新创建新容器。
第五步,正常启动容器。如果 Docker 返回 "container already started",系统照样不会报错,直接返回成功。
重启实例:POST /api/app/instances/:id/restart
restart 和 start 有区别:start 是从停止状态恢复,restart 是让正在运行的容器重新启动。
代码里 restart 的流程是:
- 检查 expiresAt,过期了直接拒绝。
- 检查 containerId 是否存在。
- 调用 Docker 的 restartContainer。
- 等待 5 秒,确保容器完全重启。
- 特殊处理 runtime 类型:如果实例是 claude-code runtime,重启后重新跑 preconfigureClaudeCodeContainer(重建 tmux 会话);如果是 codex runtime,重启后重新写 ~/.codex/config.toml(因为容器可能基于新镜像替换过,配置需要重新落盘)。
这里的 5 秒等待不是随便写的——容器 restart 之后需要时间让内部进程启动,如果不等就直接返回,前端可能拿到的还是旧状态。
改完模型配置,要不要重启?
PUT /api/app/instances/:id/llm-config 更新大模型配置后,实例并不会自动重启。代码里会把 instance.needsRecreate 标记为 true。
下次调用 start 或 restart 的时候,系统检测到 needsRecreate 为 true,会先删掉旧容器,释放旧端口,创建新容器,再启动。这样新的 LLM 配置才会生效。
也就是说,改完配置之后,需要手动调一次 start 或 restart 来激活新的配置。
关于订阅和暂停的联动
接到过几次用户反馈:"我续费了,为什么实例还是用不了?"
原因在于:续费只是更新了套餐的到期时间,但实例本身还处于暂停状态。需要调一次 start 接口,代码里才会把 suspendReason 和 suspendedAt 清掉。
所以续费之后的完整操作流程是:
- 续费套餐
- GET /api/app/instances/stats 确认 subscriptionStatus 已恢复正常
- POST /api/app/instances/:id/start 恢复实例
总结
| 接口 | 做什么 | 注意点 |
|---|---|---|
| GET /api/app/instances | 查实例列表 | 敏感字段已脱敏 |
| GET /api/app/instances/stats | 查汇总数据 | 含订阅状态和暂停数量 |
| POST /api/app/instances/:id/stop | 暂停实例 | 幂等,不会删数据 |
| POST /api/app/instances/:id/start | 恢复实例 | 自动清除暂停状态,过期检查 |
| POST /api/app/instances/:id/restart | 重启实例 | 5 秒等待,runtime 特殊处理 |
上面这些接口都是跟 Docker 容器直接打交道的。实例的本质就是一个容器,启动、停止、重启都对应 Docker 的同名操作。理解了这一点,API 的行为就很好理解了。
本文基于怡途Claw 后端 app-instances.controller.ts 与 instances.service.ts 的源码分析。