引言
Agent系统最令人头疼的故障模式之一就是无限循环——Agent反复调用同一个工具、在两个状态间来回切换、或者陷入"思考但不行动"的死循环。这类问题不仅浪费Token和计算资源,还可能导致用户长时间等待无响应。
2026年,随着Agent自主能力的增强(如AutoGPT式的自主规划),循环检测和超时控制变得更加关键。一个能够自主决策的Agent,如果不能有效检测和打破循环,其危害性远大于传统软件的死循环。
循环类型分析
Agent系统中的四种典型循环
类型1:工具调用循环 类型2:状态转移循环
┌─────────────────┐ ┌──────────────────┐
│ Agent ──▶ Tool A│ │ State A ──▶State B│
│ ▲ │ │ │ ▲ │ │
│ └──────┘ │ │ └─────────┘ │
│ (反复调用同一工具)│ │ (状态来回切换) │
└─────────────────┘ └──────────────────┘
类型3:推理循环 类型4:工具链循环
┌─────────────────┐ ┌──────────────────┐
│ Think ──▶ Think │ │ Tool A ──▶Tool B │
│ ▲ │ │ │ ▲ │ │
│ └─────────┘ │ │ └─────────┘ │
│ (反复思考不行动) │ │ (工具间互相触发) │
└─────────────────┘ └──────────────────┘
循环检测算法
基于状态指纹的检测
import hashlib
from collections import defaultdict
from dataclasses import dataclass
@dataclass
class CycleDetector:
"""基于状态指纹的循环检测器"""
max_history: int = 50 # 保留最近50步
cycle_threshold: int = 3 # 重复出现3次判定为循环
def __init__(self):
self.state_history: list = []
self.fingerprint_counts: dict = defaultdict(int)
def record_state(self, state: dict) -> bool:
"""记录状态,返回是否检测到循环"""
# 生成状态指纹
fingerprint = self._generate_fingerprint(state)
self.state_history.append({
"fingerprint": fingerprint,
"state": state,
"timestamp": datetime.now()
})
# 限制历史长度
if len(self.state_history) > self.max_history:
old = self.state_history.pop(0)
self.fingerprint_counts[old["fingerprint"]] -= 1
self.fingerprint_counts[fingerprint] += 1
# 检测循环
if self.fingerprint_counts[fingerprint] >= self.cycle_threshold:
return True
# 检测模式循环(A-B-A-B模式)
if self._detect_pattern_cycle():
return True
return False
def _generate_fingerprint(self, state: dict) -> str:
"""生成状态指纹"""
# 提取关键状态信息
key_info = {
"intent": state.get("intent"),
"active_tool": state.get("active_tool"),
"tool_params_hash": hashlib.md5(
json.dumps(state.get("tool_params", {}), sort_keys=True).encode()
).hexdigest()[:8],
"fsm_state": state.get("fsm_state"),
"pending_actions": state.get("pending_actions", [])
}
fingerprint_str = json.dumps(key_info, sort_keys=True)
return hashlib.md5(fingerprint_str.encode()).hexdigest()
def _detect_pattern_cycle(self) -> bool:
"""检测模式循环(如A-B-A-B)"""
if len(self.state_history) < 4:
return False
# 检查最近的状态是否形成周期模式
recent = [s["fingerprint"] for s in self.state_history[-6:]]
# 尝试不同的周期长度
for period in range(2, 4):
if len(recent) >= period * 2:
pattern = recent[-period:]
previous = recent[-period*2:-period]
if pattern == previous:
return True
return False
基于行为序列的检测
class BehaviorSequenceAnalyzer:
"""基于行为序列的循环检测"""
def __init__(self):
self.action_sequences = []
def analyze(self, actions: list) -> dict:
"""分析行为序列"""
result = {
"has_cycle": False,
"cycle_type": None,
"cycle_length": 0,
"suggestion": None
}
# 使用Floyd算法检测环
cycle = self._floyd_cycle_detection(actions)
if cycle:
result["has_cycle"] = True
result["cycle_length"] = len(cycle)
result["cycle_type"] = self._classify_cycle(cycle)
result["suggestion"] = self._suggest_break_strategy(cycle)
return result
def _floyd_cycle_detection(self, sequence: list) -> list:
"""Floyd环检测算法"""
if len(sequence) < 2:
return None
# 快慢指针
slow = 0
fast = 0
while True:
slow = (slow + 1) % len(sequence)
fast = (fast + 2) % len(sequence)
if sequence[slow] == sequence[fast]:
# 找到环,确定环的起始和长度
start = 0
ptr1 = start
ptr2 = slow
while ptr1 != ptr2:
ptr1 = (ptr1 + 1) % len(sequence)
ptr2 = (ptr2 + 1) % len(sequence)
# 提取环
cycle_start = ptr1
cycle = [sequence[cycle_start]]
ptr = (cycle_start + 1) % len(sequence)
while ptr != cycle_start:
cycle.append(sequence[ptr])
ptr = (ptr + 1) % len(sequence)
return cycle
if fast == 0:
return None # 无环
def _classify_cycle(self, cycle: list) -> str:
"""分类循环类型"""
tools_in_cycle = [a for a in cycle if a.get("type") == "tool_call"]
thoughts_in_cycle = [a for a in cycle if a.get("type") == "thought"]
if len(tools_in_cycle) == len(cycle):
return "tool_cycle"
elif len(thoughts_in_cycle) == len(cycle):
return "reasoning_cycle"
else:
return "mixed_cycle"
def _suggest_break_strategy(self, cycle: list) -> str:
"""建议打破循环的策略"""
cycle_type = self._classify_cycle(cycle)
strategies = {
"tool_cycle": "尝试用不同参数调用工具,或切换到替代工具",
"reasoning_cycle": "强制进入执行阶段,或向用户请求澄清",
"mixed_cycle": "重置上下文窗口,或回退到上一个成功状态"
}
return strategies.get(cycle_type, "终止当前任务并重试")
多级超时控制
class MultiLevelTimeout:
"""多级超时控制体系"""
LEVELS = {
"tool_call": {
"default": 30, # 单次工具调用
"search": 15, # 搜索类工具
"code_exec": 60, # 代码执行
"file_io": 10, # 文件操作
},
"step": {
"understanding": 10, # 意图理解
"planning": 15, # 规划
"retrieving": 10, # 检索
"executing": 120, # 工具执行
"generating": 30, # 响应生成
},
"session": {
"max_duration": 600, # 单次会话最大10分钟
"idle_timeout": 120, # 空闲2分钟超时
},
"workflow": {
"max_steps": 50, # 工作流最大步骤数
"max_tool_calls": 20, # 最大工具调用次数
"max_tokens": 100000, # 最大Token消耗
}
}
def __init__(self):
self.active_timers = {}
async def with_timeout(
self,
level: str,
operation: str,
coro: asyncio.coroutines
):
"""带超时执行协程"""
timeout = self.LEVELS.get(level, {}).get(operation, 30)
try:
result = await asyncio.wait_for(coro, timeout=timeout)
return result
except asyncio.TimeoutError:
logger.warning(
f"Timeout at {level}.{operation} after {timeout}s"
)
await self._handle_timeout(level, operation)
raise
async def _handle_timeout(self, level: str, operation: str):
"""超时处理策略"""
if level == "tool_call":
# 记录工具超时,可能降级
await self._record_tool_timeout(operation)
elif level == "step":
# 步骤超时,尝试跳过或降级
await self._try_degrade_step(operation)
elif level == "session":
# 会话超时,优雅终止
await self._graceful_session_end()
elif level == "workflow":
# 工作流超限,强制终止
await self._force_terminate()
循环打破与自恢复
class CycleBreaker:
"""循环打破器"""
def __init__(self, cycle_detector, timeout_controller):
self.detector = cycle_detector
self.timeout = timeout_controller
async def monitor_and_break(
self,
agent_session,
check_interval: float = 1.0
):
"""持续监控并打破循环"""
while not agent_session.is_complete():
current_state = agent_session.get_state()
if self.detector.record_state(current_state):
logger.warning("Cycle detected, initiating break sequence")
await self._break_cycle(agent_session)
return True
await asyncio.sleep(check_interval)
return False
async def _break_cycle(self, agent_session):
"""执行循环打破策略"""
# 策略1:注入扰动——修改Agent的上下文
perturbation = {
"system_message": (
"你似乎陷入了循环。请尝试不同的方法,"
"或者明确说明你无法完成此任务。"
),
"temperature_boost": 0.3, # 提高温度增加随机性
"disable_repeated_tool": True # 禁用循环工具
}
await agent_session.inject_perturbation(perturbation)
# 策略2:重置到上一个健康状态
healthy_state = agent_session.get_last_healthy_state()
if healthy_state:
await agent_session.restore_state(healthy_state)
# 策略3:降级为简化流程
await agent_session.switch_to_degraded_mode(
simplified_tools=True,
max_steps=5
)
# 策略4:最终兜底——请求人工介入
if not await agent_session.try_recover():
await agent_session.escalate_to_human(
reason="无法自动打破循环",
context=agent_session.get_debug_context()
)
生产实践:超时配置矩阵
| 场景 | 工具超时 | 步骤超时 | 会话超时 | 最大步数 | 最大Token |
|---|---|---|---|---|---|
| 简单问答 | 10s | 15s | 60s | 5 | 5K |
| 工具调用 | 30s | 120s | 300s | 15 | 30K |
| 复杂分析 | 60s | 300s | 600s | 30 | 80K |
| 自主任务 | 120s | 600s | 1800s | 50 | 200K |
| 批处理 | 300s | 1800s | 7200s | 100 | 500K |
监控与告警
class CycleMonitor:
"""循环监控器"""
async def collect_metrics(self) -> dict:
return {
"cycle_detected_rate": await self._get_rate("cycle_detected"),
"timeout_rate_by_level": {
"tool": await self._get_rate("timeout.tool_call"),
"step": await self._get_rate("timeout.step"),
"session": await self._get_rate("timeout.session"),
},
"avg_steps_to_cycle": await self._get_avg("steps_before_cycle"),
"break_success_rate": await self._get_rate("cycle_break_success"),
"human_escalation_rate": await self._get_rate("human_escalation"),
}
总结
循环检测和超时控制是Agent系统鲁棒性的基石。基于状态指纹的检测能够高效识别精确循环,基于行为序列的分析能够发现模式循环。多级超时体系确保任何级别的异常都有对应的兜底机制。当检测到循环时,系统应按照"注入扰动→重置状态→降级模式→人工介入"的顺序尝试恢复。
核心原则:Agent系统必须假设循环会发生,并为此做好充分的检测和恢复准备。与其追求"不产生循环"的完美设计,不如建立"快速检测、有效打破、优雅恢复"的鲁棒机制。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
