社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

CSDN • 6 月前 • 287 次点击  

【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」。


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

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

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

(1)你发送指令(比如:“创建一个包含 hello world 函数的新文件”)
(2)LLM 分析指令后,判断需要调用工具,并返回结构化的工具调用请求(可以是单次或多次调用)
(3)你的本地程序执行这个工具调用(比如真的创建文件)
(4)工具执行结果被回传给 LLM
(5)LLM 结合结果继续处理,直到完成任务

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


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

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

(1)读取文件:让 LLM 能查看你现有代码的内容
(2)列出文件:帮 LLM 理清项目的目录结构,实现 “导航”
(3)编辑文件:让 LLM 能创建新文件、修改已有代码

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


第一步:搭建基础框架

我们从基础的导入和 API 客户端开始。我这里用的是 Anthropic,但换成其他 LLM 供应商的 SDK 也完全适用。
import inspectimport jsonimport os
import anthropicfrom dotenv import load_dotenvfrom pathlib import Pathfrom 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

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

注意:工具函数的文档字符串(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 的代码助手会有更复杂的容错逻辑,但这个极简版本足以验证核心原理。


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

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

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

第四步:给 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."""

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

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

def extract_tool_invocations(text: str) -> List[Tuple[strDict[str

Any]]]:    """    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

第六步:封装 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

第七步:组装核心循环,让 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 能够串联多个工具调用(例如:读取文件→编辑文件→确认编辑)。


最后一步:启动程序

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

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 中添加乘法函数


我们的极简版 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 代码助手,底层原理其实一点都不神秘。

推荐阅读:

11天狂写10万行代码!13年Rust老兵,与Claude联手从零造了一门新语言

全网380万人围观!连代码都不看,4个月“烧掉”30亿Token,不懂编程的他却做出了50+个产品……

开发网站被欠薪?Web开发者怒删客户官网,只留三行催款留言:先付钱才能访问网站!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/191521