不到 200 行 Python 代码,就能复刻一个「Claude Code」?

本文通过不到 200 行 Python 代码,演示了如何复刻一个最小可用的 AI 编程 Agent,旨在揭示 Claude Code、Devin 等 AI 编程助手的底层原理并非神秘。作者 Mihail Eric 提出,这类工具的本质是“大语言模型(LLM)+ 工具库”的对话循环,LLM 负责决策,本地程序执行文件读写、目录列表等操作。文章详细介绍了搭建 Agent 的七个步骤,包括基础框架搭建、实现文件读写和编辑三大核心工具、工具注册、系统提示词编写、LLM 指令解析以及核心循环的组装,并提供了完整的代码实现。最终,文章对比了极简版与商用产品的差异,强调了两者核心工作流的一致性,鼓励读者动手实践,深入理解 AI 编程助手的运作机制。




【CSDN 编者按】当 Claude Code、Devin 这些 AI 编程工具被神话成“下一代程序员”时,很多人以为背后是某种高不可攀的黑科技。但本文作者用不到 200 行 Python 代码,亲手拆解了一个最小可用的 Coding Agent,让开发者可以直观地看到:所谓“会读代码、会改项目”的智能体,本质只是一个围绕 LLM 的工具调用循环。

原文链接:https://www.mihaileric.com/The-Emperor-Has-No-Clothes/

作者 | Mihail Eric       翻译 | 郑丽媛

出品 | CSDN(ID:CSDNnews)

现在的 AI 编程助手,用起来简直像魔法。

你随口用几句不太通顺的英语描述需求,它就能自动读代码、改项目、写出能跑的功能模块。Claude Code、Cursor、Devin,看起来都像是掌握了某种神秘黑科技。

但真相是:这些工具的核心,一点也不神秘——不到 200 行的 Python 代码,就能复刻一个可用的 AI 编程 Agent。

下面,让我们从零开始,亲手实现一个「会改代码的 LLM」。

不到 200 行 Python 代码,就能复刻一个「Claude Code」?

先搞懂核心逻辑:Coding Agent 到底在干什么?

在写代码前,先搞清楚:你在用 Claude Code 时,后台究竟发生了什么?

本质上,它就是大语言模型(LLM)+ 工具库的对话循环,具体步骤只有五步:

(1)你发送指令(比如:“创建一个包含 hello world 函数的新文件”)

(2)LLM 分析指令后,判断需要调用工具,并返回结构化的工具调用请求(可以是单次或多次调用)

(3)你的本地程序执行这个工具调用(比如真的创建文件)

(4)工具执行结果被回传给 LLM

(5)LLM 结合结果继续处理,直到完成任务

整个过程里,LLM 自始至终都没有直接操作你的文件系统,它只负责下达指令,真正干活的是你写的本地代码。

不到 200 行 Python 代码,就能复刻一个「Claude Code」?

核心三件套:实现 Agent 只需要 3 个工具

一个能用的代码助手,底层只需要三个核心功能,再多的功能都只是锦上添花:

(1)读取文件:让 LLM 能查看你现有代码的内容

(2)列出文件:帮 LLM 理清项目的目录结构,实现 “导航”

(3)编辑文件:让 LLM 能创建新文件、修改已有代码

没错,就是这么简单。像 Claude Code 这样的商用产品,还会额外集成 grep 检索、bash 命令执行、网页搜索等功能,对我们来说,这三个工具就足够实现核心能力了。

不到 200 行 Python 代码,就能复刻一个「Claude Code」?

第一步:搭建基础框架

