从Claude Code源码拆解AI Agent架构:规划、执行与技能系统实战

📅 2026/8/14 3:07:35
从Claude Code源码拆解AI Agent架构:规划、执行与技能系统实战
1. 项目概述一次代码考古的意外发现最近在技术社区里Claude Code 这个项目突然火了起来。它被描述为一个“AI Agent”开发框架但说实话这个名字本身就有点让人摸不着头脑。Claude 是 Anthropic 家的 AI 模型Code 是代码这俩组合在一起听起来像是一个专门写代码的 AI 工具或者是一个基于 Claude 的代码生成器。但当我真正拿到它的源码准备一探究竟时却发现事情远没有这么简单。这更像是一次“开盲盒”源码里藏着的不是某个单一功能而是一整套关于如何构建、管理和执行“AI 代理”的工程化思考。对于任何一个对 AI 应用开发特别是 Agent智能体架构感兴趣的人来说这都是一份不可多得的、来自一线的实战参考。简单来说Claude Code 的核心价值在于它试图回答一个问题如何将一个强大的大语言模型LLM从单纯的“聊天对话”或“代码补全”工具变成一个可以自主规划、使用工具、并完成复杂任务的“智能体”它不是一个最终产品而更像是一个脚手架、一个样板间展示了构建这类应用所需的核心组件和设计模式。这次“意外的礼物”指的就是通过阅读其 TypeScript 源码我们能逆向工程出当前 AI Agent 领域的最佳实践、技术选型背后的逻辑以及那些在官方文档里不会明说的“坑”和技巧。2. 核心架构与设计哲学拆解拿到 Claude Code 的源码包通常是一个 npm 包第一件事就是看它的目录结构和核心入口文件。这能最快地理解作者的设计意图。2.1 项目结构与技术栈选择解压或克隆项目后一个典型的 Claude Code 项目结构可能如下所示根据网络信息推断和常见模式整合claude-code-project/ ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts // 主入口导出核心类 │ ├── core/ │ │ ├── agent.ts // Agent 核心逻辑类 │ │ ├── planner.ts // 任务规划模块 │ │ ├── executor.ts // 任务执行模块 │ │ └── memory.ts // 记忆上下文管理模块 │ ├── skills/ // “技能”目录即工具集 │ │ ├── web-search.ts // 网络搜索技能 │ │ ├── code-interpreter.ts // 代码解释执行技能 │ │ └── filesystem.ts // 文件系统操作技能 │ ├── harness/ // 关键基础设施层 │ │ ├── index.ts │ │ ├── logger.ts // 日志 │ │ ├── config.ts // 配置管理 │ │ ├── error-handler.ts // 错误处理 │ │ └── lifecycle.ts // 生命周期管理初始化、清理 │ └── types/ // TypeScript 类型定义 │ └── index.ts ├── examples/ // 使用示例 │ └── simple-agent.ts └── tests/ // 测试用例技术栈解析TypeScript这是现代 Node.js 工具链和前端框架的标配。对于 AI Agent 这种逻辑复杂、状态多变的应用强类型系统能在开发阶段就捕获大量潜在错误比如工具调用参数类型不匹配极大提升开发效率和代码可维护性。源码里随处可见的接口interface和泛型generic正是为了严谨地定义 Agent、Skill、Message 等核心概念之间的关系。npm 包管理项目通过package.json来管理依赖。从热搜词npm install、npm 国内源以及各种 npm 报错可以看出这是开发者接触它的第一道门槛。依赖项里很可能包含openaiSDK用于调用 Claude API、langchain或类似框架的部分工具链、以及各种工具函数库如axios用于 HTTP 请求zod用于运行时数据验证。注意如果你在安装依赖或运行时遇到npm : 无法加载文件 ... npm.ps1, 因为在此系统上禁止运行脚本这样的错误这不是 Claude Code 的问题而是 Windows 系统 PowerShell 的执行策略限制。解决方法是以管理员身份打开 PowerShell执行Set-ExecutionPolicy RemoteSigned并选择Y。但这会降低安全性请仅在可信环境下操作。2.2 “Harness”基础设施层被忽视的工程基石在众多热词中有一条非常关键的定义“harness 是一套包裹在ai agent核心推理逻辑之外的基础设施层。它不负责代替 agent”。这几乎是理解 Claude Code 架构精髓的钥匙。很多初学者包括之前的我一上来就直奔agent.ts想看看 AI 是怎么“思考”的。但很快就会被各种琐事淹没API 密钥怎么管理日志怎么打才能方便调试任务执行超时了怎么办多个 Agent 实例如何共享配置这些“脏活累活”正是harness层要解决的。为什么需要 Harness想象一下Agent 的核心大脑LLM是一个天才工程师但它需要在一个稳定、可靠的工作室里才能发挥最大价值。Harness 就是这个工作室的配置管理Config统一管理 API 密钥、模型参数、超时时间等。避免硬编码支持环境变量和配置文件。生命周期管理Lifecycle控制 Agent 的初始化、启动、暂停、重启和销毁。确保资源如数据库连接、API 会话被正确创建和释放。可观测性Observability通过结构化的日志Logger和指标Metrics记录 Agent 的每一步决策、工具调用和结果。这是调试复杂 Agent 行为的生命线。错误处理与韧性Error Handling Resilience捕获和处理工具调用失败、API 限流、网络异常等并提供重试、降级或优雅退出的策略。安全与合规沙箱Sandbox特别是对于code-interpreter这类执行任意代码的技能Harness 需要提供一个隔离的环境防止对主机系统造成破坏。在 Claude Code 的源码中harness/目录下的代码可能并不炫酷但它决定了整个 Agent 系统是否健壮、可维护、可运维。忽略 Harness直接裸写 Agent 逻辑是项目后期难以维护和技术债高筑的主要原因。2.3 Agent 核心规划、执行与记忆的循环剥开 Harness 这层“外壳”我们才看到 Agent 的核心。这通常是一个循环业界常称为ReAct (Reasoning Acting)模式或类似变种。Claude Code 的core/目录很可能实现了这一模式。1. 规划器 (Planner)它的职责是将一个模糊的用户目标如“帮我分析这个项目的依赖漏洞”分解成一系列可执行的子任务。源码中的planner.ts可能包含一个plan方法它调用 LLM并提示Prompt模型进行任务分解。这里的关键是Prompt 工程。源码会展示如何构造一个高效的规划提示词可能要求 LLM 以 JSON 或特定格式输出任务列表方便后续程序化处理。2. 执行器 (Executor)执行器负责携带当前上下文记忆调用合适的“技能”Skill来执行规划器给出的单个任务。executor.ts的核心是一个executeStep函数。它需要技能路由根据任务描述从注册的技能池中找到最匹配的那个。这里可能用到向量相似度搜索如果技能很多或简单的关键词匹配。参数绑定将自然语言描述的任务解析并绑定到技能函数所要求的结构化参数上。这通常也需要 LLM 辅助函数调用/工具调用功能。调用与结果处理执行技能并处理返回结果成功、失败、需要更多信息。3. 记忆体 (Memory)这是 Agent 的“工作记忆”。它不仅仅是保存完整的对话历史更重要的是管理上下文窗口。LLM 有 token 限制不能把所有的历史记录都塞进去。memory.ts需要实现摘要压缩将冗长的历史对话或工具执行结果总结成精炼的要点存入长期记忆。关键信息提取从交互中提取出实体如文件名、URL、数字结果并结构化存储便于后续任务直接引用。上下文窗口管理智能地选择最相关的历史片段作为下一次 LLM 调用的上下文。这可能涉及基于最近性、重要性或与当前任务相关性的筛选算法。这个“规划 - 执行 - 更新记忆 - 再规划”的循环会一直运行直到规划器认为最终目标已达成或遇到无法克服的障碍。3. “技能”系统的深度解析与实现Skills技能是 Agent 能力的延伸是它将“思考”转化为“行动”的双手。Claude Code 的skills/目录是宝藏所在展示了如何将各种外部能力封装成 Agent 可调用的标准化接口。3.1 技能的标准接口设计一个良好的技能设计首先体现在其 TypeScript 接口定义上。在types/index.ts或每个技能文件中你可能会看到类似这样的定义interface Skill { name: string; // 技能唯一标识如 “web_search” description: string; // 给 LLM 看的自然语言描述用于路由和参数生成 parameters: Recordstring, ParameterDefinition; // 输入参数的模式定义 execute: (args: any, context: AgentContext) PromiseSkillResult; // 执行函数 } interface ParameterDefinition { type: string | number | boolean | object; description: string; required?: boolean; } interface SkillResult { success: boolean; output: string; // 返回给 LLM 和用户的自然语言结果 data?: any; // 结构化的原始数据可供其他技能或记忆使用 }这种设计实现了“人机兼容”description和parameters的描述是给 LLM 看的用于让模型理解何时以及如何调用该技能execute方法是给程序执行的。这正是 OpenAI 的 Function Calling 或 Anthropic 的 Tool Use 功能所遵循的范式。3.2 典型技能实现剖析1. 网络搜索技能 (web-search.ts)这是 Agent 获取实时信息的必备技能。源码实现会揭示几个关键点API 选择是直接用 Google Search API、Bing API还是通过 SerpAPI 等聚合服务源码的选择反映了对稳定性、成本和易用性的权衡。通常会有一个SearchProvider的抽象层方便切换后端。结果处理原始搜索结果HTML 或 JSON需要被清洗、提取摘要和链接并格式化成 LLM 易于理解的文本。这里可能会用到简单的 DOM 解析库如cheerio或直接依赖 API 返回的摘要字段。限流与容错必须实现请求重试、失败回退等逻辑这部分代码会放在execute函数内部是健壮性的体现。2. 代码解释器技能 (code-interpreter.ts)这是最强大也最危险的技能。它允许 Agent 编写并执行代码通常是 Python来分析数据、处理文件或进行计算。安全沙箱源码中必须有一个牢不可破的沙箱机制。常见做法是在 Docker 容器中运行代码或者使用严格的vm2Node.js或Pyodide浏览器等隔离环境。Claude Code 的源码会展示如何配置资源限制CPU、内存、运行时间、网络访问控制和文件系统白名单。依赖管理如何动态安装 Python 包可能预装一个常用包集合如pandas,numpy,matplotlib或实现一个安全的、受限的pip install机制。结果捕获需要捕获代码的标准输出、标准错误、最终返回值甚至是生成的图表图片并将其转换为适合返回给 LLM 的格式如将图片保存为 Base64 或文件链接。3. 文件系统技能 (filesystem.ts)允许 Agent 读写文件。这需要极其精细的权限控制。工作区限制Agent 只能访问指定的工作目录如./workspace绝对不允许向上遍历../../。操作审计所有文件的读、写、删操作都必须记录在日志中以便追溯。敏感文件过滤避免读取或写入.env、config.json等包含密钥的配置文件。实操心得在实现或使用代码解释器技能时永远不要在生产环境中赋予其不受限制的访问权限。即使有沙箱也应将其视为“不受信任的第三方代码”。一个最佳实践是让这个技能只在特定的、隔离的“数据分析任务”中被激活并且任务完成后立即清理所有临时资源。3.3 技能注册与发现机制Agent 如何知道它有哪些技能可用这通常通过一个中央注册表来实现。在项目初始化时所有技能模块被导入并注册到一个SkillRegistry类中。class SkillRegistry { private skills: Mapstring, Skill new Map(); register(skill: Skill) { this.skills.set(skill.name, skill); } getSkill(name: string): Skill | undefined { return this.skills.get(name); } getAllSkillDescriptions(): Array{name: string, description: string, parameters: ...} { // 返回所有技能的描述信息用于构造给 LLM 的提示词 return Array.from(this.skills.values()).map(s ({...})); } }当 Planner 或 Executor 需要调用技能时就向这个注册表查询。这种设计支持动态加载技能非常灵活。4. 从零开始构建与调试实战理解了架构下一步就是动手。假设我们已经有了 Claude Code 的源码如何将它运行起来并定制我们自己的 Agent4.1 环境准备与依赖安装首先确保你的系统有 Node.js建议 LTS 版本和 npm。然后像对待任何一个 TypeScript 项目一样操作# 1. 克隆或下载源码到本地 git clone claude-code-repo-url # 假设有开源仓库 cd claude-code # 2. 安装依赖 npm install # 如果网络慢可以配置国内镜像源 npm config set registry https://registry.npmmirror.com # 3. 配置环境变量 # 复制示例配置文件 cp .env.example .env # 编辑 .env 文件填入你的 Claude API 密钥和其他服务密钥 # ANTHROPIC_API_KEYsk-your-key-here # SERPAPI_KEYyour-serpapi-key (如果需要搜索技能)常见安装问题排查npm ERR!各种找不到模块最常见的是网络问题。重试npm install或使用cnpm或配置镜像源。也可能是 Node.js 版本不兼容检查package.json中的engines字段。Error: cannot find module rollup/rollup-linux-x64-gnu这是一个典型的 npm 包二进制文件下载或平台不匹配错误。可以尝试删除node_modules和package-lock.json然后重新npm install。如果问题依旧可能是某个依赖包本身的问题。根据报错信息去该包的 GitHub issue 页面搜索。在 CI/CD 环境中确保操作系统和架构与包匹配。4.2 运行第一个示例并理解流程项目通常会在examples/目录下提供最简单的示例。我们以simple-agent.ts为例import { Agent, SkillRegistry, WebSearchSkill } from ../src; import { config } from dotenv; config(); // 加载 .env 环境变量 async function main() { // 1. 初始化技能注册表并注册技能 const skillRegistry new SkillRegistry(); skillRegistry.register(new WebSearchSkill()); // 2. 创建 Agent 配置 const agentConfig { name: MyFirstAgent, model: claude-3-opus, // 指定模型 skills: skillRegistry, // ... 其他配置如温度、最大token数等 }; // 3. 实例化 Agent const agent new Agent(agentConfig); // 4. 运行 Agent 处理任务 const query 谁是2023年诺贝尔物理学奖得主; console.log(用户提问: ${query}); const response await agent.run(query); console.log(Agent 回答: ${response}); } main().catch(console.error);运行它npx ts-node examples/simple-agent.ts如果一切顺利你会看到控制台输出 Agent 的“思考”过程它识别出需要搜索调用web-search技能获取结果然后生成最终答案。这个过程在日志中应该是清晰可见的这得益于harness中的日志模块。4.3 核心配置项详解与调优要让 Agent 表现更好必须理解几个关键配置这些通常在agent.ts的构造函数或配置文件中模型参数 (modelConfig):model: 如claude-3-sonnet、gpt-4-turbo。不同模型在推理能力、速度和成本上差异巨大。对于规划任务可能需要能力最强的模型对于简单的执行总结可以用小模型。temperature: 创造性。对于需要严格遵循步骤的任务如代码生成设为较低值0.1-0.3对于头脑风暴可以调高0.7-0.9。maxTokens: 单次回复的最大长度。需要为 Agent 的“内心独白”推理过程和最终输出留足空间。规划与执行控制:maxSteps: 最大执行步数。防止 Agent 陷入无限循环。根据任务复杂度设置一般 10-20 步。timeout: 单次任务总超时时间。stopConditions: 停止条件例如当最终输出中包含特定关键词时提前结束。记忆配置 (memoryConfig):maxContextTokens: 上下文窗口的最大 token 数。需要小于模型限制并留有余地。summaryInterval: 每隔多少轮对话或 token 数后触发一次历史摘要。embeddingModel: 用于提取和检索关键信息的嵌入模型如果实现了向量记忆。调优心得初期调试时把日志级别调到最详细DEBUG/VERBOSE。观察 Agent 每一步收到的 Prompt、生成的规划、选择的技能和参数。你会发现很多问题不是模型不够聪明而是 Prompt 没写清楚或者技能描述不够准确。调整这些描述效果立竿见影。5. 高级主题自定义技能与系统集成当你掌握了基础就可以开始扩展 Agent 的能力将其融入你自己的系统。5.1 开发一个自定义技能假设我们要为 Agent 添加一个“查询数据库”的技能。// src/skills/query-database.ts import { Skill, SkillResult, AgentContext } from ../types; import { DatabaseClient } from ../your-db-client; // 假设的数据库客户端 interface QueryArgs { sql: string; // LLM 生成的 SQL 查询语句 } export class QueryDatabaseSkill implements Skill { name query_database; description 执行一个安全的 SQL SELECT 查询从数据库中获取数据。切勿执行 INSERT, UPDATE, DELETE 等写操作。; parameters { sql: { type: string as const, description: 要执行的 SELECT SQL 查询语句。, required: true, }, }; private dbClient: DatabaseClient; constructor(dbConfig: any) { // 初始化数据库连接配置从 harness 的 config 注入进来更好 this.dbClient new DatabaseClient(dbConfig); } async execute(args: QueryArgs, context: AgentContext): PromiseSkillResult { const { sql } args; // !!! 关键安全步骤验证 SQL 是否为只读查询 !!! if (!this.isSafeSelectQuery(sql)) { return { success: false, output: 拒绝执行只允许执行 SELECT 查询。, }; } try { const result await this.dbClient.query(sql); // 将结果格式化为易读的文本例如表格形式 const formattedResult this.formatResultAsText(result); return { success: true, output: 查询成功。结果如下\n${formattedResult}, data: result, // 保留结构化数据 }; } catch (error: any) { // 错误信息要清晰帮助 LLM 理解问题所在 return { success: false, output: 数据库查询失败${error.message}, }; } } private isSafeSelectQuery(sql: string): boolean { const trimmed sql.trim().toUpperCase(); return trimmed.startsWith(SELECT); // 更严格的检查可以包括正则表达式排除子查询中的危险操作等 } private formatResultAsText(rows: any[]): string { if (rows.length 0) return 没有找到数据。; // 简单实现将对象数组转为 Markdown 表格字符串 const headers Object.keys(rows[0]); let table | ${headers.join( | )} |\n; table | ${headers.map(() ---).join( | )} |\n; for (const row of rows) { table | ${headers.map(h String(row[h] || )).join( | )} |\n; } return table; } }然后在主程序中注册这个新技能import { QueryDatabaseSkill } from ./src/skills/query-database; const dbSkill new QueryDatabaseSkill({ host: localhost, user: readonly_user, // 务必使用只读权限的用户 database: my_app_db }); skillRegistry.register(dbSkill);现在你的 Agent 就能理解“帮我查一下上个月销量最高的产品”这样的指令并转化为安全的 SQL 查询了。5.2 集成到现有应用作为后台服务Claude Code 的 Agent 可以很容易地包装成一个 HTTP 服务使用 Express.js、Fastify 等或一个消息队列的消费者。Express.js 集成示例// server.ts import express from express; import { Agent, SkillRegistry /*, ... */ } from ./src; const app express(); app.use(express.json()); // 全局初始化一个 Agent 实例或使用连接池管理多个 let globalAgent: Agent; (async () { const skillRegistry new SkillRegistry(); // ... 注册所有技能 globalAgent new Agent({ /* 配置 */, skills: skillRegistry }); })(); app.post(/api/agent/query, async (req, res) { const { query, sessionId } req.body; if (!query) { return res.status(400).json({ error: Missing query }); } try { // 可以根据 sessionId 从数据库加载特定的记忆/上下文 const response await globalAgent.run(query, { sessionId }); res.json({ success: true, response }); } catch (error: any) { // 利用 harness 的 logger 记录错误 console.error(Agent execution failed:, error); res.status(500).json({ success: false, error: error.message }); } }); const PORT process.env.PORT || 3000; app.listen(PORT, () console.log(Agent server running on port ${PORT}));这样前端或移动端应用就可以通过 RESTful API 与 AI Agent 交互了。6. 避坑指南与性能优化在实际开发和运行中你会遇到各种问题。以下是一些高频“坑点”和优化建议。6.1 常见问题与排查清单问题现象可能原因排查步骤与解决方案Agent 一直“思考”不行动或重复执行同一动作。1.规划 Prompt 不清晰导致 LLM 无法分解出有效步骤。2.技能描述不准确LLM 无法匹配到正确技能。3.记忆上下文混乱包含了误导信息。1.检查日志查看 Planner 收到的 Prompt 和输出的规划结果。优化 Prompt加入更明确的指令如“请将任务分解为不超过5个具体步骤”。2.精简技能列表暂时只保留1-2个核心技能确保描述 (description) 精准无歧义。3.清空或重置记忆开始新会话或实现记忆“修剪”功能移除无关历史。工具调用参数总是错误。LLM 未能正确理解技能所需的参数格式。1.强化参数描述在parameters的description字段里用例子说明格式。例如格式为 YYYY-MM-DD。2.使用更结构化的输出要求要求 LLM 以严格的 JSON 格式输出参数。3.实现参数后处理与验证在技能execute方法开头用zod等库验证参数并返回清晰的错误信息给 LLM 让其重试。API 调用费用飙升。1.循环失控产生过多步骤。2.上下文过长每次请求都携带大量 token。3.使用了昂贵模型处理简单任务。1.设置严格的maxSteps。2.优化记忆摘要策略积极压缩历史。3.实现模型路由简单任务如格式化文本使用便宜/小模型复杂规划再用大模型。代码解释器技能执行慢或有安全风险。1.沙箱启动开销大。2.执行了复杂或无限循环代码。1.使用沙箱连接池避免每次执行都启动新容器。2.加强资源限制更严格的 CPU/内存/超时控制。3.代码静态分析在执行前用简单规则扫描代码禁止import os,eval()等危险操作。6.2 性能与成本优化策略分层缓存Prompt 缓存对于固定格式的 Planner Prompt、技能描述等可以预先渲染并缓存避免每次请求都重新拼接字符串。LLM 响应缓存对于相同或高度相似的输入可以缓存 LLM 的响应。可以使用简单的内存缓存如 LRU Cache或 Redis。注意对于创造性任务要禁用缓存。工具结果缓存网络搜索、数据库查询的结果在一定时间内TTL可以缓存避免重复调用和产生费用/负载。异步与流式响应Agent 执行多步任务可能很耗时。不要阻塞 HTTP 请求。可以采用任务队列将/api/agent/query请求放入队列如 Bull、RabbitMQ立即返回一个taskId。客户端通过轮询或 WebSocket 获取结果。Server-Sent Events (SSE)在任务执行过程中实时流式输出 Agent 的“思考过程”和中间结果提升用户体验。监控与告警在harness的日志和错误处理基础上集成监控系统。关键指标记录每个任务的步骤数、总耗时、Token 使用量、各技能调用次数和成功率。告警规则设置告警例如平均任务耗时突增、某个技能失败率超过阈值、API 费用每日超标等。阅读 Claude Code 的源码就像拿到了一张精心绘制的地图。它没有直接给你宝藏但清晰地标出了通往“构建强大 AI Agent”这个宝藏的所有路径、桥梁和可能遇到的沼泽。从扎实的 Harness 基础设施到清晰的规划-执行-记忆循环再到可扩展的技能系统每一处设计都体现了工程化的考量。这份“意外的礼物”最大的秘密或许就是它无声地强调了AI 应用的未来不仅在于模型有多强大更在于我们如何用扎实的软件工程为这些“大脑”构建可靠、可控、可扩展的“身体”和“工具”。通过拆解和实践它你学到的远不止如何使用一个框架而是如何思考下一代人机交互应用的架构。