ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

拆解 Claude Code 内核:手写一个最小 Agent Harness

2026/8/31 1:52:43 拓冰建站 浏览量
拆解 Claude Code 内核:手写一个最小 Agent Harness 最近在终端里重度使用 Claude Code 做代码迁移和批量重构发现很多同学对它的理解仍停留在“一个能聊天的命令行工具”。真正驱动它在仓库里读文件、跑命令、改代码的是一套完整的 Agent Harness。本文不打算逐行搬运官方闭源代码而是从工程视角把它的运行机制拆开再动手写一个最小可运行的 Harness 示例帮助你理解 Agent 循环、工具调用、权限模型与上下文管理。无论你是 AI Agent 初学者还是想深入自定义工具链的开发者这篇文章都能给你一套可复用的分析框架。1. 背景与核心概念1.1 Claude Code 到底做了什么Claude Code 是 Anthropic 推出的终端 AI 编程助手它不是一个简单的“对话机器人”而是一个能在真实项目里完成读文件、写文件、执行命令、运行测试、提交代码等操作的 Agent。你可以把它理解成“住在终端里的开发搭档”。从使用者的角度看Claude Code 做的事情大致是接收用户的中文或英文自然语言指令。分析当前目录下的项目结构读取相关文件内容。自主决定调用哪些工具比如读取文件、执行 Shell 命令、编辑代码。根据工具返回结果继续推理下一步操作。遇到需要用户确认的操作时暂停等待或直接遵循预设的权限规则执行。在这个过程中Claude Code 的价值并不只是“模型很强”更关键的是它把模型能力封装成了一整套可运行、可控制、可观测的工程系统。这套系统就是本文要拆解的 Agent Harness。1.2 什么是 Agent HarnessHarness 在英文里原本是“挽具、背带”的意思在 AI Agent 工程中它被引申为“把模型装进可控运行环境中的整套装置”。很多人会把 Agent 理解成“大语言模型本身”这是一个常见的误区。大语言模型本质上是一个文本生成器它只能接收文字输入并输出文字。模型自己不会读文件、不会执行命令、不会循环尝试。真正让模型“动手做事”的是模型外面包着的那层系统接收用户输入并组装成模型能理解的上下文。提供工具列表让模型可以“选择”调用哪些函数。执行模型选择的函数并把结果回传给模型进入下一轮推理。控制循环次数、处理异常、记录日志、限制权限。这一整套机制就是 Agent Harness。Claude Code 是 Harness 模型 工具集 权限系统 上下文管理器的综合产品。理解 Harness就等于理解 Agent 的核心骨架。1.3 为什么要“拆开”看 Claude Code需要先说明一点Claude Code 的核心代码并未完全开源我们无法做到逐行阅读官方源码。但源码级理解并不等于逐行阅读源码我们可以通过三条路径做到行为观察通过终端里的调试模式查看 Claude Code 发给模型的真实请求。接口分析观察工具调用格式、权限提示、结果回传格式。原理复现用代码实现一个最简 Harness复现它的核心流程。三种方式结合起来就能从“会用 Claude Code”升级为“理解 Claude Code”甚至能自己实现一个类似工具。在 AI Agent 开发中这种能力是通用的无论是 Claude Code、Codex 还是各种开源 Harness核心架构都遵循相似的思路。2. 环境准备与基础安装2.1 安装 Claude Code 命令行工具Claude Code 官方提供了 npm 包安装之前需要确保本机有 Node.js 环境。Claude Code 对 Node 版本有要求建议使用当前较新的 Node.js LTS 版本。如果你还不知道本机是否安装了 Node.js可以先执行node -v npm -v确认 Node.js 环境可用后使用 npm 全局安装 Claude Codenpm install -g anthropic-ai/claude-code安装完成后检查版本claude --version如果能够输出版本号说明安装成功。如果提示命令找不到可能是 npm 全局目录没有加入 PATH需要根据你当前操作系统的 npm 配置来解决。2.2 登录与授权首次运行 Claude Code 需要登录账号并授权终端使用。在项目目录下执行claude根据提示完成登录授权。需要注意的是Claude Code 的生态更新非常快登录方式、模型选择、命令参数都可能随版本调整如果执行过程中遇到差异优先查看当前版本的官方帮助信息claude --help2.3 准备一个最小工作区为了观察 Claude Code 在真实项目里的行为建议准备一个最小工作区mkdir claude-demo cd claude-demo git init echo Hello from README README.md这个工作区有一个 README 文件和一个空的 git 仓库。后续观察 Claude Code 如何读取文件、如何执行命令时这个最小环境足够了。2.4 通过调试模式观察 Harness 行为Claude Code 支持调试模式你可以通过命令行参数或终端内的调试命令开启。以--debug为例具体参数名以当前版本claude --help输出为准开启调试后终端会输出更多内部日志包括模型请求、工具调用、上下文压缩等信息。这是理解 Agent Harness 最直接的方式你能看到用户一句话是如何被组装成模型请求又是如何触发工具调用并回传结果的。在开始观察之前我们先从原理层面拆解 Claude Code 背后的 Agent Harness 核心机制。3. Agent Harness 核心机制拆解3.1 主循环Agent LoopAgent Harness 的第一个核心机制是主循环Agent Loop可以理解为 Agent 的“心跳”。整个循环大致如下接收用户输入组装系统提示、历史消息、工具定义。调用语言模型得到模型输出。判断模型输出是普通文本还是工具调用。如果是工具调用执行对应工具把结果追加到消息列表回到第 2 步。如果模型输出了最终答案循环结束把结果展示给用户。这个循环就是 Agent 能够“多步思考、逐步行动”的底层原因。大模型单次推理只能给出一步决策但通过循环Harness 让模型可以反复观察工具结果并调整策略。Claude Code 之所以能完成“先读代码再改代码再跑测试”这种复杂任务靠的就是这样一个循环。从源码实现的角度看这个循环通常需要控制几个关键参数最大步数max steps防止模型无限循环。单次工具调用数量有的 Harness 支持一次并行调用多个工具。中止信号处理例如用户按 CtrlC 时能优雅退出。3.2 上下文工程把什么塞给模型模型本身没有“看到文件”的能力它只能看到被放进 Prompt 里的文本。所以 Harness 需要做大量的上下文工程Context Engineering决定哪些项目信息进入模型视野。Claude Code 的上下文管理大致包含这几层系统提示定义 Claude 的身份角色、操作规范、输出约束。工具定义把每个可用工具的 JSON Schema 传给模型让模型知道有哪些函数可调用。对话历史记录之前的用户指令、助手回复、工具结果。文件内容根据用户需求选择性读取项目文件并写入上下文。代码库索引大型项目里通常会对代码做索引避免每次把所有文件塞进模型。在实际开发中上下文管理是 Agent 工程质量的关键。如果上下文塞得太多模型容易“迷失”还会造成 Token 成本飙升如果塞得太少模型缺少必要信息决策质量下降。Claude Code 的另一个重要能力是上下文压缩当对话历史过长时它会自动总结历史内容把较早的对话压缩成摘要从而让上下文保持在一个可控范围内。3.3 工具调用机制Tools 与 Function CallingAgent Harness 与普通聊天机器人的最大差异在于工具调用。Claude Code 具备一系列内置工具比如读取文件、编辑文件、执行 Bash 命令等。它的实现原理是 Function Calling函数调用模型输出的不是普通文本而是一段结构化指令其中包括函数名和参数。一次典型的工具调用流程如下Harness 把工具列表以 JSON Schema 形式传给模型。模型决定调用ReadFile工具并输出参数{path: README.md}。Harness 解析模型输出校验参数格式调用真实的文件读取函数。把读取结果以tool角色消息回传给模型。模型看到文件内容后继续下一步推理。在 Claude Code 中你可以观察到模型调用工具时往往带有明确的权限判断。例如执行 Bash 命令前Claude Code 会检查该命令是否在允许名单中不在名单里就弹确认提示。工具调用的设计质量直接影响 Agent 的实际效果。如果工具粒度太粗模型无法精细控制如果工具太多模型会频繁选错工具如果工具描述含糊模型就更难做出正确选择。优秀 Harness 需要持续打磨工具 Schema 的定义和描述。3.4 权限系统Harness 的安全边界Agent 一旦具备执行命令的权限安全问题就会凸显。Claude Code 设计了一套权限控制机制用来平衡“自动化”与“安全性”。在交互模式下Claude Code 遇到高风险操作会请求用户确认在自动模式下它依靠用户预设的 allow/deny 规则决定执行还是拒绝。常见的权限配置点包括哪些 Bash 命令允许自动执行。哪些文件允许读取和编辑。哪些网络请求允许发出。是否允许跳过所有确认提示。从工程角度看权限系统是 Agent Harness 的“安全边界延伸”。一个合格的 Harness 应该在默认情况下采用最小权限原则只授予完成当前任务所必需的权限并在执行敏感操作前保留人工确认的入口。3.5 Hook 与 Skill扩展接口Claude Code 还提供了 Hook 机制允许用户在特定生命周期事件中插入自定义脚本。例如在工具调用前执行安全检查在会话开始时加载自定义配置在输出结果后发送通知等。从 Harness 架构角度看Hook 本质上是一组事件监听器Harness 在执行到特定阶段时会触发预定义的回调命令。这种设计让用户不用修改 Harness 核心代码也能实现自定义逻辑。Skill 机制则把提示词、工具、工作流封装成可复用的能力单元适合将团队的最佳实践沉淀下来。这类扩展机制是 Agent Harness 走向工程化的标志。4. 实战手写一个最小可运行的 Agent Harness理解了核心机制后我们来手写一个最小可运行的 Agent Harness。这个示例会聚焦主循环、工具注册表、上下文组装、权限确认四部分完整复现 Claude Code 的核心工作方式。4.1 需求与设计我们的最小 Harness 要支持以下能力通过 OpenAI 兼容接口调用语言模型。让模型能够选择调用工具。内置两个演示工具读取文件和执行命令。工具执行结果回传给模型。敏感命令执行前进行拦截确认。为了保证代码简洁我们使用 OpenAI 的 Python SDK 作为模型客户端模型服务只要兼容 OpenAI 协议即可接入。4.2 项目结构mini_harness/ ├── harness.py ├── requirements.txt └── README.mdrequirements.txt内容如下openai1.0.04.3 实现 Tool 注册表工具注册表是 Agent Harness 的“工具箱”。它负责存储工具函数、维护工具 Schema 列表、执行模型指定的工具。# 文件路径mini_harness/harness.py import json from typing import Dict, List, Callable class ToolRegistry: def __init__(self): self._tools: Dict[str, Callable] {} self._schemas: List[Dict] [] def register(self, name: str, description: str, parameters: Dict): 注册一个可被模型调用的工具 def decorator(func: Callable): self._tools[name] func self._schemas.append({ type: function, function: { name: name, description: description, parameters: parameters, } }) return func return decorator def execute(self, name: str, arguments: Dict) - str: if name not in self._tools: return json.dumps({error: funknown tool: {name}}, ensure_asciiFalse) func self._tools[name] result func(**arguments) return json.dumps(result, ensure_asciiFalse)这里把工具注册和工具执行分离符合 Harness 常见的工具管理设计。后续要增加新工具只需要用register装饰器注册函数即可。4.4 定义两个内置工具接下来我们注册两个演示工具。第一个工具负责读取文件第二个工具负责执行命令。# 文件路径mini_harness/harness.py接上面的代码 registry ToolRegistry() registry.register( read_file, 读取指定路径的文本文件内容, { type: object, properties: { path: {type: string, description: 文件路径} }, required: [path] } ) def read_file(path: str): try: with open(path, r, encodingutf-8) as f: content f.read() return {content: content[:2000]} except FileNotFoundError: return {error: f文件不存在: {path}} registry.register( run_command, 在 shell 中执行命令并返回输出, { type: object, properties: { command: {type: string, description: 要执行的 shell 命令} }, required: [command] } ) def run_command(command: str): import subprocess # 注意这里仅用于演示生产环境必须做白名单校验和沙箱隔离 result subprocess.run( command, shellTrue, capture_outputTrue, textTrue, timeout30 ) return { stdout: result.stdout[-2000:], stderr: result.stderr[-2000:] }需要特别注意run_command执行真实系统命令是一个高风险操作。这个示例中我们加入了简单的危险命令拦截但真实生产环境中工具执行层必须运行在沙箱或容器里并使用白名单机制。4.5 实现主循环 Harness主循环是整个 Harness 的核心。它组装消息、调用模型、解析工具调用、执行工具、回传结果并且控制最大步数。# 文件路径mini_harness/harness.py接上面的代码 import os from typing import List, Dict class MiniAgentHarness: def __init__(self, model: str gpt-4o-mini): self.model model self.messages: List[Dict] [] self.registry registry def _build_messages(self, user_input: str) - List[Dict]: system_prompt ( 你是一个运行在终端里的编程助手。 你可以调用工具读取文件、执行命令但要遵守最小权限原则。 每次只能调用一个工具观察工具返回结果后再决定下一步。 ) if not self.messages: self.messages.append({role: system, content: system_prompt}) self.messages.append({role: user, content: user_input}) return self.messages def _call_llm(self): from openai import OpenAI client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) response client.chat.completions.create( modelself.model, messagesself.messages, toolsself.registry._schemas, tool_choiceauto, ) return response.choices[0].message def run(self, user_input: str, max_steps: int 10): self._build_messages(user_input) for step in range(max_steps): print(f\n Step {step 1} ) message self._call_llm() # 没有工具调用说明模型给出了最终回答 if not message.tool_calls: print(Assistant:, message.content) self.messages.append({role: assistant, content: message.content}) return # 把 assistant 消息包含工具调用指令加入历史 self.messages.append({ role: assistant, content: message.content, tool_calls: [ { id: tc.id, type: function, function: { name: tc.function.name, arguments: tc.function.arguments, } } for tc in message.tool_calls ] }) # 逐个执行工具调用 for tc in message.tool_calls: fn_name tc.function.name fn_args json.loads(tc.function.arguments or {}) print(fCall tool: {fn_name}({fn_args})) # 权限确认演示用敏感命令拦截 if fn_name run_command: result self._safe_execute_command(fn_args) else: result self.registry.execute(fn_name, fn_args) self.messages.append({ role: tool, tool_call_id: tc.id, content: result, }) print(达到最大步数结束本轮任务。) def _safe_execute_command(self, fn_args: Dict) - str: command fn_args.get(command, ) dangerous_keywords [rm -rf, sudo, mkfs, dd if] if any(keyword in command for keyword in dangerous_keywords): print(f危险命令被拦截: {command}) return json.dumps({error: 用户拒绝执行该命令}, ensure_asciiFalse) return self.registry.execute(run_command, fn_args)这个主循环已经很接近真实 Agent Harness 的形态了。需要注意在这个实现里工具执行结果会以tool角色消息回传并携带对应的tool_call_id这是 OpenAI 兼容接口要求的字段模型会根据它把工具结果和之前的工具调用请求关联起来。4.6 入口与运行最后添加一个程序入口方便直接运行示例# 文件路径mini_harness/harness.py接上面的代码 if __name__ __main__: harness MiniAgentHarness(modelgpt-4o-mini) harness.run(请读取当前目录下的 README.md并告诉我第一行写了什么, max_steps5)运行前需要安装依赖并配置环境变量pip install -r mini_harness/requirements.txt export OPENAI_API_KEYyour_api_key_here # 可选如果使用兼容 OpenAI 协议的自建服务可设置 base_url # export OPENAI_BASE_URLhttps://your-endpoint.example.com python mini_harness/harness.py如果你的模型服务兼容 OpenAI 协议可以通过OPENAI_BASE_URL环境变量切换。这里需要说明不同模型对工具调用的支持程度不一样示例代码以带工具调用能力的模型为前提。4.7 预期运行结果与解释当模型正确理解任务时你会看到类似下面的输出 Step 1 Call tool: read_file({path: README.md}) Step 2 Assistant: README.md 的第一行内容是Hello from README这个过程展示了 Agent Harness 最核心的闭环用户指令进入消息列表。模型决策调用read_file工具。Harness 执行工具并回传内容。模型基于工具结果生成最终回复。虽然这个 Harness 还非常简陋但它已经具备了 Claude Code 的核心骨架工具注册表、主循环、上下文消息维护、权限检查。在这个基础上继续扩展就能逐步演进成一个可用的终端编程助手。5. 从 Claude Code 中学习 Harness 的进阶设计5.1 真实 Harness 的差距在哪里对比我们写的最小 HarnessClaude Code 在工程化层面多出了很多关键设计会话管理支持持久化会话重启后能继续对话。权限层级拥有多种权限模式支持细粒度的 allow/deny 规则。上下文压缩对话过长时自动摘要防止上下文窗口溢出。代码索引大型代码库通过索引实现快速检索而不是每次读取整个目录。并行工具调用一次推理可以同时调用多个工具提升执行效率。Hook 事件提供生命周期钩子方便用户插入自定义脚本。MCP 生态支持通过 MCP 协议接入第三方工具。这些设计是真实 Agent 系统“能用”和“好用”之间的分水岭。如果你打算开发自己的 Agent Harness建议优先补齐会话管理和上下文压缩这两项对长任务体验影响最大。5.2 利用调试模式持续分析我们在环境准备阶段提到Claude Code 支持调试模式。打开调试模式后你能观察到模型请求体、工具调用参数、上下文压缩策略等内部信息。这是持续分析 Harness 行为的最佳入口。建议你在实际项目中做一个小实验开启调试模式让 Claude Code 修改一个函数然后观察它在一轮修改中发送了几次请求、每一步的工具参数是什么。这种观察比阅读任何源码分析文章都更直接。5.3 从 Harness 到 Agent 平台理解了 Harness 之后再看 Agent 开发领域就会清晰很多。Harness 是 Agent 的单机运行内核而 Agent 平台则是在 Harness 之上加入任务调度、队列、人工审批、知识库、模型路由等能力。Claude Code 本身更偏 Harness 层而企业内部 Agent 平台通常是在 Harness 之上包装更多业务能力。6. 常见问题与排查思路在实际使用 Claude Code 或开发 Harness 的过程中经常遇到以下几类问题。下面整理一个排查表格并展开说明几个高频场景。问题现象常见原因解决思路启动报错提示模型无法识别CLI 版本与模型版本不匹配升级 Claude Code或检查模型名称配置Agent 长时间无响应模型服务响应超时检查网络连接、服务状态和超时设置权限请求过多任务频繁中断默认权限策略过于严格配置 allow 白名单按需放行安全操作工具执行结果不正确工具 Schema 描述不清晰检查工具参数定义和返回结果格式长对话后模型“忘记”早期内容上下文窗口溢出触发压缩拆分子任务减少不必要的历史消息修改代码后未生效文件编辑工具未保存或路径错误检查工具返回结果和文件实际内容6.1 模型不识别错误很多用户在 Claude Code 升级后遇到类似“is not a model this version recognizes”的报错。这个问题的原因通常是CLI 版本内部维护了一个模型白名单新模型发布后旧版 CLI 不认识新模型名称或者用户通过配置强行指定了当前版本不支持的模型名。排查思路执行claude --version确认当前 CLI 版本。执行命令查看当前版本支持的模型列表。如果刚升级过模型优先升级 Claude Code 到最新版本。如果使用第三方模型网关检查模型名称是否在网关支持的范围内并确认是否与 CLI 的模型前缀兼容。6.2 Agent 执行提供方响应超时有时你会看到类似 “the agent execution provider did not respond in time” 的提示意思是模型调用方超过预期时间没有返回结果。原因可能包括模型服务负载过高生成时间过长。网络链路不稳定或超时时间设置过短。输入上下文过长模型推理耗时增加。使用了不兼容的模型服务请求被挂起。排查时建议先降低输入规模例如把任务拆小再看是偶发还是必然触发。如果必然触发优先检查模型服务和网络状态。Harness 开发中也应设计合理的超时与重试机制。6.3 权限请求过多使用 Claude Code 时如果执行的命令频繁触发确认提示说明安全策略比较严格。这本身是安全设计但如果任务确实需要多次执行同类操作可以在配置中预设 allow 白名单把安全检查前置到规则层避免每次打断。需要强调的是放行规则必须严格只能在充分理解命令风险后配置不能为了省事把危险命令全部加入白名单。7. 最佳实践与工程建议7.1 从最小权限原则设计工具集无论是使用 Claude Code 还是自研 Harness工具集的设计都要遵循最小权限原则。只暴露当前任务必需的工具不要给 Agent 提供它不需要的高危能力。每个工具的参数都应该限制边界例如命令执行工具不允许使用shellTrue之后再拼接用户输入文件写入工具应该限制可写目录范围。7.2 控制工具数量与质量模型在工具选择上的准确率会随着工具数量增加而下降。工具数量不要贪多每个工具的 Schema 描述要清晰准确尤其是参数说明和返回值格式。如果一个工具描述模糊模型会产生大量无效调用拉低整个 Agent 的执行效率。7.3 让上下文保持精简上下文管理是 Agent 工程质量的分水岭。建议不相关文件不要自动读入。单次工具结果不要无限制回传可以截断到合理长度。长任务定期总结中间结果替换掉完整历史。对大型代码库建立索引而不是每次全量扫描。这些策略不仅降低 Token 成本也能提升模型在关键任务上的专注度。7.4 建立日志与审计机制Agent 会自主执行命令和修改文件因此必须记录日志。至少需要记录每次模型请求的时间、工具调用参数、工具执行结果、用户确认动作、最终输出。日志不仅能帮助定位问题也能用于安全审计。在自研 Harness 中建议在工具执行层统一埋点不要散落在各个工具函数里。7.5 加入超时、重试和熔断真实环境中的模型服务不可能永远稳定。Harness 需要为模型调用和工具执行都设置超时时间并在失败时进行有限次重试。如果连续失败超过阈值应该停止任务并反馈错误而不是盲目重试造成额外开销。7.6 生产环境中使用沙箱隔离如果你的 Harness 运行着来自模型动态生成的命令强烈建议在沙箱或容器中执行。即使模型本身没有恶意代码生成模型的偶发错误也可能产生破坏性命令。沙箱隔离是最后一层安全防线不能省略。7.7 为 Harness 编写测试用例Agent 系统的行为随机性较大更需要单元测试。可以给工具注册表、Tool Schema 生成、权限判断函数编写独立测试保证核心机制稳定。对于模型调用层则可以通过 mock 固定模型响应验证 Harness 主循环在不同输出下的分支逻辑是否正常。8. 结语动手观察你的第一个 Agent 请求Agent Harness 并不神秘它本质上是把“模型决策 工具执行 上下文维护 流程控制”组装起来的一层工程代码。Claude Code 之所以强大一方面来自底层模型能力另一方面来自它对 Harness 细节的持续打磨。理解了这套架构之后你再使用 Claude Code 时会更有底气也知道如何把它的能力接入自己的项目。如果你真的想深入“手撕源码”我的建议是别急着找各种源码解析文章先打开终端进入一个真实项目的目录执行claude并开启调试模式然后下达一个“请帮我看看 xxx 文件为什么报错”的任务。观察它第一次发送给模型的请求体长什么样工具列表如何定义工具结果如何回传。这个动手实验比读十篇源码分析文章都更有价值。当你把这套观察方法迁移到代码生成、AI 编程助手、企业 Agent 平台等方向时你就真正拥有了 Agent 工程的核心分析能力。