Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 324 additions & 0 deletions docs/design/2026-07-28-lark-chat-rename-skill-requirement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
# botmux Skill 支持 AI 动态修改飞书群名称

## 1. 背景

botmux 在飞书群内持续运行时,群名称通常由建群者一次性设定。随着任务推进,群的目标、阶段和当前状态可能已经变化,原名称无法及时表达群内正在进行的工作。

希望为 botmux 增加一个内置 Skill,使群内 AI 可以根据对话上下文,在满足权限与安全约束时动态修改当前群名称。例如:

- `支付链路排障` → `支付链路排障|定位中`
- `支付链路排障|定位中` → `支付链路排障|待验证`
- `支付链路排障|待验证` → `支付链路排障|已完成`

## 2. 目标

提供一项由 AI 主动调用的群名称修改能力:

1. AI 能在群运转过程中,根据任务主题或阶段灵活修改当前飞书群名称。
2. 发起改名的 bot 必须是目标群的当前成员。
3. 默认只允许修改当前会话所在群,避免 AI 越权操作其他群。
4. 操作结果对 AI 可判断、对用户可感知、对系统可审计。
5. 避免同名写入、频繁抖动、循环改名和恶意输入。

## 3. 非目标

首期不包含:

- 修改单聊名称。
- 修改任意指定群或跨群批量改名。
- 绕过飞书权限、群角色或租户限制。
- 根据每一条消息自动强制改名。
- 自动修改群头像、群描述、公告等其他群属性。
- 允许 AI 选择其他 bot 的身份执行改名。

## 4. 用户故事

### 4.1 AI 主动更新任务阶段

当 AI 判断群内任务已从“分析”进入“执行”阶段时,可以将当前群名称从 `数据库迁移` 修改为 `数据库迁移|执行中`,无需用户手工操作。

### 4.2 用户明确要求改名

用户在群内说“把群名改成数据库迁移验收”,AI 识别意图并调用该 Skill。成功后回复最终群名;失败时说明是权限、成员身份、名称校验还是飞书 API 导致。

### 4.3 多 bot 群

群内存在多个 bot 时,只能由实际调用 Skill 的 bot 使用自己的应用身份修改群名称。该 bot 不在群内时必须拒绝,不能静默切换到群内其他 bot 代为执行。

## 5. 功能需求

### FR-1:新增内置 Skill

新增内置 Skill,建议名称为 `botmux-chat-rename`。

触发场景包括:

- 用户明确要求修改、更新或重命名当前群。
- AI 判断当前群名已明显不能反映任务主题。
- AI 判断任务进入具有用户价值的关键阶段,例如“执行中”“待确认”“已完成”。

Skill 完整说明必须通过现有渐进披露机制提供:

```bash
botmux skill show botmux-chat-rename
```

Skill 应指导 AI:

- 只处理当前群。
- 先生成简短、稳定、可读的新名称。
- 名称未发生实质变化时不调用。
- 不因细小进度变化反复改名。
- 成功后简短告知用户;失败时输出可行动的原因。

### FR-2:提供受控 CLI

提供供 Skill 调用的 CLI,建议形式:

```bash
botmux chat rename "新的群名称"
```

CLI 从当前 botmux 会话上下文解析:

- `chatId`
- `chatType`
- `larkAppId`
- 当前 bot 身份

首期不提供 `--chat-id` 和 `--lark-app-id` 给 AI 使用,避免绕过当前会话边界。

机器可解析的成功输出至少包含:

```json
{
"ok": true,
"chatId": "oc_xxx",
"oldName": "数据库迁移|分析中",
"newName": "数据库迁移|执行中",
"changed": true
}
```

同名请求返回 `ok: true`、`changed: false`,且不调用飞书更新接口。

失败时使用非零退出码,并返回稳定错误码,例如:

- `not_group_chat`
- `missing_session_context`
- `bot_not_in_chat`
- `invalid_chat_name`
- `permission_denied`
- `rate_limited`
- `lark_api_error`

