# Agent Echo AgentTeam Prevention Spec

> Canonical path: `specs/agent/echo-agentteam-prevention.md`
> Related specs: [../collaboration/collaboration.md](../collaboration/collaboration.md), [../message/delivery-runtime.md](../message/delivery-runtime.md), [../daemon/prompt-compiler-architecture.md](../daemon/prompt-compiler-architecture.md)

Date: 2026-04-13
Status: draft
Scope: agent system prompt improvements to prevent echo loops and reduce unnecessary replies in multi-agent conversations (channel scopes, thread scopes, DMs)
Non-goals: platform-level suppression of legitimate `@agent` wake delivery; reaction/emoji system; removing legitimate agent-to-agent collaboration delivery

Implementation note (2026-04-20, updated 2026-04-22): 这份 spec 的 canonical wording 现已落到 markdown prompt templates。AgentTeam channel 的 channel-level wording 维护在 `desktop/prompts/channel/agentteam/{core,reference}.md`，由 `skilldoc.go` materialize；事件级 wording 维护在 `desktop/prompts/events/*`，再由 `desktop/internal/promptcompiler/*` 编译为 runtime input。

Implementation note (2026-05-08): `@agent` 的直觉语义是无论 human 还是 agent 发起都应该唤醒目标 agent；死循环防护不能把 agent-authored mention 降级为 no-wake。当前实现把防护放在 runtime turn contract：agent-authored wake 仍然是真实 wake，但不代表必须发一条可见确认；被唤醒 agent 应该直接处理实质请求 / handoff，只在有结果、阻塞、决策请求或新信息时发送可见消息，轻量确认使用 reaction。

## 1. 问题

### 1.1 回声循环

在多个 agent 共同参与的对话空间中（channel、thread、甚至多人 DM），以下连锁反应频繁发生：

1. Agent A 发了一条消息（完成报告、状态更新、回复 human 等）
2. Agent B 被唤醒（通过 thread participant inheritance、channel mention、广播等任何投递路径），看到 A 的消息，觉得应该回应，回复 "收到" / "了解"
3. Agent A 被唤醒，看到 B 的消息，又回复 "好的"
4. 循环继续，直至所有 agent 耗尽 context 或超时

这是多 agent 共同参与对话时的通用问题，不限于特定的投递路径或触发方式。只要一个对话空间中有两个以上的 agent，且其中一个 agent 发了消息，就有可能引发连锁回复。

常见场景：

- **Thread 协作开发**：human 在 thread 里拉了 agent-coder 和 agent-reviewer，coder 提交了代码，reviewer 回复了 review 意见，coder 回复 "收到正在改"，reviewer 又回复 "好的"
- **Channel 内多 agent 讨论**：多个 agent 在同一个 channel 中被 mention 或参与讨论，互相对对方的发言做出不必要的回应
- **广播完成**：`@all` 广播后，每个 agent 完成各自工作并报告，其他 agent 看到报告后回复确认

### 1.2 为什么会发生

回声循环的形成需要三个条件同时成立：

1. **多 agent 共同参与**：多个 agent 处于同一个对话空间（channel 或 thread），通过各种投递路径（direct mention、thread participant inheritance、broadcast 等）都能收到该空间内的消息
2. **Agent 无法判断"不需要我回应"**：当前 prompt 没有提供足够的判断规则，agent 倾向于回复所有收到的消息
3. **Agent 回复再次触发投递**：agent 的回复作为新消息进入对话空间，唤醒其他 agent

条件 1 和 3 是平台机制，且是正确的——多 agent 共同参与对话是协作的基础，投递机制保证了 agent 能看到相关消息。本 spec 只解决条件 2。

### 1.3 当前 prompt 的不足

审查当前 compiler-owned AgentTeam prompt packs，与回声循环相关的现有规则：

**`desktop/prompts/channel/agentteam/reference.md` — Broadcast and task-notify protocol**：
- "Decide yourself whether a public reply would add value"
- "avoid duplicating the same public update"

**`desktop/prompts/channel/agentteam/core.md` — Conversation etiquette**：
- "Don't interrupt ongoing conversations"
- "Only the person doing the work should report on it"
- "Don't echo another agent's work"

**`desktop/prompts/events/wake_message.md` — wake event wording**：
- "Respond as appropriate — reply using send_message, or take action as needed"

问题在于：

1. **规则过于模糊**："Decide yourself" 和 "Respond as appropriate" 给了 agent 太大的自由裁量空间。LLM 的默认倾向是回复——沉默需要比回复更强的理由。
2. **缺少"不回复"的明确指导**：没有具体列出哪些场景应该静默。Agent 面对每条消息都需要做"回还是不回"的判断，但缺少 negative examples。
3. **规则只针对广播场景**：Broadcast protocol 只提到了 `@all` / `@online` 后的行为，但回声循环在普通 thread 协作中同样频繁发生。
4. **`type=agent` 标记利用不足**：消息头中已经包含 `type=agent` 标记（`process.go` 第 1122-1123 行），但 prompt 中没有告诉 agent 如何利用这个标记辅助判断。

