夜雨聆风学习资料网

ARTICLE · 980731

AI 能读不能写?两个工具让它真正“动手”改代码

AI 能读不能写?两个工具让它真正“动手”改代码

# 从零做编程助手 · 007 | 文件操作:让 AI 真正“动手”改代码


先看一个尴尬场景

前面我们实现了工具调用框架,但只有 read_file(读)和 list_directory(列目录)。这让 AI 只能“看”,不能“写”:

你: 帮我创建一个 hello.py,打印 Hello WorldAI: 好的,我来帮你创建这个文件。    但是我目前只有 read_file 工具,无法写入文件。    你可以手动创建这个文件,内容如下:    print(”Hello World”)    (AI 表示,只有眼睛,没有手,有什么活还是得你自己干)

这篇补上写的能力,加入两个最核心的文件操作工具:

工具
作用
类比
write_file
创建新文件或覆盖已有文件
新建一个文档
edit_file
精确替换文件中的某段内容
用查找替换修改文档中的一段

为什么需要 edit_file ?不能直接都让 write_file 干了吗?

修改文件有两种思路:

方式 A:全量覆盖(write_file)

1. read_file(”main.py”)  → 读入完整内容2. 在脑子里修改3. write_file(”main.py”, 修改后的完整内容)  → 覆盖原文件

方式 B:精确替换(edit_file)

1. read_file(”main.py”)  → 找到要改的那几行2. edit_file(”main.py”, old_str=”x = 1”, new_str=”x = 2”)

方式 A 简单直接,但有两个问题:在文件很大时,每次传输完整内容很浪费 Token;LLM 可能会以为"幻觉"的关系,不小心改到不想改的地方。

方式 B 是目前主流 Harness 工具采用的方式,也是本文的重点:old_str → new_str 精确替换

edit_file(    file_path="main.py",    old_str="x = 1"# 要找的旧文本(必须精确匹配)    new_str="x = 2"# 要替换成的新文本)

这个设计有三个好处:

特性
好处
强制精确匹配
LLM 必须先读过文件,知道确切内容,才能给出正确的 old_str
唯一性检查
old_str 出现多次就拒绝替换,防止误改
原子操作
要么完全替换成功,要么完全不改——不会“改了一半”

工具定义

write_file(写入文件)

{    "type": "function",    "function": {        "name": "write_file",        "description""创建新文件或覆盖已有文件。如果文件已存在,其内容将被完全替换。",        "parameters": {            "type": "object",            "properties": {                "file_path": {"type": "string""description""要写入的文件路径"},                "content": {"type": "string""description""要写入的文件完整内容"}            },            "required": ["file_path""content"]        }    }}

edit_file(编辑文件)

{    "type": "function",    "function": {        "name": "edit_file",        "description": "精确替换文件中的指定内容。找到 old_str 并用 new_str 替换。old_str 必须在文件中精确匹配且唯一。",        "parameters": {            "type": "object",            "properties": {                "file_path": {"type": "string", "description": "要编辑的文件路径"},                "old_str": {"type": "string", "description": "要被替换的原始文本,必须精确匹配(包括空格、缩进、换行)"},                "new_str": {"type": "string", "description": "替换后的新文本"}            },            "required": ["file_path", "old_str", "new_str"]        }    }}

工具实现

write_file

def _write_file(file_path, content):    """创建或覆盖文件"""    try:        os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)        with open(file_path, "w", encoding="utf-8"as f:            f.write(content)        return f"文件 '{file_path}' 写入成功({len(content)} 字符)"    except Exception as e:        return f"写入文件失败: {e}"

edit_file

def _edit_file(file_path, old_str, new_str):    """精确替换文件中的内容"""    try:        with open(file_path, "r", encoding="utf-8"as f:            file_content = f.read()    except FileNotFoundError:        return f"错误:文件 '{file_path}' 不存在,请先使用 read_file 确认"    # 查找 old_str 在文件中的出现次数    count = file_content.count(old_str)    if count == 0:        return f"错误:在文件 '{file_path}' 中未找到指定的 old_str。请使用 read_file 重新读取文件确认内容。"    if count > 1:        return f"错误:old_str 在文件中出现了 {count} 次,不够唯一。请提供更多上下文使 old_str 唯一。"    new_content = file_content.replace(old_str, new_str, 1)    try:        with open(file_path, "w", encoding="utf-8"as f:            f.write(new_content)        return f"文件 '{file_path}' 编辑成功:将 {len(old_str)} 字符替换为 {len(new_str)} 字符"    except Exception as e:        return f"编辑文件失败: {e}"

为什么匹配失败时要报错,不能兼容或容错?

这是一个刻意的设计,逼着 LLM 先读文件

正确流程:  read_file("main.py")                        ← 先读  edit_file("main.py", old_str="...精确内容...", new_str="...")错误流程:  edit_file("main.py", old_str="...猜测的内容...", new_str="...")    错误:未找到 old_str!  ← 被拒绝    LLM 被迫回头 read_file,再给出正确的 old_str

这个“严厉”的设计,会让 AI 的行为更可控、更安全。

完整代码变化

与第 6 步相比,变化集中在 3 处:

  1. TOOLS 列表新增 write_file 和 edit_file 两个定义;
  2. execute_tool() 新增两个分支;
  3. 系统提示词更新使用指引:
SYSTEM_PROMPT = """...使用工具时:1. 修改文件前,必须先 read_file 读取文件内容2. 创建新文件用 write_file,修改已有文件用 edit_file3. edit_file 的 old_str 必须从文件中精确复制(包括缩进和空格)4. 如果 edit_file 报错”未找到 old_str”,重新 read_file 后再试..."""

运行演示

你: 创建一个 hello.py,内容打印 Hello World 并包含一个 main 函数AI: 好的,我来创建这个文件。  执行工具: write_file({'file_path''hello.py''content''...'})文件 'hello.py' 写入成功你: 给 hello.py 加上类型注解和文档AI: 让我先读取文件内容。  执行工具: read_file({'file_path''hello.py'})def main():    print(”Hello World”)if __name__ == ”__main__”:    main()我看到文件内容了,现在添加类型注解和文档。  执行工具: edit_file({      'file_path''hello.py',      'old_str''def main():\n    print(”Hello World”)',      'new_str''def main() -> None:\n    ”””程序入口,打印问候语”””\n    print(”Hello World”)'  })文件 'hello.py' 编辑成功:42 字符替换为 75 字符已完成!给 main 函数加上了返回类型注解 -> None 和文档字符串。

小结

变化
说明
新增工具
write_file(创建/覆盖文件)
新增工具
edit_file(精确替换,old_str → new_str)
设计重点
edit_file 的严格匹配机制,old_str 必须精确且唯一
系统提示词
更新指引:修改前先读、创建用 write、修改用 edit

关键:edit_file 的 old_str → new_str 模式是 AI 编程助手的核心设计之一。它通过“严格匹配”逼 LLM 先读后改、精确操作,有效防止幻觉修改。

下一篇

现在 AI 能读、能写、能改文件了,但还缺一个能执行命令的关键能力。下一篇加入 bash 工具,让 AI 能运行 pip install、python test.py、git status,真正成为全能的编程助手。


如果这篇文章对你有帮助:

  • 点个「在看」,让我知道这个系列有人需要
  • 转发给一个好奇“AI 怎么改代码”的朋友

💡 思考题:如果 LLM 在 edit_file 时故意给出一个模糊的 old_str(比如只写一个字母 "x"),会发生什么?看看代码里的唯一性检查怎么处理。

相关学习资料

返回首页浏览学习资料