1. AI Code编辑器 是什么?

可以把它们理解为:在“聊天大脑”(LLM)外面,加了一层专门为“写代码”设计的壳和工具系统

大模型本来只会“根据文本预测下一个词”,要变成编程助手,至少得多几个能力:

  1. 理解工程上下文

    • 不只是你当前打开的一个文件
    • 还要理解整个项目结构(目录、依赖、配置等)
  2. 能“动手操作代码”

    • 不是只给你一段建议,而是直接替你批量改文件
    • 能在多处同步修改、插入注释、重构等
  3. 能执行一些辅助工具

    • 搜索代码
    • 跑测试 / 编译
    • 调试、查看错误日志

Claude Code / OpenAI CodeX 的“魔法体验”主要来自于后台这几件事做得比较好,而不是靠一个“更神秘的大模型”。

可以先看一个非常简化的架构图(适用于 Claude Code / OpenAI Code / Cursor,一家一套,思路类似):

          ┌──────────────────────────────┐
          │          IDE / 客户端        │
          │  - VSCode / Cursor / Web IDE │
          └──────────────┬──────────────┘
                         │
                         ▼
          ┌──────────────────────────────┐
          │      AI 编程“中间层”         │
          │  - 会话管理(聊天记录)      │
          │  - 项目索引(文件、符号)    │
          │  - 上下文构造(prompt 构造) │
          │  - 代码操作(apply edits)   │
          │  - 工具调用(搜索、运行)    │
          └──────────────┬──────────────┘
                         │(API 请求)
                         ▼
          ┌──────────────────────────────┐
          │       大语言模型(LLM)      │
          │  - Claude / GPT-4.x / o3 等  │
          │  - 有“工具调用”能力          │
          └──────────────────────────────┘

1.1 IDE / 客户端层

例如:

  • Cursor 自己的 IDE
  • VSCode 插件(OpenAI / Claude Code)
  • Web 版 IDE(像 GitHub Codespaces 内嵌的)

主要负责:

  • 监控文件编辑、光标位置
  • 把用户操作和当前文件内容传给后台
  • 用“差异(highlight) / diff / inline” 的方式展示 AI 建议

1.2 “中间层”是核心:让模型“懂项目、能动手”

可以把中间层当作一个“AI 管家系统”:

  1. 项目索引 / 语义搜索

    • 扫描项目所有文件,构建索引(关键词 + 向量)
    • 支持按文件名、符号、语义搜索:
      “找到所有调用 getUserInfo 的地方”
  2. 上下文构造(prompt engineering)

    • 用户提问:“帮我重构这个函数”
    • 中间层会去:
      • 找到相关文件
      • 找到相关类型 / 依赖函数
      • 找到报错信息 / commit 记录(视产品而定)
    • 然后拼成一条“大而有序的提示词(prompt)”发给 LLM:
      • 用户目标
      • 当前文件片段
      • 相关文件摘要
      • 项目结构/约定(如框架、代码风格)
  3. 工具调用(tool calling / function calling)

    • 比如模型说:“我需要搜索全局对 User 类型的定义”
    • 中间层提供一个 search_code(query) 工具
    • 模型调用 → 中间层去代码库里搜 → 把结果返回给模型
    • 如此循环,模型像一个“会问问题的程序员”,一步步探索项目
  4. 代码编辑 / diff 应用

    • 模型返回的不是“整文件”,而是:
      • 修改建议(diff)
      • 要插入/替换的片段位置
    • 中间层负责:
      • 确认冲突
      • 应用补丁(patch)
      • 可能先展示预览,再让用户确认

1.3 底层大模型(LLM)负责“脑力活”

  • 训练好了的通用大模型(Claude 3.5, GPT-4.1/4.5, o3 等)
  • 关键增强点:
    • 单次上下文长度较大(几十万 token),才能容纳多文件
    • 支持“工具调用”(function calling / tool use)
    • 对代码做过特别多训练(代码语料 + RL 代码任务)

