跳转至

name: write-file-recovery-procedure category: software-development description: Recovery procedure when write_file accidentally overwrites a large file instead of appending. Covers detection, data recovery via session search, and safe reconstruction using terminal/cat instead of write_file for appends. trigger: write_file was used to 'append' to a file but actually overwrote it, destroying existing content; or any time you need to append to a large existing file


write_file 覆盖恢复流程

核心教训:write_file 总是覆盖整个文件

write_file() 总是完全覆盖目标文件,从不追加。 即使文件有 3500+ 行且你只想加 100 行,使用 write_file 会销毁全部内容。

❗ 致命陷阱:read_file + execute_code + write_file 链导致行号污染

场景:在 execute_code() 内部调用 read_file() 读取大文件内容到变量,然后拼上新内容用 write_file() 写回。

问题read_file() 返回的内容包含行号前缀格式 LINE_NUM|CONTENT(例如 375|text)。将此字符串直接传给 write_file() 会导致每一行都被行号污染

# ❌ 错误写法 — 文件被行号污染
from hermes_tools import read_file, write_file
content = read_file(path="big_file.md")   # 返回 "     1|# Title\n     2|\n..."
write_file(path="big_file.md", content=content['content'] + new_stuff)
# 结果:文件每行变成 "     1|# Title" "     2|" 等

正确做法:永远通过 terminal cat 来读取和拼接,或使用 shell 管道:

# ✅ 正确做法:用 terminal 读取绝对内容
from hermes_tools import terminal, write_file
content = terminal("cat /path/to/file.md")
# content['output'] 是纯文件内容,无行号
mport terminal, write_file
content = terminal("cat /path/to/file.md")
# content['output'] 是纯文件内容,无行号# ✅ 追加的正确姿势:写临时文件 + cat >>
write_file(path="/tmp/new_part.md", content="新的章节内容...")
terminal("cat /tmp/new_part.md >> /path/to/file.md")
terminal("rm /tmp/new_part.md")

检测是否发生覆盖

执行 write_file 后立即检查文件行数:

wc -l /path/to/file
如果行数骤降(如从 3542 降到 2),则已发生覆盖。

恢复流程(当文件被覆盖时)

第一步:不要惊慌,不要重复写入

一旦发现覆盖,不要再对同一个文件执行 write_file——每次都会覆盖上一次的结果。

第二步:搜索会话历史恢复数据

from hermes_tools import session_search, terminal, read_file

# 搜索相关的历史会话,获取文件内容描述
results = session_search(query="关键文件名 或 关键章节标题")

# 从会话摘要中提取技术内容
# 会话摘要通常包含章节清单和关键发现

第三步:分部件重建(推荐方案)

不要试图一次性 write_file 整个重建——内容太长会被工具拒绝或出问题。

正确方案:分部件写入独立文件,然后 cat 拼接:

from hermes_tools import write_file, terminal

# 1. 写出第一部分(覆盖原有)
write_file(path="/root/part1.txt", content="...第一章到第十章的内容...")

# 2. 写出其余部分为独立文件
write_file(path="/root/part2.txt", content="...第十一章到三十章的内容...")
write_file(path="/root/part3.txt", content="...第三十一章到四十五章的内容...")
write_file(path="/root/part4.txt", content="...新增章节的内容...")

# 3. 用 terminal 的 cat 命令拼接——不触发 write_file 的覆盖行为
terminal(command="cat /root/part1.txt /root/part2.txt /root/part3.txt /root/part4.txt > /path/to/target.md")

# 4. 验证
terminal(command="wc -l /path/to/target.md")

第四步:验证文件完整性

# 检查章节标题完整性
terminal(command='grep -n "^## " /path/to/target.md')
# 检查文件末尾
read_file(path="/path/to/target.md", offset=-30)
# 检查文件大小
terminal(command="wc -lc /path/to/target.md")

如何安全追加内容到已有文件

任何时候需要在文件末尾添加内容(不是覆盖),使用以下方法之一: inal(command="wc -lc /path/to/target.md")