### 1.4 影响

- **用户体验**：thread 被无意义的 "收到" 消息刷屏，有用信息被淹没
- **Token 成本**：每次回声循环中每个 agent 都消耗 context window 来处理和回复无意义消息
- **Agent 可靠性**：真正需要处理的消息可能因 context 被填满而被遗漏
- **Agent 拉起成本**：每次唤醒（从 sleep → active）都有 runtime 启动成本

## 2. 设计原则

### 2.1 不改动平台机制

本 spec 只修改 agent 系统 prompt，不改动消息路由、thread participant inheritance、agent 唤醒机制、消息投递格式。

理由：当前的投递机制本身是正确的——无论是 thread participant inheritance、direct mention 还是 broadcast，它们保证了 agent 能看到相关对话。问题不在于 agent 收到了消息，而在于 agent 对不需要回应的消息做了不必要的回复。

### 2.2 明确的 negative rules

与其告诉 agent "自己判断要不要回复"（模糊），不如明确列出"以下场景不要回复"（具体）。LLM 对 explicit negative rules 的遵从度远高于 vague positive guidance。

### 2.3 通用规则，不限场景

规则应该适用于所有多 agent 交互场景（thread 协作、广播、讨论），而不是只针对某个特定触发方式。

### 2.4 兼容协作场景

不能一刀切地让 agent 忽略其他 agent 的消息。以下场景中 agent-to-agent 通信是必要的：

- Review agent 通知干活 agent "review 完了，可以 merge"
- Agent A 在 thread 中请求 Agent B 协助
- 多个 agent 在 thread 中讨论方案，互相补充信息

判断依据应该是**消息内容是否需要我采取行动或补充实质信息**，而不是**发送者是不是 agent**。

## 3. Prompt 变更

### 3.1 AgentTeam core/reference prompt pack 变更

#### 3.1.1 在 `desktop/prompts/channel/agentteam/core.md` 的 "Conversation etiquette" 后新增 "When NOT to reply"

新增 canonical prompt text：

```text
### When NOT to reply

Not every message you receive needs a response. Unnecessary replies cause echo loops — where agents keep replying to each other's acknowledgments in a cycle. This is the #1 noise problem in multi-agent conversations.

1. **Acknowledgment-only messages**: Never send a message whose only content is confirming receipt — "got it", "understood", "收到", "了解", "好的". If you have no substantive content to add, stay silent.
2. **Status updates that don't need you**: When another agent reports completion or progress ("done", "PR created", "merged"), do not reply unless the message asks you to take a specific action.
3. **Your work is already done**: After you finish your part and report your result, do not reply to subsequent messages in the same thread unless someone asks you something or requests further action.

**Rule of thumb**: Before sending any message, ask: "Does the recipient need this message to do their job, or am I just being polite?" If it is just politeness, do not send it.
```

#### 3.1.2 修改 `desktop/prompts/channel/agentteam/reference.md` 的 "Broadcast and task-notify protocol"

- 旧：`@all` / `@online` 后让 agent 自行判断 public reply 是否有价值
- 新：明确要求只有在 broadcast 需要你报告结果或采取可见动作时才回复；禁止只为确认而回复

#### 3.1.3 修改 `desktop/prompts/channel/agentteam/core.md` 的 "Communication style"

- 旧：收到任务就先确认并简述计划
- 新：只有收到 **human** 的任务或直接请求时，才要求先确认并简述计划

### 3.2 Wake event prompt

不修改 `desktop/prompts/events/wake_message.md` 的核心语义。"Respond as appropriate - reply using send_message, or take action as needed" 保持不变。去掉 "reply using send_message" 可能导致 agent 对 human 消息也不回复，副作用大于收益。

## 4. 变更前后对比

### 4.1 场景：多 agent thread 协作

**变更前**：

```
[thread]
Human: @agent-A 写代码, @agent-B review
Agent-A: 好的，开始写...
Agent-A: 代码写好了，PR: https://...
Agent-B: (唤醒) 好的，开始 review...
Agent-A: (唤醒看到 B 的消息) 收到
Agent-B: (唤醒看到 A 的 "收到") 了解
Agent-A: (唤醒看到 B 的 "了解") 好的
...
```

**变更后**：

```
[thread]
Human: @agent-A 写代码, @agent-B review
Agent-A: 好的，开始写...
Agent-A: 代码写好了，PR: https://...
Agent-B: (唤醒) review 完了，有两个 comment 需要改 [有实质内容，回复]
Agent-A: (唤醒，判断：B 要求我改 comment → 有行动 → 回复) 收到，正在改...
Agent-B: (唤醒看到 A 说"正在改"，判断：规则2 状态更新 + 规则3 我的 review 工作已完成 → 静默)
```

### 4.2 场景：@all 广播后 agent 完成各自工作

**变更前**：

```
Human: @all 设置自己的头像
Agent A: done
Agent B: done
Agent A: (唤醒) 收到
Agent B: (唤醒) 了解
...
```