2. AI Code编辑器 实现思路分解

  1. IDE 监控到:

    • 当前文件路径
    • 你选中的代码范围
    • 编辑器里其他未保存的改动
  2. 中间层会收集:

    • 当前文件的全内容/相关上下文
    • 相关类型定义 / interface / class
    • 相关文件(根据索引召回:比如调用链上游/下游)
  3. 构造 prompt,大致结构如下(简化版):

系统指令:
  你是一个资深工程师,负责对项目进行安全的局部重构。
  避免引入破坏性变化,注意继承现有风格与约定。

项目背景:
  - 项目使用 React + TypeScript + Zustand
  - 使用 ESLint 规则 XYZ
  - ...

用户请求:
  请重构下文选中的函数,目标是...

当前文件内容(含选中片段标记):
  // ... 上文代码
  // [BEGIN_SELECTED]
  function ...
  // [END_SELECTED]
  // ... 下文代码

相关文件摘要:
  文件A.ts:...
  文件B.ts:...

输出约定:
  只返回 JSON 格式的 patch 列表:
  [
    {
      "file": "src/xxx.ts",
      "range": {...},
      "replacement": "..."
    }
  ]
  1. 调用 LLM API,模型返回 JSON patch。

模型如何“自己找文件”和“自己看日志”? 依赖的是:工具调用(tool use)机制

中间层先这样告诉模型(在系统提示词工具描述中):

{
  "tools": [
    {
      "name": "search_code",
      "description": "在当前项目中搜索代码",
      "parameters": {
        "type": "object",
        "properties": { "query": { "type": "string"} },
        "required": ["query"]
      }
    },
    {
      "name": "read_file",
      "description": "读取指定文件的内容",
      "parameters": { ... }
    },
    {
      "name": "run_tests",
      "description": "执行测试命令并返回输出",
      "parameters": { ... }
    }
  ]
}
  • 模型在回答时,可以输出一段“调用工具”的结构
  • 中间层看到这段结构,就去执行实际的函数(比如在本地跑 npm test
  • 把执行结果(测试输出 / 搜索结果)再作为“工具结果”塞回给模型
  • 模型再继续思考、修改方案

这就让模型:
从“纯文本预测机” → “能主动调用 IDE/CLI 的智能体”。

3. AI Code编辑器 核心技术

不管是 Claude Code、OpenAI Code 还是 Cursor,核心都依赖三类技术:

3.1 大模型本身(LLM)

  • 训练数据中大量代码
  • 使用专门的训练目标(例如:补全代码、修 bug、执行测试反馈)
  • 通过 RL / 反馈学习强化“可执行性”、“正确性”

这决定了“它会不会写、会不会改”。

3.2 上下文管理 & 检索(RAG)

  • 如何从超大的代码库中,只挑出“这一问真正相关的几部分”塞给模型
  • 如何在有限上下文长度下,构造一个最佳 prompt:
    • 用户意图
    • 当前文件(重点标记改动区)
    • 关键依赖文件的片段 / 摘要
    • 错误信息 / 日志
    • 项目风格和约定(例如 lint / 框架结构)

这决定了“它能否理解整个工程”。

3.3 工具调用 & 自动化操作

  • 通过 tool calling,让模型“主动拉取信息”和“主动执行动作”
    • 搜索代码
    • 查看某个文件
    • 执行测试 / 编译
  • 通过 patch 机制精确修改文件
    • 小范围 diff
    • 多轮迭代(改完再跑测试、再修)

这决定了“它能否自己动手,闭环解决问题”。

4. 一个“最小但完整”的伪代码架构,

基于

  • run_command(命令行)
  • 少量高级工具:read_file / write_file / apply_patch

完成一个闭环:

读文件 → 修改 → 写回 → 跑测试 → 看失败原因 → 再修一轮

4.1 工具定义(提供给 LLM 的 function / tool)

假设你用的是支持 tool calling 的模型(OpenAI / Claude 都类似),你需要给模型声明这些工具:

TOOLS = [
    {
        "name": "read_file",
        "description": "读取项目中的一个文本文件内容",
        "parameters": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "文件路径,相对于项目根目录"},
            },
            "required": ["path"],
        },
    },
    {
        "name": "write_file",
        "description": "写入文件(覆盖),慎用。通常用于应用模型生成的修改。",
        "parameters": {
            "type": "object",
            "properties": {
                "path": {"type": "string"},
                "content": {"type": "string"},
            },
            "required": ["path", "content"],
        },
    },
    {
        "name": "run_command",
        "description": "在受限 shell 中执行命令,返回 stdout/stderr 和退出码。",
        "parameters": {
            "type": "object",
            "properties": {
                "cmd": {"type": "string", "description": "要执行的 shell 命令,比如 'pytest' 或 'npm test'"},
                "timeout_sec": {"type": "integer", "description": "超时时间(秒)", "default": 120}
            },
            "required": ["cmd"],
        },
    },
    {
        "name": "apply_patch",
        "description": "对已有文件内容应用一个统一 diff(unified diff)或简单 patch,返回新内容。",
        "parameters": {
            "type": "object",
            "properties": {
                "original_content": {"type": "string", "description": "文件当前的完整内容"},
                "patch": {"type": "string", "description": "一个简单的 diff/patch 描述(例如 unified diff)"},
            },
            "required": ["original_content", "patch"],
        },
    },
]

