Feishu Document Editing — Performance Best Practices
Core Principle: Minimum Change
Never use write (full document replace) for partial edits — it destroys existing rich-text formatting (colors, highlights, inline styles). Always use targeted block-level operations.
Full decision tree (when to use update_block vs delete+insert vs append, with protection mechanisms) is in ~/self-improving/domains/feishu.md under "最小改动原则".
Strategy 1: Scan Once, Act in Parallel
Pattern: One list_blocks → plan all changes → fire independent operations together.
CODEBLOCK0
Why: update_block on different blocks has zero dependency. OpenClaw executes independent tool calls within a single response concurrently (other AI frameworks may vary — verify before assuming).
Result: 10 serial updates (3–5 s) → 10 concurrent updates (~0.3–1 s).
Cache the block tree: Reuse list_blocks throughout the session. Re-fetch only after inserts/deletes that shift the structure.
Strategy 2: Back-to-Front for Insert/Delete
When mixing insert and delete, operate from the document's end toward the beginning.
Why: Inserting or deleting a block shifts indices of all subsequent siblings. Back-to-front keeps earlier indices stable — no need to re-query after each op.
Example: Delete blocks at indices 5, 12, 20 → delete 20 first, then 12, then 5.
Strategy 3: Merge Adjacent Inserts
Combine consecutive inserts into one call:
CODEBLOCK1
Images as  inside insert/append are auto-positioned inline — no separate upload_image needed when the URL is public.
Strategy 4: Positioned Image Insertion
| Method | When to use |
|---|
INLINECODE11 with  markdown | Public URL, image goes with surrounding text |
INLINECODE13 + parent_block_id + INLINECODE15 |
Local file / base64, precise position needed |
upload_image with position:
CODEBLOCK2
Finding the index: From list_blocks, locate the target in its parent's children array. Use that index to insert before, or index+1 to insert after.
Strategy 5: Table Performance
Docx tables are the slowest — each cell needs separate clear + convert + insert API calls.
- - Prefer Bitable for data-heavy tables. Bitable has native batch record APIs.
- Minimize cell count.
- New tables: use
create_table_with_values (one-step). - Existing tables: update only changed cells, not the entire table.
Strategy 6: Large Document Chunking
The Feishu document.convert API and documentBlockChildren.create have per-request size limits. For long content:
- - Chunk appends at 300–600 lines (or ~3K chars) per call to avoid 400 errors.
- Split at heading boundaries (
# or ##) — keeps each chunk semantically coherent. - Avoid splitting inside fenced code blocks.
- OpenClaw's
append/insert auto-chunk internally, but feeding smaller pieces reduces conversion failures.
Strategy 7: Rate-Limit & Retry
Feishu API returns 429 when request frequency is too high. Handle it:
- - Space concurrent calls — if firing 10+ parallel operations, use a concurrency limit of ~5.
- Exponential backoff on 429 — wait 1s, 2s, 4s before retrying. Max 3 retries.
- Batch block creation where possible — one request with multiple
children beats N separate requests.
OpenClaw's built-in tool handles basic retries, but be aware when orchestrating many operations in rapid succession.
Strategy 8: Rich-Text Preservation
INLINECODE27 replaces the entire text element array — all inline styles (bold, italic, color, links) are lost. To preserve formatting:
- - Read the block first via
get_block to inspect existing text_element_style fields. - Only update blocks where the text actually changed — skip unchanged blocks even if you're rewriting a section.
- For style-only changes (color, highlight), use
color_text action instead of update_block. - When rich text must be preserved exactly, consider
delete_block + insert with markdown that re-expresses the formatting, rather than update_block which strips styles.
Strategy 9: Conflict Avoidance
When multiple agents or users may edit the same document simultaneously:
- - Check
revision_id from read action before making changes. If the revision has advanced since your list_blocks, re-fetch before editing. - Minimize the edit window — scan, plan, and execute in quick succession. Don't hold a stale block tree for minutes.
- Prefer append for additive content — appending to the end rarely conflicts with other editors working in the middle.
- Avoid deleting blocks you didn't create unless explicitly instructed.
Anti-Patterns
| Don't | Do instead |
|---|
| INLINECODE38 to change a few paragraphs | INLINECODE39 on specific blocks |
| Serial update_block one-by-one |
Batch independent ops in one tool-call |
|
upload_image without position params | Pass
parent_block_id +
index, or use
insert with
 |
| Parallel
upload_image with
after_block_id to different positions | Serial: append text → upload
image → append text → uploadimage (API ignores after
blockid in parallel) |
| Re-fetch
list_blocks after every edit | Cache and reuse; re-fetch only after structural changes |
| Insert top-to-bottom when mixing insert/delete | Back-to-front to avoid index drift |
| Append 2000-line markdown in one call | Chunk at ~300–600 lines per append |
| Ignore 429 rate limits | Exponential backoff, limit concurrency to ~5 |
|
update_block on formatted text blindly | Check existing styles; use
color_text for style-only edits |
Compatibility
- - Works with OpenClaw's built-in
feishu_doc tool — no code changes needed - Complements
feishu-doc skill (API reference) and feishu-readability skill (formatting rules) - Applies to all Feishu accounts (球球星球 / 小米 / any tenant)
飞书文档编辑 — 性能最佳实践
核心原则:最小改动
切勿对部分编辑使用write(全文替换)——它会破坏现有的富文本格式(颜色、高亮、内联样式)。始终使用有针对性的块级操作。
完整的决策树(何时使用update_block vs delete+insert vs append,以及保护机制)位于~/self-improving/domains/feishu.md中的最小改动原则部分。
策略1:一次扫描,并行操作
模式: 一次list_blocks → 规划所有变更 → 同时触发独立操作。
- 1. feishudoc(action: listblocks, doc_token: xxx)
→ 包含ID、类型、内容的完整块树
- 2. 识别需要更新/删除/插入的块
- 3. 在一次工具调用批次中发出所有独立操作:
- update
block(blockid: A, content: new text)
- update
block(blockid: B, content: new text)
- delete
block(blockid: C)
- insert(after
blockid: D, content: ...)
原因: 对不同块执行update_block没有依赖关系。OpenClaw在单次响应中并发执行独立的工具调用(其他AI框架可能不同——在假设前请验证)。
结果: 10次串行更新(3-5秒)→ 10次并发更新(约0.3-1秒)。
缓存块树: 在整个会话中复用list_blocks。仅在插入/删除导致结构变化后重新获取。
策略2:插入/删除时从后往前操作
当混合使用insert和delete时,从文档末尾向开头操作。
原因: 插入或删除一个块会改变所有后续兄弟块的索引。从后往前操作可保持前面的索引稳定——无需在每次操作后重新查询。
示例: 删除索引为5、12、20的块 → 先删除20,然后12,最后5。
策略3:合并相邻插入
将连续的插入合并为一次调用:
json
{
action: insert,
doc_token: xxx,
afterblockid: target_block,
content: ## 新章节\n\n第一段。\n\n第二段。\n\n
}
在insert/append中使用
形式的图片会自动内联定位——当URL公开时无需单独的upload_image。
策略4:定位图片插入
| 方法 | 使用时机 |
|---|
使用 markdown的insert | 公开URL,图片与周围文本一起显示 |
| uploadimage + parentblock_id + index |
本地文件/base64,需要精确定位 |
带位置的upload_image:
json
{
action: upload_image,
doc_token: xxx,
file_path: /tmp/chart.png,
parentblockid: docrootorparentid,
index: 5
}
查找索引: 从list_blocks中,在父块的children数组中定位目标。使用该索引在目标前插入,或使用index+1在目标后插入。
策略5:表格性能
Docx表格是最慢的——每个单元格需要单独的clear + convert + insert API调用。
- - 数据密集型表格优先使用多维表格。 多维表格有原生的批量记录API。
- 尽量减少单元格数量。
- 新表格: 使用createtablewith_values(一步完成)。
- 现有表格: 只更新变化的单元格,而非整个表格。
策略6:大文档分块
飞书document.convert API和documentBlockChildren.create有每次请求的大小限制。对于长内容:
- - 每次追加分块为300-600行(或约3K字符),以避免400错误。
- 在标题边界处拆分(#或##)——保持每个分块的语义连贯性。
- 避免在围栏代码块内拆分。
- OpenClaw的append/insert在内部自动分块,但提供较小的片段可减少转换失败。
策略7:速率限制与重试
飞书API在请求频率过高时返回429。处理方法:
- - 控制并发调用——如果触发10个以上并行操作,使用约5的并发限制。
- 对429使用指数退避——重试前等待1秒、2秒、4秒。最多重试3次。
- 尽可能批量创建块——一次包含多个children的请求优于N次单独请求。
OpenClaw的内置工具处理基本重试,但在快速连续编排多个操作时需注意。
策略8:富文本保留
update_block替换整个文本元素数组——所有内联样式(粗体、斜体、颜色、链接)都会丢失。要保留格式:
- - 首先通过getblock读取块,检查现有的textelementstyle字段。
- 只更新实际文本发生变化的块——即使正在重写某个部分,也跳过未更改的块。
- 对于仅样式更改(颜色、高亮),使用colortext操作而非updateblock。
- 当必须精确保留富文本时,考虑使用deleteblock + insert配合重新表达格式的markdown,而非会剥离样式的update_block。
策略9:冲突避免
当多个代理或用户可能同时编辑同一文档时:
- - 在进行更改前检查revisionid(来自read操作)。如果自listblocks以来版本已更新,在编辑前重新获取。
- 最小化编辑窗口——快速连续地扫描、规划和执行。不要持有过时的块树数分钟。
- 追加内容优先使用append——追加到末尾很少与在中间编辑的其他编辑者冲突。
- 除非明确指示,否则避免删除非自己创建的块。
反模式
| 不要做 | 应该做 |
|---|
| 使用write更改几个段落 | 对特定块使用updateblock |
| 逐个串行updateblock |
在一次工具调用中批量处理独立操作 |
| 不带位置参数的upload
image | 传递parentblock_id + index,或使用带

的insert |
| 并行upload
image配合afterblock
id到不同位置 | 串行:追加文本 → uploadimage → 追加文本 → upload
image(API在并行时忽略afterblock_id) |
| 每次编辑后重新获取list_blocks | 缓存并复用;仅在结构变化后重新获取 |
| 混合插入/删除时从上到下插入 | 从后往前以避免索引偏移 |
| 一次调用追加2000行markdown | 每次追加分块为约300-600行 |
| 忽略429速率限制 | 指数退避,将并发限制在约5 |
| 盲目对格式化文本使用update
block | 检查现有样式;对仅样式编辑使用colortext |
兼容性
- - 与OpenClaw内置的feishu_doc工具配合使用——无需代码更改
- 补充feishu-doc技能(API参考)和feishu-readability技能(格式规则)
- 适用于所有飞书账号(球球星球/小米/任何租户)