Skip to content
 

AI Coding Agent 是如何修改项目代码的? ​

更新: 10/15/2026字数: 0 字 时长: 0 分钟

当我们使用 AI 编程工具时,只需要输入一句话,例如:

帮我给这个项目增加一个用户登录页面,使用 React + TypeScript 实现,并接入现有的登录接口。

接下来,AI 可能会自动查看项目目录、读取现有组件、创建新的页面文件、修改路由配置、执行构建命令,甚至在发现错误后继续修复。

整个过程看起来就像 AI 直接操作了我们的电脑。

但这里有一个值得思考的问题:大模型生成的代码,是如何真正进入项目文件的?AI 又是如何知道应该修改哪个文件,而不是随意创建一堆代码?

要理解这些问题,就需要拆解 AI Coding Agent 的工作机制。从项目上下文的获取,到代码生成、文件修改,再到执行验证,我们可以一步步还原 AI 编程工具背后的完整流程。

一、AI Coding Agent 并不是简单地生成代码 ​

首先,需要明确一个概念:大模型本身主要负责理解需求、分析上下文和生成内容。它通常不会因为生成了一段代码,就自动把这段代码保存到开发者电脑上的项目目录中。

真正让代码发生变化的,是围绕大模型构建的Agent Runtime,以及它所使用的文件操作工具、终端工具和其他开发工具。

一个典型的 AI Coding Agent,可以拆分为以下几个部分:

组成部分主要职责
大语言模型(LLM)理解需求、分析代码、决定下一步操作
上下文管理器收集相关文件、项目规范、历史执行结果
Agent Loop协调模型调用、工具执行和反馈迭代
文件操作工具读取、创建、修改和删除项目文件
终端执行工具运行测试、构建、Lint 等命令
权限控制模块限制可访问的目录和允许执行的操作

它们共同组成一个可以持续执行开发任务的系统。

例如,当我们要求 AI 修改登录页面时,大致会发生这样的事情:

用户提出开发需求

增加登录页面,接入现有接口

AI 分析项目上下文

查找路由、组件、接口封装和开发规范

大模型生成修改方案

决定修改哪些文件,以及具体怎么修改

Agent 调用文件工具

创建文件、更新代码、修改路由

运行检查并反馈结果

测试、构建、分析错误,必要时再次修改

这意味着,AI 修改项目代码并不是一个单独的“生成代码”动作,而是一个由模型、执行环境和开发工具共同完成的工作流程。

二、AI 是如何知道应该修改哪个文件的? ​

在真正修改代码之前,AI 必须先了解项目的结构。

假设一个 React 项目包含以下目录:

my-app/
├── src/
│   ├── components/
│   │   ├── Header.tsx
│   │   └── Button.tsx
│   ├── pages/
│   │   └── Home.tsx
│   ├── services/
│   │   └── auth.ts
│   ├── App.tsx
│   └── main.tsx
├── package.json
└── AGENTS.md

当用户提出“增加登录页面”的需求时,AI 不一定需要读取整个项目。更合理的做法是先获取目录结构,再逐步查找与登录功能相关的文件。

2.1 第一步:读取项目目录 ​

Agent 可以通过文件系统工具获取目录列表。例如,底层使用 Node.js 时,可以调用 fs/promises:

import { readdir } from "node:fs/promises";
import path from "node:path";

async function listFiles(
  directory: string
): Promise<string[]> {
  const entries = await readdir(directory, {
    withFileTypes: true,
  });

  const results: string[] = [];

  for (const entry of entries) {
    const fullPath = path.join(
      directory,
      entry.name
    );

    if (entry.isDirectory()) {
      const children = await listFiles(fullPath);
      results.push(
        ...children.map((child) =>
          path.join(entry.name, child)
        )
      );
    } else {
      results.push(entry.name);
    }
  }

  return results;
}

这里的 listFiles 是一个简化的目录读取示例。实际 Agent 通常还会过滤 node_modules、.git、构建产物等目录,并限制扫描深度。

