跳转到内容

01 Agent 核心循环与循环检测

学习目标

本章要解决什么问题:Cline 的 Agent 循环如何保证不会无限空转、不会陷入重复调用?当 LLM 卡住或网络失败时,循环如何恢复?

前置知识

本章涉及 Agent 核心循环的通用原理,建议先阅读:

下文假设你已理解 Session/Turn 模型和事件驱动架构,直接聚焦 Cline 的具体实现。


项目实践

核心循环结构

Cline 的 Agent 循环实现在 Task 类中,采用 while (!abort) 结构。调用链:

initiateTaskLoop()
└── while (!abort)
└── recursivelyMakeClineRequests()
├── attemptApiRequest() → 流式调用 LLM
└── presentAssistantMessage() → 解析并执行工具调用

关键设计:每次 recursivelyMakeClineRequests 返回后,循环判断是否继续。如果模型调用了工具但未调用 attempt_completion(完成信号),循环自动进入下一轮,用 noToolsUsed 提示推动模型继续。

noToolsUsed 推动机制

当模型既不调用工具也不完成任务时,Cline 不会等待用户介入,而是自动注入引导提示词:

# 伪代码:推动逻辑
if (parsedMessage.toolCalls.length === 0 && !message.isComplete) {
systemPrompt += "你必须调用工具来推进任务..."
continue // 进入下一轮循环
}

这保证了 Agent 不会因为模型”只说不动”而停滞。

循环检测(loop-detection.ts)

Cline 使用两级阈值检测模型是否陷入重复调用:

检测维度

  • 工具名称完全匹配
  • 参数 SHA-256 哈希匹配
  • 连续调用计数(非累计)

升级策略

  1. 达到软阈值(3 次):向下一轮请求注入”你正在重复调用同一工具,请改变策略”
  2. 达到硬阈值(5 次):调用 abortTask() 终止,向用户报告循环详情

流式失败重试

attemptApiRequest 中的重试策略:

退避间隔选择:2s 起步平衡了快速恢复和避免 API 限流,8s 上限避免了用户体验过差。


问题与规避

陷阱场景对策
noToolsUsed 误触发模型输出纯文本但意图是继续(如思考下一步)仅在确认无工具调用且任务未完成时注入
循环检测误杀合理的重试(如网络超时重连)被判定为循环软阈值给模型自我纠正机会,仅硬阈值强制终止
退避时间过长连续重试导致用户等待 >14s3 次自动重试后降级到用户决策,不无限重试

设计取舍

自动推动 vs 等待用户

方案优势代价
自动推动流畅、不中断、模型自主性高可能产生意外行为
等待用户绝对可控体验差、效率低

Cline 选择自动推动 + 循环检测兜底:先尝试让模型自我纠正,检测到持续循环后强制终止。

两级阈值 vs 单级阈值

方案优势代价
单级(直接终止)实现简单误杀合理重试
两级(警告 + 终止)给模型纠正机会多消耗 Token

Cline 选择两级,因为 3 次重复可能是合理重试,但 5 次几乎可以确认是无效循环。


参考来源

  • Cline 源码:apps/vscode/src/core/task/index.ts
  • Cline 源码:apps/vscode/src/core/task/loop-detection.ts