Skip to content

[Feature] drive +list-comments:默认按用户实际可见口径返回评论,含孤立锚识别与按位置排序 #1111

@kyalpha313

Description

@kyalpha313

背景

drive file.comments list 当前直接透传开放平台 API,返回的评论包含锚定文本已被删除/重写的评论(飞书 UI 在文档侧栏隐藏它们,但 API 仍原样返回)。对 AI Agent 而言,这会产生"幽灵评论"——根据已不存在的引用上下文起草无意义的回复。

本 issue 提议新增一个 shortcut drive +list-comments,默认行为对齐用户在飞书 UI 看到的评论卡片。

复现

以一个 docx 为例(实际线上文档):

  • 飞书 UI 侧栏显示 22 条未解决评论
  • drive file.comments list --params '{"file_token":"<token>","file_type":"docx","is_solved":false}' 返回 29
  • 差异 = 7 条评论锚定原文已被改 / 删除

7 条孤立评论的两种成因

类型 例子 占比
A. 锚定文本整体被删除 "Initiate Project"、"SME 完成 KEDB 的最终更新…" 6 / 7
B. 锚定的结构化元素(sticky note 等)被删除 quote 为 [Sticky note],文档里无 <readonly-block> 1 / 7

一个微妙的反例:HTML 标签拆分锚文本

API 返回的 quote 是 由 P2 / P3 事件聚类触发:…,但 XML 正文里这段文本被加粗:由 <b>P2 / P3 事件聚类</b>触发:…。简单 strings.Contains 会误判为孤立。算法必须先剥 HTML 标签 / 解 HTML entities,再做匹配。

单一锚文本在文档多处出现

API 返回的 quote 是 已知错误(4 字短锚)。文档里 已知错误 出现 43 次,但这条评论锚定的具体位置已被删除。仅靠"quote 在文档里有无出现"会误判为 valid。

结论:API 没有暴露 anchor block_id,纯文本匹配在短锚 + 多处出现场景下会有假阳性。本 shortcut 接受这个限制(属保守降级),并在文档中说明。

提议

CLI 形态

# 默认:筛选=未解决 + 锚定有效,输出=含评论的回复 + reaction,顺序=按 anchor 出现顺序
lark-cli drive +list-comments --doc <docx-url-or-token>

# Opt-in 扩展
--include-orphaned     # 把孤立锚的评论加回来(尾部)
--include-resolved     # 加上已解决评论
--no-reactions         # 关闭 reaction 拉取(节省一次接口往返)
--order=created        # 切到创建时间排序(默认 anchor 出现顺序)

输出结构

复用 drive file.comments list 原响应结构,每条评论多两个派生字段:

{
  "comment_id": "...",
  "quote": "...",
  "is_solved": false,
  "is_whole": false,
  "anchor_state": "valid" | "orphaned" | "structural",
  "anchor_position": 12345,
  "reply_list": { "replies": [ { ..., "reactions": [...] } ] }
}

内部流程

  1. 解析 --doc,wiki URL 复用 parseCommentDocRef / resolveCommentTarget 解包。
  2. GET /open-apis/drive/v1/files/:file_token/comments 分页拉到底(按需带 need_reaction=true)。
  3. POST /open-apis/docs_ai/v1/documents/:token/fetch 一次拉取正文(format=xml,详见 fix: remove unsupported docs fetch text format #1109text 不可用)。
  4. 对每条评论计算 anchor_state
    • is_whole=truevalid(全文评论无需锚检测)
    • quote == "[Sticky note]" → 检查正文是否有 <readonly-block> 元素 → 有则 structural,否则 orphaned
    • 其它 → 用 quote 首行 + 剥 HTML 标签 / 解 entities / 归一空格的子串匹配 → valid / orphaned
  5. anchor_position 排序(orphan 末尾,可丢弃)。
  6. 按 flag 过滤后输出。

MVP 范围

  • 仅支持 docx(sheet / slides 锚是 cell / xml-id,单独工作)
  • 不解决"短锚多处出现"的歧义(在 doc 里说明 known limitation)

为什么是新 shortcut 而非给原命令加 flag

  1. 不动 drive file.comments list 现有行为 / 输出结构,避免破坏依赖原始计数的脚本。
  2. 当前 shortcuts/drive 下唯一与评论有关的 shortcut 是 +add-comment;新增 +list-comments 为后续 +resolve-comment / +update-reply 等读改类 shortcut 建立命名 pattern。
  3. shortcut 是飞书 / AI 双使用方的"傻瓜版"入口,智能默认(unresolved + non-orphan + 按位置 + 带回复)契合 shortcut 的设计哲学(参考 +search+inspect 等)。

Skill 同步

落地后建议在 skills/lark-drive/SKILL.md "评论查询与统计口径" 一节中:

  • drive +list-comments 设为"读评论"任务的默认入口
  • 保留 drive file.comments list 文档供高阶 / 调试场景使用;
  • 补充孤立锚识别的算法局限。

替代方案考虑过的

方案 否决原因
drive file.comments list--exclude-orphaned flag 改默认会破坏脚本;只加 flag 又难推动普通用户主动使用
在 SKILL.md 加"必须先 fetch 文档交叉验证 quote"强制规则 LLM 每次手动复算,开销高 + 易跳过;其他 CLI 用户得不到收益
等 Lark Open API 暴露 is_orphaned 字段 周期长,且 API 已稳定,client-side 兜底更现实

如果方向 OK,我可以同步提 PR。

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain/coreCLI framework and core librariesdomain/docDocs domainenhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions