16 KiB
钉钉文档改写流程
本文只处理已有文档的结构和排版策略。普通执行入口固定为 +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 编辑形态优先级
改写已有文档优先 JSONML,markdown / element 只在 JSONML 不适用时兜底:
| 优先级 | 形态 | 适用 |
|---|---|---|
| ① 首选 | --content-format jsonml |
保真度最高;callout / 分栏 / 表格 / @人 / 附件 / 颜色 / 嵌套结构都能 1:1 round-trip;写入端有 validator 兜底(§4.4) |
| ② 次选 | --content-format element(JSON,老接口) |
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 insert(element JSON 或 jsonml) | §4.3 / §4.4 |
| 末尾追加一节纯文本 | doc update --mode append(markdown) | §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 overwrite(markdown) | §4.1 |
四、改写路径详细
首选 JSONML(§4.4)——保真度最高且 validator 兜底;本节其余路径(markdown / element)仅在 §1.3 列出的"次选 / 兜底"场景下使用。
4.1 段落级 overwrite(markdown 兜底路径)
适用范围:纯文本改写一段或替换某节内容。若该段含 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 路径 B(block 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 场景下的额外约束:
- 按段落/标题边界切分,禁止在表格、代码块、列表内部截断
- 每写一片记录已写入的最后一个标题/段落标记,供 §6 回读比对
- 与既有内容衔接位置不能产生悬空标题或断列表
五、改写时的样式约束
按 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
- 回读验收结果(哪些章节确认改写成功、保真要素是否完整)
- 如有缺失或异常,说明具体位置和已采取的修复动作
未回读前,不要说「内容完整」「改写完成」。