深入解析Claude Code核心,query.ts里藏着所有AI编程Agent的底层密码

张开发
2026/4/7 19:42:01 15 分钟阅读

分享文章

深入解析Claude Code核心,query.ts里藏着所有AI编程Agent的底层密码
在AI编程Agent领域有一个共识真正决定产品竞争力的从来不是动辄几十万行的外围代码而是藏在核心文件里的循环逻辑。如果你想快速吃透Claude Code的精髓不用逐行啃完它50万行代码只需聚焦一个文件src/query.ts。这个仅有1729行的文件承载着Claude Code作为AI编程Agent的全部核心逻辑是整个产品的“心脏”。有人曾做过一个极端假设把Claude Code其他所有代码全部删除只保留query.ts、QueryEngine.ts以及工具实现它依然能正常运行依然是一个可用的AI编程Agent。这个假设并非夸张因为query.ts里的核心循环定义了AI编程Agent的本质行为模式而UI界面、命令系统、Skill模块、MCP协议等所有内容都只是围绕这个核心循环搭建的外围辅助设施目的是让这个核心循环更好地落地、更易用、更符合工业级场景的需求。今天我们就从query.ts入手一层层拆解AI编程Agent的核心逻辑看看Claude Code如何把一个简单的循环打造成工业界最健壮的agentic loop智能体循环实现也看看这些设计思路能给我们自己做AI Agent带来哪些启发。一、AI编程Agent的本质一个永不言弃的核心循环所有AI编程Agent无论外观多复杂、功能多丰富底层逻辑都离不开一个简单的循环。剥掉query.ts里的错误处理、Feature Flag功能开关、日志打印、消融实验开关等辅助代码后我们能看到这个核心循环的最简洁形态它就像AI Agent的“行为模板”决定了Agent如何与用户、工具、环境交互。以下是query.ts核心循环的简化版代码保留了最关键的逻辑流程哪怕是不懂编程的人也能看懂它的运行逻辑asyncfunction*queryLoop(params:QueryLoopParams){letstate{messages:params.initialMessages,turnCount:1,outputTokenRecoveryCount:0,}while(true){// 1. 构造上下文constsystemPromptbuildSystemPrompt(params)constmessagesForApimaybeCompressHistory(state.messages)// 2. 调 API流式conststreamcreateMessageStream({model:params.model,system:systemPrompt,messages:messagesForApi,tools:params.toolDefinitions,max_tokens:calculateMaxTokens(state),})// 3. 处理流式响应constresponseawaitprocessStreamEvents(stream)// 4. 有工具调用执行它们if(response.toolUses.length0){constresultsawaitexecuteToolCalls(response.toolUses,params.toolContext)state.messages.push(response.assistantMessage)state.messages.push(...results.map(toToolResultMessage))state.turnCountcontinue// → 回到 while(true) 顶部}// 5. 没有工具调用 LLM 说完了state.messages.push(response.assistantMessage)return// 退出循环}}这段代码看似简单却包含了AI编程Agent的全部行为逻辑我们可以把它拆解成5个步骤一步步理解AI Agent是如何“思考”和“行动”的第一步构造上下文。AI Agent不是凭空“思考”的它需要知道当前的环境、用户的历史需求、可用的工具等信息这些信息会被整合成语境提示词systemPrompt同时会对历史消息进行压缩避免上下文过长导致API调用失败。这一步就像我们做事前先理清手头的资源和过往的经历做到心中有数。第二步调用LLM API流式。Agent将构造好的上下文发送给大语言模型LLM这里用的是流式调用意味着模型不会一次性返回所有结果而是像打字一样一点点输出内容。这种方式的优势的是Agent可以提前处理部分结果不用等到模型完全输出从而减少用户的等待时间。第三步处理流式响应。Agent监听模型返回的流式数据把这些碎片化的内容整合起来形成完整的响应结果同时判断模型是否需要调用工具。第四步执行工具调用。如果模型的响应中包含工具调用请求比如读文件、执行bash命令、查询代码等Agent就会执行这些工具调用然后把工具执行的结果重新加入到历史消息中接着回到循环顶部再次调用模型让模型根据工具结果继续“思考”。第五步输出最终结果。如果模型的响应中没有工具调用说明模型已经完成了“思考”给出了最终的纯文本回复此时循环退出把结果返回给用户。说白了这个循环的本质就是用户说话 → Agent调LLM → LLM要工具就执行 → 把工具结果喂回LLM → 重复直到LLM给出纯文本回复。不止Claude Code市面上所有优秀的AI编程Agent比如Cursor、Cline、Aider底层都是这个循环。它们之间的差别不在于核心逻辑的不同而在于细节的处理——如何让这个循环更健壮、更高效、更符合用户需求。而Claude Code的厉害之处就在于它在这个简单的骨架上堆了一层又一层的防御性逻辑、优化逻辑把一个基础循环打造成了能应对各种极端场景的工业级实现。二、细节见真章Claude Code的5个核心优化打造工业级健壮性如果说核心循环是AI Agent的“骨架”那么各种细节优化就是“血肉”它们决定了Agent的体验和健壮性。Claude Code的query.ts之所以被称为“最好的教材”就是因为它在细节上的处理极其周全每一个优化都针对实际使用中的痛点解决了普通AI Agent容易出现的崩溃、卡顿、低效等问题。下面我们就来逐一拆解这些关键细节。细节一动态拼接的系统提示词让Agent适配不同场景很多AI Agent框架的系统提示词都是一个写死的字符串比如“你是一个AI编程助手负责帮助用户解决编程问题”。这种方式的好处是简单但缺点也很明显Agent无法适配不同的场景在不同的工作目录、不同的模型、不同的会话状态下表现都一样灵活性极差。Claude Code则完全不同它的系统提示词是动态拼接的每次循环迭代时都会根据当前的环境和状态组装出不同的系统提示词。负责拼接逻辑的是src/constants/prompts.ts文件这个文件有914行里面包含了各种提示词模块根据不同的条件进行组合。以下是prompts.ts拼接逻辑的概念性简化能清晰看到它的组装思路functionbuildSystemPrompt(context){constparts[]parts.push(CORE_IDENTITY)// 你是 Claude Code一个终端里的 AI 助手parts.push(getToolDescriptions())// 当前可用的工具列表和使用建议parts.push(getEnvironmentInfo())// OS、工作目录、Git 状态、Python 版本parts.push(getCWDInfo())// 当前目录下的关键文件parts.push(getMemoryFiles())// CLAUDE.md 和 memory 文件的内容parts.push(getSkillInstructions())// 已加载 Skill 的指令if(context.isResumedSession){parts.push(RESUMED_SESSION_NOTICE)// 这是从之前中断的会话恢复的}// [MODEL LAUNCH] TODO: 针对不同模型版本的行为修正if(isCapybara(context.model)){parts.push(CAPYBARA_COMMENT_FIX)// 不要过度注释代码}returnparts.join(\n\n)}从这段代码可以看出系统提示词的拼接的逻辑非常灵活它会根据当前的上下文整合以下几个关键模块核心身份CORE_IDENTITY明确Agent的定位让模型知道自己的角色是终端里的AI助手专注于编程相关的任务。工具描述getToolDescriptions告诉模型当前可用的工具列表以及如何正确使用这些工具避免模型调用不存在的工具或者使用错误的方式调用工具。环境信息getEnvironmentInfo包含当前的操作系统、工作目录、Git状态、Python版本等让模型了解当前的运行环境比如在Windows系统和Linux系统下执行的命令会有差异模型可以根据环境信息调整行为。当前目录信息getCWDInfo告诉模型当前工作目录下的关键文件让模型不用额外调用工具就能知道当前目录的结构提高效率。记忆文件getMemoryFiles加载CLAUDE.md和memory文件的内容这些文件包含了用户之前的操作记录、项目相关的配置信息等让Agent具备“记忆”能力能衔接之前的会话内容。技能指令getSkillInstructions如果当前加载了额外的Skill模块就会把这些模块的指令加入到系统提示词中让模型知道如何使用这些扩展技能。除此之外还有两个特殊的条件判断如果是恢复中断的会话会加入恢复提示让模型知道当前会话是延续之前的需要衔接之前的内容如果使用的是Capybara模型会加入针对该模型的行为修正比如“不要过度注释代码”。这种动态拼接的方式带来的最大好处是灵活性。同一个用户在不同的目录下、不同的时间点、使用不同的模型看到的系统提示词都是不一样的。这也是为什么很多人会觉得Claude Code在不同项目里的表现差别很大其实不是LLM变了而是系统提示词变了Agent根据不同的场景调整了自己的“思考方式”。更有意思的是Claude Code的源码里有大量[MODEL LAUNCH]注释每一个这样的注释都是“当某个新模型发布时需要修改的地方”。其中关于Capybara模型的注释有好几条比如“v8版本的虚假声明率29-30%需要在prompt层面修补”。这说明新模型的行为矫正是在系统提示词层面做的而不是在模型层面这种方式更灵活、成本更低也能快速适配新模型的特性。其实这种动态拼接提示词的思路在其他优秀的AI Agent中也有体现。比如CoreCoder的prompt.py虽然只有35行是这个机制的极简版但核心思路完全一致根据当前工作目录、OS信息、可用工具列表动态拼接提示词只是没有模型特定修正和Skill加载的逻辑。这也从侧面说明动态拼接系统提示词是AI Agent适配不同场景的核心技巧。细节二StreamingToolExecutor让工具调用速度翻倍的关键如果说动态提示词解决了Agent的“适配性”问题那么StreamingToolExecutor就解决了Agent的“效率”问题。这是整个Claude Code源码里最值得深入研究的部分之一它位于独立文件src/services/tools/StreamingToolExecutor.ts共530行核心作用是优化工具调用的流程减少用户的等待时间。要理解StreamingToolExecutor的优势我们先看看普通AI Agent框架的工具调用方式等待LLM的完整响应接收完毕 → 解析所有tool_use块 → 串行或并行执行这些工具调用。这种方式的问题很明显中间会有一段空白的等待时间LLM在生成工具调用指令的时候Agent什么都不做只能等到所有指令都生成完才开始执行工具效率很低。Claude Code的做法则完全不同LLM还在生成后面的内容时前面的工具就已经开始跑了。这种“边生成边执行”的方式能最大限度地利用时间大幅减少用户的感知延迟。它的实现方式核心是监听Anthropic API的Server-Sent EventsSSE流。我们知道LLM的流式响应是由一个个独立的content block内容块组成的每个content block可能是文本块也可能是tool_use块工具调用块每个content block都有独立的start开始、delta增量、stop结束事件。当一个tool_use块的stop事件到达时就意味着这个工具的输入JSON已经完整了不需要再等其他的content blockAgent可以立即解析这个JSON提交工具执行。以下是StreamingToolExecutor.ts事件处理逻辑的简化版能清晰看到它的工作原理classStreamingToolExecutor{privatependingBlocksnewMapnumber,PartialToolUse()privaterunningTools:PromiseToolResult[][]onStreamEvent(event:StreamEvent){switch(event.type){casecontent_block_start:if(event.content_block.typetool_use){this.pendingBlocks.set(event.index,{id:event.content_block.id,name:event.content_block.name,inputJson:,})}breakcasecontent_block_delta:if(event.delta.typeinput_json_delta){// 工具输入 JSON 的碎片陆续到来拼接constblockthis.pendingBlocks.get(event.index)!block.inputJsonevent.delta.partial_json}breakcasecontent_block_stop:{constblockthis.pendingBlocks.get(event.index)if(block){// 输入完整了解析 JSON**立即**提交执行constinputJSON.parse(block.inputJson)constpromisethis.executeToolWithPermissionCheck(block,input)this.runningTools.push(promise)this.pendingBlocks.delete(event.index)}break}}}}这段代码的逻辑可以拆解成三个步骤对应三个事件类型第一步content_block_start事件当LLM开始生成一个content block并且这个block是tool_use类型时Agent会在pendingBlocks待处理块中创建一个新的记录用于存储这个工具调用的相关信息比如工具ID、工具名称、输入JSON初始为空。第二步content_block_delta事件当LLM生成工具输入JSON的碎片时Agent会把这些碎片一点点拼接起来存入对应的pendingBlocks记录中。比如工具输入JSON是{“file”: “test.ts”}LLM可能先输出{“file”: “然后再输出test.ts”}Agent会把这两部分拼接起来得到完整的JSON。第三步content_block_stop事件当LLM完成一个tool_use块的生成stop事件触发Agent会从pendingBlocks中取出对应的记录解析完整的输入JSON然后立即提交工具执行同时把执行任务加入到runningTools运行中工具列表中最后删除pendingBlocks中的该记录。除了“边生成边执行”StreamingToolExecutor还有一个关键设计——并发安全标记。每个工具有一个isConcurrencySafe并发安全属性用来判断这个工具是否可以并行执行。比如读文件、grep、glob这些只读操作不会对系统造成副作用就标记为safe可以并行执行而写文件、bash命令这些可能会修改系统状态、产生副作用的操作就需要独占执行不能和其他工具并行。StreamingToolExecutor会维护一个调度队列严格遵守这个并发安全约束确保工具执行时不会出现冲突。这个设计看似简单却能大幅提升工具执行的效率同时保证系统的稳定性。我们可以举一个具体的例子感受一下这种设计的优势假设LLM一次返回三个工具调用分别是读文件A耗时200ms、读文件B耗时150ms、跑测试耗时2000ms。如果用普通的串行执行方式总耗时是20015020002350ms而用StreamingToolExecutor这三个工具在LLM还在生成的时候就已经开始跑了实际延迟接近于三个工具中耗时最长的那个也就是2000ms甚至更短因为工具执行的时间被LLM生成的时间“吸收”了用户几乎感觉不到等待。当然这种设计的实现难度也相对较高需要精准监听SSE流处理各种边界情况比如JSON碎片拼接错误、工具执行异常等。如果你觉得这种实现太复杂也可以参考CoreCoder的折中方案不做流式解析但当LLM一次返回多个tool_calls时用ThreadPoolExecutor并行执行这些工具调用。这种方式虽然损失了“在LLM生成期间就开始执行”的优势但保留了“多工具并行”的核心收益而且代码量很少只有十几行适合快速落地。细节三全方位错误恢复让Agent永不轻易认输普通的AI Agent很容易因为一次API超时、一次工具执行失败就直接崩溃退出用户体验极差。而工业级的AI Agent必须具备强大的错误恢复能力遇到问题时能自己想办法解决而不是转头问用户。Claude Code的query.ts里错误处理写得极其细致几乎覆盖了所有可能出现的异常场景我数了一下大致有以下几个关键的错误处理分支每一个都针对一个实际的痛点。第一个分支API 429限流当调用LLM API时出现429错误说明当前请求频率过高被服务器限流了。此时Agent不会直接报错退出而是采用指数退避重试策略最多重试5次每次等待的时间翻倍。比如第一次等待1秒第二次等待2秒第三次等待4秒以此类推直到重试成功或者达到最大重试次数。这种方式能最大限度地规避限流问题保证Agent的正常运行而不是让用户因为限流而被迫中断操作。第二个分支API 400/413请求体太大出现这种错误说明当前的上下文消息太多超过了API的请求体大小限制。此时Agent会自动触发上下文压缩逻辑裁掉最旧的消息只保留最关键的内容然后用压缩后的消息重新调用API而不是直接报错退出。这种处理方式非常贴心用户不需要手动清理历史消息Agent会自己“瘦身”确保流程能继续进行。第三个分支API 529服务过载当LLM服务出现过载无法正常响应时Agent会自动切换到配置里的fallbackModel备用模型。比如主力模型是Opus当Opus不可用时就自动切换到Sonnet确保Agent能继续提供服务不会因为主力模型故障而完全瘫痪。这种冗余设计是工业级产品必备的能大幅提升系统的可用性。第四个分支输出触顶max_output_tokens这种情况是指LLM的回复被截断了可能一个工具调用的JSON只输出了一半或者文本回复只写了一部分。大多数AI Agent遇到这种情况都会直接报错让用户重新发起请求。但Claude Code的处理方式非常巧妙它会“扣留”这个不完整的响应悄悄进行重试最多重试3次试图获取完整的响应。更有意思的是这段代码上面有一段中世纪巫师口吻的注释让人印象深刻“Heed these rules well, young wizard. For they are the rules of thinking, and the rules of thinking are the rules of the universe. If ye does not heed these rules, ye will be punished with an entire day of debugging and hair pulling.” 翻译过来大概是“好好记住这些规则年轻的巫师。因为它们是思考的规则而思考的规则就是宇宙的规则。如果你不遵守这些规则你将受到一整天调试和抓头发的惩罚。” 不难看出维护这段代码的人显然在处理这种边界情况时被坑过不少次所以才会写下这样幽默又无奈的注释也从侧面说明这种错误处理的重要性。第五个分支用户中断CtrlC当用户在Agent执行工具的过程中按下CtrlC中断操作时Agent不会直接终止所有流程而是通过AbortController取消正在运行的工具调用同时保留已经完成的工具结果在消息历史中。这样下次用户输入时LLM可以看到“你之前做了A和BC被用户取消了”从而衔接之前的操作不会让用户的努力白费。第六个分支工具执行异常这是最关键的一个错误处理分支也是“agentic”智能体的核心含义所在。当工具执行出现异常时这个异常不会传播到外层不会导致整个循环崩溃。Claude Code会把异常信息包装成一条tool_result工具结果将角色设为error然后把这条结果喂回LLM让LLM自己决定如何处理。比如Agent调用工具读一个不存在的文件工具执行失败此时Agent不会报错说“文件不存在请检查路径”而是把“文件不存在”这个异常信息喂给LLM让LLM自己判断——是路径输错了还是文件被删除了然后决定是重新调用工具修改路径还是告诉用户“这个文件不存在我试试另一种方式”。这种处理方式让Agent具备了“自主决策”的能力遇到问题时自己想办法而不是转头依赖用户这才是真正的智能体。对比之下CoreCoder的agent.py虽然也实现了工具异常捕获并喂回LLM的逻辑以及max_rounds最大轮次限制但没有做API层面的重试和备用模型切换。这是因为不同的LLM provider模型提供商的API差异很大把这些逻辑放在上层处理更合适也能让核心逻辑更简洁。但无论如何工具异常喂回LLM、上下文超限时压缩重试这两个错误处理逻辑是所有AI Agent都必须具备的。细节四双重预算控制防止Agent“失控”AI Agent在运行过程中很容易出现“失控”的情况比如LLM陷入无限循环反复调用同一个工具却得不到想要的结果导致API调用次数不断增加费用飙升或者自动化任务运行时间过长消耗过多的资源。为了解决这个问题Claude Code的QueryEngine查询引擎管理着两种预算从根本上防止Agent失控。第一种预算轮次预算maxTurns。每完成一轮工具调用就记为一个turn轮次当轮次超过maxTurns的上限时Agent会强制停止循环避免LLM陷入无限循环。比如设置maxTurns10当Agent完成10轮工具调用后无论是否得到最终结果都会停止运行防止它反复做无用功。这种预算主要用来防范LLM的“死循环”问题比如反复读同一个文件但每次都“没找到”它要的东西。第二种预算美元预算maxBudgetUsd。每次API调用的token消耗都会换算成对应的费用累计费用超过maxBudgetUsd的上限时Agent会强制停止。这种预算在SDK模式下特别有用比如你可以限制一个自动化任务最多花0.5美元这样即使任务出现异常也不会产生过高的费用能有效控制成本。以下是QueryEngine.ts中相关的配置定义能清晰看到这两种预算的设置typeQueryEngineConfig{maxTurns?:number// 最大轮次maxBudgetUsd?:number// USD 预算上限taskBudget?:{total:number}// API 侧 token 预算// ...}除了这两种核心预算还有一个taskBudget任务预算用来限制API侧的token消耗总量进一步细化成本控制。这三种预算相互配合形成了一套完整的“失控防护机制”让Agent的运行始终在可控范围内。CoreCoder的实现相对简单只用max_rounds50做了轮次限制没有做美元预算。这是因为CoreCoder面向的是多provider环境不同provider的费率不统一计算美元预算的难度较大放在上层处理更合适。但轮次预算是必不可少的它能有效防止LLM无限循环是AI Agent的“安全护栏”。细节五投机执行让Agent“预判”用户需求用过Claude Code的人大概率会有这样的感受它“想”完之后开始干活的速度很快几乎没有延迟。这背后的秘密就是Claude Code的投机执行机制Agent会根据上一轮的结束方式预判用户的下一步操作提前做好准备从而减少用户的等待时间。在Claude Code的AppState应用状态里有一个字段叫speculationState投机状态用来追踪每一轮的结束方式比如上一轮是执行了bash命令、编辑了文件、输出了正常文本回复还是被拒绝了权限。系统会根据这个状态预判下一步的操作提前做好准备。举几个具体的例子如果上一轮LLM编辑了一个文件系统会提前准备好diff渲染组件等工具执行完成后立即渲染出文件的修改差异用户能瞬间看到编辑结果如果上一轮跑了bash命令系统会提前分配一个终端buffer等命令执行完成后立即显示命令的输出结果不用再等待buffer分配如果上一轮被拒绝了权限系统会提前准备好权限申请的提示方便用户快速授予权限。这种投机执行的机制本质上是“预判式优化”它没有改变核心循环的逻辑却能大幅提升用户的感知体验。因为用户的等待时间不仅仅是工具执行的时间还包括系统准备资源的时间而投机执行把这部分准备时间提前了让用户感觉Agent“反应更快”。这种设计思路虽然实现起来不难但非常考验产品思维——需要深入理解用户的使用场景知道用户在不同操作之后大概率会做什么才能做出精准的预判。Claude Code的投机执行之所以能让人感觉流畅就是因为它精准捕捉了编程场景下的用户行为习惯提前做好了应对准备。三、核心收获做AI Agent这些设计模式必学读完Claude Code的query.ts我们不难发现它的核心优势不在于复杂的代码而在于那些贴合实际场景的设计模式。这些设计模式不仅适用于Claude Code也适用于所有AI编程Agent甚至是其他类型的AI智能体。如果你正在做自己的Agent产品从query.ts里可以带走的最重要的5个设计模式值得你深入研究和落地。第一个设计模式工具异常喂回LLM不要替Agent做决定。这是“agentic”的核心也是AI Agent区别于普通聊天机器人的关键。遇到工具执行异常时不要直接报错退出也不要替Agent决定如何处理而是把异常信息喂回LLM让LLM自己判断、自己重试、自己调整策略。这样才能让Agent具备真正的“自主决策”能力应对各种复杂场景。第二个设计模式上下文超限时压缩重试不要报错退出。用户不会关心上下文是否超限他们只关心自己的需求能否被解决。当上下文超过API限制时自动压缩历史消息重新调用API而不是让用户手动清理消息能大幅提升用户体验也能让Agent的流程更顺畅。第三个设计模式工具并行执行降低用户感知延迟。无论是Claude Code的StreamingToolExecutor边生成边执行还是CoreCoder的ThreadPoolExecutor多工具并行核心都是通过并行执行工具减少用户的等待时间。尤其是在需要调用多个工具的场景下并行执行能让效率翻倍这是提升Agent体验的关键优化点。第四个设计模式轮次预算防止LLM无限循环。LLM很容易陷入“死循环”比如反复调用同一个工具却得不到想要的结果此时轮次预算就能起到“安全护栏”的作用强制停止循环避免资源浪费和费用飙升。这是工业级Agent必备的防护机制不能省略。第五个设计模式系统提示词动态拼装让Agent适配不同场景。固定的系统提示词无法应对复杂的场景变化而动态拼接的提示词能让Agent根据当前的环境、模型、会话状态调整自己的“思考方式”在不同的场景下都能有出色的表现。这种灵活性是AI Agent适配多场景的核心能力。其实这些设计模式我在CoreCoder的agent.py里都做了最小实现只用了110行代码就涵盖了工具异常处理、上下文压缩、轮次限制、动态提示词拼接等核心逻辑没有多余的冗余代码完全贴合轻量型AI Agent的落地需求。不同于Claude Code的工业级实现CoreCoder的agent.py更注重“极简可用”它剥离了所有非必要的防御性逻辑和优化细节只保留了最核心的设计模式非常适合新手入门学习也适合快速嵌入到各类小型项目中使用。在CoreCoder的agent.py中工具异常处理的逻辑非常简洁却实用当工具执行出现异常时会直接捕获异常信息将其封装成固定格式的tool_result消息标记角色为error然后直接加入到历史消息队列中无需额外的异常传播处理也不用手动干预让LLM自动读取异常信息并调整后续策略。这种极简实现虽然没有Claude Code那样细致的异常分类处理但核心思路完全一致能满足大部分轻量场景的需求。上下文压缩逻辑则采用了最基础的“截断式压缩”——当检测到历史消息的token总量超过预设阈值时会自动删除最早期的非关键消息只保留最近的用户输入、工具调用及结果确保每次调用LLM时上下文都能控制在API的token限制范围内。这种方式虽然没有Claude Code的智能压缩比如保留关键信息、合并相似消息那样高效但胜在简单易懂、易于实现对于轻量型Agent来说完全足够应对日常使用场景。轮次限制的实现更是简洁通过一个全局变量记录当前的工具调用轮次每完成一轮工具调用就自增1当轮次达到预设的max_rounds默认50轮时直接终止循环输出当前的结果并提示用户“已达到最大轮次限制”。这种方式虽然简单却能有效防止LLM陷入无限循环避免不必要的API调用和资源消耗与Claude Code的maxTurns预算异曲同工只是少了复杂的配置和灵活的调整机制。动态提示词拼接则聚焦于最核心的场景适配只根据当前的工作目录、操作系统信息和可用工具列表拼接出最基础的系统提示词没有模型特定修正、会话恢复提示、Skill指令加载等复杂模块。比如当用户在Linux系统下运行CoreCoder时提示词会自动加入Linux相关的命令使用建议当可用工具列表发生变化时提示词也会实时更新确保LLM能获取到最新的工具信息避免调用不存在的工具。可能有人会觉得CoreCoder的实现过于简单没有Claude Code那样的工业级健壮性但这正是它的价值所在,它把复杂的设计模式拆解成了最基础、最易实现的代码让更多人能快速理解AI Agent的核心逻辑而不用陷入Claude Code那50万行代码的细节中。对于新手来说先通过CoreCoder的agent.py掌握核心设计模式再去研究Claude Code的query.ts就能更容易理解那些复杂的优化逻辑和防御性代码事半功倍。当然CoreCoder的极简实现也有其局限性比如没有API重试、备用模型切换、投机执行等优化无法应对高并发、高复杂度的工业级场景但这并不影响它的参考价值。它就像一个“设计模式模板”我们可以在此基础上根据自己的需求逐步添加Claude Code中的那些优化细节比如加入API限流重试、工具并行执行、美元预算控制等慢慢将其打磨成一个更健壮、更高效的AI Agent。

更多文章