AI Coding Agent:从需求到代码修改的完整过程

这篇笔记用一个具体例子说明:

用户提出需求
-> AI coding 工具接收
-> Agent 检索项目上下文
-> 模型基于上下文推理
-> 定位文件、变量、行号
-> 生成 patch
-> 修改代码
-> 运行验证
-> 输出结论

先强调一个关键点:

当前项目上下文不是在一次任务中“后训练进模型权重”的。它是在推理阶段由 Agent 检索、筛选、压缩后放进模型上下文窗口的。

更准确地说:

预训练/后训练:让模型学会如何理解代码、如何使用上下文、如何按工具反馈工作。
推理时上下文注入:把当前项目的真实文件片段、搜索结果、报错、diff 放进 prompt,让模型基于这些证据推理。

所以在 AI coding 中:

模型权重通常不变。
变化的是每一轮 prompt 里的上下文。

1. 总览图

用户需求 Prompt

AI Coding Agent 接收任务

任务理解与拆解

生成检索计划

工具检索项目上下文

rg 搜索关键词/符号

读取相关文件片段

查看测试/类型/调用链

上下文打包 Context Pack

模型推理

形成工程假设

定位修改点

文件路径

函数/组件

变量/状态

具体行号或 anchor 文本

生成最小 patch

Agent 应用修改

运行测试/类型检查/lint

验证通过?

读取报错并补充上下文

总结修改和证据

这个流程有效,不是因为模型一次性知道整个项目,而是因为 Agent 不断把“与当前任务最相关的证据”放进模型上下文。

2. 示例需求

用户输入:

登录失败时页面没有错误提示,请修复。

这个 prompt 进入 AI coding 工具后,会被转成一个工程任务:

目标:登录失败时展示用户可见的错误信息。
需要找:
1. 登录表单组件
2. 提交处理函数
3. API 调用和错误分支
4. UI 中是否已有 error state
5. 相关测试是否存在

3. Agent 如何把需求转成检索动作

模型不会凭空知道代码在哪里。Agent 会先用工具查证。

可能的检索动作:

rg "login|Login|signIn|auth" src tests
rg "setError|errorMessage|toast|role=\"alert\"" src tests
rg "useLogin|loginUser|authApi" src

假设搜索结果返回:

src/components/LoginForm.tsx:12:function LoginForm()
src/components/LoginForm.tsx:28:const [errorMessage, setErrorMessage] = useState("")
src/components/LoginForm.tsx:41:await authApi.login(email, password)
src/api/auth.ts:8:export async function login(email, password)
tests/LoginForm.test.tsx:15:it("submits credentials")

这一步的关键是:

行号不是模型猜出来的。
行号来自工具对真实文件的搜索和读取结果。

4. 上下文如何进入模型

Agent 会读取相关文件片段,并组装成模型可读的上下文包。

例如:

用户需求:
登录失败时页面没有错误提示,请修复。

相关搜索结果:
- src/components/LoginForm.tsx:28 errorMessage state
- src/components/LoginForm.tsx:41 authApi.login
- tests/LoginForm.test.tsx 登录测试

