弱网下怎么把一篇长文写完

我平时用 AI 编辑器写代码,网络环境不太稳定,一次提问跑到中途就断了是常事。前几天让它改一个几百行的文件,写到一半网络波动,那次工具调用整个失败,刚才"想好"的内容一点没存下来,只能从头再来。来回折腾了三趟,我才想明白问题不全在网络,写法也得改。

后来这套应对办法被整理成了一个 skill,叫 Resilient Batch Writing。这篇就讲讲它到底在解决什么、怎么用。文末我把这个 skill 的完整原文也附上了。

一次写完,为什么是最脆的做法

得先搞清楚断连时到底发生了什么。模型的输出和工具调用,在网络中断的瞬间是会整体丢掉的。你用一次调用去写一个几百行的大文件,只要在生成中途断了,这次写入就彻底失败——哪怕模型脑子里已经把全部内容想得清清楚楚,落盘这个动作没做完,那些想好的东西也一起作废。

这就是反直觉的地方。我们本能地觉得"一口气写完最省事",可在弱网里,一口气恰恰是最危险的。批次越大,单次调用要撑的时间越长,被网络抖动打断的概率就越高,而一旦被打断,损失的是整批。

换个思路就豁然开朗了:把活切成小块,每一块用一次独立、能很快结束的落盘动作搞定。这样就算断了,最多丢掉手头正在写的那一小块,前面已经存下来的纹丝不动,恢复的时候从断点接着写就行。

说白了,这是拿"写入粒度"去换"抗中断能力"。块越小、单次调用结束得越快,越不容易被网抖打断。

核心就一句话:小,而且完整

这套方法的原则不多,记住四条就够。

第一,每次写入要小而完整。一次调用只写一个能独立成立的片段——一个完整的函数、一个章节、一段配置,控制在大约 50 到 150 行,保证调用能很快收尾。

第二,先骨架后填充。先落一个能跑通、能成文的最小骨架,再一段段往里补。代码就先写 import、类型、函数签名,函数体里搁个 // TODO 占位;文档就先把标题和各级小标题列出来,段落留空。骨架到位之后,后面每一批都是在一个已经存在的文件上做增量。

第三,进度要能追踪。一旦涉及多个文件或多个任务,就单独维护一份进度清单,做完一项立刻勾掉。断了之后扫一眼就知道写到哪了。

第四,每批都能独立恢复。任意一批写完之后,文件都得是语义完整、能被下一批安全接上的状态。绝不能留半句话、半个括号在那儿。这点我后面会专门说,因为它最容易被忽略。

三种场景

写大文件、大段代码

第一批只写骨架,写完马上落盘。代码就是 import、类型定义、函数签名加占位的 // TODO: 实现;文档就是标题和空着的章节。

后面每一批填一个完整单元。用顺序追加或者替换某个占位段的方式,一次调用补一个函数、一个章节,写完就停、就存,再开下一段。

举个具体的。假设要写一个 600 行的 Server Action 文件,别想着一次写完。先写大约 30 行的骨架——'use server'、import、三个函数签名加 // TODO,落盘。然后把第一个函数的 // TODO 替换成完整实现,落盘。再补第二个、第三个。每步都独立存下来,就算断在第三步,丢的也只是第三个函数那几十行。

多文件、多任务

这种情况先建一份进度清单。在工作目录写个 PROGRESS.md,把要写的文件全列出来,用复选框标状态:

# 写入进度
- [ ] src/lib/db/article.ts
- [ ] src/components/ArticleList/index.tsx
- [ ] messages/zh.json 补充文案

然后一项一项推。做完一个文件就立刻把对应的 [ ] 改成 [x],再做下一个。恢复的时候先读这份清单,从第一个没勾的接着干,勾过的不碰。

比如用户说"网络差,帮我把这四个组件都建了",那就先写清单列四个组件标成未完成,每建好一个就勾掉一个,全程随时能断、随时能续。