### FR-3:群成员约束

执行改名前必须验证当前 `larkAppId` 对应的 bot 确实在目标群中。

验证原则:

1. 目标必须是当前会话所在群。
2. 当前 bot 必须能以自己的身份读取或更新该群。
3. 若飞书返回“bot 不在群内”、无权访问或群不存在,统一转成明确、可操作的错误。
4. 不允许在失败后自动遍历其他 bot 凭据重试。

即使某个其他 bot 在群内且具备权限,也不能替代当前调用 bot 执行。

### FR-4:飞书群名称更新

通过飞书群更新接口修改群名称,使用当前 bot 的应用身份调用:

```ts
client.im.v1.chat.update({
path: { chat_id: chatId },
data: { name: newName },
})
```

应用需要具备飞书要求的群信息更新权限,当前权限清单中的 `im:chat:update` 应纳入启动检查或能力诊断。

群角色和租户策略仍以飞书实际返回为准。botmux 不伪造成功状态。

### FR-5:名称校验

写入前必须:

- 去除首尾空白。
- 拒绝空名称和仅含空白的名称。
- 按飞书实际限制校验长度;限制值应集中定义,不散落在 Skill 文案中。
- 拒绝换行、控制字符和不可见格式控制字符。
- 保留正常的中文、英文、数字、空格及常用标点。
- 获取当前群名并进行规范化比较,避免重复写入。

若名称超长,不应静默截断;返回校验错误,让 AI 重新生成,避免含义被意外改变。

### FR-6:防抖与幂等

为了避免 AI 在连续对话中频繁修改群名:

- 同一群同一名称请求必须幂等。
- 同一 bot 对同一群设置改名冷却时间,建议默认 10 分钟。
- 用户在当前消息中明确要求改名时,可以绕过 AI 主动改名的冷却限制,但仍受飞书 API 限流约束。
- 冷却判断应基于持久化审计记录,daemon 重启后仍有效。
- 并发请求需要按 `chatId` 串行化,避免后到请求被先到响应覆盖。

首期不要求 daemon 自动判断阶段并改名;是否调用由 AI 根据 Skill 说明决定。

### FR-7:结果同步

改名成功后:

1. 更新 botmux 已缓存的群名称,或使相关缓存立即失效。
2. Dashboard、群组列表、relay picker 和后续会话上下文应最终展示新名称。
3. AI 向当前群发送简短结果,例如:`群名称已更新为「数据库迁移|执行中」`。

若飞书写入成功但本地缓存刷新失败,操作仍视为成功,同时记录缓存刷新告警。

### FR-8:审计

每次实际写入记录结构化审计日志:

- 时间
- `chatId`
- `larkAppId`
- bot 的 open ID
- 旧名称
- 新名称
- 触发类型:`user_explicit` 或 `ai_proactive`
- 结果和飞书错误码
- 当前 botmux session ID

日志不得记录应用密钥、access token 或完整对话内容。

## 6. AI 命名策略

Skill 对 AI 提供以下默认策略:

1. 优先保持群的核心主题稳定,只在必要时更新阶段后缀。
2. 名称应让不了解上下文的人也能快速判断群的用途。
3. 避免写入时间敏感但价值很低的信息,例如精确百分比或每分钟变化的状态。
4. 避免夸张、评价性、敏感或可能冒犯群成员的措辞。
5. 以下情况适合主动改名:
- 原名称是无意义的默认名。
- 群内目标已经明确且与原名称明显不符。
- 任务跨越关键生命周期阶段。
6. 以下情况不应主动改名:
- 只是出现了一个临时支线话题。
- AI 对群的主要目标没有足够把握。
- 上次改名后没有发生实质阶段变化。
- 用户明确表示不要自动改名。

## 7. 安全与权限

- 该能力属于有外部副作用的写操作。
- 用户明确要求改名时,AI 可直接执行。
- AI 主动改名仅适用于低风险、可恢复且与当前任务明显相关的名称调整。
- 部署方应能按 bot 或全局关闭 AI 主动改名,同时保留用户明确触发能力。建议配置:

