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 通常需要进行上下文筛选:
- 获取项目目录和关键配置。
- 根据任务搜索相关文件。
- 读取必要的代码片段。
- 根据代码引用继续追踪依赖。
- 将筛选后的上下文交给模型。
这种方式可以理解为:先建立项目的基本认知,再围绕当前任务获取必要的信息。
不同的 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 生成的代码究竟是怎么进入项目的?
整个过程可以归纳为以下几个步骤:
- 理解需求:模型分析用户希望实现的功能。
- 获取上下文:Agent 读取项目结构、相关代码和开发规范。
- 生成修改方案:模型决定创建哪些文件,或修改哪些代码。
- 调用编辑工具:模型通过工具调用请求表达修改意图。
- 执行文件操作:Agent Runtime 校验权限,并由实际程序写入文件或应用补丁。
- 运行验证:Agent 调用测试、Lint、类型检查或构建命令。
- 根据反馈迭代:如果出现错误,将执行结果交给模型分析,必要时继续修改。
- 结束任务:达到完成条件或触发停止规则后,向用户汇报结果。
理解这条链路之后,就会发现 AI Coding Agent 并不只是一个更强大的代码生成器。
它真正的价值在于将大模型的理解与生成能力,和文件系统、开发工具、执行环境及验证机制结合起来,让 AI 能够在一个受控的工作环境中持续推进开发任务。
而要真正实现这样的系统,还需要继续研究三个重要方向:Tool Calling 如何驱动工具执行、MCP 如何统一外部工具接入,以及 AGENTS.md 和 Skills 如何帮助 AI 理解项目规范与复用开发流程。