长篇对话回复

把长回复拆成边界清楚的小节,每节作为独立的一块发出去。每节末尾标个进度,比如"(第 2 / 5 段)",断了能从指定段续。

还有个小技巧:把结论和关键信息放前面的批次。这样即便后面断了,最值钱的部分也已经送到了。

断了之后怎么接着写

当你跟它说"继续""接着写""从断的地方往下",它该做的第一件事是先读再写。先确认目标文件或者 PROGRESS.md 现在写到哪了,再动手。

接着只补缺口。从断点往下接,已经落盘的不重写。这既省事,也避免覆盖掉之前没问题的内容。

万一连断点都拿不准,就直接问你最后看到的完整片段是哪一段,确认了再往下,而不是瞎猜着重写一遍。

几个别踩的坑

有几个做法看着顺手,其实是给断连埋雷。

一次 fs_write 写三五百行的大文件——这是最典型的。把好几个文件的内容塞进同一次调用一起输出,也一样危险。

还有种心态要改:别想着"等全部想清楚了再统一落盘"。这恰恰是断连时损失最大的做法,想得越多、攒得越久,断的时候赔得越惨。

最后这条容易被忽略:别落盘一个语法不完整、半截截断的片段。缺个右括号、剩半个函数,下一批接的时候就很难安全续上,反而要先收拾残局。每批都让文件停在一个干净、完整的状态。

它适合什么时候用

这个 skill 是手动触发的,不用一直开着。网络正常的时候,给个小任务还要走分批落盘这套流程,纯属给自己添麻烦。

它的用武之地很明确:网络不稳、容易断的环境,加上一次要写不少东西。像我这种网络偶尔波动的情况,或者要一口气建好几个文件、改一个大文件的时候,就值得让它接管。

说到底,它换来的是一种踏实感。知道就算断了也只丢一小块、前面的都还在、还能接着写——这点踏实,在弱网里比什么都重要。


附录:Resilient Batch Writing 完整 skill 原文

下面是这个 skill 的完整内容,已用代码块包裹,方便整段复制取用。

---
name: resilient-batch-writing
description: Resilient Batch Writing。在网络不稳定、容易断连的环境下,把大段写入(代码、文档、长回复)拆成多个独立小批次增量落盘,每批写完即保存,断连最多只损失当前这一小批、已完成部分不丢,并支持断点续写。当用户提到「网络差」「容易断线/断连」「分批写」「增量写入」「别一次性写完」「断点续写」「写着写着断了」「network unstable」时,必须使用本 skill。即使用户只是抱怨「老是写到一半断」「文件太大写不完」,也应主动套用本 skill 的分批落盘策略。
---

# Resilient Batch Writing

在弱网、易断连的环境里,帮助你把"大块输出"改造成"多次小批次落盘",从而最大限度保住已完成的工作。

## 为什么需要这么做

模型的输出和工具调用在网络中断时会整体丢失。如果你用**一次**工具调用去写一个几百行的大文件,一旦在生成中途断连,这次写入会**整体失败**——哪怕模型已经"想好"了全部内容,落盘动作没完成,已构思的部分也全部作废,只能重来。

破局的核心思想很简单:**把工作切成小批,每一批用一次独立、完整、能快速结束的落盘操作**。这样断连最多只损失"当前正在写的这一小批",之前已经落盘的批次完全不受影响,恢复时从断点接着写即可。

这是一种用「写入粒度」换「抗中断能力」的策略——批次越小、单次调用越快结束,越不容易被网络抖动打断。

## 何时使用

本 skill 为**手动触发**:仅当用户明确表达了弱网/断连相关诉求时启用,例如:

- "我网络不太好,容易断,你分批写"
- "别一次性全写完,写一段存一段"
- "刚才写到一半又断了,接着写"
- "这文件太大,老是写不完就断线"

平时正常网络下不必启用,避免给小任务增加无谓的交互开销。

