Skip to content

循环检测

问题:无限循环

Agent 可能陷入无限循环:

Agent: [读取文件] → 文件不存在
Agent: [创建文件] → 创建失败(权限不足)
Agent: [读取文件] → 文件不存在
Agent: [创建文件] → 创建失败
... 无限重复 ...

如果不检测和中断,Agent 会一直循环下去,消耗资源和费用。

检测策略

1. 最大轮次限制

最简单的方法:

typescript
const MAX_TURNS = 50

async function agentLoop(userMessage: string) {
  let turns = 0

  while (turns < MAX_TURNS) {
    turns++
    // ... 循环逻辑
  }

  return '达到最大轮次限制,请尝试简化任务'
}

2. 重复模式检测

检测是否在重复相同的操作:

typescript
class LoopDetector {
  private recentActions: string[] = []
  private windowSize = 10
  private threshold = 3

  addAction(action: string) {
    this.recentActions.push(action)
    if (this.recentActions.length > this.windowSize) {
      this.recentActions.shift()
    }
  }

  isLooping(): boolean {
    // 检查最近的操作是否有重复模式
    const actionCounts = new Map<string, number>()

    for (const action of this.recentActions) {
      const count = (actionCounts.get(action) || 0) + 1
      actionCounts.set(action, count)

      if (count >= this.threshold) {
        return true // 同一操作重复太多次
      }
    }

    return false
  }
}

3. 错误累积检测

连续错误可能表示陷入困境:

typescript
class ErrorTracker {
  private consecutiveErrors = 0
  private maxConsecutiveErrors = 5

  recordResult(success: boolean) {
    if (success) {
      this.consecutiveErrors = 0
    } else {
      this.consecutiveErrors++
    }
  }

  shouldStop(): boolean {
    return this.consecutiveErrors >= this.maxConsecutiveErrors
  }
}

gemini-cli 的实现

源码位置packages/core/src/services/loopDetection.ts

gemini-cli 使用多种策略组合:

typescript
class LoopDetectionService {
  private turnCount = 0
  private recentToolCalls: ToolCall[] = []
  private errorCount = 0

  onTurnComplete(turn: Turn) {
    this.turnCount++

    // 记录工具调用
    for (const event of turn.events) {
      if (event.type === 'ToolCallRequest') {
        this.recentToolCalls.push(event)
      }
      if (event.type === 'Error') {
        this.errorCount++
      }
    }

    // 保持滑动窗口
    if (this.recentToolCalls.length > 20) {
      this.recentToolCalls = this.recentToolCalls.slice(-20)
    }
  }

  detectLoop(): LoopDetectionResult {
    // 检查最大轮次
    if (this.turnCount > 100) {
      return { detected: true, reason: 'max_turns' }
    }

    // 检查连续错误
    if (this.errorCount > 10) {
      return { detected: true, reason: 'too_many_errors' }
    }

    // 检查重复模式
    if (this.hasRepeatingPattern()) {
      return { detected: true, reason: 'repeating_pattern' }
    }

    return { detected: false }
  }

  private hasRepeatingPattern(): boolean {
    if (this.recentToolCalls.length < 6) return false

    // 获取最近 3 次调用
    const recent = this.recentToolCalls.slice(-3)
    const before = this.recentToolCalls.slice(-6, -3)

    // 检查是否完全相同
    return JSON.stringify(recent) === JSON.stringify(before)
  }
}

循环处理策略

检测到循环后怎么办?

1. 通知 LLM

typescript
if (loopDetector.isLooping()) {
  messages.push({
    role: 'system',
    content: `警告:检测到你在重复相同的操作。
请尝试不同的方法,或者告诉用户当前遇到的困难。`
  })
}

2. 中断并报告

typescript
if (loopDetector.isLooping()) {
  return {
    status: 'loop_detected',
    message: '检测到循环,已自动中断。',
    lastActions: loopDetector.getRecentActions()
  }
}

3. 请求用户干预

typescript
if (loopDetector.isLooping()) {
  const userResponse = await askUser(
    '检测到可能的循环,是否继续?',
    ['继续', '中断', '换个方法']
  )

  switch (userResponse) {
    case '继续':
      loopDetector.reset()
      break
    case '中断':
      return '用户中断操作'
    case '换个方法':
      messages.push({
        role: 'system',
        content: '用户要求尝试其他方法'
      })
      break
  }
}

完整实现示例

typescript
class Agent {
  private loopDetector = new LoopDetector()
  private maxTurns = 50

  async run(userMessage: string) {
    const messages = [{ role: 'user', content: userMessage }]
    let turns = 0

    while (turns < this.maxTurns) {
      turns++

      // 调用 LLM
      const response = await this.llm.chat(messages)
      const toolCalls = response.functionCalls()

      if (!toolCalls?.length) {
        return response.text()
      }

      // 记录并检测循环
      for (const call of toolCalls) {
        this.loopDetector.addAction(`${call.name}:${JSON.stringify(call.args)}`)
      }

      const loopResult = this.loopDetector.detect()
      if (loopResult.detected) {
        // 尝试让 LLM 意识到问题
        messages.push({
          role: 'system',
          content: `检测到循环(原因:${loopResult.reason})。请尝试不同的方法或说明遇到的问题。`
        })

        // 给 LLM 一次机会调整
        const retryResponse = await this.llm.chat(messages)
        if (retryResponse.functionCalls()?.length) {
          // 仍然在调用工具,强制中断
          return `任务中断:检测到循环。最后尝试的操作:${toolCalls.map(c => c.name).join(', ')}`
        }
        return retryResponse.text()
      }

      // 正常执行工具
      for (const call of toolCalls) {
        const result = await this.executeTool(call)
        messages.push({
          role: 'function',
          name: call.name,
          content: JSON.stringify(result)
        })
      }
    }

    return `达到最大轮次限制 (${this.maxTurns})`
  }
}

小结

  • 无限循环是 Agent 的常见问题
  • 检测方法:最大轮次、重复模式、错误累积
  • 检测到循环后可以通知 LLM、中断、或请求用户干预
  • gemini-cli 使用多策略组合检测

下一步

Agent 循环讲完了,接下来走读 gemini-cli 源码:项目结构 →

通过实际源码学习 AI Agent 开发