这些是 后端真实实现的函数,模型只能通过 tool 调用它们。

4.2 后端工具实现(非常简化版本)

伪代码(Python 风格):

import subprocess
import textwrap

PROJECT_ROOT = "/path/to/your/project"

def tool_read_file(args):
    path = args["path"]
    with open(PROJECT_ROOT + "/" + path, "r", encoding="utf-8") as f:
        return {"content": f.read()}

def tool_write_file(args):
    path = args["path"]
    content = args["content"]
    with open(PROJECT_ROOT + "/" + path, "w", encoding="utf-8") as f:
        f.write(content)
    return {"ok": True}

def tool_run_command(args):
    cmd = args["cmd"]
    timeout_sec = args.get("timeout_sec", 120)

    try:
        result = subprocess.run(
            cmd,
            shell=True,
            cwd=PROJECT_ROOT,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=timeout_sec,
            text=True,
        )
        return {
            "returncode": result.returncode,
            "stdout": result.stdout,
            "stderr": result.stderr,
        }
    except subprocess.TimeoutExpired as e:
        return {
            "returncode": -1,
            "stdout": e.stdout or "",
            "stderr": f"TIMEOUT: command exceeded {timeout_sec}s\n" + (e.stderr or ""),
        }

def tool_apply_patch(args):
    original = args["original_content"]
    patch = args["patch"]
    # 这里可以:
    #  - 自己实现一个极简 patch 应用器
    #  - 或者直接让模型返回“新的全文内容”,这时 apply_patch 可以是 no-op
    # 为了简化 Demo,我们假设模型不搞复杂 diff,而是直接输出新文件内容,
    # 那 apply_patch 只负责返回它。
    # 实际产品里,这里会解析 unified diff,再对 original 应用。
    # 在最小 Demo 中,可以忽略 patch,直接返回 patch 作为新内容:
    new_content = patch  # 假装 patch 就是完整新内容
    return {"new_content": new_content}

说明:
真正的 apply_patch 会解析 diff,这里为了“最小 demo”,可以先让模型直接生成 完整的新文件内容,那 patch 参数就等价于 new_content;将来你再换成真正的 diff 应用器即可。

4.3 Agent 主循环(核心逻辑)

这个“主循环”负责:

  • 收到用户的任务描述
  • 多轮调用 LLM + 工具,直到:
    • 测试通过,或者
    • 达到最大迭代次数

伪代码:

from some_llm_client import call_llm_with_tools  # 你自己封装的 LLM 调用

MAX_ITERATIONS = 5

SYSTEM_PROMPT = """
你是一个自动化代码修复 Agent。
目标:在给定项目中,修复指定的 bug 或让测试通过。

你可以使用这些工具:
- read_file: 阅读某个文件
- write_file: 将修改写入文件
- run_command: 执行测试命令(如 pytest / npm test)
- apply_patch: 根据你生成的 patch 更新文件内容

工作流建议(但你可以自己决策):
1. 先通过 read_file 查看相关文件内容。
2. 分析问题,生成修改方案。
3. 使用 apply_patch 生成新文件内容,再用 write_file 写回。
4. 用 run_command 运行测试命令(如 'pytest')。
5. 如果测试失败,分析 stderr,继续修正。
6. 在你认为问题已经解决、测试通过时,以总结说明结束。

非常重要:
- 不要进行危险操作(删除大量文件、运行与任务无关的命令)。
- 在一次改动中尽量改少量文件,分步进行。
"""

