ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Claude Code 架构解析:从 CLI 到 AI 原生开发工作台

Claude Code 架构解析:从 CLI 到 AI 原生开发工作台 1. 从命令行工具到工作台这个认知差决定了你用得顺不顺手大多数人第一次接触 Claude Code脑子里蹦出来的第一印象就是哦又一个 CLI 工具。装完、跑起来、敲几个命令、让它改个 bug然后觉得跟其他 AI 编程助手差不多嘛。这个判断本身没错但如果你只停在这一层后面大概率会遇到两个问题一是觉得它也就那样二是遇到复杂任务时不知道怎么组织工作流最后把它当成一个高级版的代码补全来用。我一开始也是这么想的。直到有一次我试着让它在一个中型 TypeScript 项目里做一次跨模块的重构——涉及 6 个文件、一个接口定义的变更、以及对应的测试更新。我原本预期它会像普通 CLI 那样一次只处理一个文件我得反复喂上下文。结果它自己规划了执行顺序先改接口、再改实现、最后跑测试中间还主动读了我没提到的配置文件。那一刻我才意识到这东西的架构定位跟传统 CLI 根本不在一个层面上。Claude Code 的本质是一个 AI 原生开发工作台AI-native development workbenchCLI 只是它的入口形态。这个区别很关键CLI 是你敲命令、它执行的被动工具而工作台是你给目标、它组织资源去达成的主动环境。理解这一点你才能理解它为什么要有 agent runtime、为什么要有工具调用循环、为什么要有上下文管理策略。这篇文章我会从源码架构的角度把这个工作台拆开来看。适合两类人一是已经装了 Claude Code 但用得不够深的开发者想知道它内部到底怎么运转的二是正在做 AI 原生工具、想参考它的架构设计的工程师。我会尽量把 TypeScript 层面的实现逻辑讲清楚同时补充大量实际使用中的经验和坑。提示本文讨论的是架构层面的设计思路不涉及任何具体版本的安装配置细节。不同版本实现可能有差异但核心设计理念是相通的。2. Agent Runtime工作台的心脏不是一个循环而是三层调度2.1 为什么一个大循环的解释是错的网上很多讲 Claude Code 架构的文章喜欢用一个简单的图来描述用户输入 → 模型推理 → 工具调用 → 结果回填 → 再推理 → 直到完成。这个描述不能说错但它把最核心的东西省略了——调度。如果你真的去看它的运行逻辑会发现它不是一个扁平的 while 循环而是至少三层结构会话层Session Layer管理一次对话的生命周期包括消息历史、上下文窗口的裁剪策略、会话状态的持久化。任务层Task Layer把用户的自然语言目标拆解成可执行的步骤序列决定每一步用什么工具、按什么顺序。执行层Execution Layer真正调用工具读文件、写文件、跑命令、搜索处理返回结果做错误恢复。这三层不是简单的嵌套关系而是有反馈回路的。执行层的结果会反过来影响任务层的规划任务层的规划又会触发会话层对上下文的重新组织。这就是为什么它能在多文件重构时表现得像一个有全局观的助手而不是一个每次只看一个文件的工具。2.2 工具调用循环里的刹车机制Agent runtime 最容易出问题的地方是无限循环。模型调用工具、拿到结果、觉得不对、再调用、再拿结果……如果没有刹车机制token 会烧得飞快任务还完不成。Claude Code 在这块的设计思路值得借鉴。它至少有三重刹车第一重是步数上限。每个任务有一个最大工具调用次数超过就强制停止并要求模型给出当前最佳答案。这个数字不是拍脑袋定的而是根据任务类型动态调整的——简单的文件读取可能只给 5 步复杂的重构可能给 50 步。第二重是重复检测。如果模型连续多次调用同一个工具、传相似的参数、拿到相似的结果runtime 会判定它卡住了主动注入一条提示让模型换思路。第三重是用户中断点。某些高风险操作比如删除文件、执行有副作用的命令会强制暂停等用户确认。这个设计在实际使用中非常关键我后面会专门讲。// 这是基于常见 agent runtime 实践的示意性伪代码不是源码 interface AgentLoopConfig { maxSteps: number; // 步数上限 repeatThreshold: number; // 重复检测阈值 requireConfirmation: string[]; // 需要确认的工具列表 } async function runAgentLoop(goal: string, config: AgentLoopConfig) { let steps 0; const history: ToolCall[] []; while (steps config.maxSteps) { const action await planNextAction(goal, history); if (isRepeating(action, history, config.repeatThreshold)) { injectHint(检测到重复操作请换一种思路); continue; } if (config.requireConfirmation.includes(action.tool)) { await waitForUserConfirmation(action); } const result await executeTool(action); history.push({ action, result }); steps; } return synthesizeAnswer(history); }2.3 上下文窗口管理工作台最容易被低估的能力一个开发工作台要处理的项目动辄几万行代码。上下文窗口再大也装不下。所以上下文管理策略才是决定它好不好用的核心。我观察到的策略大致是这样的它不会一次性把所有相关文件都塞进上下文而是按需加载。当你提到某个函数时它先读那个文件当它发现这个函数依赖另一个模块时再去读那个模块。这个过程是渐进的而且有淘汰机制——不相关的旧内容会被移出上下文给新内容腾空间。这个策略的好处是 token 利用率高坏处是它可能忘记你之前提过的约束。我踩过这个坑在一个长任务里我一开始说了不要改测试文件结果做到一半它还是改了。后来我学乖了重要的约束要反复强调或者写进项目根目录的配置文件里让它每次都能读到。注意上下文管理是概率性的不是确定性的。不要假设它一定记得你说过的每一句话。关键约束要显式、重复、或者持久化。3. TypeScript 在这套架构里扮演的角色比你想的更重3.1 为什么是 TypeScript 而不是别的看到 Claude Code 用 TypeScript 写很多人的第一反应是因为它是给前端/Node 开发者用的。这个理解太浅了。TypeScript 在这套架构里的价值主要体现在三个地方第一工具接口的类型安全。一个 agent runtime 要调用几十种工具每个工具的输入输出格式都不一样。如果用动态类型语言写工具之间的数据流转很容易出错而且错误往往在运行时才暴露。TypeScript 的接口定义让每个工具的契约都是显式的编译期就能发现大部分类型不匹配的问题。第二流式响应的类型建模。AI 的输出是流式的token 一个一个吐出来。要正确处理这种流式数据需要一套清晰的类型系统来描述部分结果和完整结果的关系。TypeScript 的联合类型和泛型在这里非常好用。第三配置和状态的可序列化。工作台需要保存会话状态、工具配置、用户偏好。TypeScript 的类型定义让这些结构在序列化和反序列化时不容易丢字段或类型错乱。3.2 从类型定义反推架构设计如果你有机会看到它的类型定义文件会发现一些很有意思的设计。比如工具的定义大概长这样interface ToolTInput, TOutput { name: string; description: string; inputSchema: JSONSchema; // 给模型看的输入格式说明 execute: (input: TInput, context: ToolContext) PromiseTOutput; requiresConfirmation?: boolean; category: read | write | execute | search; }这个定义里有几个细节值得琢磨inputSchema是 JSON Schema不是 TypeScript 类型。因为模型看不懂 TypeScript 类型它需要的是自然语言描述 JSON Schema 这种结构化说明。这说明工具的定义是双轨的一套给编译器看一套给模型看。category字段把工具分成了四类。这个分类不是装饰性的它直接影响 runtime 的调度策略——读类工具可以并行写类工具要串行执行类工具要确认。requiresConfirmation是工具级别的不是全局的。这意味着不同的工具可以有不同的安全等级。3.3 实际使用中TypeScript 项目和非 TS 项目的体验差异我用它处理过纯 JavaScript 项目、TypeScript 项目、Python 项目。体验差异很明显项目类型类型推断准确度重构安全性需要人工干预的频率TypeScript高高低JavaScript中中中Python有类型注解中高中高中Python无类型注解低低高原因很简单TypeScript 的类型信息本身就是给工具用的地图。有了这张地图agent 在改一个函数签名时能准确知道哪些地方需要同步修改。没有类型信息它只能靠文本搜索猜漏改和误改的概率大幅上升。所以我的建议是如果你打算深度使用这类 AI 开发工作台把项目迁移到 TypeScript 的收益远不止类型安全本身还包括 AI 助手的准确度提升。这是一个很多人没意识到的隐性收益。4. 工具生态的设计哲学为什么它不追求什么都能干4.1 工具集的最小化原则一个常见的误解是AI 工作台的工具越多越好最好能直接操作数据库、调用 API、部署服务。但 Claude Code 的工具集设计走的是相反的路子——核心工具非常少大部分能力通过组合实现。它的核心工具大概就是这几类读文件、写文件、执行命令、搜索。就这些。没有专门的重构工具没有测试工具没有部署工具。那它是怎么完成这些任务的通过组合基础工具 模型的理解能力。比如重构一个函数它的做法是读文件 → 理解代码 → 生成新代码 → 写文件 → 跑测试验证。这一串操作里每一步都是基础工具但组合起来就完成了复杂任务。这个设计的好处是可预测性强。工具越少行为越可控出错时越容易定位。坏处是对模型能力依赖高——模型不够聪明组合出来的结果就不好。4.2 工具描述的提示工程工具能不能被正确使用很大程度上取决于它的description写得好不好。这不是普通的文档而是给模型看的提示。我研究过一些工具描述发现几个规律描述里会明确说什么时候用这个工具而不只是这个工具能干什么。会说明什么时候不要用避免模型误用。参数说明会给出具体例子而不是抽象的类型描述。会说明工具的副作用和限制。这其实是把提示工程的思路用在了工具设计上。如果你自己在做 AI 工具这一点非常值得学工具描述的质量直接决定了模型使用它的准确率。4.3 我自己扩展工具时的经验Claude Code 支持一定程度的工具扩展。我试过给它加自定义工具踩了不少坑分享几个关键经验第一工具粒度要适中。太细的工具比如读一行会导致调用次数爆炸太粗的工具比如重构整个项目会让模型不知道怎么用。一个好的粒度是一个工具做一件明确的事但这件事有实际价值。第二错误信息要写给模型看。工具执行失败时返回的错误信息不只是给用户看的更是给模型看的。错误信息里要包含为什么失败和可以怎么补救模型才能自我纠正。我一开始返回的错误信息太简略模型就一直在重试同样的操作。第三幂等性很重要。如果一个工具被重复调用会产生副作用比如重复写文件、重复发请求一定要做幂等处理。因为 agent runtime 在错误恢复时可能会重试。5. 那些文档里不会写的实战坑5.1 上下文污染为什么它有时候会越改越乱这是我最常遇到的问题。在一个长会话里如果你让它做了好几件事前面任务的上下文会污染后面的任务。表现就是它开始把不相关的代码风格、命名习惯、甚至错误的假设带到新任务里。我的应对方法是任务隔离一个任务做完如果下一个任务跟它没关系就开新会话。不要在一个会话里连续做多个不相关的任务。这个习惯养成后输出质量明显提升。如果确实需要在同一会话里做多个任务我会在切换任务时明确说一句接下来是一个全新的任务跟之前无关请忽略之前的上下文。虽然不能完全清除污染但能缓解。5.2 文件写入的部分成功问题让 AI 改一个大文件时偶尔会遇到改了一半的情况——前面的改动生效了后面的没生效或者格式乱了。这通常是因为输出被截断或者写入过程中出了错。我的做法是大改动分步做。不要让它在一次操作里改超过 200 行的代码。分成几个小步骤每步做完检查一下。虽然麻烦一点但比事后修复一个半成品要省时间。另外改之前先提交 git。这是铁律。有了 git改坏了随时回滚。我见过太多人因为没提交被 AI 改乱了代码又找不回来。5.3 命令执行的边界执行命令是最危险的工具因为它有真实的副作用。我总结了几条自己的规则涉及删除、覆盖、推送的操作一定要人工确认。涉及网络的命令先看清楚它要访问什么。长时间运行的命令比如全量测试先在小范围验证。环境相关的命令比如改配置、装依赖先确认当前环境。这些规则听起来是常识但在实际使用中人很容易因为信任 AI而放松警惕。我踩过一次坑让它跑一个清理脚本结果它把一些我需要的临时文件也删了。从那以后所有删除类操作我都要求它先列出要删的文件我确认后再执行。5.4 模型选择的实际影响不同的模型在同一个工作台里的表现差异很大。我的观察是复杂重构、架构设计类任务需要更强的推理能力用能力强的模型。简单的文件读写、格式调整用轻量模型就够还快。涉及多步骤规划的任务模型的长程一致性很关键弱模型容易中途跑偏。实际使用中我会根据任务复杂度切换模型。不是所有任务都值得用最强的模型那样又慢又贵。但关键任务上省模型往往会付出更大的返工成本。6. 把 Claude Code 当工作台用工作流应该怎么组织6.1 项目级的约定文件工作台要高效运转需要一些项目级的约定。我习惯在项目根目录放一个约定文件内容包括项目的技术栈和版本约束代码风格和命名规范测试运行方式不允许修改的文件或目录常用的命令和脚本这个文件的作用是给 agent 提供项目常识避免每次都要重新解释。实测下来有了这个文件任务的一次成功率明显提升。6.2 任务描述的写法跟工作台协作任务描述的质量直接决定结果质量。我的经验是说清楚目标而不是步骤。告诉它我要什么结果而不是你先做 A 再做 B。它的规划能力比你手动拆步骤更强。给出验收标准。比如改完后所有测试要通过、不能引入新的依赖。说明约束。比如不要改公共接口、保持向后兼容。提供必要的背景。比如这个模块是给 XX 场景用的。一个反例是帮我优化一下这个函数。这种描述太模糊它不知道优化什么维度——性能可读性还是减少依赖结果往往不是你想要的。6.3 验证环节不能省工作台再智能也不能替代验证。我的流程是它改完代码先看 diff确认改动范围符合预期。跑测试确认没有破坏现有功能。关键改动手动 review 一遍。有问题就带着具体反馈让它修而不是笼统地说不对。带着具体反馈这一点很重要。说这个函数的边界条件处理错了当输入为空时应该返回空数组而不是抛异常比说这里有问题有效得多。7. 从这套架构里能学到什么给做 AI 工具的人7.1 工作台思维 vs 工具思维如果你在做 AI 相关的产品Claude Code 的架构给了一个重要启示不要做工具要做工作台。工具思维是我提供一个功能用户来调用。工作台思维是我提供一个环境用户给目标我来组织资源达成。这两者的产品形态、技术架构、用户体验都不一样。工作台思维要求你解决几个工具思维不需要解决的问题任务规划、上下文管理、错误恢复、状态持久化。这些才是难点也是壁垒。7.2 类型系统是 AI 工具的基础设施TypeScript 在这套架构里的作用让我重新认识了类型系统的价值。它不只是给开发者用的更是给 AI 用的。类型定义是机器可读的契约是 agent 理解代码结构的地图。如果你在做 AI 编程工具投资类型系统的建设收益是双重的既提升了代码质量又提升了 AI 的准确度。7.3 安全边界要设计在架构里而不是靠提示让模型不要做危险操作这种靠提示词约束的做法是不可靠的。可靠的做法是在架构层面设置边界哪些工具需要确认、哪些操作有步数限制、哪些资源不可访问。Claude Code 的requiresConfirmation字段就是这个思路——安全不是靠模型自觉而是靠 runtime 强制。这个设计原则值得所有做 agent 的人学习。8. 一些零散但有用的观察用久了之后我积累了一些零散但实用的观察一并分享关于速度它的响应速度受模型推理速度和工具执行速度双重影响。如果觉得慢先看是卡在推理还是卡在工具执行。工具执行慢通常是命令本身慢跟工作台无关。关于成本token 消耗主要来自上下文。上下文越长每次调用的成本越高。所以及时开新会话、及时清理不必要的历史不只是为了准确性也是为了省钱。关于稳定性长任务比短任务更容易出问题。如果一个任务预计要跑很久考虑拆成几个短任务。每个短任务完成后确认一下比一个长任务跑到底再检查要稳。关于学习曲线这东西的上手门槛不高但用好需要时间。前几次用可能觉得也就那样用熟了、摸清了它的脾气才会发现它的价值。给它一点耐心也给自己一点时间适应新的协作方式。关于边界它不是万能的。有些任务它做得好有些任务它做不好。识别出哪些任务适合交给它、哪些任务自己动手更快这个判断力比会用工具本身更重要。我的经验是重复性的、有明确模式的、需要跨文件一致性的任务它做得很好需要深度领域知识、需要创造性判断、需要跟人频繁沟通的任务还是自己来。这套架构最打动我的地方不是它现在能做什么而是它展示了一种新的可能性AI 不只是补全代码而是可以成为一个真正的工作伙伴理解你的项目、记住你的约定、按你的方式工作。这个方向上的探索才刚刚开始。
返回列表