需要注意:这段代码是真正执行目录读取的程序,不是大模型本身。

Agent 会把获取到的目录信息交给模型,让模型进一步判断哪些文件值得读取。

2.2 第二步:查找相关代码 ​

AI 可能优先查看以下文件:

  • App.tsx:了解页面和路由的组织方式。
  • services/auth.ts:了解项目是否已经封装登录接口。
  • AGENTS.md:了解项目的开发约定。
  • package.json:了解技术栈、依赖和可用脚本。

随后,Agent 使用文件读取工具获取这些文件的内容,并将相关信息传递给大模型。

如果发现项目已经有登录接口,模型就可以复用它,而不是重新编写一套接口调用逻辑。

2.3 为什么 AI 不需要读取整个项目? ​

因为大型项目可能包含数千个文件,全部塞进模型上下文既浪费 Token,也可能超过上下文窗口限制。

因此,AI Coding Agent 通常需要进行上下文筛选:

  1. 获取项目目录和关键配置。
  2. 根据任务搜索相关文件。
  3. 读取必要的代码片段。
  4. 根据代码引用继续追踪依赖。
  5. 将筛选后的上下文交给模型。

这种方式可以理解为:先建立项目的基本认知,再围绕当前任务获取必要的信息。

不同的 AI 编程工具可能采用不同策略,例如文件搜索、语义检索、代码索引或直接读取文件。具体实现并不完全相同。

三、大模型生成代码后,究竟是谁把代码写入项目的? ​

这是整个流程中最关键的一环。

假设模型分析完项目后,生成了一个新的登录组件:

// src/pages/Login.tsx

import { useState } from "react";

export default function Login() {
  const [username, setUsername] = useState("");

  return (
    <main>
      <h1>用户登录</h1>

      <input
        value={username}
        onChange={(event) =>
          setUsername(event.target.value)
        }
        placeholder="请输入用户名"
      />

      <button type="button">
        登录
      </button>
    </main>
  );
}

这时,代码可能只是 模型返回的文本内容。

如果没有后续的文件操作,代码并不会因为出现在模型的回答中,就自动成为项目里的 Login.tsx 文件。

那么,代码是如何真正保存下来的?

3.1 方式一:模型请求调用文件写入工具 ​

Agent 可以向模型提供一个文件写入工具,例如:

type WriteFileArgs = {
  path: string;
  content: string;
};

模型根据工具的参数定义,生成一个结构化调用请求,表达自己希望把代码写入哪个文件。

例如:

{
  "name": "writeFile",
  "arguments": {
    "path": "src/pages/Login.tsx",
    "content": "export default function Login() { ... }"
  }
}

这里需要区分两个动作:

  • 模型生成工具调用请求:决定调用什么工具、使用什么参数。
  • Agent 执行工具调用:校验请求,并由真实的程序执行文件写入。

上面的 JSON 只是示意,实际工具调用的消息结构由所使用的模型 API 决定。

3.2 方式二:本地程序真正写入文件 ​

假设 Agent 运行在开发者电脑上,并且拥有项目目录的写入权限,那么它可以使用 Node.js 将代码保存到本地。

下面是一个简化的文件写入实现:

import {
  mkdir,
  writeFile,
} from "node:fs/promises";
import path from "node:path";

async function writeProjectFile(
  projectRoot: string,
  relativePath: string,
  content: string
): Promise<void> {
  const root = path.resolve(projectRoot);
  const target = path.resolve(
    root,
    relativePath
  );

  const relative = path.relative(root, target);

  if (
    relative === ".." ||
    relative.startsWith(`..${path.sep}`) ||
    path.isAbsolute(relative)
  ) {
    throw new Error("禁止写入项目目录之外的文件");
  }

  await mkdir(path.dirname(target), {
    recursive: true,
  });

  await writeFile(target, content, "utf8");
}

当 Agent 调用:

