agentscope-ai / agentscope-ai/AgentTeams

Team Room ack mirror loop after Leader final response: missing task lifecycle / ack-only suppression

Aperta
#804 2 commenti 0 reazioni 1 assegnatario Rivendicata da @Sunrisea Vedi su GitHub
area:matrix-element bug
Lingua principale
Go
Stelle
5.6k
Fork
692
Merge medio
5g 4h
PR unite (30g)
23

Descrizione

### 摘要

This behaves like a bug for Team Room / Manager-Workers users, but the root
cause is a missing protocol-level lifecycle primitive — **HiClaw 内置 CoPaw
worker runtime + MatrixChannel + Team Room 协议**缺 task lifecycle guard,所有
问题都从这一缺口派生。

具体来说,当前对入站 Matrix `m.room.message` event 的唤醒逻辑(在
`copaw/src/matrix/channel.py`)是:满足 channel allowlist + `requireMention`
即触发 agent reply turn,**无 task-level lifecycle 信号**。结果:

1. **Team Leader 发出 `FINAL:` 类终态消息后**,runtime 不知道本轮任务已结束;
只要 specialist worker 回了一句 `@项目经理 收到`,Leader 又会被 wake 起来再
回一句"任务已完成,无需进一步操作",specialist 再 ack,循环 4–8 轮直到 LLM
自己累了或抽中"farewell"分支。
2. **specialist worker 对 Leader 的 closure ack** 同样会被 wake;它本应静默忽
略却写了一句 `@项目经理 收到`,于是给 Leader 反向贡献了一个 wake 信号。

由 `hiclaw-controller/internal/agentconfig/coordination.go` 注入 Leader /
specialist AGENTS.md 的 prompt 规则(*"Forbidden Team Room bubbles"* /
*"Mirror loop safeguard"* / *"Farewell = conversation closed"*)已经覆盖该
case,但 qwen3.6-plus 这一档 LLM 不严格遵守 —— 实测每个新 team room 第一次
mixed query 后必出 8+ 条 mirror loop。

需要 Team Room 协议层加 task lifecycle guard,把 "agent emits end-of-turn
marker" 和 "ack-only inbound message" 这两个信号物化成不依赖 LLM 自律的运行时
门 —— 这是 HiClaw Manager-Workers 协议的核心能力,应该 framework 提供,不应
该让每个下游应用(e.g. haopaw)在 prompt + classifier 层重复 patch。

### 环境