文件片段:
src/components/LoginForm.tsx:24-48
24  export function LoginForm() {
25    const [email, setEmail] = useState("")
26    const [password, setPassword] = useState("")
27    const [loading, setLoading] = useState(false)
28    const [errorMessage, setErrorMessage] = useState("")
29
30    async function handleSubmit(event) {
31      event.preventDefault()
32      setLoading(true)
33
34      try {
35        await authApi.login(email, password)
36        navigate("/dashboard")
37      } catch (error) {
38        console.error(error)
39      } finally {
40        setLoading(false)
41      }
42    }
43
44    return (
45      <form onSubmit={handleSubmit}>
46        {errorMessage && <p className="error">{errorMessage}</p>}
47        ...
48      </form>

注意:这些内容是推理时 prompt 的一部分,不是训练数据写进权重。

模型此时看到的是:

需求 + 搜索结果 + 文件路径 + 行号 + 真实代码片段

然后才开始推理。

5. 模型如何推理

模型基于上下文会形成因果链:

1. LoginForm 已经有 errorMessage 状态。
2. UI 第 46 行已经会展示 errorMessage。
3. 但 catch 分支第 37-39 行只 console.error,没有 setErrorMessage。
4. 因此登录失败时 UI 状态没有变化。
5. 最小修复是在 catch 分支设置 errorMessage。

这不是模型“记住项目”,而是模型对上下文片段做局部推理。

6. 如何定位到哪一行、哪些变量

定位通常靠三类信息共同完成。

1. 工具给出的行号

搜索和读取文件会返回路径和行号:

src/components/LoginForm.tsx:28
src/components/LoginForm.tsx:37
src/components/LoginForm.tsx:46

模型把这些行号作为证据引用。

2. 代码结构和变量关系

模型看到:

const [errorMessage, setErrorMessage] = useState("")

它会推断:

errorMessage 是 UI 显示状态。
setErrorMessage 是更新错误信息的 setter。

看到:

catch (error) {
  console.error(error)
}

它会推断:

错误发生时没有调用 setErrorMessage。

看到:

{errorMessage && <p className="error">{errorMessage}</p>}

它会推断:

只要设置 errorMessage,页面就会显示错误。

3. 修改 anchor

真正应用 patch 时,Agent 不应只依赖“第 37 行”。因为文件可能变化,行号会漂移。

更稳的方式是用 anchor 文本:

} catch (error) {
  console.error(error)
} finally {

然后替换成:

} catch (error) {
  console.error(error)
  setErrorMessage("登录失败,请检查邮箱和密码。")
} finally {

所以精确定位不是单点能力,而是:

工具行号 + 代码结构理解 + anchor 文本 patch

7. 修改前后代码

修改前

async function handleSubmit(event) {
  event.preventDefault()
  setLoading(true)

  try {
    await authApi.login(email, password)
    navigate("/dashboard")
  } catch (error) {
    console.error(error)
  } finally {
    setLoading(false)
  }
}

修改后

async function handleSubmit(event) {
  event.preventDefault()
  setLoading(true)
  setErrorMessage("")

  try {
    await authApi.login(email, password)
    navigate("/dashboard")
  } catch (error) {
    console.error(error)
    setErrorMessage("登录失败,请检查邮箱和密码。")
  } finally {
    setLoading(false)
  }
}

这里有两个修改点:

1. 提交开始时清空旧错误:setErrorMessage("")
2. 登录失败时设置新错误:setErrorMessage("登录失败,请检查邮箱和密码。")

为什么要清空旧错误?

如果用户第一次失败,页面显示错误;第二次重新提交时,旧错误不应一直残留。

8. 这个 patch 是如何生成的

模型生成的不是“随便写一段代码”,而是基于上下文做最小编辑:

目标变量:errorMessage / setErrorMessage
目标函数:handleSubmit
目标分支:catch
辅助位置:try 前清空旧错误
不改动:authApi.login、navigate、form 结构

伪代码:

context = retrieve_relevant_context(user_request)

analysis = model.reason(
    request=user_request,
    files=context.files,
    search_results=context.search_results,
)

edit_plan = {
    "file": "src/components/LoginForm.tsx",
    "anchors": [
        "setLoading(true)",
        "} catch (error) {\n    console.error(error)\n  } finally {",
    ],
    "changes": [
        "insert setErrorMessage(\"\") after setLoading(true)",
        "insert setErrorMessage(\"登录失败,请检查邮箱和密码。\") after console.error(error)",
    ],
}

patch = model.generate_patch(edit_plan, context)
agent.apply_patch(patch)

9. 验证过程

修改后 Agent 会运行测试:

npm test LoginForm

如果测试失败,比如:

TestingLibraryElementError: Unable to find role="alert"

Agent 会再次读取 UI 片段:

{errorMessage && <p className="error">{errorMessage}</p>}

模型会推断:

测试希望错误提示具备 role="alert"。
当前 p 标签只有 className,没有 role。

于是二次修改:

{errorMessage && (
  <p className="error" role="alert">
    {errorMessage}
  </p>
)}

这就是闭环:

修改 -> 测试 -> 报错 -> 补上下文 -> 再推理 -> 再修改

10. Sequence Diagram

测试/类型检查 文件系统 大模型 工具层 AI Coding Agent 用户 测试/类型检查 文件系统 大模型 工具层 AI Coding Agent 用户 alt [测试失败] [测试通过] 登录失败时没有错误提示,请修复 解析需求,生成检索计划 搜索 login/auth/error/setError rg 搜索关键词 读取文件索引和内容 返回路径、行号、片段 搜索结果 读取 LoginForm.tsx 相关行 read file 代码片段 24-48 行 注入需求 + 搜索结果 + 文件片段 推理出 catch 分支未设置 errorMessage 请求生成最小 patch patch: 在 handleSubmit 中设置 setErrorMessage 应用 patch 修改 LoginForm.tsx 运行 LoginForm 测试 通过或返回报错 注入报错 + 相关代码 生成修正 patch 再次应用 patch 再次验证 汇报修改内容和验证结果

11. 大模型和 Agent 分别负责什么

大模型负责

理解自然语言需求
解释代码片段
建立因果链
识别变量和函数关系
判断最小修改点
生成 patch
阅读测试失败信息
总结结果

Agent 负责

搜索文件
读取文件
管理上下文窗口
提供路径和行号
应用 patch
运行命令
收集报错
循环验证
控制任务状态

一句话:

模型负责推理。
Agent 负责取证、执行和验证。

12. 为什么它能很好地工作

它能工作,不是因为模型一次性知道全部代码,而是因为形成了工程闭环:

真实代码上下文
+ 模型代码推理能力
+ 工具精确定位
+ patch 小步修改
+ 测试硬反馈
= 相对可靠的 AI coding 过程

其中最关键的是上下文注入:

没有上下文,模型只能猜。
有上下文,模型可以基于真实代码推理。
有测试反馈,模型可以从错误中修正。

13. 再次澄清:上下文和后训练的关系

要区分两个概念:

后训练阶段

后训练发生在模型发布或使用前:

SFT / DPO / RLHF / GRPO / agent trajectory training

它改变模型参数,让模型学会:

如何听指令
如何看代码
如何调用工具
如何根据报错修复
如何输出 patch

推理阶段上下文

AI coding 时,当前项目文件片段是推理输入:

用户需求 + 文件片段 + 搜索结果 + 报错 + diff

它不改变模型参数,只改变当前这一次回答的依据。

所以正确理解是:

后训练教会模型“如何使用上下文”。
Agent 在推理时把“当前项目上下文”放进去。
模型基于这些上下文进行代码推理和 patch 生成。

14. 最小伪代码

def ai_coding_task(user_prompt):
    task = model.parse_task(user_prompt)

    search_queries = model.plan_search(task)
    search_results = tools.rg(search_queries)

    files = tools.read_relevant_files(search_results)
    context_pack = build_context_pack(
        user_prompt=user_prompt,
        search_results=search_results,
        files=files,
    )

    diagnosis = model.reason(context_pack)
    edit_plan = model.plan_edit(diagnosis)

    patch = model.generate_patch(edit_plan, context_pack)
    tools.apply_patch(patch)

    result = tools.run_tests(edit_plan.related_tests)

    while not result.ok:
        failure_context = build_context_pack(
            user_prompt=user_prompt,
            previous_patch=patch,
            test_output=result.output,
            files=tools.read_relevant_files(result.related_files),
        )

        diagnosis = model.reason(failure_context)
        patch = model.generate_patch(diagnosis, failure_context)
        tools.apply_patch(patch)
        result = tools.run_tests(edit_plan.related_tests)

    return summarize_changes_and_verification()

这个伪代码就是 AI coding agent 的核心:

检索真实上下文 -> 模型推理 -> 修改 -> 验证 -> 失败再循环。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