## 如何安全追加内容到已有文件

任何时候需要**在文件末尾添加内容**(不是覆盖),使用以下方法之一:### 方法一:terminal + cat/heredoc

```bash
cat >> /path/to/existing.md << 'EOF'

## 新章节标题

新内容......
EOF

方法二:Python 写临时文件然后 cat 拼接

from hermes_tools import terminal, write_file

# 写新增部分为独立文件
write_file(path="/root/new_content.md", content="新内容...")

# 拼接
terminal(command="cat /root/new_content.md >> /path/to/existing.md")

# 清理
terminal(command="rm /root/new_content.md")

方法三:execute_code 用 terminal 追加

from hermes_tools import terminal, write_file

# 写新增部分为独立临时文件(小文件,安全)
write_file(path="/tmp/new_content.md", content="新内容...")

# 用 cat 追加到目标
terminal(command="cat /tmp/new_content.md >> /path/to/existing.md")

# 清理临时文件
terminal(command="rm /tmp/new_content.md")

⚠️ 千万不要在 execute_code 内部用 read_file 读取大文件内容后拼上新字符串传给 write_file — read_file 返回的行号前缀格式 LINE_NUM|CONTENT 会污染文件。见上方「行号污染」陷阱。

🚨 子代理写文件危险(delegate_task + write_file 致死模式)

这是本文件第三次丢失的根因。当使用 delegate_task 派生子代理执行任务时:

危险模式

# 在父代理中:
delegate_task(goal="研究并追加到 /path/to/study.md", ...)
# 子代理内部调用 write_file("/path/to/study.md", ...) 或直接使用 write_file
# → write_file 是完整覆盖!子代理不知道父代理的文件有多大
# → 如果子代理写的是 partial content(如只写了新内容),整个文件被截断

致命因果链: 1. 父代理有大文件(5262 行) 2. 派生子代理 append 任务 3. 子代理内部执行 write_file("/path/to/study.md", new_content_only) 4. 文件被截断为 179 行——5300 行内容丢失

子代理安全追加协议

永远不要让子代理直接 write_file 到父代理的已有文件。使用以下安全模式:

模式 A:子代理写 staging 文件,父代理 cat 追加

# 父代理中:
永远不要**让子代理直接 write_file 到父代理的已有文件使用以下安全模式

#### 模式 A:子代理写 staging 文件,父代理 cat 追加

```python
# 父代理中:# 1. 派生子代理,让子代理写 staging 文件
delegate_task(
    goal="研究并输出到 /tmp/vrchat_new_chapter.md",
    context="...",
    toolsets=["file", "terminal"]
)

# 2. 父代理从 staging 文件 cat 追加到目标
terminal("cat /tmp/vrchat_new_chapter.md >> /root/study.md")
terminal("wc -l /root/study.md")  # 验证
terminal("rm /tmp/vrchat_new_chapter.md")

模式 B:父代理通过 terminal 分块重建(当文件已被截断时)

# 1. 每一块写为独立 staging 文件
write_file(path="/tmp/part1.md", content="...")
write_file(path="/tmp/part2.md", content="...")
write_file(path="/tmp/part3.md", content="...")

# 2. 用终端 cat 拼接——不触发 write_file 覆盖
terminal("cat /tmp/part1.md /tmp/part2.md /tmp/part3.md > /path/to/study.md")

# 3. 验证
terminal("wc -l /path/to/study.md")