await writeProjectFile(
  "D:/MyProjects/my-app",
  "src/pages/Login.tsx",
  generatedCode
);

程序才会真正执行文件写入操作。

这里的 generatedCode 代表模型生成并经过应用程序接收的代码内容。

这段示例展示了基本的目录约束,但生产环境还需要考虑符号链接、文件覆盖、并发修改、权限检查和写入前审批等问题,不能只依赖路径字符串校验。

3.3 所以,AI 生成的代码一定会写入本地吗? ​

不一定,关键取决于 Agent 的运行位置和执行方式。

运行模式代码通常写入哪里
本地 AI 编程工具开发者电脑上的项目目录
云端开发环境远程容器或云端工作区
浏览器代码编辑器浏览器工作区、远程服务或关联存储
仅聊天的 AI通常只返回代码文本,不会直接修改项目

因此,不能简单地认为“大模型生成代码,然后模型自己保存文件”。

更准确的描述是:

大模型生成代码或提出文件修改请求,拥有相应权限的 Agent 运行环境负责执行写入操作。

这也解释了为什么某些 AI 工具可以直接修改本地项目,而另一些工具只能给出代码片段供开发者复制。

四、AI 为什么有时直接创建文件,有时却只修改几行? ​

实际开发中,AI Coding Agent 并不总是把整个文件重新生成一遍。

常见的代码修改方式主要有三种。

4.1 直接创建或覆盖文件 ​

当需要创建新页面、生成配置文件,或者整个文件都需要重新组织时,Agent 可以使用文件写入工具。

例如:

src/pages/Login.tsx

如果文件不存在,就创建新文件;如果文件已经存在,则可能覆盖原有内容。

这种方式实现简单,但覆盖文件可能丢失已有逻辑,因此必须谨慎处理。

4.2 使用 Diff / Patch 修改部分代码 ​

当只需要修改一个函数或增加一项配置时,使用差异补丁往往更加合适。

例如,原代码是:

function getUserName() {
  return "Guest";
}

需求是把默认用户名改为 Anonymous。

Agent 可以生成类似这样的修改差异:

 function getUserName() {
-  return "Guest";
+  return "Anonymous";
 }

Agent 再由补丁应用程序定位原始代码,并应用修改。

这种方式有几个优点:

  • 修改范围更明确。
  • 更容易查看变更前后的差异。
  • 更容易发现意外删除或覆盖。
  • 更方便在修改前后进行审查。

不过,补丁也可能因为原文件发生变化、匹配内容不唯一或上下文不一致而应用失败。因此,成熟的实现需要检查补丁是否匹配预期,并在失败时重新读取文件或重新生成修改方案。

4.3 通过编辑器或代码操作接口修改 ​

有些工具会通过编辑器 API、语言服务或专门的代码编辑接口修改文件。

例如,Agent 可能只提交一个编辑请求:

{
  "file": "src/pages/Login.tsx",
  "operation": "replace",
  "oldText": "return 'Guest';",
  "newText": "return 'Anonymous';"
}

执行程序会检查旧内容是否存在,再决定是否应用修改。

需要注意,以上只是示意性的编辑接口,并不是某个特定 AI 编程工具的通用协议。

选择哪种修改方式,通常取决于任务规模、工具能力、代码结构和修改风险。 创建新文件可以直接写入,而已有复杂文件通常更适合精确修改并检查差异。

五、代码写入后,AI 为什么还要运行测试? ​

文件保存成功,并不意味着功能已经实现。

新代码可能存在语法错误、类型错误、接口参数不匹配,或者与现有组件发生冲突。

例如,AI 创建了登录页面,却忘记将它加入路由配置。文件本身没有问题,但用户仍然无法通过应用正常访问这个页面。

因此,AI Coding Agent 通常还会调用终端工具,运行项目中的检查命令。

对于一个 TypeScript 项目,可能执行:

npm run lint
npm run typecheck
npm test
npm run build

这些命令只是示例,实际能否执行取决于 package.json 中定义的脚本。

