AI Code编辑器到底是怎么做出来的?

1. AI Code编辑器 是什么?

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

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

  1. 理解工程上下文

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

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

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

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

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

text 复制代码
          ┌──────────────────────────────┐
          │          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,大致结构如下(简化版):

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

项目背景:
  - 项目使用 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)机制

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

jsonc 复制代码
{
  "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 都类似),你需要给模型声明这些工具:

python 复制代码
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 风格):

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 + 工具,直到:
    • 测试通过,或者
    • 达到最大迭代次数

伪代码:

python 复制代码
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. 如果它想用工具,会返回类似:

    json 复制代码
    {
      "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"。

相关推荐
NAGNIP4 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab5 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab5 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP9 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年9 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼9 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS9 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区10 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈10 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang11 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx