Files
2026-09-02 11:44:52 +08:00

16 KiB
Raw Permalink Blame History

钉钉文档改写流程

本文只处理已有文档的结构和排版策略。普通执行入口固定为 +fetch+update+checkpoint-update;这些 shortcut 统一确认、写入与验证。原子 doc read/update/block 只保留给 shortcut 未公开参数的 JSONML 专家路径,使用前必须读取精确 leaf Schema。

适用边界

进入本文前,必须已经确认用户要改写的是已有钉钉文档 (adoc)。如果用户要新建、要操作表格 / AI 表格 / 文件 / 知识库空间 / 发消息,不要套用本文。

本文覆盖:

  • 已有文档的局部改写、润色、章节补充
  • 段落 ↔ 列表 ↔ 表格 的形态转换
  • 块级精修(callout、分栏、附件插入)
  • overwrite 整篇改写的风险提示与执行
  • JSONML 无损结构改写的入口

本文不覆盖:

  • 新建文档(见 doc-create-workflow.md
  • 知识库空间管理
  • 文档权限、消息分发、待办分派

一、核心原则

1.1 精准手术优于全量覆盖

默认走精准手术——只改用户指定的章节或 block,不动其他内容。具体路径见 §3 速查表。

1.2 保真约束

改写时必须原样保留以下要素,不许替换为纯文本/姓名/链接/占位符:

  • @人 引用(用户、机器人、群)
  • @文档 / @群 等卡片引用
  • 已上传的附件、图片
  • 用户原话引用块
  • 表格表头(除非语义错误且用户确认)

JSONML 模式下这些元素的节点结构见 doc-jsonml-schema.md

1.3 编辑形态优先级

改写已有文档优先 JSONMLmarkdown / element 只在 JSONML 不适用时兜底

优先级 形态 适用
① 首选 --content-format jsonml 保真度最高;callout / 分栏 / 表格 / @人 / 附件 / 颜色 / 嵌套结构都能 1:1 round-trip;写入端有 validator 兜底(§4.4
② 次选 --content-format elementJSON,老接口) JSONML 不支持某个块字段时;或快速插入 callout / 分栏不想构造 JSONML 时;不保真改写正文
③ 兜底 markdown(不带 --content-format 即默认) 纯文本追加、整篇重排骨架;callout / 分栏 / 颜色 / 部分属性会被 markdown 还原过程丢失

实操判断:

  • 用户给已有 nodeId 要「改一段、改属性、加 callout、动结构」——走 §4.4 JSONML 路径
  • 用户要「在末尾追加一节纯文本 / 整篇按新骨架重写」——走 §4.2 / §4.5 markdown 路径
  • 同一次任务里两类需求都有——分别走对应路径,不要为了省事全部 markdown overwrite

1.4 写入风险提示

doc update 在以下场景可能产生静默失败(返回 success=true 但实际写入不完整):

  • overwrite 降级为 append:大文档 overwrite 被后端静默降级,导致旧内容未清除、新内容追加在末尾
  • 分块 append 内容截断:超长文档分片写入时部分片段丢失或顺序错乱
  • 编码/通道问题:特殊终端下 UTF-8 内容传输乱码

因此写入必须通过带确认与验证的 shortcut。+update/+checkpoint-update 返回的验证结果是主证据;禁止无条件追加一次原子 doc read,只有 partial/unknown 或需要定点结构检查时才用 +fetch


二、读取策略

改写前必须先读现有内容,但要节省上下文。按粒度选读取方式(按 §1.3 优先级排序,优先 JSONML):

用户需求 读取方式 定位方法
单块精修(首选) doc block list --node <id> --content-format jsonml → 拿 uuid → doc block list --node <id> --content-format jsonml --block-id <uuid> 读子树 节点结构见 doc-jsonml-schema.md
多处保真改写 / 改 root sectPr +fetch --node <id> --detail full 解析 JSONML;担心并发覆盖时记下 revision
整篇按新骨架重写(纯文本场景) +fetch --node <id> 直接处理 markdown 全文
末尾追加纯文本章节 不必读全文,直接 §4.2 append 必要时 +fetch --scope section 看末尾衔接
老接口快速找 BLOCK_ID(无需 jsonml 时) doc block list --node <id> 默认输出 JSON;用 grep -B2 -A2 "<关键词>" 在 children 里定位(结构 {"blocks":[{...,"children":[...]}]}jq 需 ..|.text? // empty 递归查文本)

读取后,把改写计划告诉用户(要改哪几节、走 JSONML 还是 markdown、改成什么形态),等用户确认后再写。


三、改写路径速查

按用户请求形态查表,跳到对应详细节执行(按 §1.3 优先级排序:JSONML 路径在前,markdown / element 兜底在后):

用户请求 推荐路径 详细节
改某一章 / 某一节(首选) block list 拿 uuid → block update --content-format jsonml §4.4 路径 B
改属性 / 改 mark / 改颜色不动文本 block update --content-format jsonml §4.4 路径 B
插入 callout / 分栏 / 嵌套结构(首选) block insert --content-format jsonml --element '[...]' §4.4 路径 B
多处保真改写 / 改 root sectPr 整篇 JSONML overwrite(默认不带 --revision;并发敏感时再加) §4.4 路径 A
中间插一段纯文本 block insertelement JSON 或 jsonml §4.3 / §4.4
末尾追加一节纯文本 doc update --mode appendmarkdown §4.2
整篇按新骨架重写 overwrite 全文(优先 JSONML;纯文本可用 markdown §4.5
段落转表格 / 表格转段落 block update --content-format jsonml;或 markdown overwrite 单段 §4.4 / §4.1
插入附件 / 图片 doc media insert §4.3
一次追加 >200KB 内容 分块 append + 用户风险确认 + 逐片记录 §4.6
兜底:纯文本快速替换某段 doc update --content overwritemarkdown §4.1

四、改写路径详细

首选 JSONML(§4.4——保真度最高且 validator 兜底;本节其余路径(markdown / element)仅在 §1.3 列出的"次选 / 兜底"场景下使用。

4.1 段落级 overwritemarkdown 兜底路径)

适用范围:纯文本改写一段或替换某节内容。若该段含 callout / 分栏 / 颜色 / @人 / 附件 / 嵌套结构,改走 §4.4 路径 B——markdown 还原会丢失这些元素。

dws doc +update --node <nodeId> --command overwrite --content "<新内容>" --doc-format markdown

或写入临时文件:

dws doc +update --node <nodeId> --command overwrite --content @./drafts/<name>-section.md --doc-format markdown

⚠️ overwrite 须用户确认——尤其是整篇文档 overwrite。

4.2 追加章节(markdown

适用范围:在文档末尾加 X 章 / 补充纯文本段落。追加内容若含 callout / 分栏等富结构,先用本节 append 一个占位段落,再用 §4.4 路径 B 的 block insert --content-format jsonml 替换/精修。

dws doc +update --node <nodeId> --command append --content @./drafts/<name>-append.md --doc-format markdown

doc-style-guideline.md 的元素选择规则准备追加内容。

4.3 块级精修(element JSON 次选路径)

适用范围:JSONML 不支持某个字段时,或快速插入 callout / 分栏不想构造 JSONML 时。默认优先 §4.4 路径 Bblock update/insert --content-format jsonml),本节是老接口次选路径。

# 列出所有 block,定位 BLOCK_ID
dws doc block list --node <nodeId>

# 改一个 block 的文本
dws doc block update --node <nodeId> --block-id <BLOCK_ID> --content "替换后的内容" --content-format element

# 在某个 block 后插入
dws doc block insert --node <nodeId> --ref-block <BLOCK_ID> --where after --heading "补充说明" --level 2 --content-format element

# 插入复杂块(callout / 分栏)—— element 默认按 JSON 解析
dws doc block insert --node <nodeId> --ref-block <BLOCK_ID> --where after --content-format element \
  --element '{"blockType":"callout","callout":{"emoji":"⚠️","bgColor":"#FDE2E0","content":[{"text":"高风险操作,先备份"}]}}'

# 若改写过程已经在用 JSONML,整段精修也可走 jsonml 路径(uuid 必须 == --block-id
dws doc block insert --node <nodeId> --ref-block <BLOCK_ID> --where after --content-format jsonml \
  --element '["container",{"uuid":"co_new","subType":"colorBlocks","metadata":{"bgcolor":"#FDE2E0","border":"#F5C2C7"}},["p",{"uuid":"co_new_p1"},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"高风险操作,先备份"]]]]'

字段结构以 doc-block.md 为准,不要猜。callout 字段名不确定时,先用 doc block list --node <id> --block-type callout 抓现有 callout 实例看真实字段。整段 JSONML 形态与可复制范例见 §4.4 与 doc-jsonml-cookbook.md

4.4 JSONML 无损改写(首选路径

改写已有文档默认走本节——保真度最高,callout / 分栏 / 表格 / @人 / 附件 / 颜色 / 嵌套都能 1:1 round-trip;写入端有 validator 兜底。其他路径(§4.1/4.2/4.3/4.5 markdown)仅在 §1.3 列出的"次选 / 兜底"场景下使用。

两条子路径:

路径 B:单 block JSONML 精修(最常用——只动一个 block 时的默认选择)

# 1. 列出所有 block 拿到 uuid
dws doc block list --node <nodeId> --content-format jsonml

# 2. 读单个 block 完整子树
dws doc block list --node <nodeId> --content-format jsonml --block-id <BLOCK_UUID>

# 3. 改完后写回(uuid 必须 == --block-id
dws doc block update --node <nodeId> --block-id <BLOCK_UUID> --content-format jsonml \
  --element '["p", {"uuid": "<BLOCK_UUID>"}, ["span", {"data-type": "text"}, ["span", {"data-type": "leaf"}, "新内容"]]]'

# 在某个 block 前/后插入新 block
dws doc block insert --node <nodeId> --ref-block <BLOCK_UUID> --where after --content-format jsonml \
  --element '["container", {"uuid": "co1", "subType": "colorBlocks", "metadata": {"bgcolor": "#E8F2FE", "border": "#B3D4FC"}}, ["p", {"uuid": "co1p1"}, ["span", {"data-type": "text"}, ["span", {"data-type": "leaf"}, "提示内容"]]]]'

路径 A:整篇 JSONML overwrite(一次改多处、改 root 级 sectPr 才用)

# 1. 读出完整 JSONML 结构(输出含 revision,普通改写场景下不需要)
dws doc +fetch --node <nodeId> --detail full --format json

# 2. 解析 JSON,修改 jsonml 数组中的目标节点
#    节点结构见 doc-jsonml-schema.md,可复制范例见 doc-jsonml-cookbook.md

# 3. 写回工作目录内相对文件 ./drafts/doc_modified.json,格式 {"jsonml": [...]}

# 4. 提交修改(默认直接覆盖,不做并发检查)
dws doc +update --node <nodeId> --command overwrite --content @./drafts/doc_modified.json \
  --doc-format jsonml

并发安全模式(担心被并发覆盖时使用):如果担心多 agent 同时改这篇文档,可以把第 1 步 read 返回的 revision 通过 --revision <N> 透传给第 4 步:服务端会做并发检查,版本不一致返回 VersionConflict,此时回到第 1 步重读重写即可。普通单 agent 改写场景默认不传 --revision

JSONML 写入端的 validator

写入命令(doc create/update + doc block insert/update)走 validate 一步,不做结构修复:

行为 缺省 --fix-jsonml
JSON 语法修复(括号/逗号补全) ✓(打印 [FIX]
validator 阻断(HasErrors → 拒发)
root 校验(仅 doc create/update

报错格式(agent 友好):

$[2][2]: paragraph child must be span wrapper, got raw string.
Suggestion: ["span",{"data-type":"text"},["span",{"data-type":"leaf"},"<your text>"]]

设计要点:

  • 缺省为严格模式:不做结构修复,裸字符串、缺 uuid 等错误会被 validator 抦下。
  • doc create/update 要求 body 必须以 ["root", ...] 为根节点,缺少会报错。doc block insert/update 不要求 root。
  • --fix-jsonml:启用 JSON 语法修复(修复 LLM 遗漏的括号/逗号),推荐 agent 调用。

何时不走本节、改用 markdown:纯文本追加章节(§4.2)、整篇按全新骨架重写(§4.5,且无富结构需要保留时)、只在乎"加一段文字"且确认目标段落无 callout / 分栏 / 颜色 / @人 / 附件。其余场景默认本节。

字段细节见 doc-jsonml-schema.md;可复制范例见 doc-jsonml-cookbook.md

4.5 整篇 overwrite

适合「按新风格重写整篇」「按新骨架重组结构」。

形态选择(按 §1.3 优先级)

  • 若原文档含 callout / 分栏 / 颜色 / @人 / 附件 / 嵌套结构且需要保留——走 §4.4 路径 A(整篇 JSONML overwrite;默认不带 --revision,担心并发时再加)
  • 若是纯文本骨架重写、原文档没有富结构需要保真——走本节 markdown overwrite

执行前必须先向用户显式提示

注意:本次操作将覆盖整篇文档内容(约 {size})。可能存在以下风险:

  • 大文档 overwrite 可能被后端静默降级为 append,导致旧内容残留 + 新内容追加在末尾
  • markdown overwrite 会丢失原文档的 callout / 分栏 / 颜色等富结构;如需保真改走 §4.4 路径 A
  • 写入完成后我会回读校验,发现异常会主动报告

是否继续?

得到确认后执行(markdown 兜底路径):

dws doc +checkpoint-update --node <nodeId> --mode overwrite --content @./drafts/<name>-full.md

读取 +checkpoint-update 的 checkpoint、write、verify 步骤;只有 partial/unknown 时才按 §6 恢复。

4.6 超长内容追加(分块 append)

当一次性追加内容 超过 200KB 时,必须拆分为多片 --mode append,并在执行第一片之前向用户发出截断风险提示等待确认。

完整规范(提示话术模板、触发条件、失败处理)见 04-document.md «分块 append 截断风险提示»

update 场景下的额外约束:

  1. 按段落/标题边界切分,禁止在表格、代码块、列表内部截断
  2. 每写一片记录已写入的最后一个标题/段落标记,供 §6 回读比对
  3. 与既有内容衔接位置不能产生悬空标题或断列表

五、改写时的样式约束

doc-style-guideline.md 处理:

  • 文档类型保持不变;用户明确要求转型时除外(如从「执行型 SOP」改成「说明型接口文档」)
  • 同类信息保持一致:改写时不要把原本统一的元素改为多种表达
  • 颜色/emoji 语义:改写后仍满足 style guideline §5「颜色与视觉语义」的一致性
  • 不删除附件/图片;用户明确要求时除外

六、回读验收

+update/+checkpoint-update 已统一确认与验证。正常成功禁止额外整篇读取;partial/unknown 或确需检查富结构时,使用最小范围 +fetch

校验要点:

  • 改写章节的关键标题、段落首句、表格表头是否符合预期
  • overwrite 后旧内容是否真的被清除
  • append 后新内容是否在期望位置
  • 表格、代码块、列表跨块元素是否完整
  • @人、附件、图片等保真要素是否原样保留

异常处理

现象 可能原因 处理
overwrite 后旧内容残留 + 新内容追加在末尾 overwrite 被静默降级为 append 告知用户 overwrite 降级,按下方「先清空再重建」路径修复
append 后部分片段缺失 分块写入丢失 定位缺失片段,针对该段单独再 append 一次
@人 / 附件被替换为纯文本 改写时未走保真约束 用 JSONML 无损编辑修复(§4.4)
整篇内容乱序 写入顺序异常 报告给用户;若可重做,按下方「先清空再重建」路径修复

禁止在未回读的情况下向用户报告"已完成"。


七、交付口径

只报告已经验证过的信息:

  • 改写涉及的章节范围
  • 改写后的 nodeId 与 docUrl
  • 回读验收结果(哪些章节确认改写成功、保真要素是否完整)
  • 如有缺失或异常,说明具体位置和已采取的修复动作

未回读前,不要说「内容完整」「改写完成」。