如果构建失败,Agent 可能拿到这样的结果:

src/pages/Login.tsx:18:10

error TS2304:
Cannot find name 'loginUser'.

模型接收到错误信息后,可以进一步读取相关文件,判断是否遗漏了接口导入、函数定义或其他依赖,然后再次提交修改。

完整的流程可能变成:

生成或修改代码

保存到项目文件

运行测试或构建

读取执行结果

验证通过:继续完成任务

验证失败:分析错误并再次修改

这就是前一篇文章中介绍的 Agent Loop 在 AI 编程场景下的具体应用。

模型不只是生成一段代码就结束,而是利用真实的运行结果判断是否需要继续行动。

当然,自动修复也不是无限可靠的。模型可能误解错误信息,或者修改后引入新的问题。因此,自动化流程需要限制重试次数,并尽可能通过测试、类型检查和人工审查确认结果。

六、AI 修改项目代码时,如何避免误操作? ​

既然 Agent 能够读写文件、运行命令,那么它就可能覆盖重要代码、删除文件,甚至执行有风险的操作。

因此,生产级 AI Coding Agent 需要在执行能力之外建立安全机制。

6.1 限制文件访问范围 ​

不要让 Agent 无条件访问整台电脑。

更合理的做法是将可操作范围限制在明确的项目工作区内,并针对敏感文件设置额外限制。

例如:

  • 允许修改 src/ 下的业务代码。
  • 对 .env、私钥和凭据文件设置访问限制。
  • 禁止随意修改 .git/ 等内部目录。
  • 对删除文件和批量覆盖操作进行额外确认。

具体规则应根据开发场景设置,不能仅凭文件扩展名判断一个文件是否安全。

6.2 修改前保留差异记录 ​

对于已有文件,建议在修改前后生成 Diff,让开发者能够快速查看:

  • 新增了哪些代码。
  • 删除了哪些代码。
  • 哪些文件被修改。
  • 是否存在超出任务范围的变更。

结合 Git 分支、提交记录或临时快照,可以进一步降低误修改带来的恢复成本。

6.3 对高风险命令进行人工审批 ​

例如,读取项目文件和运行普通测试,与删除目录、修改生产配置、部署线上服务,具有完全不同的风险。

可以根据命令类型、目标目录和执行环境设置不同的权限等级。

尤其需要注意:模型认为某个操作有必要,不代表这个操作就应该被允许执行。

权限校验必须由 Agent 运行环境执行,而不能仅依赖模型自觉遵守提示词。

七、总结:AI Coding Agent 修改代码的完整链路 ​

回到最初的问题:AI 生成的代码究竟是怎么进入项目的?

整个过程可以归纳为以下几个步骤:

  1. 理解需求:模型分析用户希望实现的功能。
  2. 获取上下文:Agent 读取项目结构、相关代码和开发规范。
  3. 生成修改方案:模型决定创建哪些文件,或修改哪些代码。
  4. 调用编辑工具:模型通过工具调用请求表达修改意图。
  5. 执行文件操作:Agent Runtime 校验权限,并由实际程序写入文件或应用补丁。
  6. 运行验证:Agent 调用测试、Lint、类型检查或构建命令。
  7. 根据反馈迭代:如果出现错误,将执行结果交给模型分析,必要时继续修改。
  8. 结束任务:达到完成条件或触发停止规则后,向用户汇报结果。

理解这条链路之后,就会发现 AI Coding Agent 并不只是一个更强大的代码生成器。

它真正的价值在于将大模型的理解与生成能力,和文件系统、开发工具、执行环境及验证机制结合起来,让 AI 能够在一个受控的工作环境中持续推进开发任务。

而要真正实现这样的系统,还需要继续研究三个重要方向:Tool Calling 如何驱动工具执行、MCP 如何统一外部工具接入,以及 AGENTS.md 和 Skills 如何帮助 AI 理解项目规范与复用开发流程。