Skip to content

feat(doc-comment): 支持会议场景实时监听与自动回复#429

Draft
ejjcc wants to merge 6 commits into
masterfrom
agent/watch-comment-realtime
Draft

feat(doc-comment): 支持会议场景实时监听与自动回复#429
ejjcc wants to merge 6 commits into
masterfrom
agent/watch-comment-realtime

Conversation

@ejjcc

@ejjcc ejjcc commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

使用场景

会议开始前,发起人把评审文档分享给参会人或投屏,并在飞书话题中执行:

/watch-comment <文档链接> --all

Botmux 会立即创建或复用当前话题的 AI session,启动 CLI 并让 Agent 预读整篇文档。会议过程中,参会人可以直接在文档中发表评论,不需要 @Bot;Botmux 自动发现评论,把选中原文、评论串上下文和当前问题交给已预热的 Agent,并将答案写回原评论讨论串。

目标是把 CLI 冷启动、文档读取和上下文建立提前到会前完成,让会议中的评论回复只承担增量推理成本。

背景与根因

飞书的 drive.notice.comment_add_v1 属于评论通知事件,能够可靠覆盖真正 @Bot 的评论,但普通评论不会稳定推送给应用。

逐文档 subscribe API 可以覆盖全部评论,但飞书要求 User Token;应用身份调用实测返回 HTTP 403。仅保留本地 watch 记录因此无法实现“不 @ 也回复”。

旧实现还会在没有活跃会话时等待第一条评论再启动 CLI,导致会议中的第一条问题承担 CLI 启动、工具安装和全文读取延迟。

架构设计

1. 命令职责拆分

  • /subscribe-lark-doc 保留原有逐文档 API 订阅语义和 User Token 流程。
  • /watch-comment 负责评论监听、会话绑定/创建、审批、预热和自动回复。
  • 两个命令不互为别名,恢复、关闭和权限检查按 managedBy 分流。

2. 混合评论入口

  • mention-only:继续使用长连接事件,真正 @Bot 时即时触发。
  • all:每 5 秒使用应用身份增量读取评论列表,普通评论无需 @Bot
  • 应用身份只负责读取和回复评论,不获取文档 User Token。

3. 增量游标与防循环

  • 登记监听时建立历史评论基线,不重放旧评论。
  • 持久化 createdAt + replyId 游标,daemon 重启后继续增量读取。
  • 长连接事件与轮询共用 turn 去重。
  • 通过 Bot open_id、已创建 reply id 和隐形 sentinel 过滤 Bot 自己的回复,避免自触发循环。

4. 会前 session 预热

真正的 /watch-comment <链接> 不再作为纯 sessionless 管理命令处理:

  • fresh topic 立即创建正常话题 session;
  • 立即启动 CLI;
  • 注入会前预热 prompt;
  • Agent 读取文档、建立结构/结论/术语/潜在讨论点上下文;
  • 在话题中发送简短就绪说明,然后进入评论待命。

list/off/pending/approve/deny 仍保持 sessionless,不会产生空会话。

未执行命令、直接在陌生文档中第一次 @Bot 的零命令入口仍使用 doc:<fileToken> 文档原生会话。

5. 文档评论 prompt

评论轮次会向 Agent 提供:

  • 文档 URL、file token 和 file type;
  • 局部评论选中的原文;
  • 当前评论串的历史回复;
  • 当前问题和作者;
  • 文档优先回答约束;
  • 正文不足时先读取文档、不可猜测;
  • 评论内容按不可信输入处理;
  • Agent 不直接调用评论/reaction API,只输出用户可见答案。

6. 回复分流

  • 文档评论轮次的正文写回原评论串。
  • 真实飞书话题 session 的预热就绪说明和状态卡仍留在话题。
  • doc: 文档原生会话禁止向虚拟 chat id 发送流式卡片。
  • 回复由 Botmux 统一投递并清理 Typing reaction。

影响范围

  • 主要影响 im/lark/、文档订阅存储、command handler 和 daemon 文档评论路径。
  • 其它 IM 平台不受影响。
  • CLI 无关:通过现有 worker/session 抽象注入,未修改具体 CLI 适配器。
  • 同时覆盖正常话题会话和文档原生会话;PTY/Tmux 共用既有 worker 路径。
  • /subscribe-lark-doc 的旧 User Token/逐文件订阅逻辑保持不变。
  • Dashboard、命令帮助和中英文文档同步更新。

测试结论

自动化验证

pnpm vitest run \
  test/doc-comment-prompt.test.ts \
  test/doc-watch-command.test.ts \
  test/doc-comment-poller.test.ts \
  test/command-handler.test.ts \
  test/worker-ready-display-mode.test.ts \
  test/event-dispatcher.test.ts \
  test/dashboard-i18n.test.ts

结果:合并最新 master 后复验,7 个测试文件、378 项测试全部通过。

pnpm build
git diff --check

结果:TypeScript 构建、Dashboard bundle 和 diff 检查均通过。

补充尝试了全量 pnpm vitest run;当前机器 Git 2.25 不支持部分既有测试使用的 git init -b,因此 worktree/tmux 相关用例出现环境兼容性失败,与本 PR 文档评论路径无关。

真实飞书 E2E

  1. 普通评论无需 @

    • /watch-comment --all 成功建立历史基线;
    • 新增不带 @Bot 的普通评论后,约 5 秒内出现轮询 dispatch;
    • 评论上下文成功注入 Agent;
    • 回复成功写回原评论串;
    • 游标继续推进,Bot 自己的回复未触发循环。
  2. 会议前预热

    • 12:58:17:fresh topic 收到 /watch-comment 并创建 session;
    • 12:58:19:预热 prompt 注入,worker 启动;
    • 12:58:19:worker ready,未等待任何评论;
    • Agent 完成全文解析,在话题中发送就绪说明;
    • CLI 回到 idle,进入评论待命状态。

首次预热耗时约 5 分 46 秒,主要是首次安装文档解析工具依赖;该成本被提前到会议开始前,后续同环境预热会更快。

caiyijia added 6 commits July 10, 2026 02:13
…tion

- Auto-subscribe documents on first @mention (no manual /subscribe-lark-doc needed)
- Auto-create virtual session (anchor=doc:{fileToken}) when no active session
- Add 'Typing' reaction to user's reply when bot starts processing
- Remove reaction after bot reply is posted (both botmux send + fallback paths)
- Store replyId + reactionId in docCommentTurns + currentDocCommentTarget
…poMap

- Add workingDir field to DocSubscription type
- Add docRepoMap to BotConfig (file_token → local path mapping)
- /subscribe-lark-doc --dir <path> to bind a doc to a repo directory
- Auto-subscribe looks up docRepoMap for workingDir
- autoCreateDocSession uses sub.workingDir as first priority
- /subscribe-lark-doc list shows workingDir per subscription
Events come through WebSocket (drive.notice.comment_add_v1) automatically,
no need for explicit per-document subscribe API call. subscribeDocFile is now
best-effort (skipped gracefully when no user token). The local subscription
record (with --dir workingDir) is always created.
- /subscribe-lark-doc --watch: sets commentTriggerMode to 'all' (all comments trigger reply)
- /doc-watch <url> and /doc-unwatch <url>: quick toggle watch mode
- /doc-watch (no arg): list subscribed docs for this session
- Non-owner @ on unsubscribed doc → pending approval, DM to owner
- /doc-approve <token> / /doc-deny <token>: owner approves/denies
- /doc-pending: list pending approvals
- Owner @ on unsubscribed doc → auto-subscribe immediately
- New doc-pending-approvals store for tracking pending requests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant