56行代码,两个Loop的ClaudeCode工作原理的详细分析"

admin 2026-10-22 04:24:55 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

56 行代码,两个 Loop 的 Claude Code 工作原理的详细分析

原创

王建硕
王建硕

王建硕

2026年6月10日 11:19
上海

在小说阅读器读本章

去阅读

Claude Code 虽然很复杂(50 万行),但是却非常精巧。从本质上来说,就是两层 while(true) {} 的循环,组织起来的读写查文件三个工具调用。我用了 56 行,复刻了一下 Claude Code 的最核心代码,虽然不能真的做到平替,但是写些简单的代码是可以的,最主要是理解 Claude Code(或者大多数 Agent)的工作原理。以下是全部代码,Github 源代码放在「查看原文」里面。

import OpenAI from \"openai\";import fs from \"fs\";import path from \"path\";import readline from \"readline\";const client = new OpenAI({ apiKey: process.env.MOONSHOT_API_KEY, baseURL: \"https://api.moonshot.cn/v1\" });const fn = (name, desc, props, req = []) => ({    type: \"function\",    function: { name, description: desc, parameters: { type: \"object\", properties: props, required: req } },});const TOOLS = [    fn(\"read_file\",  \"Read a file.\",               { path: { type: \"string\" } },                               [\"path\"]),    fn(\"list_files\", \"List files in a directory.\", { path: { type: \"string\" } }),    fn(\"edit_file\",  \"Write content to a file.\",   { path: { type: \"string\" }, content: { type: \"string\" } }, [\"path\", \"content\"]),];function runTool(name, input) {    try {        if (name === \"read_file\")  return fs.readFileSync(input.path, \"utf8\");        if (name === \"list_files\") return fs.readdirSync(input.path ?? \".\").sort().join(\"\
\");        if (name === \"edit_file\") {            fs.mkdirSync(path.dirname(path.resolve(input.path)), { recursive: true });            fs.writeFileSync(input.path, input.content);            return `Wrote ${input.content.length} bytes to ${input.path}`;        }    } catch (e) { return `Error: ${e.message}`; }}async function agentLoop(userMessage, history) {    history.push({ role: \"user\", content: userMessage });    while (true) {        const { choices } = await client.chat.completions.create({ model: \"moonshot-v1-8k\", tools: TOOLS, tool_choice: \"auto\", messages: history });        const msg = choices[0].message;        history.push(msg);        if (msg.content) console.log(`\
Assistant: ${msg.content}`);        if (!msg.tool_calls?.length) return;        for (const tc of msg.tool_calls) {            const input = JSON.parse(tc.function.arguments);            console.log(`  → ${tc.function.name}(${tc.function.arguments})`);            const out = runTool(tc.function.name, input);            console.log(`  ← ${String(out).slice(0, 120)}`);            history.push({ role: \"tool\", tool_call_id: tc.id, content: out });        }    }}const rl = readline.createInterface({ input: process.stdin, output: process.stdout });const ask = q => new Promise(r => rl.question(q, r));const history = [{ role: \"system\", content: \"You are a coding assistant. Use tools to read, list, and edit files — never guess file contents. Always call a tool when the task involves the filesystem. Always output code into filesystem\" }];console.log(\"Mini Code Assistant (Kimi) — press Ctrl+C to exit\
\");while (true) {    const msg = (await ask(\"You: \")).trim();    if (msg) await agentLoop(msg, history);}

两层循环

最外层,就是最后四句,收到一个用户输入,把它和历史一起开启一个内部的小循环。

