AgentRunner 状态管理:内存、SQLite 与 Intelligence 三种实现
AgentRunner 状态管理:内存、SQLite 与 Intelligence 三种实现
学习目标
理解 CopilotKit 的 AgentRunner 架构:
- AgentRunner 抽象类的四个核心方法
- InMemoryAgentRunner 的无状态特性
- SQLiteAgentRunner 的持久化方案
- IntelligenceAgentRunner 的 WebSocket + Redis 锁
前置知识
本章涉及记忆系统设计的通用原理,建议先阅读:
项目实践
AgentRunner 抽象类
抽象接口(源码验证):abstract class AgentRunner { abstract run(request: AgentRunnerRunRequest): Observable<BaseEvent> abstract connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> abstract isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> abstract stop(request: AgentRunnerStopRequest): Promise<boolean | undefined>}Request 结构:
AgentRunnerRunRequest:{ threadId: string, agent: AbstractAgent, input: RunAgentInput, persistedInputMessages?: Message[] // 从持久化恢复的历史消息}
AgentRunnerConnectRequest:{ threadId: string, headers?: Record<string, string>, joinCode?: string // Intelligence 模式的 WebSocket 加入码}InMemoryAgentRunner(默认)
最简单的实现,状态存于内存:
| 特性 | 说明 |
|---|---|
| 存储 | Map<threadId, ThreadState> |
| 持久化 | 无,进程重启丢失 |
| 并发 | 单实例内线程安全 |
| 适用 | 开发环境、单实例部署 |
关键实现(467 行源码):
run():克隆 Agent → 设置 threadId/messages/state → 执行 → 发射事件connect():恢复已有线程的状态 → 订阅事件stop():标记线程为停止 → 清理资源isRunning():检查线程的 isRunning 标志
SQLiteAgentRunner
持久化到 SQLite 数据库:
| 特性 | 说明 |
|---|---|
| 存储 | SQLite 文件数据库 |
| 持久化 | 进程重启不丢失 |
| 并发 | 单写入者(SQLite 写锁) |
| 适用 | 单机生产部署、轻量场景 |
数据模型:
threads表:threadId、创建时间、最后活跃时间messages表:threadId、消息内容、角色、时间戳states表:threadId、状态 JSON
IntelligenceAgentRunner
Intelligence 模式:WebSocket + Redis 锁 + 云端持久化:
| 特性 | 说明 |
|---|---|
| 传输 | WebSocket 实时连接 |
| 锁 | Redis 分布式锁(TTL + 心跳续约) |
| 持久化 | 云端存储(线程历史永久保留) |
| 并发 | 多实例部署支持 |
| 适用 | 生产部署、多实例、持久化线程 |
线程锁机制(498 行源码):
锁生命周期:1. run 开始时获取锁(Redis SETNX with TTL)2. 运行期间定期续约(lockHeartbeatIntervalSeconds,默认 15s)3. run 结束时释放锁4. TTL 过期自动释放(兜底机制,最大 3600s)关键约束:
lockTtlSeconds:最大 3600s(1 小时),防止无限期锁定lockHeartbeatIntervalSeconds:最大 3000s(50 分钟),必须小于 TTL- 如果续约间隔 >= TTL,锁可能在下次续约前过期
WebSocket 连接管理:
- 前端通过 WebSocket 连接到 Intelligence 平台
joinCode用于多用户协作场景(共享线程)- 断线自动重连(指数退避,最大
maxReconnectMs)
Runner 选择策略
问题与规避
| 问题 | 规避策略 |
|---|---|
| InMemory Runner 多实例状态不一致 | 生产环境使用 Intelligence 模式 + Redis 锁 |
| SQLite 写锁竞争 | 高并发场景切换到 Intelligence 模式 |
| Redis 锁泄漏(进程崩溃未释放) | TTL 兜底;lockTtlSeconds 设置合理值 |
| 心跳续约间隔 >= TTL | 确保 lockHeartbeatIntervalSeconds < lockTtlSeconds |
| WebSocket 断线后消息丢失 | Intelligence 模式的云端持久化确保消息不丢失;断线重连后从游标恢复 |
设计取舍
三种 Runner 的权衡
| 维度 | InMemory | SQLite | Intelligence |
|---|---|---|---|
| 部署复杂度 | 最低 | 低 | 高(需要 Redis) |
| 持久化 | 无 | 文件级 | 云端 |
| 并发性 | 单实例 | 单写入者 | 多实例 |
| 成本 | 零 | 低 | 中(Redis + 云服务) |
| 适用规模 | 开发/测试 | 小型生产 | 中大型生产 |
抽象类 vs 策略模式
抽象类(CopilotKit 的选择):
- 优势:统一的接口和类型约束
- 代价:新增 Runner 需要继承并实现所有抽象方法
策略模式(运行时注入不同实现):
- 优势:更灵活,可以动态切换
- 代价:类型安全较弱
参考来源
packages/runtime/src/v2/runtime/runner/agent-runner.tspackages/runtime/src/v2/runtime/runner/in-memory.tspackages/runtime/src/v2/runtime/runner/intelligence.tspackages/sqlite-runner/