循环检测
问题:无限循环
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 源码:项目结构 →