mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
移除 TypeScript 代码、源码路径和实现常量, 聚焦风险分级设计、引号标准化的AI能力边界补偿、 原子性读改写的竞态防御和行尾处理的智能陷阱教训。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
5.4 KiB
Plaintext
122 lines
5.4 KiB
Plaintext
---
|
||
title: "文件操作"
|
||
description: "Read/Edit/Write 三个工具不是功能划分,而是风险分级。理解读取去重、原子性编辑、文件历史快照和安全防线的设计。"
|
||
keywords: ["文件操作", "FileRead", "FileEdit", "FileWrite", "代码编辑", "原子写入"]
|
||
---
|
||
|
||
## 核心设计:风险分级
|
||
|
||
Claude Code 将文件操作拆分为三个独立工具——这不是功能划分,而是**风险分级**:
|
||
|
||
| 工具 | 风险级别 | 典型场景 |
|
||
|------|---------|----------|
|
||
| **Read** | 只读(免审批) | 查看代码、搜索内容 |
|
||
| **Edit** | 写入(需确认) | 修改已有代码 |
|
||
| **Write** | 写入/创建(需确认) | 创建新文件、全量重写 |
|
||
|
||
拆成三个工具让权限系统可以精确控制:只读模式只禁用 Edit/Write,允许 AI 自由探索代码;而全禁用模式则连 Read 都受限。
|
||
|
||
## Read:多模态读取引擎
|
||
|
||
Read 工具不只是一个 `cat` 命令。它是一个多格式分发器:
|
||
|
||
| 文件类型 | 处理路径 | 特殊处理 |
|
||
|---------|---------|---------|
|
||
| 文本文件 | 分页读取 | 支持行号范围 |
|
||
| 图片 | 压缩 + 降采样 | 自动调整到 token 预算内 |
|
||
| PDF | 页面级提取 | 超大 PDF 强制分页读取 |
|
||
| Notebook | JSON cell 解析 | 保留 cell 结构 |
|
||
|
||
### 读取去重
|
||
|
||
当 AI 重复读取同一个文件时,系统通过文件修改时间(mtime)比对避免重复发送相同内容。约 18% 的 Read 调用是重复读取——去重机制直接节省了这部分 token 开销。
|
||
|
||
**设计细节**:去重只对 Read 工具自身的读取生效。Edit/Write 也会更新内部状态,但不会误触发去重——通过 offset 字段区分读取来源。
|
||
|
||
### 安全防线
|
||
|
||
Read 工具有多层安全门:
|
||
|
||
- **设备文件屏蔽**:`/dev/zero`、`/dev/random` 等被直接拒绝——它们会产生无限输出或阻塞
|
||
- **二进制文件拒绝**:排除图片/PDF 后,`.exe`、`.so` 等二进制文件被阻止
|
||
- **UNC 路径跳过**:Windows 下 `\\server\share` 路径跳过操作,防止 SMB 凭据泄露
|
||
|
||
### 智能错误提示
|
||
|
||
文件不存在时,Read 不只是报错——它会尝试提供修复建议:
|
||
- 相似文件名的推荐
|
||
- 基于 CWD 的相对路径建议
|
||
- macOS 截图文件名中特殊空格字符的纠正
|
||
|
||
## Edit:精确字符串替换
|
||
|
||
Edit 工具的核心操作是"找到旧字符串,替换为新字符串"。听起来简单,实际充满了边缘情况。
|
||
|
||
### 引号标准化
|
||
|
||
AI 模型只能输出直引号(`'` `"`),但源码中可能使用弯引号(`'` `'` `"` `"`)。Edit 工具在匹配时自动标准化引号,但写入时保持文件原有的引号风格——如果文件用弯引号,替换后的新内容也用弯引号。
|
||
|
||
**设计洞察**:这是一个典型的"AI 能力边界补偿"设计。AI 的输出限制(只能用直引号)不应该成为文件修改的问题。系统在 AI 和文件系统之间做了透明翻译。
|
||
|
||
### 原子性读-改-写
|
||
|
||
Edit 的执行过程是一个无锁原子更新协议:
|
||
|
||
```
|
||
备份旧内容 → 同步读取 → mtime 校验 → 查找匹配 → 计算 diff → 写入磁盘 → 更新缓存
|
||
```
|
||
|
||
**关键约束**:从"同步读取"到"写入磁盘"之间不允许任何异步操作。这确保在 mtime 校验和实际写入之间不会有其他进程修改文件——否则就会出现"读到的内容和写入时的内容不一致"的竞态条件。
|
||
|
||
### 防覆写校验
|
||
|
||
Edit 前置条件:
|
||
1. **必须先读取**文件(AI 不能编辑没看过的文件)
|
||
2. **文件未被外部修改**(mtime 未变)
|
||
|
||
Windows 上的 mtime 可能因云同步或杀毒软件被修改而不改变内容,因此对全量读取做了内容级比对作为兜底。
|
||
|
||
## Write:全量写入与创建
|
||
|
||
Write 与 Edit 共享大部分基础设施(权限检查、mtime 校验、历史备份),但有两个关键差异。
|
||
|
||
### 行尾处理
|
||
|
||
Write 始终使用 LF 行尾。早期版本会保留旧文件的行尾风格,但这导致 Linux 上 bash 脚本被注入 `\r`——现在 AI 发什么行尾就用什么行尾,不再尝试"智能"转换。
|
||
|
||
**设计教训**:有时"不做智能处理"比"做智能处理"更安全。
|
||
|
||
### 创建 vs 更新
|
||
|
||
Write 返回操作类型:
|
||
- **create**:文件不存在,全新创建
|
||
- **update**:文件存在且被覆盖,包含完整 diff
|
||
|
||
这让用户和 AI 都能清楚知道操作的实际影响。
|
||
|
||
## 文件历史快照
|
||
|
||
每次 Edit/Write 前都会备份旧内容。快照系统最多保留 100 个版本,使用内容哈希去重(同一文件多次未变只存一份)。
|
||
|
||
**设计目的**:不是版本控制(那是 git 的工作),而是"撤销"功能——用户可以在会话中回退 AI 的任何文件修改。
|
||
|
||
## LSP 通知链路
|
||
|
||
Edit 和 Write 完成后会通知 LSP 服务器和 IDE 扩展:
|
||
1. 清除旧的诊断信息
|
||
2. 通知 LSP 文件已变更
|
||
3. 触发 LSP 重新计算诊断(如 TypeScript 类型检查)
|
||
4. 通知 IDE 更新 diff 视图
|
||
|
||
这确保文件修改后 IDE 端的实时反馈是同步的——AI 改了一个文件,TypeScript 的类型错误立刻出现在编辑器中。
|
||
|
||
## 安全提醒
|
||
|
||
Read 工具在读取文件内容后追加安全提醒:如果文件看起来像恶意代码,AI 应该分析但拒绝改进。这是在"帮助用户"和"防止滥用"之间的平衡。
|
||
|
||
## 接下来
|
||
|
||
- **搜索与导航** — Glob/Grep 的搜索策略
|
||
- **Shell 执行** — Bash 的沙箱和超时控制
|
||
- **权限模型** — 理解工具权限的完整设计
|