- HiClaw runtime:`hiclaw-controller` (Manager-Workers 编排) + 多 worker container(Leader + 2 specialist 共处一 Team CR)
- Worker image:`higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/hiclaw-copaw-worker:v1.1.1`(HiClaw 自家 image,bootstrap 自 `copaw/src/copaw_worker/worker.py`)
- Worker runtime stack:
- CoPaw `AgentRunner`(QwenPaw 上游,`copaw` PyPI package)
- HiClaw `MatrixChannel`(`copaw/src/matrix/channel.py`,继承 QwenPaw `copaw.app.channels.base.BaseChannel`)
- HiClaw Team Leader agent template(`manager/agent/team-leader-agent/`)
- 所有 dispatch 走 Matrix REST `m.room.message` event;Tuwunel IM server
- LLM model:`qwen3.6-plus`(通过 hiclaw-gateway provider)
- 上层应用:[haopaw](https://github.com/...) — Manager-less Team Room(ADR 0010 path A),Leader 是 haopaw-managed team-leader package;specialist 是 haopaw 路灯 / 人口 worker

### 复现(2026-05-12 prod haopaw team room 完整 Matrix room transcript)

User 在 `/shared-rooms/15`(mode=team,team-X-Y)问 `路灯有多少?`。Team Room `!:` 的实际 timeline event 列表(按时间序,已脱敏):

```
1. [@user-u003] 路灯有多少?
2. [@team-leader-X-Y] (reasoning / dispatch bubble)
3. [@team-leader-X-Y] @worker-specialistA-uN 请查询路灯总数
4. [@worker-specialistA-uN] @team-leader-X-Y 共 23 台设备,全部处于关闭状态。
其中:路灯类:17 台 / 景观灯类:4 台 / 喷泉:2 台 / 其他:4 台
5. [@team-leader-X-Y] FINAL: 路灯共 17 台。全部 23 台设备中… ← 终态信号
6. [@team-leader-X-Y] @user-u003 已完成任务。我向用户发送了 FINAL 消息… ← 多余 narrative
7. [@team-leader-X-Y] @worker-specialistA-uN 任务已完成。我已向用户发送 FINAL… ← 多余 closure ack
8. [@worker-specialistA-uN] @team-leader-X-Y 收到,任务已完成。 ← specialist mirror ack
9. [@team-leader-X-Y] @worker-specialistA-uN 任务已完成。用户已回复…
10.[@worker-specialistA-uN] @team-leader-X-Y 收到,确认任务完成。
11.[@team-leader-X-Y] @worker-specialistA-uN 任务已完成,无需进一步操作。
12.[@worker-specialistA-uN] @team-leader-X-Y 收到,任务已关闭。
13.[@team-leader-X-Y] @worker-specialistA-uN 任务已完成。
14.[@worker-specialistA-uN] @team-leader-X-Y 收到。
15.[@team-leader-X-Y] @worker-specialistA-uN 任务已完成。
```

8 条 bubble (event 6-15) 是 step 5 FINAL: 之后的 mirror loop,每条都 wake 了一次 agent reply turn。`copaw/src/copaw_worker/worker.py` bootstrap 起来的 CoPaw AgentRunner pipeline 能在 log 看到对应数量的 turn 进入。

#### 几个关键观察

- event 5 的 body 以 `FINAL:` 开头 — runtime 完全没识别这是终态信号
- event 8 的 worker body trimmed-after-leading-mention 仅 `收到,任务已完成。` —— `copaw/src/matrix/channel.py` 没有"短 ack 不 wake" 过滤器
- AGENTS.md 段含 `Mirror loop safeguard — if 2+ rounds of @mentions exchanged with no new task/question/decision, stop replying immediately` — LLM 在 prompt 看到了但实际生成阶段不严格执行
- 每条 mirror ack 都消耗一次 LLM call(qwen3.6-plus 每次 ~3-5s + token cost),mirror loop = 8x 浪费 + 污染 chat history + 下游应用必须 classifier 折叠

### 影响

1. **token 浪费 + 延迟尾巴**:每个 user query 之后必跟 8-12 次额外 LLM call。同 1 个 query 端到端 30s 而非 ~5-10s。
2. **chat history 噪音**:每条 mirror bubble 都是 Matrix event,最终都进下游应用 SSE 流。下游必须做 classifier 折叠才能 user 体验可看(haopaw 已实施),但 server-side 历史还是有这些 event。
3. **race 风险**:8x worker container wake 期间,user 任何新 query 进 room 都会被卷进当前 mirror loop session — LLM 可能误以为还在处理上一条 task;语义错乱。
4. **新业务 worker 重复踩坑**:每加一个 specialist 业务都要在 prompt 层写"禁止 closure ack"约束才能不放大 mirror loop。是 HiClaw framework 该提供的能力,不应该由每个下游 worker prompt 重写。
5. **审计 / 运维负担**:客户端不只是折叠就够 —— 历史搜索 / 导出 / 审计场景都看得到 mirror noise;运维要解释"为什么 Leader 发完 FINAL 又重复 8 次"。

### 提议方向

3 个 layer 可独立或叠加上线。**Layer C 是我们推荐的 preferred long-term direction** —— 它本质是 HiClaw Team Room 协议层的扩展,符合 multi-agent 协作的协议本质,不依赖 LLM 自律也不依赖正则启发式。Layer A 和 Layer B 是 short-term tactical 补丁,落地快、风险面小,可以先上 buy 时间。

#### Layer C (preferred long-term)— Team Room task lifecycle 协议扩展

在 HiClaw Team Room 协议中加 `TaskLifecycle` 抽象(跨 `hiclaw-controller` + `copaw/src/matrix/channel.py` + Team Leader agent template):

- 每次 user message 进 Team Room → 创建 task,分配 `task_id`,channel state 持 `task_state ∈ {open, closed}`。
- Leader 显式调用 `mark_task_closed(task_id)` tool(或 emit 特殊 metadata field)后 task 进入 `closed`。
- closed 后该 task 关联 thread 内的 ack / follow-up event **不 wake agent**(但仍写 session log 供后续 LLM 上下文)。
- 新 user message in same room → 新 task_id(或时间窗 ~60s 内复用旧 task)。

这把"FINAL 是 marker"上升到"任务有显式生命周期"。framework 不依赖 string convention 也不依赖 regex 启发;多 user / 多 task 并发场景能区分;自动化测试有稳定 anchor。

实施路径草图:
- `copaw/src/matrix/channel.py`:room state 增 `task_state`;inbound event handler 在 `closed` task 上拒 wake ack-only follow-up(但允许新 user message 开新 task)
- Team Leader agent template:注入 `mark_task_closed()` tool;prompt 规则改为"发 FINAL = 调 mark_task_closed"而非纯文本约定
- `hiclaw-controller/internal/agentconfig/coordination.go`:Team CR 启用时把 task-lifecycle 协议元数据 + tool registration 写进 Leader / worker AGENTS.md

风险:API 改动较大,需 channel 层 + agent tool 层 + memory 层协调。建议作为 HiClaw v1.2+ milestone。如果 QwenPaw 上游 tool registry 无法承载 `mark_task_closed` 这类协议级 tool,HiClaw 可在 MatrixChannel 内 sniff Leader outbound 的特殊 system marker,channel 自己维护 task state。

#### Layer B (short-term)— `copaw/src/matrix/channel.py` ack-only inbound suppression

`copaw/src/matrix/channel.py` 在 inbound event 进入 agent reply pipeline 之前做 body 内容预过滤,**marker-agnostic**:

- 短 ack 正则(默认 conservative,可禁用 / 可扩展):
```
^(@\S+\s+)?(收到|OK|got it|received|acknowledged|了解|明白|好的)[\s\S]{0,30}$
```
- pure-mention 无内容(只有 `@name`)→ 同样 skip
- 命中后 event **仍写入 Matrix timeline + session log**(保留协作历史,供下游渲染 / 审计 / 后续 LLM 上下文),**只是不触发 agent reply turn**

**关键 invariant**:

1. **默认 conservative** —— 出厂只匹配最短最显式的 ack 短语;user / agent 可在配置里追加 pattern 但 framework 不主动扩
2. **可配置 + 可关闭** —— Team-level + per-channel + per-agent 三层 override
3. **只 suppress wake,不删 timeline / session log** —— 历史保留,下次 LLM turn 仍能看到这些事件作上下文
4. **error / status / data message 不能误杀** —— pattern 必须显式只匹配纯 acknowledgement;任何带数字 / 错误关键词 / 多行内容的 message 必须 wake

**这是完全 HiClaw self-contained 的修复 —— 不需要 QwenPaw 上游改动**,落地 cost 最低。

跟 Layer A 互补:A 关心 Leader 端 marker 之后停;B 关心 specialist 端短 ack 不放大 wake。两者一起上覆盖最全;任一上都比现状好。

风险:误判 — 有些短消息("好的,需要重新查")看似 ack 但有 follow-up 意图。通过严格 anchor + length-bound + 配置开关组合控制。

#### Layer A (short-term)— end-of-turn marker detection (Leader 端)

agent 拿到 LLM output 后扫一次 body:

- 若 body 起始命中 **configurable end-of-turn markers**(list 形式;Team-level config + per-agent override),把当前 room 标记 `task_closed=True`
- 后续入站 event 时 channel handler 先检查 flag;若 True 且 event sender 不是 root user → drop without agent reply turn

**实施层选择**(两个选项,倾向 Option 1 先做):

- **Option 1 (HiClaw self-contained)**:在 `copaw/src/matrix/channel.py` outbound message hook 拦截 Leader send 之后的 body,做 marker check;channel 维护 room-level `task_closed` flag。**优点**:HiClaw 内闭环,不依赖 QwenPaw 改动,可立即合入。**缺点**:和 Layer C 的 task_id 模型 future 可能重叠(迁移成本可控)。
- **Option 2 (推 QwenPaw 上游)**:在 QwenPaw `AgentRunner.reply()` 之后扫 marker;channel layer 暴露 `on_turn_complete(marker_hit: bool)` hook。**优点**:更 clean,符合 BaseChannel 抽象。**缺点**:需要 cross-repo coordination + QwenPaw schedule。

**重要约束**:不要把 `FINAL:` 写死成唯一 marker。`FINAL:` 是 haopaw 的短期约定,HiClaw 框架不应该 bake 进 framework。建议 framework 提供:

- 默认 list 为空(行为不变,兼容现有 user)
- 用户可在 Team CR 或 worker config 配置项 e.g.
```yaml
channels:
matrix:
end_of_turn_markers:
- "FINAL:"
- "[TASK_DONE]"
```
- **或** Layer A 直接放弃 marker 路径,统一走 Layer C 的 `mark_task_closed()` tool — 由 agent 显式调用而非靠 string heuristic

风险:marker 是字符串约定,少数 LLM 可能在引用其他人输出时不慎触发;以及未来需要更稳的信号(tool call / metadata field)。但 short-term 实施成本最低。

### 4 个 doctrine 问(给 maintainer)

> 这些不是 issue body 必带,但 RFC 阶段贴出来能加速决策。

1. **`FINAL:` 这种 marker 是 haopaw-specific 约定还是 HiClaw 想做项目级 standard?** —— 决定 Layer A 是 user-config 还是 builtin。
2. **ack-only suppression 是 channel-level(HiClaw MatrixChannel)还是 agent-level(QwenPaw AgentRunner)?** —— 我倾向 channel-level,因为 Team Room 协议本身就是 channel-driven;agent loop 适合做"上一轮我发了什么 / inbound 是不是空响应"推理。两层都做风险互补。
3. **多 user 同 room 时 task identity 怎么定义?** —— 单 room 单 task 假设在 Manager-less Team Room 成立(haopaw path A),但通用 HiClaw room 可能多 user 并行 task。Layer C 设计要考虑。
4. **Layer C 的 `task_id` / `mark_task_closed()` tool 应该 in-HiClaw 还是推 QwenPaw 上游?** —— HiClaw 定义 Team Room 协议;QwenPaw 是否需要 tool registry 协调(让 agent 能 emit 协议级 signal)?

### 当前 workaround(haopaw 短期 mitigation,已上线)

1. **Leader prompt** (`workers/team-leader-haopaw/config/AGENTS.md`):
- 新增 `## Post-FINAL absolute stop rule (MANDATORY)` 段:发 FINAL 后本轮 task 立即终止;不许 @worker;不许发完成通知;worker ack 静默忽略
- `Forbidden Team Room bubbles` 段补 post-FINAL closure-ack 反例
2. **Specialist prompts** (`workers/streetlight-worker/config/AGENTS.md` + `workers/population-worker/config/AGENTS.md`):
- 新增 `## Team Room @-mention 静默约束(强契约)` 段:6 类空 ack 禁用 + 仅 3 类情况才 @ Leader(新数据 / service 失败 / 澄清问题)
3. **下游 classifier**:客户端 SSE 渲染前折叠 post-FINAL narrative + compound ack(haopaw 已实现,但所有用 HiClaw Team Room 的下游都要重复实现 — 这是 framework gap 的下游放大)

prompt 加强后实测 mirror loop 从 8 条降到 3-5 条,但**未消除** — 因此 file 此 issue 推进 framework 级修复。

### 修复后 acceptance

- 用 haopaw team-X-Y fixture(或上游构造的等价 Team Room fixture)跑同一 `路灯有多少?` query
- expected:Matrix Team Room 只产生 5 个 event (user query → leader dispatch → worker reply → leader FINAL → 终)
- 实际:mirror loop 0 条 / `copaw/src/copaw_worker/worker.py` bootstrap 起的 agent reply pipeline log 行数 ≤ 3(Leader 入 + worker 入 + Leader 综合入)
- session log retain 全 5 event(不要 silent-drop session 上下文)

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.