**变更后**：

```
Human: @all 设置自己的头像
Agent A: done
Agent B: done
(A 唤醒看到 B 的 "done"，判断：规则2 状态更新不需要我行动 + 规则3 我的工作已完成 → 静默)
(B 唤醒看到 A 的 "done"，判断：规则2 状态更新不需要我行动 + 规则3 我的工作已完成 → 静默)
```

### 4.3 场景：Agent 之间有实质协作需求

**变更前后应该一致**：

```
[thread]
Agent-reviewer: review 完了，有两个 comment 需要修改
Agent-worker: (唤醒，判断：消息要求我采取行动 → 回复) 收到，正在修改...
```

消息包含需要 agent-worker 行动的具体内容，不是单纯的状态通知，应该回复。

### 4.4 场景：Human 在 thread 中追问

**变更前后应该一致**：

```
[thread]
Human: @agent-A 帮我看看这个 bug
Agent-A: 在看了...
Human: 找到原因了吗？
Agent-A: (唤醒，判断：human 在追问我 → 回复) 找到了，是...
```

Human 的消息始终应该被认真对待和回复。

## 5. 实现

### 5.1 修改文件

| 文件 | 变更 |
|------|------|
| `desktop/prompts/channel/agentteam/core.md` | 新增 "When NOT to reply" 部分；修改 communication style |
| `desktop/prompts/channel/agentteam/reference.md` | 修改 broadcast and task-notify protocol |

### 5.2 测试验证

由于 prompt 变更效果依赖 LLM 行为，无法通过 unit test 验证。建议：

1. **人工测试**：在测试 workspace 中重现回声循环场景（多 agent 共同参与的 channel 或 thread），确认 prompt 变更后循环不再发生
2. **观察期**：部署后观察 1-2 天，收集 agent 回复模式变化数据
3. **调优**：如果发现 agent 在应该回复时过于沉默，调整 "When NOT to reply" 规则的措辞强度

### 5.3 回滚策略

Prompt 变更是纯文本修改，回滚只需 revert commit。已运行的 agent session 需要重启才能生效新 prompt。

## 6. 平台层边界

宽泛的平台级过滤仍然不采用：

| 方案 | 未采纳原因 |
|------|-----------|
| Agent 消息不触发其他 agent 唤醒 | 破坏了合法的 agent-to-agent 协作场景（review → worker） |
| 只有 @mention 才唤醒 | 破坏了 thread participant inheritance 和 channel 内的正常协作 |
| Agent 的消息不触发投递给其他 agent | 过于粗暴，有些 agent-to-agent 交互是 human 期望的 |
| 加入 cooldown / rate limit | 治标不治本，只是延迟了回声循环 |

这些方案的共同问题是：**在投递层做过滤会损失信息**。Agent 不收到消息 ≠ agent 不应该处理消息。正确的做法是让 agent 收到消息但学会判断是否需要回应。

2026-05-08 补充的实现原则：

- agent-authored direct mention 仍然 immediate wake 目标 agent；
- 防循环约束属于被唤醒 runtime 的行为语义：不要为了确认收到、确认接手、说自己暂停、或要求对方暂停而发送可见消息；
- 如果 agent-authored wake 只是 handoff，被唤醒 agent 应直接开始处理，并在有结果、阻塞、决策请求或新信息时再发可见消息；
- durable delivery / targeted event / wake 语义不因发送者是 agent 而降级；
- 因此它不是“agent 消息不投递”或“agent 消息不唤醒”的平台过滤，而是避免社交确认消息驱动 wake cascade。

## 7. 与 Reaction 系统的关系

Reaction 已上线（见 [reaction.md](../message/reaction.md)）。与回声循环防护的交互：

- Reaction 是"我看到了但不需要回复"的轻量表达方式，agent 应优先使用 reaction 代替纯确认消息（"收到"、"了解"）
- 本 spec 的 "When NOT to reply" 规则仍然适用——agent 不应对不需要回应的 reaction delivery 做出文字回复或反向 reaction（否则 reaction 本身可能形成回声）
- Agent prompt 中已通过 `desktop/prompts/channel/agentteam/core.md` 追加 reaction 规则（见 reaction.md §9），与本 spec 的 "When NOT to reply" 协同生效

## 8. 开放问题

1. **规则强度**：当前 "When NOT to reply" 用了 "do NOT reply" / "Never send" 这样的强指令。建议先用强指令上线，如果发现 agent 在应该回复时沉默，再调弱。
2. **跨 runtime 适用性**：当前变更同时影响 Claude Code、Codex、OpenCode 等 runtime。它们共享同一组 compiler-owned AgentTeam prompt packs，但渲染/transport 行为不同，仍需要分别观察。
3. **未来 channel 类型**：本 spec 只修改当前支持的 AgentTeam channel prompt packs。若未来新增其他 channel 类型，需要在对应 `desktop/prompts/channel/*/*.md` 中评估并加入同类规则。
