# 群聊观察者模式协议

> 定义 Agent 在群聊中的"什么时候说话、什么时候沉默"的标准协议。
> **代码 + LLM 双向配合**，非纯代码硬闸，也非纯 LLM 自由发挥。

---

## 整体流程

```
hmo/xxm 发群消息
      │
      ▼
XMPP Bot 代码层
  ├─ 过滤自己发的消息
  ├─ 过滤非 hmo/xxm 的消息
  └─ 全部转发给后端 LLM（不做 @mention 硬过滤）
      │
      ▼
LLM 根据 session 上下文判断
      │
      ├─ 不应回应 → 输出 __SILENT__
      │                    │
      │                    ▼
      │              代码收到 __SILENT__
      │              → 丢弃，不上屏
      │
      └─ 应该回应 → 输出 __REPLY__ + 正文
                           │
                           ▼
                     代码收到 __REPLY__
                     → 剥标记，发送到群
```

---

## 协议定义

### 1. 代码层职责

| 职责 | 说明 |
|------|------|
| 消息过滤 | 只处理群聊中 `hmo` 和 `xxm` 的消息，过滤自己的消息 |
| 转发 | 不做 @mention 硬检查，全部转发给 LLM |
| 输出处理 | 检查 LLM 回复的开头标记 |
| `__SILENT__` | 以 `__SILENT__` 开头 → 整条丢弃，不发送 |
| `__REPLY__` | 以 `__REPLY__` 开头 → 剥掉标记，正文发送到群 |
| 兜底 | 无标记 / 其他开头 → 直接发送（兼容无标记模式） |

### 2. LLM 层职责

| 场景 | 行为 | 输出 |
|------|------|------|
| 话题完全无关 | 沉默 | `__SILENT__`（裸文本，无格式） |
| 是之前对话的延续 | 正常参与 | `__REPLY__` + 内容（裸文本，无格式） |
| 被 @ 或直接提问 | 必须回应 | `__REPLY__` + 内容（裸文本，无格式） |
| 别人刚说过同样观点 | 不重复，除非被追问 | `__SILENT__`（裸文本，无格式） |
| 不确定是否该回应 | 倾向于沉默（宁缺毋滥） | `__SILENT__`（裸文本，无格式） |

### 3. 标记规范

```
__SILENT__     — LLM 决定不回应，代码静默丢弃
（LLM 决定回应时正常输出内容即可，不需要前缀标记）
```

- 标记必须位于回复的**最开头**
- **必须是裸文本**，不加反引号、星号、括号或其他格式
- 标记后直接跟正文，或换行均可
- 大小写敏感：全大写 + 双下划线

---

## 各 Agent 实现参考

### 代码端（Python 示例）

```python
# XMPP Bot — 群聊消息处理核心逻辑

async def on_groupchat_msg(self, msg):
    sender = str(msg['from'])
    if '自身JID' in sender:
        return  # 过滤自己的消息
    nickname = sender.split('/')[-1] if '/' in sender else ''
    if nickname not in ('hmo', 'xxm'):  # 只处理关键人类的发言
        return

    # 转发给 LLM，不做 @mention 过滤
    await self.call_llm(f"[群聊] {nickname}: {msg['body']}")

async def handle_llm_reply(self, reply):
    if reply.strip().startswith('__SILENT__'):
        return  # 静默丢弃
    # 非 __SILENT__ → 直接发送
    self.send_message(mto=room, mbody=reply, mtype='groupchat')
```

### LLM 提示词（放入 Agent 的指令层）

```
群聊观察者模式协议：
- 所有 hmo/xxm 的群消息都会转发给你，代码层不做 @mention 硬过滤
- 你根据 session 上下文自行判断是否应该回应：
  - 话题与你无关 → 以 __SILENT__ 开头输出（代码收到后丢弃）
  - 话题与你有关 / 是对话延续 / 被直接问 → 正常回复，不需要前缀
- 不需要每条消息都 @，对话延续时基于上下文自然参与
- 注意不要重复别人刚说过的话，除非被追问
```

---

## 设计原则

1. **协议不绑定平台** — Windows OpenCode / Linux Hermes / Mac oMLX 都适用
2. **协议不绑定 LLM** — GPT / Claude / Qwen 都适用，标记格式不变
3. **LLM 做判断，代码做执行** — LLM 决定"说不说"，代码决定"怎么发"
4. **宁缺毋滥** — 不确定时倾向于沉默
