
1. 从终端工具到开发范式的转变第一次接触 Claude Code 的人十有八九会把它当成又一个命令行 AI 助手。毕竟市面上这类工具太多了输入问题、返回答案本质上就是个套了终端外壳的聊天窗口。但真正用过一段时间之后你会发现这个判断错得离谱。Claude Code 的设计出发点压根不是让 AI 回答问题而是让 AI 像一个工程师一样在你的项目里干活。这个区别听起来像是文字游戏实际上决定了整个工具的架构走向。一个问答型工具核心指标是回答准确率和响应速度而一个干活型工具核心指标变成了任务完成度、上下文理解深度、以及对项目结构的尊重程度。前者只需要一个足够强的模型加一个漂亮的输入框后者需要一整套围绕 Agent 循环构建的工程体系。我之所以想认真聊聊 Claude Code 的设计理念是因为它代表了一种正在成型的 LLM 应用开发范式。这个范式里有几个关键词反复出现Agent、MCP、工具调用、上下文管理、权限控制。这些概念单独拎出来都不新鲜但 Claude Code 把它们组合成了一个能实际跑起来、而且跑得还挺稳的系统。对于正在做 AI Agent 开发的同行来说拆解它的设计思路比单纯学怎么用有价值得多。这篇文章不会教你怎么安装 Claude Code或者怎么配置 VS Code 插件——那些内容网上已经够多了。我想做的是把这套工具背后的设计决策掰开揉碎讲清楚它为什么这么设计、每个设计解决了什么问题、以及这些思路能怎么迁移到你自己的 Agent 项目里。不管你是刚接触 LLM 应用开发的新手还是已经在做 Agent 框架的老手应该都能从中找到一些有用的东西。2. Agent 循环才是骨架对话只是表皮2.1 为什么一问一答撑不起真正的开发任务大部分人第一次用 LLM 做开发辅助流程是这样的遇到问题打开对话框描述问题拿到代码复制粘贴发现不对再问一轮。这个流程在简单场景下能用但一旦任务复杂起来就崩了。原因很简单——真实的开发任务是多步骤、有状态、需要验证的。举个例子你让 AI 给这个项目加一个用户认证模块。这不是一个能一次性回答的问题。它需要先看项目结构了解用的什么框架再看现有的路由和中间件怎么组织的然后决定认证方案接着写代码写完还要跑测试测试挂了还得看日志改代码。这一连串动作里每一步的输出都是下一步的输入中间任何一步出错都需要回退重来。Claude Code 的核心设计就是围绕这个现实构建的。它的运行骨架是一个Agent 循环模型接收任务 → 决定下一步动作 → 调用工具执行 → 观察结果 → 决定下一步 → 循环直到任务完成。对话界面只是这个循环的外在表现真正干活的是循环本身。这个循环里最关键的三个要素是工具集、上下文窗口管理、终止条件判断。工具集决定了 Agent 能做什么上下文管理决定了它能记住多少终止条件决定了它什么时候停下来。三者缺一不可。2.2 工具调用的粒度设计为什么是文件读写而不是执行代码Claude Code 给模型暴露的工具集很有意思拆得非常细读文件、写文件、编辑文件精确替换、搜索文件内容、列出目录、执行 shell 命令、搜索网页等等。你可能会问为什么不直接给一个执行任意代码的工具让模型自己写脚本搞定一切这个设计选择背后有两层考虑。第一层是可控性。如果模型能执行任意代码那它的行为边界就完全不可预测了安全审计也无从谈起。拆成细粒度工具之后每个工具的行为是确定的权限可以逐个控制日志可以逐条审计。第二层是模型友好性。细粒度工具的参数结构简单明确模型更容易正确调用。让模型生成一段完整的 Python 脚本来完成文件操作出错概率远高于让它调用一个参数清晰的read_file工具。这里有个容易被忽略的细节Claude Code 的编辑工具用的是精确字符串替换而不是整文件重写。这个设计非常关键。整文件重写的问题是模型需要重新生成整个文件内容token 消耗大而且容易在无关的地方引入意外改动。精确替换只要求模型给出要替换的旧内容和替换后的新内容改动范围最小出错面也最小。这个思路在做自己的 Agent 工具时非常值得借鉴——工具的设计要顺应模型的能力特点而不是让模型去适应工具。2.3 上下文窗口的取舍什么该记住什么该丢掉Agent 循环跑起来之后很快会遇到一个硬约束上下文窗口是有限的。一个复杂任务可能涉及几十次工具调用每次调用的输入输出都往上下文里塞很快就会撑爆。Claude Code 在这方面做了不少工程上的取舍。首先是工具结果的截断策略。读一个大文件不可能把整个文件内容都塞进上下文。Claude Code 会根据文件大小做截断只保留关键部分同时告诉模型这个文件被截断了完整内容有多少行。模型看到这个提示就知道需要的时候可以用搜索工具去定位具体内容而不是傻等着全文。其次是历史消息的压缩。当对话轮次多了之后早期的消息会被摘要化处理保留关键决策和结论丢掉冗余的中间过程。这个策略的本质是Agent 需要的是我做过什么决定而不是我执行过什么动作。动作的细节可以丢决策的脉络不能断。还有一个很聪明的设计是系统提示词的动态组装。Claude Code 的系统提示不是一成不变的它会根据当前项目类型、可用工具、用户配置动态拼装。比如检测到是 Python 项目就会注入 Python 相关的编码规范提示检测到有测试框架就会提示模型写完代码后跑测试。这种动态组装让有限的上下文预算花在刀刃上。3. MCP 协议把工具生态从内置变成可插拔3.1 内置工具的瓶颈在哪里如果 Claude Code 只有内置的那几个工具它的能力边界就被锁死了。文件操作、shell 命令、网页搜索这些是通用能力但真实开发场景里有大量领域特定的需求查数据库、调内部 API、操作设计工具、读取特定格式的文件。这些需求不可能全部内置否则工具会变成一个臃肿的怪物。传统的解法是写插件系统让第三方开发者扩展。但插件系统有个老问题每个平台的插件规范都不一样开发者要为每个平台写一遍适配层。这在 AI Agent 领域尤其严重因为 Agent 框架层出不穷谁也不想为每个框架都维护一套工具实现。MCPModel Context Protocol就是冲着这个问题来的。它定义了一套标准化的协议让工具提供方和工具使用方解耦。一个 MCP Server 实现了某个能力比如查询 PostgreSQL任何支持 MCP 的客户端都能直接调用不需要为每个客户端单独适配。3.2 MCP 的通信模型为什么选 JSON-RPCMCP 底层用的是 JSON-RPC 2.0这个选择很务实。JSON-RPC 足够简单请求和响应的结构清晰任何语言都能轻松实现。它不像 gRPC 那样需要 protobuf 编译也不像 REST 那样需要设计一套资源模型。对于调用一个工具、拿到一个结果这种场景JSON-RPC 的抽象层级刚刚好。MCP 定义了三种核心原语Tools可调用的函数、Resources可读取的数据、Prompts预定义的提示模板。Tools 是最常用的对应让模型执行一个动作Resources 对应让模型读取一些数据Prompts 则是把常用的提示词封装成可复用的模板。传输层上MCP 支持两种模式stdio标准输入输出和HTTP with SSE服务端推送。stdio 模式适合本地工具启动一个子进程通过管道通信简单直接。HTTP 模式适合远程服务可以部署在服务器上供多个客户端共享。这个双模式设计覆盖了绝大多数使用场景。3.3 自己写一个 MCP Server 的实操要点如果你想让自己的 Agent 接入某个内部系统写一个 MCP Server 是最省事的路径。我用 Python 的 MCP SDK 写过几个分享一些实操中的经验。首先是工具描述的写法。工具描述是模型决定要不要调用这个工具的唯一依据写得好不好直接决定调用准确率。我的经验是描述里要包含什么时候用而不只是这个工具做什么。比如不要只写查询用户信息而要写根据用户 ID 查询用户的详细信息包括注册时间、订单历史、会员等级。当需要了解某个用户的具体情况时使用。其次是参数设计。参数越少越好必填参数和可选参数要分清。参数名要有语义别用arg1、param2这种。如果某个参数有固定取值范围一定要在描述里列出来模型会照着填。第三是错误处理。工具执行失败时返回的错误信息要具体不要只返回操作失败。要告诉模型失败原因比如用户 ID 不存在、数据库连接超时、权限不足。模型看到具体错误才能决定下一步怎么办。from mcp.server import Server from mcp.types import Tool, TextContent server Server(user-service) server.list_tools() async def list_tools(): return [ Tool( nameget_user_info, description根据用户ID查询用户详细信息。当需要了解某个用户的具体情况时使用。, inputSchema{ type: object, properties: { user_id: { type: string, description: 用户的唯一标识符格式为 U 开头的 8 位字符串 } }, required: [user_id] } ) ] server.call_tool() async def call_tool(name: str, arguments: dict): if name get_user_info: user_id arguments[user_id] # 实际查询逻辑 result query_user(user_id) if not result: return [TextContent(typetext, textf用户 {user_id} 不存在请检查ID是否正确)] return [TextContent(typetext, textformat_user_info(result))]这段代码展示了一个最小可用的 MCP Server。注意工具描述和参数描述都写得很具体这是提高调用准确率的关键。4. 权限模型让 Agent 跑得放心4.1 为什么权限控制是 Agent 的生死线一个能读写文件、执行 shell 命令的 Agent如果没有权限控制就是一个随时可能闯祸的定时炸弹。它可能误删重要文件可能执行危险的命令可能把敏感数据发到外部。这不是危言耸听而是所有做 Agent 的人都必须面对的现实问题。Claude Code 的权限模型设计得相当细致值得单独拿出来讲。它的核心思路是默认保守按需授权全程可审计。默认情况下读操作读文件、列目录、搜索是允许的因为这些操作不会改变系统状态。写操作写文件、编辑文件和执行操作运行 shell 命令需要用户确认。这个默认策略很合理——读操作的风险是信息泄露写操作的风险是数据损坏后者的后果通常更严重。4.2 权限的粒度从全部允许到逐条确认Claude Code 的权限配置支持多个粒度级别。最粗的粒度是全部允许适合在隔离环境里跑批处理任务。最细的粒度是逐条确认每次工具调用都弹出来问用户。中间还有按工具类型允许、按路径允许、按命令模式允许等粒度。我自己的配置习惯是读操作全部允许写操作限定在项目目录内shell 命令用白名单模式。白名单里放常用的安全命令比如git status、npm test、python -m pytest这些。危险命令比如rm -rf、curl到外部地址、修改系统配置的命令一律需要手动确认。这个配置策略背后的逻辑是风险分级。不同操作的风险等级不一样没必要用同一套规则对待。把低风险操作放开让 Agent 流畅运行把高风险操作卡住让用户把关。这样既保证了效率又守住了安全底线。4.3 审计日志出事了能查清楚权限控制解决的是事前预防审计日志解决的是事后追溯。Claude Code 会记录每一次工具调用的详细信息什么时间、调用了什么工具、参数是什么、返回结果是什么、用户是否批准。这些日志在排查问题时非常有用。我遇到过几次 Agent 行为异常的情况都是靠审计日志定位到根因的。有一次 Agent 反复修改同一个文件看起来像是陷入了死循环。查日志发现是因为文件里有个语法错误导致测试一直失败Agent 每次改完跑测试都挂就继续改。问题出在 Agent 没有意识到这个错误不是我的改动引起的而是文件本来就有的问题。这种问题不看日志根本发现不了。对于自己做 Agent 的开发者我的建议是审计日志要从第一天就做。不要等到出了问题才想起来加日志那时候已经晚了。日志的字段要包含时间戳、会话 ID、工具名、参数、结果、耗时、是否成功。这些字段在后续做性能分析和问题排查时都会用到。5. 上下文工程Agent 的隐形战场5.1 系统提示词不是写一次就完事很多人做 Agent 的时候系统提示词是写死的一次写好就不再改。这个做法在简单场景下能用但复杂场景下会出问题。因为 Agent 面对的任务是多样的用一套提示词应对所有情况效果必然打折扣。Claude Code 的做法是动态组装系统提示。它会根据当前会话的状态从多个提示片段里挑选合适的组合起来。这些片段包括基础行为规范、当前项目类型相关的编码规范、可用工具的说明、用户的自定义指令、当前任务的上下文信息等等。这个设计的好处是提示词预算花在刀刃上。上下文窗口是有限的如果把所有可能的提示都塞进去会挤占真正有用的信息。动态组装保证了每次只加载当前需要的那部分。5.2 工具结果的呈现方式影响模型判断工具调用返回的结果怎么呈现给模型这个细节很多人不注意但它对模型的理解影响很大。同样一个文件内容用不同的格式呈现模型提取信息的效率可能差好几倍。Claude Code 在这方面做了不少优化。比如读文件时会带上行号方便模型引用具体位置。搜索结果会按相关性排序最相关的排前面。命令执行结果会区分 stdout 和 stderr让模型知道哪些是正常输出、哪些是错误信息。文件被截断时会明确标注避免模型误以为看到的是全部内容。这些细节单独看都很小但累积起来对 Agent 的表现影响很大。做自己的 Agent 时值得在工具结果的格式化上多花点心思。一个原则是假设模型是个聪明但粗心的实习生你要把信息整理得让它一眼就能看懂。5.3 长任务的上下文压缩策略前面提到过上下文压缩这里展开讲讲具体怎么做。长任务跑下来上下文里会积累大量工具调用记录。这些记录不能全留也不能全丢需要有个取舍策略。我的经验是分三层处理。第一层是决策记录比如决定用 JWT 做认证、选择 PostgreSQL 而不是 MySQL这些必须保留因为后续所有工作都依赖这些决策。第二层是关键结果比如测试通过了、发现了一个 bug 在 XX 文件的 XX 行这些要保留因为影响后续判断。第三层是执行细节比如读取了 XX 文件、运行了 XX 命令这些可以压缩成一句话摘要甚至直接丢掉。Claude Code 的压缩策略大致也是这个思路。它会定期把早期的对话轮次做摘要保留决策和结论丢掉过程细节。这个操作对用户是透明的用户看到的是对话在继续实际上底层已经做了压缩。6. 从 Claude Code 的设计里能抄到什么作业6.1 工具设计的第一原则让模型容易用对回顾 Claude Code 的工具集设计最核心的一条经验是工具是给模型用的不是给人用的。这个视角转换很重要。人用的工具可以复杂因为有文档、有 IDE 提示、有试错空间。模型用的工具必须简单明确因为模型只能通过描述来理解工具没有交互式学习的机会。具体到设计上有几个要点。工具的功能要单一一个工具只做一件事不要搞万能工具。参数要少必填参数控制在三个以内。参数类型要简单字符串和数字优先避免复杂的嵌套结构。描述要具体说清楚什么时候用、什么时候不用。错误信息要有用告诉模型失败原因和可能的解决办法。6.2 Agent 循环的终止条件怎么定Agent 循环什么时候停这个问题比看起来复杂。停早了任务没完成停晚了浪费资源还可能闯祸。Claude Code 的终止条件设计是多重的模型主动表示任务完成、达到最大循环次数、遇到无法恢复的错误、用户手动中断。对于自己做 Agent 的开发者我建议至少设置三层保护。第一层是模型自判断让模型在任务完成时输出特定标记。第二层是硬性上限设置最大循环次数和最大 token 消耗超过就强制停止。第三层是异常检测如果连续多次工具调用失败或者检测到重复行为就中断循环并报告问题。第三层特别重要因为模型有时候会陷入死循环而不自知。比如反复修改同一个文件、反复执行同一个失败的命令。这种时候需要外部机制来打断它。6.3 安全边界要从架构层面保证最后聊聊安全。Claude Code 的安全设计不是靠相信模型不会做坏事而是靠架构层面的约束。权限系统、沙箱执行、审计日志这些都是架构层面的保障不依赖模型的自律。做自己的 Agent 时这个思路要贯彻。不要指望通过提示词让模型不要做危险操作而是要在工具层面限制它做不了。比如文件写入工具限制在特定目录内shell 命令工具用白名单网络请求工具限制目标域名。这些约束在代码层面实现模型再聪明也绕不过去。提示安全设计的一个实用原则是最小权限。Agent 需要什么权限就给什么权限不要多给。需要读文件就给读权限不要顺手把写权限也给了。需要访问数据库就给只读账号不要用管理员账号。这个原则在传统安全领域是老生常谈但在 Agent 开发里经常被忽略。7. 一些踩坑之后的个人体会做 Agent 开发这段时间踩过的坑不少有几个体会特别深。第一个体会是不要低估上下文管理的重要性。刚开始做的时候我觉得只要模型够强上下文多点少点无所谓。实际跑起来才发现上下文管理做得好不好直接决定 Agent 能不能完成复杂任务。同样的模型上下文管理优化之后任务完成率能提升一大截。第二个体会是工具描述值得反复打磨。我有个工具描述改了七八版调用准确率从六成提升到九成以上。每次改都是根据实际调用中的错误案例来调整看模型在什么情况下会误用这个工具然后针对性地补充描述。这个过程很枯燥但效果立竿见影。第三个体会是错误处理比成功路径更重要。成功路径跑通很容易难的是各种异常情况的处理。工具调用失败怎么办、模型输出格式不对怎么办、上下文超限怎么办、用户中途改变需求怎么办。这些异常情况处理好了Agent 才真正可用。第四个体会是可观测性要从第一天就做。日志、指标、追踪这些在传统后端开发里是标配但在 Agent 开发里经常被忽略。等到出了问题才发现没有日志可查那时候补就来不及了。我现在做任何 Agent 项目第一件事就是把可观测性基础设施搭好。Claude Code 这套设计理念说到底就是把软件工程里那些成熟的做法——模块化、权限控制、可观测性、错误处理——搬到了 LLM 应用开发里。模型能力固然重要但真正决定 Agent 能不能用的是这些工程层面的东西。这个认知是我做完几个 Agent 项目之后最大的收获。