我们从基础的导入和 API 客户端开始。我这里用的是 Anthropic,但换成其他 LLM 供应商的 SDK 也完全适用。

    import inspect
    import json
    import os


    import anthropic
    from dotenv import load_dotenv
    from pathlib import Path
    from typing import AnyDictListTuple


    load_dotenv()
    claude_client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    为了让终端输出的内容更易区分,我们定义几个颜色常量:

      YOU_COLOR = "\u001b[94m"
      ASSISTANT_COLOR = "\u001b[93m"
      RESET_COLOR = "\u001b[0m"

      再写一个工具函数,用来解析并返回文件的绝对路径,避免因相对路径导致的找不到文件问题:

        def resolve_abs_path(path_str: str) -> Path:
            path = Path(path_str).expanduser()
            if not path.is_absolute():
                path = (Path.cwd() / path).resolve()
            return path

        不到 200 行 Python 代码,就能复刻一个「Claude Code」?

        第二步:实现三大核心工具

        注意:工具函数的文档字符串(docstring)一定要写清楚,LLM 会根据这些描述判断该调用哪个工具、怎么传参——这是让 Agent 能正常工作的关键。

        工具 1:读取文件内容

        功能最简单的工具,传入文件名,返回文件的完整内容。

          def read_file_tool(filename: str) -> Dict[strAny]:
              full_path = resolve_abs_path(filename)
              with open(str(full_path), "r"as f:
                  content = f.read()
              return {
                  "file_path"str(full_path),
                  "content": content
              }

          返回字典格式,是为了给 LLM 传递结构化的执行结果,方便它理解。

          工具 2:列出目录下的文件

          帮 LLM 搞清楚项目结构,实现“导航”功能。

            def list_files_tool(path: str) -> Dict[strAny]:
                full_path = resolve_abs_path(path)
                all_files = []
                for item in full_path.iterdir():
                    all_files.append({
                        "filename": item.name,
                        "type""file" if item.is_file() else "dir"
                    })
                return {
                    "path"str(full_path),
                    "files": all_files
                }

            工具 3:编辑文件(创建 + 修改)

            这是三个工具里最复杂的一个,但逻辑依然清晰,它主要处理两种场景:

            • 当 old_str 参数为空时:创建新文件

            • 当 old_str 参数不为空时:替换文件中第一次出现的 old_str 为 new_str

              def edit_file_tool(path: str, old_str: str, new_str: str) -> Dict[strAny]:
                  full_path = resolve_abs_path(path)
                  if old_str == "":
                      full_path.write_text(new_str, encoding="utf-8")
                      return {"path"str(full_path), "action""created_file"}


                  original = full_path.read_text(encoding="utf-8")
                  if original.find(old_str) == -1:
                      return {"path"str(full_path), "action""old_str not found"}


                  edited = original.replace(old_str, new_str, 1)
                  full_path.write_text(edited, encoding="utf-8")
                  return {"path"str(full_path), "action""edited"}

              商用 IDE 的代码助手会有更复杂的容错逻辑,但这个极简版本足以验证核心原理。

              不到 200 行 Python 代码,就能复刻一个「Claude Code」?

              第三步:注册工具,让 LLM 能找到它们

              我们需要一个“工具注册表”,把工具名称和对应的函数绑定起来,方便后续调用。

                TOOL_REGISTRY = {
                    "read_file": read_file_tool,
                    "list_files": list_files_tool,
                    "edit_file": edit_file_tool 
                }

                不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                第四步:给 LLM 写 “使用说明书”

                LLM 不会天生就知道怎么用我们的工具,我们需要通过系统提示词,把工具的名称、功能、参数格式告诉它。

                我们先写两个辅助函数,从工具的函数签名和文档字符串里,自动生成工具说明:

                  def get_tool_str_representation(tool_name: str) -> str:
                      tool = TOOL_REGISTRY[tool_name]
                      return f"""
                      Name: {tool_name}
                      Description: {tool.__doc__}
                      Signature: {inspect.signature(tool)}
                      """
                  def get_full_system_prompt():
                      tool_str_repr = ""
                      for tool_name in TOOL_REGISTRY:
                          tool_str_repr += "TOOL\n===" + get_tool_str_representation(tool_name)
                          tool_str_repr += f"\n{'='*15}\n"
                      return SYSTEM_PROMPT.format(tool_list_repr=tool_str_repr)

                  然后定义核心的系统提示词模板,这是整个 Agent 的 “灵魂”:你不是教 LLM 怎么写代码,而是教它怎么调用现实世界的工具

                    SYSTEM_PROMPT = """
                    You are a coding assistant whose goal it is to help us solve coding tasks. 
                    You have access to a series of tools you can execute. Here are the tools you can execute:
                    {tool_list_repr}
                    When you want to use a tool, reply with exactly one line in the format: 'tool: TOOL_NAME({{JSON_ARGS}})' and nothing else.
                    Use compact single-line JSON with double quotes. After receiving a tool_result(...) message, continue the task.
                    If no tool is needed, respond normally.
                    """

                    不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                    第五步:解析 LLM 的工具调用指令

                    当 LLM 返回内容后,我们需要判断它是不是在请求调用工具。这个函数的作用就是从 LLM 的回复里,提取出工具名称和对应的参数。

                      def extract_tool_invocations(text: str) -> List[Tuple[strDict[strAny]]]:
                          """
                          Return list of (tool_name, args) requested in 'tool: name({...})' lines.
                          The parser expects single-line, compact JSON in parentheses.
                          """
                          invocations = []
                          for raw_line in text.splitlines():
                              line = raw_line.strip()
                              if not line.startswith("tool:"):
                                  continue
                              try:
                                  after = line[len("tool:"):].strip()
                                  name, rest = after.split("("1)
                                  name = name.strip()
                                  if not rest.endswith(")"):
                                      continue
                                  json_str = rest[:-1].strip()
                                  args = json.loads(json_str)
                                  invocations.append((name, args))
                              except Exception:
                                  continue
                          return invocations

                      不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                      第六步:封装 LLM 调用逻辑

                      写一个简单的封装函数,负责把对话历史传给 LLM,并获取回复。

                        def execute_llm_call(conversation: List[Dict[strstr]]):
                            system_content = ""
                            messages = []


                            for msg in conversation:
                                if msg["role"] == "system":
                                    system_content = msg["content"]
                                else:
                                    messages.append(msg)


                            response = claude_client.messages.create(
                                model="claude-sonnet-4-20250514",
                                max_tokens=2000,
                                system=system_content,
                                messages=messages
                            )
                            return response.content[0].text

                        不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                        第七步:组装核心循环,让 Agent 跑起来

                        这一步是把前面所有的模块串起来,实现 Agent 的核心工作流,也是“魔法”发生的地方。

                          def run_coding_agent_loop():
                              print(get_full_system_prompt())
                              conversation = [{
                                  "role": "system",
                                  "content"get_full_system_prompt()
                              }]
                              while True:
                                  try:
                                      user_input = input(f"{YOU_COLOR}You:{RESET_COLOR}:")
                                  except (KeyboardInterrupt, EOFError):
                                      break
                                  conversation.append({
                                      "role": "user",
                                      "content": user_input.strip()
                                  })
                                  while True:
                                      assistant_response = execute_llm_call(conversation)
                                      tool_invocations = extract_tool_invocations(assistant_response)
                                      if not tool_invocations:
                                          print(f"{ASSISTANT_COLOR}Assistant:{RESET_COLOR}: {assistant_response}")
                                          conversation.append({
                                              "role": "assistant",
                                              "content": assistant_response
                                          })
                                          break
                                      for name, args in tool_invocations:
                                          tool = TOOL_REGISTRY[name]
                                          resp = ""
                                          print(name, args)
                                          if name == "read_file":
                                              resp = tool(args.get("filename""."))
                                          elif name == "list_files":
                                              resp = tool(args.get("path""."))
                                          elif name == "edit_file":
                                              resp = tool(args.get("path""."), 
                                                          args.get("old_str"""), 
                                                          args.get("new_str"""))
                                          conversation.append({
                                              "role": "user",
                                              "content": f"tool_result({json.dumps(resp)})"
                                          })

                          这个主循环的逻辑可以拆解为两层:

                          • 外层循环:获取用户输入,添加至对话内容;

                          • 内层循环:调用大型语言模型,检测工具调用需求;

                            →若无需工具,输出响应并终止内层循环;

                            →若需工具,执行工具操作,将结果添加至对话内容,循环继续。

                          内层循环持续进行,直至 LLM 响应时不再请求任何工具。这使 Agent 能够串联多个工具调用(例如:读取文件→编辑文件→确认编辑)。

                          不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                          最后一步:启动程序

                          加上主函数入口,运行我们的代码助手:

                            if __name__ == "__main__":
                                run_coding_agent_loop()

                            现在,你就可以进行这样的对话了:

                            你:创建一个名为 hello.py 的新文件,并在其中实现"Hello World"功能

                            AI 助手调用 edit_file 函数,参数为 path="hello.py",old_str="",new_str="print('Hello World')"

                            AI 助手:完成!已创建包含 Hello World 实现的 hello.py 文件。

                            或者,还可以进行多步骤交互:

                            你:编辑 hello.py 并添加一个乘法函数

                            AI 助手调用 read_file 查看当前内容,再调用 edit_file 添加函数

                            AI 助手:已在 hello.py 中添加乘法函数

                            不到 200 行 Python 代码,就能复刻一个「Claude Code」?

                            我们的极简版 vs 商用版 Claude Code

                            我们的代码只有 200 行左右,但已经实现了代码助手的核心逻辑。商用产品 Claude Code 之所以更强大,是因为它在这个基础上做了这些优化:

                            (1)更完善的错误处理:比如文件权限不足、路径不存在时的容错逻辑

                            (2)流式输出:让回复内容实时显示,提升用户体验

                            (3)智能上下文管理:比如自动摘要长文件,避免 Token 超限

                            (4)更丰富的工具集:比如执行 shell 命令、搜索代码库、调用外部 API

                            (5)安全校验流程:比如修改重要文件前需要用户确认,防止误操作

                            核心工作流完全一致:LLM 决策 → 本地工具执行 → 结果反馈 → 继续决策。

                            所以,动手试试吧!完整源代码我放在这里了:https://drive.google.com/file/d/1YtpKFVG13DHyQ2i3HOtwyVJOV90nWeL2/view?pli=1

                            这不到 200 行的代码只是一个起点,你还可以对其轻松扩展:换成其他 LLM 供应商的 API,调整系统提示词,并可作为练习添加更多工具。你会发现,看似高大上的 AI 代码助手,底层原理其实一点都不神秘。


                            AI 前线

                            “别犯蠢了!”Linus 怒怼「AI 垃圾代码」争论:靠写文档,根本救不了 Linux 内核

                            2026-1-10 18:37:55

                            AI 前线

                            CES 大腕儿最多的场子,诞生了第一个「AI 产业链」

                            2026-1-10 18:38:03

                            0 条回复 A文章作者 M管理员
                              暂无讨论,说说你的看法吧
                            个人中心
                            购物车
                            优惠劵
                            今日签到
                            有新私信 私信列表
                            搜索