auto-sync: 2026-05-26 20:27:48
This commit is contained in:
@@ -410,25 +410,95 @@ await self.spawner.spawn_full_agent(
|
||||
|
||||
存储在 `task_attempts.metadata` JSON 中。
|
||||
|
||||
### counter 生命周期(v2.0:调用级)
|
||||
### counter v2.1:per (agent, session) 粒度(2026-05-26)
|
||||
|
||||
#### 问题
|
||||
|
||||
v2.0 的 `max_per_agent=1` 是 per agent 粒度,导致:
|
||||
- 张飞正在跑一个 task(sub session,use_main_session=False)
|
||||
- 这时又来一个新 task 需要张飞 spawn 新 sub session
|
||||
- counter 拦住 → AgentBusyError
|
||||
- 但 sub session 之间是独立的(不同 session_id,不同 lock),不应该互相限制
|
||||
|
||||
#### 新设计:三层控制
|
||||
|
||||
| 层级 | 配置 | 默认 | 作用 |
|
||||
|------|------|------|------|
|
||||
| per session key | `max_per_session` | 1 | 同一 (agent, session) 不能并发 spawn |
|
||||
| per agent | `max_concurrent_sessions` | 3 | 同一 agent 最多同时跑 N 个不同 session |
|
||||
| global | `max_global_agents` | 5 | 全局总并发上限 |
|
||||
|
||||
**counter key**:`f"{agent_id}:{session_id}"`
|
||||
- main session → `zhangfei:main`
|
||||
- sub session → `zhangfei:abc-123`
|
||||
- 不同 key 互不影响,同一 key 最多 1 个
|
||||
|
||||
**session_id 分配前移**:
|
||||
|
||||
```python
|
||||
# 新顺序(spawn_full_agent 内部)
|
||||
1. 分配 session_id(main → "main", reuse → 传入值, new → uuid4)
|
||||
2. session state 检查(main 时才检查)
|
||||
3. counter.can_acquire(agent_id, session_id) # 检查三层
|
||||
4. counter.acquire(agent_id, session_id) # 占用 key
|
||||
5. spawn
|
||||
```
|
||||
|
||||
#### cooldown
|
||||
|
||||
cooldown 保持 per agent 粒度(API 429 限制的是 agent 的 API key,不是 session)。
|
||||
冷却期间该 agent 的所有 spawn 都被拒绝。
|
||||
|
||||
#### 超限行为
|
||||
|
||||
- `can_acquire` 返回 False → `AgentBusyError`
|
||||
- 任务/mail 保持 pending,等下个 tick(30s)重试
|
||||
- 不会丢失
|
||||
|
||||
#### release 调用点
|
||||
|
||||
所有 release 必须带 session_id:
|
||||
|
||||
| 调用点 | 当前 | 改后 |
|
||||
|--------|------|------|
|
||||
| wrapped_on_complete 闭包 | `release(aid)` | `release(aid, session_id)` |
|
||||
| spawn 失败 fallback(行 441) | `release(agent_id)` | `release(agent_id, session_id)` |
|
||||
| _do_retry 续杯 | `release(agent_id)` | `release(agent_id, old_session_id)` |
|
||||
|
||||
wrapped_on_complete 闭包需要捕获外层 session_id 变量。
|
||||
|
||||
#### 配置变更
|
||||
|
||||
```yaml
|
||||
# config/default.yaml
|
||||
max_global_agents: 5 # 不变
|
||||
max_per_session: 1 # 新增,替代 max_per_agent
|
||||
max_concurrent_sessions: 3 # 新增,per agent 总并发
|
||||
# max_per_agent: 已删除
|
||||
```
|
||||
|
||||
### counter 生命周期(v2.1:per session 级)
|
||||
|
||||
```
|
||||
spawn_full_agent 内部 acquire
|
||||
spawn_full_agent 内部
|
||||
│
|
||||
├─ 进程退出 → wrapped_on_complete → counter.release()
|
||||
├─ 1. 分配 session_id
|
||||
├─ 2. session state 检查(main 时)
|
||||
├─ 3. can_acquire(agent_id, session_id) → 检查三层
|
||||
├─ 4. acquire(agent_id, session_id) → 占用 key
|
||||
├─ 5. spawn 进程
|
||||
│
|
||||
├─ 进程退出 → wrapped_on_complete(aid, session_id) → release(aid, session_id)
|
||||
│ ├─ A1/A4 完成 → 结束
|
||||
│ ├─ A2/A3 timeout → _do_retry 手动 release → spawn_full_agent 重新 acquire
|
||||
│ ├─ A2/A3 timeout → _do_retry: release(aid, old_sid) → spawn_full_agent(acquire 新的)
|
||||
│ └─ A7-A12 → release → 等 ticker
|
||||
│
|
||||
├─ monitor timeout(B)→ counter 不 release(进程还在跑)
|
||||
│ └─ B1 假死 → 手动 release + 复活
|
||||
│ └─ B1 假死 → 手动 release(aid, session_id) + 复活
|
||||
│
|
||||
└─ 进程崩溃/PM2 重启 → ticker _check_timeouts 检测 → 手动 release
|
||||
└─ 进程崩溃/PM2 重启 → ticker _check_timeouts 检测 → 手动 release(aid, session_id)
|
||||
```
|
||||
|
||||
counter 生命周期 = 调用级:spawn 时 acquire,进程退出时 release。
|
||||
只有情况 B(进程不退出)counter 保持占用。
|
||||
|
||||
wrapped_on_complete 保证 release(try/finally),即使业务回调异常也不泄漏。
|
||||
|
||||
## 9. escalate 消息格式
|
||||
|
||||
Reference in New Issue
Block a user