本文基于 Cline 开源仓库(2026 年 6 月版本)的实际源码分析,所有代码引用均来自 sdk/packages/ 目录下的真实文件。带你从源码层面理解 Cline 到底是怎么"自己写代码"的。
目录
- ReAct 主循环:AgentRuntime.execute() 的完整实现
- Plan Mode 的实现:不是"AI 判断",而是"工具集裁剪"
- 工具审批机制:beforeTool Hook 链
- Context Window 管理:三层防线
- 记忆系统:ConversationStore + Checkpoint
- 错误自愈:MistakeTracker + LoopDetectionTracker
- 工具定义:实际的工具列表
- 源码分析小结
一、ReAct 主循环:AgentRuntime.execute() 的完整实现
Cline 的核心引擎是 @cline/agents 包中的 AgentRuntime 类(文件:sdk/packages/agents/src/agent-runtime.ts)。其 execute() 方法实现了一个经典的 while 循环驱动的 ReAct 决策环:
| // 简化后的核心循环(agent-runtime.ts)asyncexecute(input?: AgentRunInput): Promise<AgentRunResult> {awaitthis.ensureInitialized();this.state.status = "running";this.state.iteration = 0;for (const message of input ? normalizeInput(input) : []) {this.state.messages.push(message);while (this.state.iteration < this.config.maxIterations) {this.state.iteration += 1;// === Reason + Act:调用 LLM 生成助手消息 ===const { message, finishReason } = awaitthis.generateAssistantMessage();this.state.messages.push(message);const toolCalls = message.content.filter( (part) => part.type === "tool-call"if (toolCalls.length === 0) {// 检查是否需要 completion tool(如 submit_and_exit)const completionReminder = this.getCompletionReminderMessages();if (completionReminder.length > 0) {// 无 completion 要求 → 正常结束returnthis.finishRun("completed", message);// === Observe:执行工具并收集结果 ===const toolMessages = awaitthis.executeToolCalls(toolCalls);for (const toolMessage of toolMessages) {this.state.messages.push(toolMessage);// 检查是否有终止工具被调用(如 submit_and_exit)const terminalTool = this.findCompletingToolMessage(toolCalls, toolMessages);returnthis.finishRun("completed", message); |
循环流程图
关键设计要点
| 源码机制 | 说明 |
|---|
| maxIterations | 防止无限循环,超出后抛出错误 | | completionPolicy.requireCompletionTool | 强制模型必须调用 submit_and_exit 才能结束,避免"自以为完成" | | completionPolicy.completionGuard | 团队模式下,检查是否还有未完成的任务,未做完则注入提醒消息 | | AbortController | 支持外部中断(用户点击停止),通过 throwIfAborted() 在每轮检查 | | pendingToolCalls | 记录当前待处理的工具调用 ID,用于状态快照和恢复 |
|
工具调用的并行与串行
源码中 executeToolCalls() 支持两种执行模式:
| privateasyncexecuteToolCalls(toolCalls): Promise<AgentMessage[]> {constprepared: PreparedToolExecution[] = [];for (const toolCall of toolCalls) { prepared.push(awaitthis.prepareToolExecution(toolCall));if (this.config.toolExecution === "parallel") { prepared.map((execution) => this.executePreparedTool(execution))constresults: AgentMessage[] = [];for (const execution of prepared) { results.push(awaitthis.executePreparedTool(execution)); |
toolExecution 配置为 "sequential"(默认)或 "parallel"。当模型在一轮中输出多个工具调用时,串行模式更安全(后一个工具可以看到前一个的结果),并行模式更快但需要工具之间无依赖。
二、Plan Mode 的实现:不是"AI 判断",而是"工具集裁剪"
很多人以为 Plan Mode 是通过提示词告诉 AI "你现在只能读不能写"。实际上,源码中的实现更加彻底——直接从工具集中移除写入类工具,模型连写文件的工具都看不到。
2.1 ToolPresets:模式即工具集
文件:sdk/packages/core/src/extensions/tools/presets.ts
| exportconstToolPresets = {enableBash: true, // ← 可执行命令enableEditor: true, // ← 可编辑文件enableSubmitAndExit: false,enableBash: true, // ← 仍可执行命令(用于探索)enableEditor: false, // ← 编辑器被禁用!无法写文件enableSubmitAndExit: false,enableAskQuestion: false,enableSubmitAndExit: true, // ← 只有 YOLO 有完成工具 |
2.2 核心发现
| 发现 | 说明 |
|---|
| enableEditor: false 意味着 editor(写文件/改文件)工具根本不会出现在模型可见的工具列表中 | | enableBash: true 保留了——模型可以执行 ls、grep、cat 等只读命令来探索代码 | | 模型连 write_to_file 工具都看不到,想写也写不了 |
|
2.3 模式切换机制
| functionresolveToolPresetName(options: { mode?: AgentMode }): ToolPresetName {if (options.mode === "plan") return"plan";if (options.mode === "yolo") return"yolo"; |
在 DefaultRuntimeBuilder.build() 中,模式决定了最终注册到 Agent 的工具列表:
| const preset = ToolPresets[resolveToolPresetName({ mode: config.mode })]; ...createBuiltinToolsList( normalized.mode, // ← 模式传入 |
2.4 YOLO Mode 的另一面
YOLO Mode 不仅启用工具,还通过 createToolPoliciesWithPreset("yolo") 将所有工具的 autoApprove 设为 true:
| exportfunctioncreateToolPoliciesWithPreset(presetName) {if (presetName !== "yolo") return {};constyoloPolicy: ToolPolicy = { enabled: true, autoApprove: true };constpolicies: Record<string, ToolPolicy> = { "*": yoloPolicy };for (const toolName ofALL_DEFAULT_TOOL_NAMES) { policies[toolName] = yoloPolicy; |
这意味着 YOLO = 全工具 + 全自动审批,是真正的"无人值守"模式。
三、工具审批机制:beforeTool Hook 链
每次工具调用前,AgentRuntime 会经过一条完整的审批链。这不是硬编码的弹窗逻辑,而是一个可扩展的 策略模式。
3.1 审批流程源码
| // agent-runtime.ts → prepareToolExecution()privateasyncprepareToolExecution(toolCall): Promise<PreparedToolExecution> {const tool = this.tools.get(toolCall.toolName);let input = toolCall.input;letskipReason: string | undefined;// ========== 第 1 步:检查工具输入是否解析成功 ==========const metadata = toolCall.metadata;if (metadata?.inputParseError) { skipReason = metadata.inputParseError;// ========== 第 2 步:执行 beforeTool hooks ==========// Hook 可以:修改输入 / 跳过工具 / 甚至终止整个运行if (tool && !skipReason) {for (const hook ofthis.hooks.beforeTool) {const result = awaithook({ snapshot, tool, toolCall, input });if (result?.input) input = result.input; // Hook 可修改工具输入 skipReason = result.reason;this.applyStopControl(result); // Hook 可终止整个运行// ========== 第 3 步:检查 ToolPolicy ==========if (tool && !skipReason) {const policy = resolveToolPolicy(toolCall.toolName, this.config.toolPolicies);if (policy.enabled === false) { skipReason = `Tool "${toolCall.toolName}" is disabled by policy`; } elseif (policy.autoApprove === false) {const approval = awaitthis.requestToolApproval(toolCall, input, policy);if (!approval.approved) { skipReason = approval.reason;return { toolCall, tool, input, skipReason }; |
3.2 审批链的三层结构
| ┌─────────────────────────────────────────────┐│ JSON 解析失败?→ 跳过执行,返回错误给模型 │└─────────────────────────────────────────────┘┌─────────────────────────────────────────────┐│ 第 2 层:beforeTool Hooks(可插拔) ││ LoopDetection → 发现重复调用?注入警告 ││ 自定义 Hook → 可修改输入/跳过/终止 │└─────────────────────────────────────────────┘┌─────────────────────────────────────────────┐│ enabled === false → 工具被禁用 ││ autoApprove === false → 请求用户审批 ││ autoApprove === true → 自动通过(YOLO) │└─────────────────────────────────────────────┘ |
3.3 宿主应用的角色
requestToolApproval 是一个回调函数,由宿主应用注入:
- VSCode 扩展:弹出 Diff 面板,用户点击 "Approve" 或 "Reject"
- CLI:终端输出工具调用详情,等待用户输入 y/n
- CI/CD:可配置为自动批准所有(配合 .clinerules 约束)
这就是 Cline 的"Human-in-the-loop"设计的底层实现——审批逻辑与 Agent 引擎完全解耦。
四、Context Window 管理:三层防线
源码中 Context Window 管理由三个独立但协作的机制组成,形成层层递进的防线。
4.1 第一层:MessageBuilder — 消息构建时的实时截断
文件:sdk/packages/core/src/session/services/message-builder.ts
MessageBuilder 在每次构建发给 API 的消息时执行即时截断,不修改原始对话历史:
| DEFAULT_MAX_TOOL_RESULT_CHARS = 8_000; // 单个工具结果最大 8K 字符DEFAULT_MAX_FILE_CONTENT_CHARS = 50_000; // 文件内容最大 50K 字符DEFAULT_MAX_TOTAL_TEXT_BYTES = 6_000_000; // 总文本预算 6MBDEFAULT_MAX_ASSISTANT_TEXT_CHARS = 200_000; // 助手消息最大 200K 字符 |
处理流程
| buildForApi(messages: Message[]): Message[] {// 1. 修复缺失的 tool_result(防止 API 报错)// 如果 assistant 消息中有 tool_use 但没有对应的 tool_result,// 自动补一个 "Tool execution was interrupted" 的占位结果const repaired = this.addMissingToolResults(messages);const prepared = repaired.map(msg => {// - 工具结果截断到 8K 字符(中间截断,保留首尾)// - 过期的文件读取内容替换为 "[outdated]"// 3. 图片媒体预算控制(限制 base64 图片总量)const mediaLimited = this.applyMediaBudget(prepared);// 4. 总文本预算控制(6MB 上限,按大小排序逐步截断)returnthis.truncateToTotalTextBudget(mediaLimited); |
亮点机制 — 过期文件内容标记
这是一个非常聪明的优化:如果文件被重新读取过,旧的读取结果会被标记为过期,替换为占位符:
| private latestFullContentOwnerByPathCache = newMap<string, string>();privateisOutdatedReadLocator(locator, toolUseId): boolean {const fullOwner = this.latestFullContentOwnerByPathCache.get(locator.path);if (fullOwner && fullOwner !== toolUseId) returntrue; // 有更新的读取returnthis.latestReadToolUseByLocatorCache.get(key) !== toolUseId;constOUTDATED_FILE_CONTENT = "[outdated - see the latest file content]"; |
效果:如果 Cline 读了 user.ts(500 行),后来修改了它,又读了一次,那么第一次的 500 行内容会被替换为一行占位符,大幅节省 Token。
工具结果截断策略
| functiontruncateMiddleByChars(text, maxChars, makeMarker) {if (text.length <= maxChars) return text;const keep = Math.floor((maxChars - marker.length) / 2);const start = text.slice(0, keep);const end = text.slice(-keep);return`${start}\n\n...[truncated ${removed} chars]...\n\n${end}`; |
为什么是中间截断而不是尾部截断?因为头部通常包含错误信息的关键线索(如编译错误的第一行),尾部通常包含最终状态(如命令的退出码),中间部分往往是重复的日志输出。
4.2 第二层:Context Compaction — 上下文压缩
文件:sdk/packages/core/src/extensions/context/compaction.ts
当 Token 总量接近模型上限时,触发压缩。源码支持两种策略:
| constBUILTIN_COMPACTION_STRATEGIES = {basic: (options) => runBasicCompaction(options), // 策略 1:截断式agentic: (options) => runAgenticCompaction(options), // 策略 2:AI 摘要式 |
触发条件
| DEFAULT_THRESHOLD_RATIO = 0.9; // 达到 context window 的 90%DEFAULT_RESERVE_TOKENS = 16_384; // 或距离上限不到 16K tokensfunctionresolveTriggerState({ inputTokens, maxInputTokens, config }) {const triggerTokens = Math.max( maxInputTokens - DEFAULT_RESERVE_TOKENS, maxInputTokens * DEFAULT_THRESHOLD_RATIOshouldCompact: inputTokens > triggerTokens, |
Basic 策略 — 纯算法压缩(不依赖 LLM)
文件:basic-compaction.ts
关键约束:工具调用对(tool_use + tool_result)必须原子性移除——不能只删一半,否则 API 会报错。
| // 通过 tool_use_id 关联,BFS 找到所有关联消息一起移除functioncollectAtomicRemovalIndexes(candidates, startIndex): Set<number> {const pairIndex = buildToolPairIndex(candidates);const removalIndexes = newSet();const queue = [startIndex];while (queue.length > 0) {const index = queue.shift(); removalIndexes.add(index);// 找到这条消息中所有 tool_use_id / tool_result_idfor (const id ofcollectToolPairIds(candidates[index])) {for (const linkedIndex of pairIndex.get(id) ?? []) {if (!removalIndexes.has(linkedIndex)) { |
Agentic 策略 — 调用 LLM 生成摘要
文件:agentic-compaction.ts
| 1. 找到切割点(保留最近 N tokens 的消息不动)4. 用摘要消息 + 保留的近期消息 替代原始历史 |
摘要请求模板:
| const summaryRequest = `Summarize this session for continuation. Be concise and factual.## GoalOne sentence: what is being built or fixed.## State- Done: completed steps- In Progress: current work- Blocked: blockers or open questions## HighlightsKey technical choices or notable findings.## NextImmediate next steps.## FilesRead: ${fileOps.readFiles.join(", ")}Edited: ${fileOps.modifiedFiles.join(", ")}`; |
压缩后生成一条特殊的 compaction_summary 消息:
| functionbuildSummaryMessage({ summary, fileOps, tokensBefore }) {content: `Context summary:\n\n${summary}`,kind: "compaction_summary",details: fileOps, // 记录读/改了哪些文件 |
增量摘要:如果之前已经有过压缩(存在旧的 compaction_summary),新的摘要会包含旧摘要的内容,形成递归式摘要链:
| const previousSummary = findLatestSummaryIndex(messagesToSummarize) >= 0 ? getCompactionSummaryMetadata(messages[latestSummaryIndex]).summary// 摘要请求中包含 previousSummaryconst summaryRequest = buildSummaryRequest({ previousSummary, // ← 上一次摘要的内容 conversationText, // ← 新增的对话内容 |
4.3 第三层:prepareTurn 钩子 — 每轮请求前的拦截点
| // agent-runtime.ts → generateAssistantMessage()request = awaitthis.prepareTurnForModelRequest(request); |
prepareTurn 是一个通用钩子接口,压缩只是它的用途之一:
| privateasyncprepareTurnForModelRequest(request) {if (!this.config.prepareTurn) return request;const result = awaitthis.config.prepareTurn({ agentId, conversationId, parentAgentId,messages: request.messages,systemPrompt: request.systemPrompt,emitStatusNotice: (message, metadata) => { ... },// 可以返回修改后的 messages 和 systemPromptthis.state.messages = result.messages; // 替换整个消息列表if (result?.systemPrompt !== undefined) { next = { ...next, systemPrompt: result.systemPrompt }; |
插件系统也可以通过注册 prepareTurn 回调来在每轮请求前修改消息、系统提示词等。这为外部扩展提供了极大的灵活性。
4.4 三层防线的协作关系
| ┌─ prepareTurn 钩子 ──────────────────────────┐ │ └── 超过阈值 → 触发 Compaction │ │ ├── Basic:算法裁剪(快,无成本) │ │ └── Agentic:LLM 摘要(慢,花费 Token) │ └───────────────────────────────────────────────┘ ┌─ MessageBuilder.buildForApi() ──────────────┐ │ ├── 过期文件内容 → "[outdated]" 占位 │ │ ├── 图片预算 → 限制 base64 总量 │ └───────────────────────────────────────────────┘ |
五、记忆系统:ConversationStore + Checkpoint
5.1 会话记忆:ConversationStore
文件:sdk/packages/core/src/session/stores/conversation-store.ts
| exportclassConversationStore {privatemessages: MessageWithMetadata[] = [];private conversationId = createConversationId();private sessionStarted = false;getConversationId(): stringgetMessages(): MessageWithMetadata[]appendMessage(message): voidappendMessages(messages): voidreplaceMessages(messages): void// 压缩后整体替换resetForRun(): void// 清空 + 新 conversationIdclearHistory(): void// 同上restore(messages): void// 从持久化恢复isSessionStarted(): booleanmarkSessionStarted(): void |
设计特点:
- 纯内存消息列表:没有向量数据库,没有 embedding 索引,所有"记忆"就是完整的对话历史
- sessionStarted 门控:用于判断是否应该触发 session_start hooks(只在新会话首次调用模型时触发一次)
- conversationId 隔离:每次 resetForRun() 生成新的 ID,确保不同任务的历史不会混淆
5.2 持久化:SQLite 存储
会话元数据存储在 SQLite 数据库中(CoreSessionService):
| // session/models/session-row.ts session_id TEXTPRIMARYKEY, source TEXT, // "cli" | "vscode" | "jetbrains" | "sdk" status TEXT, // "running" | "stopped" | "failed" provider TEXT, // "anthropic" | "openai" | ... metadata_json TEXT, // checkpoint 历史等 messages_path TEXT, // 消息文件路径 |
消息内容以 JSONL 格式存储在独立文件中(messages_path),与 SQLite 元数据分离,便于大消息量的高效读写。
5.3 Checkpoint 回滚:基于 Git 的物理文件快照
文件:sdk/packages/core/src/session/checkpoint-restore.ts
Checkpoint 数据结构
| interfaceCheckpointEntry {ref: string; // Git commit hash 或 stash refcreatedAt: number; // 创建时间戳runCount: number; // 对应的用户轮次(第几次用户输入) kind?: "commit" | "stash"; // 存储方式 |
Checkpoint 历史存储在 session 的 metadata.checkpoint.history 中。
恢复流程
| exportasyncfunctionapplyCheckpointToWorktree(cwd, checkpoint) {awaitexecFile("git", ["-C", cwd, "rev-parse", "--is-inside-work-tree"]);// 2. 验证 checkpoint ref 存在且有效awaitexecFile("git", ["-C", cwd, "cat-file", "-e", `${checkpoint.ref}^{commit}`]);awaitexecFile("git", ["-C", cwd, "reset", "--hard"]);awaitexecFile("git", ["-C", cwd, "clean", "-fd"]);if (checkpoint.kind === "commit") {awaitexecFile("git", ["-C", cwd, "reset", "--hard", checkpoint.ref]);awaitexecFile("git", ["-C", cwd, "stash", "apply", checkpoint.ref]); |
消息回滚
恢复时不仅回滚文件,还要回滚对话历史到对应的用户轮次:
| exportfunctiontrimMessagesToCheckpoint(messages, runCount) {const index = findCheckpointMessageIndex(messages, runCount);return messages.slice(0, index + 1); // 保留到该轮次functionfindCheckpointMessageIndex(messages, runCount) {for (let index = 0; index < messages.length; index++) {const message = messages[index];if (message?.role !== "user") continue;// 跳过 recovery_notice 类型的系统注入消息if (metadata?.kind === "recovery_notice") continue;if (userRunCount === runCount) return index;thrownewError(`Could not find user message for checkpoint run ${runCount}`); |
两种 Checkpoint 恢复模式
| 模式 | 文件操作 | 消息操作 | 使用场景 |
|---|
| Restore Workspace Only | applyCheckpointToWorktree() | 不截断 | 代码写错但还想继续对话 | | Restore Task + Workspace | applyCheckpointToWorktree() | trimMessagesToCheckpoint() | 整个方向走错,需要全部重来 |
|
六、错误自愈:MistakeTracker + LoopDetectionTracker
Cline 有两套独立的错误追踪机制,分别应对不同类型的"AI 犯错"。
6.1 LoopDetectionTracker — 重复工具调用检测
文件:sdk/packages/core/src/runtime/safety/loop-detection.ts
核心思路
检测模型是否在用相同的参数反复调用同一个工具(死循环的典型特征)。
| softThreshold: 3, // 连续 3 次相同调用 → 软警告hardThreshold: 5, // 连续 5 次相同调用 → 强制停止inspect(call): LoopDetectionVerdict {// 对工具输入进行规范化(排序 key 后 JSON 序列化)const signature = toolCallSignature(call.input);if (toolName === lastToolName && signature === lastToolSignature) { consecutiveIdenticalCount++; consecutiveIdenticalCount = 1; // 不同调用,重置计数if (consecutiveIdenticalCount >= hardThreshold) {return { kind: "hard", message: `Detected ${count} consecutive identical calls...` };if (consecutiveIdenticalCount === softThreshold) {return { kind: "soft", message: `Consider trying a different approach.` }; |
签名计算
| functiontoolCallSignature(input: unknown): string {if (input == null) return"null";if (typeof input === "string") return input;if (typeof input !== "object") returnString(input);// 递归排序 key,确保 {a:1, b:2} 和 {b:2, a:1} 产生相同签名returnJSON.stringify(sortKeys(input)); |
集成方式
LoopDetectionTracker 作为 beforeTool hook 安装到 AgentRuntime 中:
| 工具调用 → beforeTool hook → LoopDetectionTracker.inspect() ├── "soft" → 注入建议消息到对话中("考虑换个方法"),继续执行 └── "hard" → 跳过执行,返回错误给模型("检测到死循环,已停止") |
6.2 MistakeTracker — 连续错误计数
文件:sdk/packages/core/src/runtime/safety/mistake-tracker.ts
核心思路
追踪连续失败的次数,达到上限时可选择停止或注入恢复引导。
| private consecutiveMistakes = 0;asyncrecord(input: RecordMistakeInput): Promise<MistakeOutcome> {const max = this.options.maxConsecutiveMistakes;const next = input.forceAtLimit ? max : this.consecutiveMistakes + 1;this.consecutiveMistakes = next;return { action: "continue" };const decision = awaitresolveConsecutiveMistakeDecision({ iteration, consecutiveMistakes: next,maxConsecutiveMistakes: max, reason, details }, this.options.onLimitReached);if (decision.action === "continue") {const guidance = decision.guidance?.trim();this.options.appendRecoveryNotice(guidance, reason);this.consecutiveMistakes = 0; // 重置计数,给模型一次机会return { action: "continue", guidance };message: buildMistakeLimitStopMessage({ ... })this.consecutiveMistakes = 0; |
错误类型
| 类型 | 触发场景 | 典型原因 |
|---|
| api_error | LLM API 调用失败 | 网络超时、限流、密钥过期 | | invalid_tool_call | 模型生成无法解析的工具调用 | | | tool_execution_failed | 工具执行过程中抛出异常 | |
|
停止消息格式
| functionbuildMistakeLimitStopMessage(input) {`Stopped after ${consecutiveMistakes}/${maxConsecutiveMistakes} consecutive mistakes (${reason}) at iteration ${iteration}.`,if (details) parts.push(`Error: ${details}`);if (stopReason) parts.push(`Decision: ${stopReason}`); parts.push("Session state was preserved. Send a new prompt to resume from the latest state."); |
6.3 两套机制的协作
| LoopDetectionTracker(beforeTool hook) ├── 相同调用 3 次 → 软警告(注入建议消息) ├── 成功 → MistakeTracker.reset() └── 失败 → MistakeTracker.record() ├── 未达上限 → 继续(错误作为 Observe 回传给模型) |
七、工具定义:实际的工具列表
从源码 definitions.ts 中提取的 Cline 实际工具列表(新版命名):
| 工具名 | 功能 | Plan Mode | Act Mode | YOLO Mode |
|---|
| read_files | | ✅ | ✅ | ✅ | | search_codebase | | ✅ | ✅ | ❌ | | run_commands | | ✅ | ✅ | ✅ | | fetch_web_content | 抓取网页内容并分析 | ✅ | ✅ | ❌ | | editor | 创建/修改文件(合并了 write + replace) | ❌ | ✅ | ✅ | | skills | | ✅ | ✅ | ❌ | | ask_question | | ✅ | ✅ | ❌ | | submit_and_exit | 声明任务完成(终止工具) | ❌ | ❌ | ✅ |
|
工具名兼容映射
旧版工具名通过 CONFIGURED_AGENT_TOOL_NAME_ALIASES 映射到新版:
| constCONFIGURED_AGENT_TOOL_NAME_ALIASES = {attempt_completion: "submit_and_exit",execute_command: "run_commands",list_code_definition_names: "search_codebase",list_files: "run_commands", // list_files 实际通过 run_commands 执行 lsreplace_in_file: "editor",search_files: "search_codebase", |
工具的策略过滤
在 DefaultRuntimeBuilder.build() 中,工具还要经过策略过滤:
| functionfilterToolsByPolicies(tools, toolPolicies) {return tools.filter(tool => {const globalPolicy = toolPolicies?.["*"] ?? {};const toolPolicy = toolPolicies?.[tool.name] ?? {};return { ...globalPolicy, ...toolPolicy }.enabled !== false;functionfilterDisabledTools(tools) {// 通过环境变量 CLINE_DISABLED_TOOLS 全局禁用某些工具 |
八、源码分析小结
核心机制速查表
| 机制 | 源码位置 | 核心思路 |
|---|
| ReAct 循环 | agents/src/agent-runtime.ts | while 循环 + 工具调用驱动,maxIterations 防无限 | | Plan Mode | extensions/tools/presets.ts | 物理移除 editor 工具,模型看不到写入类工具 | | 工具审批 | agent-runtime.ts | 3 层链:输入验证 → beforeTool hooks → ToolPolicy | | 消息截断 | session/services/message-builder.ts | | | 上下文压缩 | extensions/context/compaction.ts | Basic(算法裁剪)+ Agentic(LLM 摘要) | | 会话记忆 | session/stores/conversation-store.ts | | | Checkpoint | session/checkpoint-restore.ts | Git commit/stash + 消息截断到对应轮次 | | 循环检测 | runtime/safety/loop-detection.ts | | | 错误追踪 | runtime/safety/mistake-tracker.ts | | | 完成策略 | agent-runtime.ts | 强制 submit_and_exit + 团队任务守卫 |
|
架构全景图
数据来源:本文所有代码引用均来自 Cline GitHub 仓库 的 sdk/packages/ 目录,克隆时间为 2026 年 6 月 24 日。具体文件路径已在各节中标注。
参考资料:
- Cline GitHub 仓库:https://github.com/cline/cline
- Cline SDK 文档:https://docs.cline.bot/cline-sdk/overview
- Cline 核心源码目录:sdk/packages/core/src/
- Agent 引擎源码:sdk/packages/agents/src/agent-runtime.ts