auto-sync: 2026-05-26 20:27:48

This commit is contained in:
cfdaily
2026-05-26 20:27:48 +08:00
parent 3995a3d4a7
commit 5e9b2bbbec
+79 -9
View File
@@ -410,25 +410,95 @@ await self.spawner.spawn_full_agent(
存储在 `task_attempts.metadata` JSON 中。
### counter 生命周期(v2.0:调用级
### counter v2.1per (agent, session) 粒度(2026-05-26
#### 问题
v2.0 的 `max_per_agent=1` 是 per agent 粒度,导致:
- 张飞正在跑一个 tasksub sessionuse_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_idmain "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,等下个 tick30s)重试
- 不会丢失
#### 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.1per 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 timeoutB)→ 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 保证 releasetry/finally),即使业务回调异常也不泄漏。
## 9. escalate 消息格式