篇章三 · 3.3
FileEditTool 与并发
精确字符串替换:old_string 换 new_string。听起来简单,但一次编辑要过 12 道验证关,还有一把基于 mtime 的乐观锁。
12 道关卡,一道都不能少
从「团队记忆里的秘密检测」到「智能引号归一化」,每道关都来自真实踩坑。点下面的按钮,看一个编辑请求怎么闯关——有的关会把它当场拦下。
选个场景再发起——看请求走到哪关被挡。
12 道关卡清单
| # | 关卡 | 拦什么 |
|---|---|---|
| 1 | 秘密检测 | 团队记忆里标了 secret 的内容,防把密钥写进代码 |
| 2 | old == new | 替换内容一样 = 无效编辑 |
| 3 | deny 规则 | 权限规则直接禁止此文件 |
| 4 | UNC 路径 | Windows 网络路径——防 NTLM 凭证泄露 |
| 5 | 大小 ≤ 1GiB | 超大文件不收 |
| 6 | 编码检测 | UTF-8 / UTF-16LE,别的编码先转换 |
| 7 | 文件存在 + 相似建议 | 不存在时给你 findSimilarFile() 建议(打错路径神器) |
| 8 | Jupyter 重定向 | .ipynb 转交 NotebookEditTool |
| 9 | 必须先读 | 没读过的文件不许编辑——readFileState 里没有它就拦 |
| 10 | mtime 乐观锁 | 读文件之后文件被别人改了(mtime 变了)→ 拒绝,要求重读 |
| 11 | 智能引号归一化 | 从网页复制的 " " ' ' 转成 " '——防匹配失败 |
| 12 | 多匹配检测 | replace_all=false 时 old_string 命中多处 → 拒绝(有歧义) |
准原子写入 · 临界区不许异步
// 临界区外——可以慢
await fs.mkdir(dirname)
await fileHistoryTrackEdit()
// ⚠️ 临界区——全部同步,一口气完成
const content = readFileForEdit(path) // 同步读
if (lastWriteTime > lastRead.timestamp) // mtime 检查
throw new StaleFileError() // 有人在编辑期间改了它
const patch = getPatchForEdit(...) // 计算 diff
writeTextContent(path, updatedFile) // 同步写 「读 → 查 → 写」之间不允许任何 await——一旦让出事件循环,别的进程就可能插进来改文件。这就是乐观并发:不锁文件,但提交前核对 mtime,冲突就放弃。
本站要点:乐观并发(mtime 比对)比悲观锁(锁文件)适合 AI 场景——冲突极低、代价极小;12 道关卡每道都是一次真实事故的化石。下一站看工具是怎么被「按需组装」出来的。