```json
{
"chatRename": {
"enabled": true,
"allowAiProactive": true,
"cooldownMinutes": 10
}
}
```

- `enabled=false` 时 CLI 返回 `feature_disabled`。
- `allowAiProactive=false` 时只接受能关联到当前用户明确指令的请求。

## 8. 兼容性与影响面

- 平台:首期仅支持飞书/Lark;其他 IM 返回 `unsupported_platform`。
- CLI:能力通过 botmux CLI 暴露,与 Claude Code、Codex 等底层 AI CLI 无关。
- 后端:PTY、tmux、zellij、riff 等会话后端行为一致。
- 会话:仅群会话可用;单聊、无群绑定的 workflow、脱离飞书上下文的本地会话不可用。
- 多 bot:每个 bot 独立鉴权、独立审计,不做凭据回退。
- Sandbox:AI 不直接访问凭据,CLI 通过 daemon/受控服务执行飞书写操作。

## 9. 验收标准

### AC-1:明确改名成功

- Given 当前 bot 在群内且具备权限
- When 用户要求 AI 将当前群改为合法的新名称
- Then AI 调用 Skill,飞书群名更新成功,并在群内反馈最终名称。

### AC-2:bot 不在群内

- Given 调用 bot 不在目标群
- When 发起改名
- Then 操作失败,返回 `bot_not_in_chat`,且不尝试使用其他 bot。

### AC-3:阻止跨群

- Given AI 尝试指定另一个 `chatId`
- When 调用改名 CLI
- Then CLI 不接受目标群参数,只能使用当前会话绑定的群。

### AC-4:同名幂等

- Given 新名称与当前群名称规范化后相同
- When 发起改名
- Then 返回成功且 `changed=false`,飞书更新 API 不被调用。

### AC-5:非法名称

- Given 名称为空、超长或包含非法控制字符
- When 发起改名
- Then 返回 `invalid_chat_name`,群名称保持不变。

### AC-6:AI 主动改名防抖

- Given AI 刚刚主动修改过群名称
- When 冷却期内再次主动改名
- Then 返回 `rate_limited`,并告知剩余冷却时间。

### AC-7:缓存同步

- Given 飞书群名更新成功
- When 用户查看 Dashboard 或创建后续会话
- Then 展示更新后的名称,不长期保留旧名称。

### AC-8:权限不足

- Given 当前应用缺少 `im:chat:update` 或不满足飞书群角色要求
- When 发起改名
- Then 返回 `permission_denied` 及权限修复提示,不显示虚假成功。

## 10. 建议测试

### 单元测试

- 名称规范化与非法字符校验。
- 同名幂等。
- 冷却期、用户明确触发绕过和并发串行化。
- 飞书错误码到稳定错误码的映射。
- Skill catalog 与 `botmux skill show botmux-chat-rename`。

### 集成测试

- 当前群、当前 bot 上下文解析。
- 使用当前 bot 客户端调用 `chat.update`。
- bot 不在群内时拒绝,且不遍历其他 bot。
- 成功后的群名缓存失效或刷新。
- 不同会话后端调用结果一致。

### 飞书实测

- 普通群内明确要求改名。
- AI 主动在关键阶段改名。
- 缺少权限、bot 被移出群、群角色受限。
- 多 bot 群中确认实际调用身份。
- Dashboard 和后续消息上下文展示新名称。

## 11. 建议实现拆分

1. 在 Lark service 层新增 `renameChat(larkAppId, chatId, name)`。
2. 新增名称校验、错误归一化和审计存储。
3. 新增 session-scoped `botmux chat rename` CLI/daemon IPC。
4. 新增 `botmux-chat-rename` 内置 Skill 并加入渐进披露目录。
5. 接入配置、冷却与缓存刷新。
6. 补齐单测、集成测试和飞书 live 验证。
Loading