# 钉钉文档改写流程 本文只处理已有文档的结构和排版策略。普通执行入口固定为 `+fetch`、`+update` 或 `+checkpoint-update`;这些 shortcut 统一确认、写入与验证。原子 `doc read/update/block` 只保留给 shortcut 未公开参数的 JSONML 专家路径,使用前必须读取精确 leaf Schema。 ## 适用边界 进入本文前,必须已经确认用户要改写的是已有钉钉文档 (`adoc`)。如果用户要新建、要操作表格 / AI 表格 / 文件 / 知识库空间 / 发消息,不要套用本文。 本文覆盖: - 已有文档的局部改写、润色、章节补充 - 段落 ↔ 列表 ↔ 表格 的形态转换 - 块级精修(callout、分栏、附件插入) - overwrite 整篇改写的风险提示与执行 - JSONML 无损结构改写的入口 本文不覆盖: - 新建文档(见 [doc-create-workflow.md](./doc-create-workflow.md)) - 知识库空间管理 - 文档权限、消息分发、待办分派 --- ## 一、核心原则 ### 1.1 精准手术优于全量覆盖 **默认走精准手术**——只改用户指定的章节或 block,不动其他内容。具体路径见 §3 速查表。 ### 1.2 保真约束 改写时必须**原样保留**以下要素,**不许**替换为纯文本/姓名/链接/占位符: - `@人` 引用(用户、机器人、群) - `@文档` / `@群` 等卡片引用 - 已上传的附件、图片 - 用户原话引用块 - 表格表头(除非语义错误且用户确认) JSONML 模式下这些元素的节点结构见 [doc-jsonml-schema.md](../format/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 --content-format jsonml` → 拿 uuid → `doc block list --node --content-format jsonml --block-id ` 读子树 | 节点结构见 [doc-jsonml-schema.md](../format/doc-jsonml-schema.md) | | 多处保真改写 / 改 root sectPr | `+fetch --node --detail full` | 解析 JSONML;担心并发覆盖时记下 revision | | 整篇按新骨架重写(纯文本场景)| `+fetch --node ` | 直接处理 markdown 全文 | | 末尾追加纯文本章节 | 不必读全文,直接 §4.2 append | 必要时 `+fetch --scope section` 看末尾衔接 | | 老接口快速找 BLOCK_ID(无需 jsonml 时)| `doc block list --node ` | 默认输出 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 还原会丢失这些元素。 ```bash dws doc +update --node --command overwrite --content "<新内容>" --doc-format markdown ``` 或写入临时文件: ```bash dws doc +update --node --command overwrite --content @./drafts/-section.md --doc-format markdown ``` > ⚠️ **overwrite 须用户确认**——尤其是整篇文档 overwrite。 ### 4.2 追加章节(markdown) > 适用范围:在文档末尾加 X 章 / 补充纯文本段落。追加内容若含 callout / 分栏等富结构,先用本节 append 一个占位段落,再用 §4.4 路径 B 的 `block insert --content-format jsonml` 替换/精修。 ```bash dws doc +update --node --command append --content @./drafts/-append.md --doc-format markdown ``` 按 [doc-style-guideline.md](./doc-style-guideline.md) 的元素选择规则准备追加内容。 ### 4.3 块级精修(element JSON 次选路径) > 适用范围:JSONML 不支持某个字段时,或快速插入 callout / 分栏不想构造 JSONML 时。**默认优先 §4.4 路径 B**(block update/insert `--content-format jsonml`),本节是老接口次选路径。 ```bash # 列出所有 block,定位 BLOCK_ID dws doc block list --node # 改一个 block 的文本 dws doc block update --node --block-id --content "替换后的内容" --content-format element # 在某个 block 后插入 dws doc block insert --node --ref-block --where after --heading "补充说明" --level 2 --content-format element # 插入复杂块(callout / 分栏)—— element 默认按 JSON 解析 dws doc block insert --node --ref-block --where after --content-format element \ --element '{"blockType":"callout","callout":{"emoji":"⚠️","bgColor":"#FDE2E0","content":[{"text":"高风险操作,先备份"}]}}' # 若改写过程已经在用 JSONML,整段精修也可走 jsonml 路径(uuid 必须 == --block-id) dws doc block insert --node --ref-block --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](../doc-block.md) 为准,不要猜。callout 字段名不确定时,先用 `doc block list --node --block-type callout` 抓现有 callout 实例看真实字段。整段 JSONML 形态与可复制范例见 §4.4 与 [doc-jsonml-cookbook.md](../format/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 时的默认选择)** ```bash # 1. 列出所有 block 拿到 uuid dws doc block list --node --content-format jsonml # 2. 读单个 block 完整子树 dws doc block list --node --content-format jsonml --block-id # 3. 改完后写回(uuid 必须 == --block-id) dws doc block update --node --block-id --content-format jsonml \ --element '["p", {"uuid": ""}, ["span", {"data-type": "text"}, ["span", {"data-type": "leaf"}, "新内容"]]]' # 在某个 block 前/后插入新 block dws doc block insert --node --ref-block --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 才用)** ```bash # 1. 读出完整 JSONML 结构(输出含 revision,普通改写场景下不需要) dws doc +fetch --node --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 --command overwrite --content @./drafts/doc_modified.json \ --doc-format jsonml ``` > **并发安全模式(担心被并发覆盖时使用)**:如果担心多 agent 同时改这篇文档,可以把第 1 步 read 返回的 `revision` 通过 `--revision ` 透传给第 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"},""]] ``` 设计要点: - 缺省为严格模式:不做结构修复,裸字符串、缺 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](../format/doc-jsonml-schema.md);可复制范例见 [doc-jsonml-cookbook.md](../format/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 兜底路径): ```bash dws doc +checkpoint-update --node --mode overwrite --content @./drafts/-full.md ``` 读取 `+checkpoint-update` 的 checkpoint、write、verify 步骤;只有 partial/unknown 时才按 §6 恢复。 ### 4.6 超长内容追加(分块 append) 当一次性追加内容 **超过 200KB** 时,必须拆分为多片 `--mode append`,并在执行第一片**之前**向用户发出截断风险提示等待确认。 完整规范(提示话术模板、触发条件、失败处理)见 [04-document.md «分块 append 截断风险提示»](../../04-document.md)。 update 场景下的额外约束: 1. 按段落/标题边界切分,**禁止**在表格、代码块、列表内部截断 2. 每写一片记录已写入的最后一个标题/段落标记,供 §6 回读比对 3. 与既有内容衔接位置不能产生悬空标题或断列表 --- ## 五、改写时的样式约束 按 [doc-style-guideline.md](./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 - 回读验收结果(哪些章节确认改写成功、保真要素是否完整) - 如有缺失或异常,说明具体位置和已采取的修复动作 未回读前,不要说「内容完整」「改写完成」。