agentscope-ai / agentscope-ai/AgentTeams

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

Abierto
#804 2 comentarios 0 reacciones 1 asignado Reclamado por @Sunrisea Ver en GitHub
area:matrix-element bug
Lenguaje dominante
Go
Estrellas
5.6k
Forks
692
Merge medio
5 d 4 h
PR fusionados (30 d)
23

Descripción

### 摘要

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 上下文)

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.