## 核心原则

1. **单次写入要小而完整**:每次工具调用只写一个能独立成立的片段(一个完整函数、一个章节、一段配置),控制在约 50–150 行以内,保证调用能快速结束。
2. **先骨架,后填充**:先落盘一个能跑通/能成文的最小骨架,再逐段补全细节。骨架先到位,后续每一批都是在已存在的文件上增量追加。
3. **进度可追踪**:多文件/多任务场景下,维护一份进度清单,每完成一项立即更新,断了能一眼看出写到哪。
4. **每批可独立恢复**:任意一批落盘后,文件都处于"语义完整、可被下一批安全接续"的状态,绝不留下半句话、半个括号。

## 工作流

### 场景 A:写大文件 / 大量代码

1. **第一批 — 骨架**:用 `fs_write` 只写文件的结构骨架,立即落盘。
   - 代码:imports、类型定义、函数签名 + 占位 `// TODO: 实现``throw new Error('未实现')`   - 文档:标题、各级章节标题、空的占位段落。
2. **后续批次 — 填充**:用 `fs_append`(顺序追加)或 `str_replace`(替换某个占位段)逐段补全,**每段一次调用**3. 每段聚焦一个完整单元(一个函数 / 一个章节),写完即停、即落盘,再开始下一段。
4. **绝不**在一次调用里写完整个大文件。

### 场景 B:多文件 / 多任务

1. **先建进度清单**:在工作目录写一份 `PROGRESS.md`(或 `.checkpoint`),列出全部待写文件/任务,用复选框标记状态:
   ```markdown
   # 写入进度
   - [ ] src/lib/db/article.ts
   - [ ] src/components/ArticleList/index.tsx
   - [ ] messages/zh.json 补充文案
   ```
2. **逐项推进**:完成一项就立刻把对应 `[ ]` 改成 `[x]`(一次独立的 `str_replace`),再做下一项。
3. **恢复时**:先读 `PROGRESS.md`,从第一个未勾选项继续,已勾选的不再重写。

### 场景 C:长篇对话回复

1. 把长回复拆成有明确边界的小节,每节作为独立的一块内容输出。
2. 每节末尾标注进度,例如 `(第 2/5 段)`,断连后可从指定段续写。
3. 优先把"结论/关键信息"放前面的批次,确保即使后续断了,最有价值的部分也已送达。

## 断点续写

当用户说"继续""接着写""从断的地方继续"时:

1. **先读后写**:先用读取工具确认目标文件或 `PROGRESS.md` 当前已写到哪里。
2. **只补缺口**:从断点接续,**不重写**已落盘的内容,避免重复劳动和覆盖风险。
3. 若无法确定断点,向用户确认最后看到的完整片段是哪一段,再继续。

## 反模式(请避免)

- ❌ 一次 `fs_write` 写 300–500+ 行的大文件。
- ❌ 把多个文件的内容拼在同一条消息/同一次调用里一起输出。
- ❌ "等全部想清楚再统一落盘"——这恰恰是断连时损失最大的做法。
- ❌ 落盘一个语法不完整、中途截断的片段(如缺右括号、半截函数),导致下一批难以安全接续。

## 示例

**示例 1:写一个 600 行的 Server Action 文件**

不要:一次 `fs_write` 写完 600 行。

要:
1. `fs_write` 写骨架——`'use server'`、imports、3 个函数签名 + `// TODO`(约 30 行)。
2. `str_replace` 把第 1 个函数的 `// TODO` 替换成完整实现。
3. `str_replace` 补第 2 个函数。
4. `str_replace` 补第 3 个函数。
   每步独立落盘,断在第 3 步也只丢第 3 个函数。

**示例 2:用户说"网络差,帮我把这 4 个组件都建了"**

要:先写 `PROGRESS.md` 列出 4 个组件并标 `[ ]`;每建好一个组件文件就把对应项改成 `[x]`;全程可中断、可续。