while (true) {    const msg = (await ask(\"You: \")).trim();    if (msg) await agentLoop(msg, history);}

这是大循环。

这是那个内部的小循环:

while (true) {    const { choices } = await client.chat.completions.create({ model: \"moonshot-v1-8k\", tools: TOOLS, tool_choice: \"auto\", messages: history });    const msg = choices[0].message;    history.push(msg);    if (msg.content) console.log(`\
Assistant: ${msg.content}`);    if (!msg.tool_calls?.length) return;    for (const tc of msg.tool_calls) {        const input = JSON.parse(tc.function.arguments);        console.log(`  → ${tc.function.name}(${tc.function.arguments})`);        const out = runTool(tc.function.name, input);        console.log(`  ← ${String(out).slice(0, 120)}`);        history.push({ role: \"tool\", tool_call_id: tc.id, content: out });    }}

两个都是死循环。

前一个大循环(User Loop),只有当用户输入 Ctrl+C 强行中断的时候跳出,第二个小循环,只有当 tool_calls.length == 0 的时候跳出。也就是说,只有 LLM 回复要求的工具调用都调用完毕以后,这一轮交互才算真正结束。

最里面有一个很小的循环,就是如果 LLM 同时要调用多个工具,就一个一个按顺序调用本地的工具完成。

没了。就这么简单。主要代码完毕。

工具定义

上面就是两个函数定义了一下工具。一个是真正的定义,里面只定义了三个代码 Agent 至少要用的工具:一个是读文件,一个是读文件夹内容,一个是写文件。都是最简单的文件操作。当然 Claude Code 内置了四十几个工具,大家可以从泄漏的代码的 Tools 文件夹里面看到,每一个工具的定义不比我下面定义的复杂多少。

const fn = (name, desc, props, req = []) => ({    type: \"function\",    function: { name, description: desc, parameters: { type: \"object\", properties: props, required: req } },});const TOOLS = [    fn(\"read_file\",  \"Read a file.\",               { path: { type: \"string\" } },                               [\"path\"]),    fn(\"list_files\", \"List files in a directory.\", { path: { type: \"string\" } }),    fn(\"edit_file\",  \"Write content to a file.\",   { path: { type: \"string\" }, content: { type: \"string\" } }, [\"path\", \"content\"]),];function runTool(name, input) {    try {        if (name === \"read_file\")  return fs.readFileSync(input.path, \"utf8\");        if (name === \"list_files\") return fs.readdirSync(input.path ?? \".\").sort().join(\"\
\");        if (name === \"edit_file\") {            fs.mkdirSync(path.dirname(path.resolve(input.path)), { recursive: true });            fs.writeFileSync(input.path, input.content);            return `Wrote ${input.content.length} bytes to ${input.path}`;        }    } catch (e) { return `Error: ${e.message}`; }}

在后面就是把这几个工具变成一个 JSON 数据传给 LLM 就好了。

最终生成的 JSON 格式就是这样,原封不动的给大模型就行:

[  {    type: 'function',    function: {      name: 'read_file',      description: 'Read a file.',      parameters: [Object]    }  },  ...]

LLM 会根据上下文和你提供的工具,找到合适的工具,把最终的项目完成。

就是这简单的 56 行,是大多数 Coding Agent 的骨架。在这之上可以加很多更多的功能,比如 Skills 的支持等。

Claude Code 也好,Codex 也好,核心框架都是这两个 Loop。而「读」,「写」,「查目录」三个工具,也分别了普通聊天和代码工具。

Boris 很厉害,但也真的没有那么厉害。不厉害的是,这个底层真的很简单。厉害的是,他在 1 年前想明白了就用这两个简单的循环,和大语言模型可以在没有人工参与的情况下完成工作这个信念,从这里开始写出了 Claude Code。


56行代码,两个Loop的ClaudeCode工作原理的详细分析" 网络安全文章

56行代码,两个Loop的ClaudeCode工作原理的详细分析"

本文通过56行代码复现了ClaudeCode的核心工作机制,揭示其本质是由两层while循环构成:外层处理用户输入交互,内层循环调用LLM并执行文件操作工具(读文件、列目录、写文件)。文章指出这是大多数代码助手Agent的基础架构,虽简化但完整展示了AI代理通过工具调用自主完成编码任务的原理。
GitFlow与企业软件研发生命周期:从Feature到生产发布的软件工程全景解析" 网络安全文章

GitFlow与企业软件研发生命周期:从Feature到生产发布的软件工程全景解析"

该文档为马哥Linux运维推出的课程宣传材料,系统解析GitFlow工作流在企业软件研发生命周期中的应用。课程涵盖从需求提出、功能开发、版本集成、测试验证到生产发布的全流程,重点讲解Feature、Develop、Release等分支设计思想,以及联调机制、环境部署、CI/CD流水线、回滚策略等核心环节,帮助运维和开发人员建立完整的软件交付体系认知。
AI流量识别指南:爬虫和代理,你真的分得清吗?" 网络安全文章

AI流量识别指南:爬虫和代理,你真的分得清吗?"

该文档深入解析AI爬虫与AI代理的核心区别:爬虫是集中式内容收集器,具有公开身份和稳定基础设施;代理则是任务驱动型,行为更接近真实用户且可能无可靠身份验证。文章指出识别爬虫可通过声明身份和IP验证,而代理需依赖加密签名或行为分析。关键建议是防御策略需区分两类流量,并进一步进行意图分类,推荐采用分层检测方案或使用专业机器人管理平台实现精准防护。
终极进化:当swanctl遇上FRR,让你的Linux加密隧道化身SD-WAN雏形" 网络安全文章

终极进化:当swanctl遇上FRR,让你的Linux加密隧道化身SD-WAN雏形"

本文详细介绍了如何在Linux系统中结合swanctl配置基于路由的IPsecVPN,并通过FRR动态路由协议实现SD-WAN雏形架构。文章通过5台Ubuntu虚拟机搭建实验环境,从基础网络配置、strongswan-swanctl安装、XFRM接口创建到OSPF协议部署,完整演示了零停机动态扩容的加密隧道方案。关键发现显示路由模式IPsec比策略模式性能提升近3Gbps,并提供了具体配置命令和优化建议。
评论:0   参与:  0