def code_agent_main(user_task: str):
    """
    user_task: 用户的自然语言描述,例如:
      "请修复这个项目中导致 pytest 失败的 bug,让所有测试通过。"
    """
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_task},
    ]

    for step in range(MAX_ITERATIONS):
        # 调 LLM,看它要不要用工具
        llm_response = call_llm_with_tools(
            messages=messages,
            tools=TOOLS,
        )

        if llm_response.type == "tool_call":
            # 模型请求调用某个工具
            tool_name = llm_response.tool_name
            tool_args = llm_response.tool_args

            # 根据 tool_name 分派到我们实现的函数
            if tool_name == "read_file":
                result = tool_read_file(tool_args)
            elif tool_name == "write_file":
                result = tool_write_file(tool_args)
            elif tool_name == "run_command":
                result = tool_run_command(tool_args)
            elif tool_name == "apply_patch":
                result = tool_apply_patch(tool_args)
            else:
                result = {"error": f"Unknown tool {tool_name}"}

            # 把工具结果作为新的 message 反馈给 LLM
            messages.append({
                "role": "assistant",
                "tool_call_id": llm_response.tool_call_id,  # 视具体 API 而定
                "content": None,
                "name": tool_name,
            })
            messages.append({
                "role": "tool",
                "tool_call_id": llm_response.tool_call_id,
                "content": json.dumps(result),
            })
            # 然后继续下一轮循环(让 LLM 基于工具结果再想下一步)

        else:
            # llm_response 是普通自然语言回答,不再请求工具
            final_answer = llm_response.content
            print("Agent 最终回答:")
            print(final_answer)
            break

    else:
        print("达到最大迭代次数,自动停止。")

call_llm_with_tools 的具体实现取决于你用哪家 API:

  • OpenAI:chat.completions + tools / tool_choice
  • Claude:messages.create + tools

核心模式都是一样的:

  1. messages + TOOLS 发给 LLM

  2. 如果它想用工具,会返回类似:

    {
      "type": "tool_call",
      "tool_name": "run_command",
      "tool_args": { "cmd": "pytest", "timeout_sec": 120 },
      "tool_call_id": "xxx"
    }
    
  3. 你执行工具 → 把结果塞回 messages

  4. 再调一次 LLM,重复直到它不再发出 tool_call,而是“正常回答”(表示完成任务)

4.4 一个典型“修 bug”流程(逻辑视角)

假设用户输入:

“请在这个 Python 项目中修复导致 pytest 失败的问题,让所有测试通过。”

一个完整的典型交互(逻辑层面,大致如下):

  1. LLM:

    • run_command(cmd="pytest")
    • 结果:测试失败,stderr 里有堆栈:
      E AttributeError: 'User' object has no attribute 'email' in user_service.py:42
  2. LLM:

    • read_file(path="user_service.py")
    • 看第 42 行附近代码
  3. LLM:

    • 分析:User 类在别处定义,没 email 字段
    • read_file(path="models.py")User 定义
  4. LLM:

    • 决定修改 models.py 中的 User 类,增加 email 字段,或修掉错误访问
    • 生成 patch(在最小 Demo 中可以是“新的完整 file 内容”)
    • apply_patch(original_content=旧内容, patch=新内容) → 得到 new_content
    • 再调 write_file(path="models.py", content=new_content)
  5. LLM:

    • 再次调 run_command("pytest")
    • 如果测试通过:返回总结说明,循环结束
    • 如果仍然失败:解析新的 stderr,继续第 2 步那样迭代

整个过程就是:

读文件 → 修改 → 写回 → 跑测试 → 看失败原因 → 再修一轮

只不过每一步都通过 tool 调用来实现,agent 只负责“决策和生成 patch”。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