子代理的安全边界规则

  1. 子代理永远不能 write_file 到父代理已知存在的文件
  2. ✅ 子代理写 staging 文件到 /tmp/ 或专用目录
  3. ✅ 父代理负责将 staging 文件安全地合并(用 cat >>
  4. ✅ 父代理在合后后验证文件完整性(行数 + grep 章节标题)
  5. ⚠️ 如果必须让子代理直接操作目标文件,使用 terminal("cat >>") 而非 write_file

预防措施

  1. 永远不要对已知存在的大文件使用 write_file 来"追加"——它总是覆盖
  2. 写新文件时,先用 wc -l 验证文件状态
  3. 对重要文件,定期保存备份(cp .bak)
  4. 连续 write_file 操作时要特别警惕——第二次调用会覆盖第一次的结果

紧急恢复:patch() 插入到错误位置

patch() 使用 old_string 作为匹配锚点。如果 old_string 不够唯一或文件结构发生变化,内容可能被插入到完全错误的位置。典型表现:文件行数暴增,但新内容出现在中间而非末尾。

检测

grep -n "^## 新章节\|新章节唯一标识关键词" /path/to/file
# 如果章节出现在文件中不该出现的位置(如第 50 章前),则已错位

恢复流程(本会话经验)

patch() 将大量新内容插入到文件中间时:

  1. 不要恐慌或再次写入 — 每次写入都会让情况更糟

  2. 使用 Python 诊断错位范围

    经验
    
     `patch()` 将大量新内容插入到文件中间时
    
    1. **不要恐慌或再次写入**  每次写入都会让情况更糟
    
    2. **使用 Python 诊断错位范围**
    ```python# 找出错位的边界行
    with open(path, 'r') as f:
        lines = f.readlines()
    # 标记插入点前后关键内容的行号
    

  3. 重建文件:切割 + 重排 + 合并

关键原理:错位插入的内容在文件中的位置是连续的区块。你需要找到这个区块的起止行号,然后:

# 假设:
# - [0:good_end] 是插入点之前正确的所有内容
# - [bad_start:bad_end] 是被错位插入的文本(要挪到末尾)
# - [good_end:bad_start] 是原本应在此处的旧内容(被挤压)
# - [bad_end:] 是文件末尾的其余正确内容

new_lines = (lines[0:good_end] +       # 前段正确内容
             lines[good_end:bad_start] + # 被挤压的旧内容
             lines[bad_start:bad_end] +  # 错位的新内容(挪到末尾)
             lines[bad_end:])            # 剩余正确内容
  1. 备份原始文件后再写入:

    cp /path/to/file /path/to/file.bak
    

  2. 写入修复后的内容(使用 write_file 重写或 Python 写入)

  3. 验证完整性

    grep -n "^## 关键章节标题" /path/to/file  # 确认顺序正确
    grep -c "unique content marker" /path/to/file  # 检查无重复
    grep -n '\\\\n' /path/to/file  # 检查有无未转义的换行符
    

本会话的实际案例

在 3700+ 行的 .md 文件中使用 patch() 追加 3 个新章节(约 580 行): - old_string 不够唯一(|--- 分隔符匹配了多处),导致内容被插入到 Ch50 之后而非 Ch65 之后 - 恢复使用了 Python 切割-重排-合并,将错位的区块移动到正确位置 - 还发现了次级问题:\\n 字面量未被正确转义、## 下次学习计划 出现两次 - 最终文件从 3761 行缩减到 3752 行(去除了重复的旧内容)

避免 patch() 插入错位的策略

  1. 提供唯一上下文old_string 包含至少 3-5 行独特的上下文,不要只用 |--- 或空行
  2. 优先追加到文件末尾:如果目标是追加,使用 cat >> 或 Python append,不要用 patch()
  3. patch() 只用于替换:查找并替换文件中少量文本(1-20 行),不适合做大规模插入
  4. 验证插入位置:patch 后立即检查:
    grep -n "^## 新章节标题" /path/to/file
    
    (1-20 行),不适合做大规模插入
  5. 验证插入位置:patch 后立即检查: bash grep -n "^## 新章节标题" /path/to/file## 扩展:write_file 的适用场景
场景 write_file 是否适合 替代方案
创建新文件 -
重写现有文件 -
追加到现有文件 ❌ 会覆盖 cat >> 或 Python 拼接
修改文件部分内容 ✅ 推荐 使用 patch 工具(但注意唯一性匹配)
在大文件中间插入大量内容 ⚠️ 危险 见下方「patch 错位恢复」章节