Agent Loop是什么?为什么?怎么做?

0 阅读20分钟

从"单次推理"到"循环交互"

大语言模型(LLM)的原始形态是单次推理系统(One-shot Inference):输入Prompt,输出Completion,对话结束。这种"问答式"交互在面对复杂任务时显得力不从心——当问题需要多步骤验证、工具调用或环境反馈时,静态的输入-输出映射无法构建持续的问题解决能力。

Agent Loop(智能体循环)正是为解决这一局限而诞生的核心架构范式。它定义了一个持续运转的控制循环:感知(Perception) → 推理(Reasoning) → 决策(Decision) → 行动(Action) → 反馈(Feedback)。每一次循环迭代都将环境反馈纳入下一轮推理的上下文,形成闭环的认知-行动系统。

AI Agent Core Loop

OODA循环与冯·诺依曼架构

Agent Loop的设计哲学深深植根于认知科学与控制论的经典理论:

观察-调整-决策-行动循环 (OODA Loop, Boyd, 1976) 最初由美国空军上校约翰·博伊德提出,用于描述战斗机飞行员的决策过程。其核心洞察是:在动态对抗环境中,决策速度取决于完成"观察(Observe)-调整(Orient)-决策(Decide)-行动(Act)"循环的速度。OODA循环强调反馈驱动迭代适应,而非预设的线性计划。

冯·诺依曼架构 (von Neumann Architecture, 1945) 的"存储程序"概念为Agent Loop提供了工程实现基础:状态(内存)与计算(处理器)的分离,使得循环可以在不同状态间跳转,同时保留历史上下文。现代Agent框架中的StateGraph(如LangGraph的实现)正是这一思想的延续——节点代表计算,边代表状态转移,循环路径实现持续推理。

同步Loop vs 异步Event-Driven Loop

在工程实现层面,Agent Loop存在两种核心架构模式:

同步Loop采用阻塞式执行:每一轮循环等待前一步骤完全完成后才继续。其优势在于实现简单、状态一致性强,适合单线程的ReAct式推理。伪代码结构通常为:

while not done:
    thought = llm.reason(context)
    action = llm.decide(thought)
    observation = environment.execute(action)
    context.append(observation)

异步Event-Driven Loop则基于事件总线架构:感知、推理、行动作为独立服务,通过消息队列松耦合连接。当传感器数据到达时触发感知节点,推理节点订阅感知事件,行动节点响应决策事件。这种模式支持高并发、容错恢复和分布式部署,但引入了状态一致性管理的复杂度。AutoGen的Actor模型和CrewAI的Flow编排均体现了这一思路。

架构选型取决于场景需求:低延迟单任务场景适合同步Loop;多Agent协作、长周期任务或需要人工介入审批的流程,则应采用异步Event-Driven架构。

二、Loop的核心状态机设计

Agent Loop的工程实现必须建立在严格的状态机之上。状态机不仅定义了Loop的合法状态空间,更通过守卫条件确保状态转换的安全性与可预测性。

状态定义与语义

一个完整的Agent Loop状态机包含以下核心状态:

  • IDLE:等待触发条件,Loop未激活
  • PERCEIVING:接收并解析环境输入,构建内部数据结构
  • REASONING:基于当前上下文进行认知处理
  • ACTING:执行外部动作(工具调用、API请求等)
  • WAITING_FOR_OBSERVATION:动作执行后等待环境反馈
  • TERMINATED:目标达成或异常终止,Loop结束

状态转换条件与守卫

状态转换不是自动的,必须满足守卫条件

当前状态目标状态守卫条件
IDLEPERCEIVING新任务到达或用户输入触发
PERCEIVINGREASONING感知数据完整性校验通过
REASONINGACTING决策输出包含可执行动作
REASONINGTERMINATEDLLM输出"Final Answer"标记
ACTINGWAITING_FOR_OBSERVATION动作已提交至执行器
WAITING_FOR_OBSERVATIONREASONING环境反馈数据到达且有效
WAITING_FOR_OBSERVATIONTERMINATED超时或连续失败达到阈值
任意TERMINATED达到最大迭代次数或人工中断

状态机核心实现(伪代码)

from enum import Enum, auto
from typing import Optional, Dict, Any
from dataclasses import dataclass

class AgentState(Enum):
    IDLE = auto()
    PERCEIVING = auto()
    REASONING = auto()
    ACTING = auto()
    WAITING_FOR_OBSERVATION = auto()
    TERMINATED = auto()

@dataclass
class LoopContext:
    iteration_count: int = 0
    max_iterations: int = 10
    history: list = None
    memory: Dict[str, Any] = None
    
    def __post_init__(self):
        if self.history is None:
            self.history = []
        if self.memory is None:
            self.memory = {}

class AgentLoopFSM:
    """Agent Loop有限状态机核心实现"""
    
    def __init__(self, llm_engine, tool_registry, max_iter=10):
        self.state = AgentState.IDLE
        self.llm = llm_engine
        self.tools = tool_registry
        self.context = LoopContext(max_iterations=max_iter)
        self._transition_map = {
            AgentState.IDLE: [AgentState.PERCEIVING, AgentState.TERMINATED],
            AgentState.PERCEIVING: [AgentState.REASONING, AgentState.TERMINATED],
            AgentState.REASONING: [AgentState.ACTING, AgentState.TERMINATED],
            AgentState.ACTING: [AgentState.WAITING_FOR_OBSERVATION, AgentState.TERMINATED],
            AgentState.WAITING_FOR_OBSERVATION: [AgentState.REASONING, AgentState.TERMINATED],
        }
    
    def _can_transition(self, from_state: AgentState, to_state: AgentState) -> bool:
        """守卫条件检查"""
        if to_state not in self._transition_map.get(from_state, []):
            return False
        
        # 特定状态转换的额外守卫
        if from_state == AgentState.REASONING and to_state == AgentState.TERMINATED:
            return self._is_goal_achieved()
        
        if from_state == AgentState.WAITING_FOR_OBSERVATION:
            # 超时检查
            if self._is_observation_timeout():
                return to_state == AgentState.TERMINATED
            return to_state == AgentState.REASONING
        
        return True
    
    def _is_goal_achieved(self) -> bool:
        """目标达成检测:由LLM输出特定标记或启发式规则判断"""
        last_output = self.context.history[-1] if self.context.history else ""
        return "[FINAL_ANSWER]" in last_output or "[TASK_COMPLETE]" in last_output
    
    def _is_observation_timeout(self) -> bool:
        """观察等待超时检测"""
        # 实现具体的超时逻辑
        return False
    
    def transition_to(self, new_state: AgentState) -> bool:
        """执行状态转换"""
        if not self._can_transition(self.state, new_state):
            raise ValueError(f"非法状态转换: {self.state.name} -> {new_state.name}")
        
        print(f"[FSM] {self.state.name} -> {new_state.name}")
        self.state = new_state
        return True
    
    def run_cycle(self, user_input: str) -> str:
        """执行完整Loop周期"""
        # 初始化
        self.context = LoopContext(max_iterations=self.context.max_iterations)
        self.context.history.append({"role": "user", "content": user_input})
        self.transition_to(AgentState.PERCEIVING)
        
        while self.state != AgentState.TERMINATED:
            self.context.iteration_count += 1
            
            if self.context.iteration_count > self.context.max_iterations:
                print("[FSM] 达到最大迭代次数,强制终止")
                self.transition_to(AgentState.TERMINATED)
                break
            
            try:
                if self.state == AgentState.PERCEIVING:
                    self._do_perceive()
                    self.transition_to(AgentState.REASONING)
                
                elif self.state == AgentState.REASONING:
                    reasoning_output = self._do_reason()
                    if self._is_goal_achieved():
                        self.transition_to(AgentState.TERMINATED)
                        return reasoning_output
                    self.transition_to(AgentState.ACTING)
                
                elif self.state == AgentState.ACTING:
                    action_result = self._do_act()
                    self.context.last_action_result = action_result
                    self.transition_to(AgentState.WAITING_FOR_OBSERVATION)
                
                elif self.state == AgentState.WAITING_FOR_OBSERVATION:
                    observation = self._do_observe()
                    self.context.history.append({
                        "role": "observation", 
                        "content": observation
                    })
                    self.transition_to(AgentState.REASONING)
                    
            except Exception as e:
                print(f"[FSM] 状态 {self.state.name} 执行异常: {e}")
                self._handle_exception(e)
        
        return self._extract_final_answer()
    
    def _do_perceive(self):
        """感知阶段:输入解析与上下文构建"""
        # 整合用户输入、环境状态、记忆检索
        recent_memories = self._retrieve_relevant_memories()
        self.context.working_memory = {
            "input": self.context.history[0],
            "retrieved_memories": recent_memories
        }
    
    def _do_reason(self) -> str:
        """推理阶段:LLM认知处理"""
        prompt = self._build_reasoning_prompt()
        response = self.llm.generate(prompt, context=self.context.history)
        self.context.history.append({"role": "assistant", "content": response})
        return response
    
    def _do_act(self) -> Dict:
        """行动阶段:工具调用执行"""
        # 解析LLM输出中的Action指令
        action_cmd = self._parse_action(self.context.history[-1]["content"])
        if action_cmd:
            tool_name = action_cmd.get("tool")
            tool_args = action_cmd.get("args", {})
            result = self.tools.execute(tool_name, tool_args)
            return result
        return {"status": "no_action"}
    
    def _do_observe(self) -> str:
        """观察阶段:获取环境反馈"""
        return str(self.context.last_action_result)
    
    def _handle_exception(self, error: Exception):
        """异常处理与熔断逻辑"""
        # 具体实现见第五章
        self.transition_to(AgentState.TERMINATED)
    
    def _build_reasoning_prompt(self) -> str:
        """构建推理提示词"""
        return f"""基于以下上下文进行推理:
历史: {self.context.history}
记忆: {self.context.memory}
请输出思考过程,如需使用工具请按格式:Action: [工具名] Args: [参数]
如需结束请标记 [FINAL_ANSWER]"""
    
    def _parse_action(self, content: str) -> Optional[Dict]:
        """解析LLM输出中的动作指令"""
        # 实现具体的解析逻辑
        import re
        match = re.search(r'Action:\s*(\w+)\s*Args:\s*(.+)', content)
        if match:
            return {"tool": match.group(1), "args": match.group(2)}
        return None
    
    def _retrieve_relevant_memories(self) -> list:
        """检索相关记忆"""
        return []
    
    def _extract_final_answer(self) -> str:
        """提取最终答案"""
        for msg in reversed(self.context.history):
            if msg["role"] == "assistant":
                content = msg["content"]
                if "[FINAL_ANSWER]" in content:
                    return content.split("[FINAL_ANSWER]")[-1].strip()
                return content
        return ""

上述实现展示了状态机的核心机制:显式状态定义守卫条件检查异常安全转换。与简单的while循环不同,FSM强制要求每个状态转换都经过合法性验证,防止非法跳转导致的系统不稳定。

三、Perception-Action循环的两种范式

Agent Loop并非单一层次的循环,而是嵌套的双层结构:内循环(Inner Loop)处理单轮推理中的认知扩展,外循环(Outer Loop)管理多轮交互中的目标追踪与计划修正。这种分层设计借鉴了控制理论中的级联控制思想。

内循环

内循环发生在单次LLM调用内部,处理思维链(Chain of Thought, CoT)或思维树(Tree of Thoughts, ToT)的扩展。它不负责与环境交互,而是专注于内部认知空间的探索。

在CoT模式下,内循环表现为"思考→思考→...→结论"的线性序列;

在ToT模式下,则分支为多个候选推理路径,通过评估函数选择最优分支。

内循环的终止条件通常是:达到最大思考深度、生成可执行动作指令、或置信度超过阈值。

内循环的关键价值在于推理深度的弹性控制。通过调整内循环的迭代次数或分支因子,可以在推理质量与计算成本之间做权衡。

外循环

外循环是Agent与环境的真实交互边界,每轮迭代对应一次完整的"思考-行动-观察"周期。外循环维护目标栈计划状态,根据环境反馈动态调整策略。

外循环的核心职责包括:

  1. 目标追踪:维护当前目标与子目标的层次结构,检测目标达成或偏离
  2. 计划修正:当行动结果与预期不符时,触发重新规划(Replanning)
  3. 记忆更新:将本轮交互的关键信息写入长时记忆,供未来循环使用
  4. 上下文压缩:当历史记录超过上下文窗口时,执行摘要或剪枝

双循环的协同机制

内外循环通过工作记忆实现信息交换。内循环生成的推理结果写入工作记忆,成为外循环决策的输入;外循环获取的环境观察则触发新一轮内循环推理。

这种分层架构的优势在于关注点分离:内循环专注于"如何想",外循环专注于"何时做"。工程师可以独立优化推理策略(内循环)和交互策略(外循环),降低系统复杂度。

四、关键变体深度解析

Agent Loop的具体实现存在多种变体,每种变体针对特定场景优化了控制流结构。以下是三种最具代表性的范式:

ReAct Loop:推理与行动

ReAct (Reasoning + Acting, Yao et al., 2023) 是目前最广泛应用的Agent Loop范式。其核心创新是将推理轨迹行动指令显式交错,形成Thought → Action → Observation的循环模式。

ReAct的控制流特点:

  • 显式思考:LLM输出"Thought: ..."前缀的推理过程,增强可解释性
  • 即时反馈:每轮行动后立即获取Observation,纳入下一轮推理
  • 动态调整:无需预设计划,根据环境反馈实时调整策略

ReAct Agent Loop架构示意: ReAct Agent Loop

class ReActLoop(AgentLoopFSM):
    """ReAct Loop实现:Thought-Action-Observation循环"""
    
    def run_react_cycle(self, query: str) -> str:
        """ReAct专用执行流程"""
        self.state = AgentState.PERCEIVING
        self.context.history = [{"role": "user", "content": query}]
        
        while self.state != AgentState.TERMINATED and \
              self.context.iteration_count < self.context.max_iterations:
            
            self.context.iteration_count += 1
            
            # Phase 1: Thought
            self.transition_to(AgentState.REASONING)
            thought_prompt = self._build_react_prompt("thought")
            thought_output = self.llm.generate(thought_prompt)
            
            # 解析Thought输出
            if "[FINAL_ANSWER]" in thought_output:
                self.transition_to(AgentState.TERMINATED)
                return self._extract_answer(thought_output)
            
            self.context.history.append({
                "role": "thought", 
                "content": thought_output
            })
            
            # Phase 2: Action
            self.transition_to(AgentState.ACTING)
            action = self._parse_action_from_thought(thought_output)
            
            if not action:
                # 无明确动作,继续思考
                continue
            
            # 执行工具调用
            try:
                observation = self.tools.execute(
                    action["tool"], 
                    action["args"]
                )
            except Exception as e:
                observation = f"Error: {str(e)}"
            
            # Phase 3: Observation
            self.context.history.append({
                "role": "observation",
                "content": f"Action: {action['tool']}\nResult: {observation}"
            })
            self.transition_to(AgentState.WAITING_FOR_OBSERVATION)
            self.transition_to(AgentState.REASONING)  # 直接进入下一轮推理
        
        return self._extract_final_answer()
    
    def _build_react_prompt(self, phase: str) -> str:
        """构建ReAct格式提示词"""
        few_shot_examples = """
示例1:
Thought: 我需要查询天气信息。
Action: weather_api Args: {"city": "北京"}
Observation: 北京今日晴,25°C。

示例2:
Thought: 根据天气结果,建议用户带伞。
Action: [FINAL_ANSWER] Args: 建议带伞,可能有雨。
"""
        history_str = "\n".join([
            f"{msg['role'].upper()}: {msg['content']}" 
            for msg in self.context.history
        ])
        
        return f"""{few_shot_examples}
当前历史:
{history_str}
请继续输出Thought和Action(如需结束请使用[FINAL_ANSWER]):
Thought:"""
    
    def _parse_action_from_thought(self, thought: str) -> Optional[Dict]:
        """从Thought输出中解析Action"""
        import re
        # 匹配 Action: tool_name Args: {...}
        pattern = r'Action:\s*(\w+)\s*Args:\s*(\{.*?\}|[^\n]+)'
        match = re.search(pattern, thought, re.DOTALL)
        if match:
            tool = match.group(1)
            args_str = match.group(2)
            try:
                args = eval(args_str) if args_str.startswith('{') else {"query": args_str}
            except:
                args = {"raw": args_str}
            return {"tool": tool, "args": args}
        return None

ReAct的优势在于简单直接,适合信息收集、多步查询等任务。但其局限也明显:缺乏长期记忆积累,相同错误可能重复发生;无法从失败中系统性学习。

Reflexion Loop:自我反思的元认知层

Reflexion (Shinn et al., 2023) 在ReAct基础上引入了元认知层(Metacognitive Layer),通过"试错-反思-重试"的循环实现言语强化学习(Verbal Reinforcement Learning)。

Reflexion的核心组件:

  • Actor:执行ReAct循环的策略模型
  • Evaluator:评估轨迹质量的评判器(可以是LLM或启发式规则)
  • Self-Reflector:生成语言形式反思的反思模型
  • Episodic Memory:存储反思经验的记忆缓冲区

Reflexion与ReAct的关键差异在于控制流:

class ReflexionLoop(ReActLoop):
    """Reflexion Loop实现:带反思的试错学习"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.reflection_memory = []  #  episodic memory存储反思
        self.max_trials = 3  # 最大尝试次数
        self.success_threshold = 0.8  # 成功阈值
    
    def run_reflexion_cycle(self, query: str) -> str:
        """Reflexion完整控制流"""
        trial = 0
        best_result = None
        best_score = 0.0
        
        while trial < self.max_trials:
            trial += 1
            print(f"\n[Reflexion] Trial {trial}/{self.max_trials}")
            
            # 执行ReAct轨迹
            trajectory = self._run_single_trial(query, trial)
            
            # 评估轨迹质量
            score = self._evaluate_trajectory(trajectory)
            
            if score >= self.success_threshold:
                print(f"[Reflexion] 任务成功,得分: {score}")
                return trajectory["final_answer"]
            
            # 生成反思
            if trial < self.max_trials:  # 非最后一次尝试才反思
                reflection = self._generate_reflection(trajectory, score)
                self.reflection_memory.append({
                    "trial": trial,
                    "reflection": reflection,
                    "score": score
                })
                print(f"[Reflexion] 生成反思: {reflection[:100]}...")
            
            if score > best_score:
                best_score = score
                best_result = trajectory["final_answer"]
        
        print(f"[Reflexion] 达到最大尝试次数,返回最佳结果 (得分: {best_score})")
        return best_result or "任务失败"
    
    def _run_single_trial(self, query: str, trial_num: int) -> Dict:
        """执行单次ReAct试验"""
        # 构建带反思历史的提示
        context = self._build_trial_context(query, trial_num)
        
        # 复用ReAct逻辑执行
        self.context = LoopContext(max_iterations=self.context.max_iterations)
        self.context.history = context
        
        # 执行标准ReAct循环(简化版)
        final_answer = self.run_react_cycle(query)
        
        return {
            "trajectory": self.context.history.copy(),
            "final_answer": final_answer,
            "iterations": self.context.iteration_count
        }
    
    def _build_trial_context(self, query: str, trial_num: int) -> list:
        """构建试验上下文,包含历史反思"""
        context = [{"role": "user", "content": query}]
        
        if self.reflection_memory and trial_num > 1:
            # 注入之前的反思作为指导
            reflections_text = "\n".join([
                f"尝试 {r['trial']} 的教训: {r['reflection']}"
                for r in self.reflection_memory
            ])
            context.append({
                "role": "system",
                "content": f"基于之前失败的反思:\n{reflections_text}\n请避免重复这些错误。"
            })
        
        return context
    
    def _evaluate_trajectory(self, trajectory: Dict) -> float:
        """评估轨迹质量(可由LLM或规则实现)"""
        # 启发式评估:检查是否包含错误标记、迭代次数等
        history = trajectory["trajectory"]
        score = 1.0
        
        # 惩罚错误
        error_count = sum(1 for msg in history if "Error:" in str(msg.get("content", "")))
        score -= error_count * 0.3
        
        # 惩罚过长轨迹
        score -= trajectory["iterations"] * 0.05
        
        # 检查是否有最终答案
        if not trajectory["final_answer"]:
            score -= 0.5
        
        return max(0.0, score)
    
    def _generate_reflection(self, trajectory: Dict, score: float) -> str:
        """生成自我反思"""
        history_text = "\n".join([
            f"{msg['role']}: {msg['content'][:200]}..."
            for msg in trajectory["trajectory"]
        ])
        
        reflection_prompt = f"""分析以下失败的执行轨迹,总结导致低分({score})的原因,并提供改进建议:
{history_text}

请用第一人称总结教训(如"我意识到..."、"我应该..."):"""
        
        reflection = self.llm.generate(reflection_prompt)
        return reflection

Reflexion的核心价值在于跨试验学习:通过将失败经验转化为自然语言反思,Agent能够在不更新模型参数的情况下"学习"并改进策略。研究表明,这种言语强化学习在编程任务和决策任务上显著优于纯ReAct方法。

Plan-and-Solve Loop:先规划后执行的解耦模式

与前两种"边想边做"的交错模式不同,Plan-and-Solve采用解耦架构:先由规划器(Planner)生成完整计划,再由执行器(Executor)按步骤执行。这种模式适合可预测、结构化的任务场景。

Plan-and-Solve的控制流:

  1. 规划阶段:LLM生成任务分解和步骤序列
  2. 执行阶段:按序执行各步骤,每步可嵌入简单的ReAct循环处理意外
  3. 重规划触发:当步骤执行失败或环境变化时,重新调用规划器

三种范式的选择取决于任务特性:ReAct适合探索性、信息收集类任务;Reflexion适合需要迭代精进的复杂任务;Plan-and-Solve适合结构化、可预测的工作流。

五、工程实现

将Agent Loop从概念转化为生产级系统,需要解决一系列工程挑战:终止条件设计、异常处理与熔断、并发优化。

循环终止条件设计

Loop终止不是简单的"达到最大次数",而是需要多层次的终止策略:

目标达成检测

  • 显式标记:LLM输出特定终止标记(如[FINAL_ANSWER]
  • 启发式规则:输出格式符合预期(如JSON结构完整)
  • 外部验证:通过单元测试、人工审核或评估器确认结果正确性

安全终止

  • 最大迭代限制:设置上限,防止无限循环
  • 预算限制:Token消耗或API调用成本上限
  • 时间限制: Wall-clock时间超时

智能终止

  • 收敛检测:连续多轮输出相似(通过文本相似度或哈希指纹判断),触发"死循环"检测
  • 价值停滞:强化学习中的价值函数不再提升

异常处理:工具失败、幻觉传播与熔断机制

Agent Loop的健壮性取决于其异常处理能力:

异常处理与熔断机制流程图;工业线稿风格;展示正常循环路径、工具超时分支、幻觉检测分支、最终人工介入出口,使用红色标注异常路径;1200x800;

工具失败处理

  • 重试策略:指数退避重试,区分暂时性错误(网络超时)与永久性错误(权限拒绝)
  • 降级方案:主工具失败时切换备选工具
  • 错误传播:将错误信息作为Observation反馈给LLM,让其决定下一步

幻觉传播抑制

  • 自我验证:要求LLM对关键事实进行交叉验证
  • 工具结果校验:对比多次工具调用的结果一致性
  • 置信度阈值:低置信度时触发人工介入或额外验证

熔断机制

class RobustAgentLoop(AgentLoopFSM):
    """带熔断机制的健壮Loop实现"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.error_counts = {}  # 错误类型计数
        self.circuit_states = {}  # 熔断器状态: CLOSED, OPEN, HALF_OPEN
        self.failure_threshold = 5  # 触发熔断的错误次数
        self.recovery_timeout = 60  # 熔断后尝试恢复的时间(秒)
    
    def _do_act(self) -> Dict:
        """带熔断保护的行动执行"""
        action = self._parse_action(self.context.history[-1]["content"])
        if not action:
            return {"status": "no_action"}
        
        tool_name = action["tool"]
        
        # 检查熔断状态
        if self._is_circuit_open(tool_name):
            return {
                "status": "circuit_open",
                "error": f"工具 {tool_name} 当前不可用(熔断中)"
            }
        
        try:
            # 执行工具调用(带超时)
            result = self._execute_with_timeout(
                tool_name, 
                action["args"],
                timeout=30
            )
            
            # 成功则重置错误计数
            self._record_success(tool_name)
            return result
            
        except ToolTimeoutError:
            self._record_failure(tool_name, "timeout")
            return {"status": "timeout", "tool": tool_name}
            
        except ToolExecutionError as e:
            self._record_failure(tool_name, "execution_error")
            # 检查是否需要熔断
            if self._should_trip_circuit(tool_name):
                self._trip_circuit(tool_name)
            return {"status": "error", "error": str(e)}
    
    def _execute_with_timeout(self, tool: str, args: Dict, timeout: int) -> Dict:
        """带超时的工具执行"""
        import concurrent.futures
        with concurrent.futures.ThreadPoolExecutor() as executor:
            future = executor.submit(self.tools.execute, tool, args)
            try:
                return future.result(timeout=timeout)
            except concurrent.futures.TimeoutError:
                raise ToolTimeoutError(f"工具 {tool} 执行超时")
    
    def _is_circuit_open(self, tool_name: str) -> bool:
        """检查熔断器状态"""
        state = self.circuit_states.get(tool_name, "CLOSED")
        if state == "OPEN":
            # 检查是否过了恢复期
            last_failure = self.error_counts.get(f"{tool_name}_last_time", 0)
            if time.time() - last_failure > self.recovery_timeout:
                self.circuit_states[tool_name] = "HALF_OPEN"
                return False
            return True
        return False
    
    def _record_failure(self, tool_name: str, error_type: str):
        """记录失败"""
        key = f"{tool_name}_{error_type}"
        self.error_counts[key] = self.error_counts.get(key, 0) + 1
        self.error_counts[f"{tool_name}_last_time"] = time.time()
    
    def _record_success(self, tool_name: str):
        """记录成功,重置计数"""
        self.circuit_states[tool_name] = "CLOSED"
        for key in list(self.error_counts.keys()):
            if key.startswith(tool_name):
                del self.error_counts[key]
    
    def _should_trip_circuit(self, tool_name: str) -> bool:
        """判断是否触发熔断"""
        total_failures = sum(
            count for key, count in self.error_counts.items()
            if key.startswith(tool_name) and not key.endswith("_last_time")
        )
        return total_failures >= self.failure_threshold
    
    def _trip_circuit(self, tool_name: str):
        """触发熔断"""
        self.circuit_states[tool_name] = "OPEN"
        print(f"[CircuitBreaker] 工具 {tool_name} 已熔断,{self.recovery_timeout}秒后尝试恢复")
    
    def _handle_exception(self, error: Exception):
        """全局异常处理"""
        if isinstance(error, HallucinationDetectedError):
            # 幻觉检测触发,注入纠正提示
            self.context.history.append({
                "role": "system",
                "content": "警告:检测到可能的幻觉输出,请基于已有事实重新推理。"
            })
            # 不终止,继续循环但标记状态
            return
        
        elif isinstance(error, CriticalToolFailureError):
            # 关键工具失败,尝试人工介入
            if self._request_human_intervention():
                return
            else:
                self.transition_to(AgentState.TERMINATED)
        
        else:
            # 未知异常,记录并终止
            print(f"[Error] 未处理异常: {error}")
            self.transition_to(AgentState.TERMINATED)
    
    def _request_human_intervention(self) -> bool:
        """请求人工介入"""
        # 实现人工审批逻辑
        return False

上述代码展示了分层防御的异常处理策略:工具层熔断防止级联故障,幻觉检测保障输出质量,全局异常处理确保系统最终可控。

并发优化:多Loop实例的状态隔离与资源共享

生产环境通常需要同时运行多个Agent Loop实例,面临以下挑战:

状态隔离:每个Loop实例必须有独立的上下文、记忆和状态机,防止实例间干扰。可通过依赖注入模式,为每个实例创建独立的Context对象。

资源共享:LLM推理引擎、工具连接池、向量数据库等昂贵资源应共享。使用连接池缓存层(如Redis)减少重复初始化开销。

并发模型选择

  • 多线程:适合I/O密集型任务(如网络请求),受GIL限制
  • 多进程:绕过GIL,适合CPU密集型推理,但内存开销大
  • 异步协程:Python的asyncio适合高并发轻量级任务,但复杂状态机实现困难

LangGraph的解决方案采用检查点(Checkpointing)机制:每个状态转换后持久化状态,支持中断恢复、时间旅行(回滚到历史状态)和并行分支执行。这种设计将状态管理外置,Loop实例变为无状态的计算节点,大大简化了并发控制。

六、从单Loop到多Loop协调

当单个Agent无法满足复杂任务需求时,多Agent系统的Loop协调成为关键挑战。

多Agent场景下的Loop同步

多Agent系统的核心问题是Loop间的交互模式

消息传递:Agent间通过显式消息通信,Loop独立运行。AutoGen采用此模式,将多Agent协作建模为对话消息交换。消息传递松耦合,但时序控制困难。

共享状态:多个Loop实例读写共同的State对象。LangGraph的SharedState模式支持此架构,但需要仔细的并发控制防止竞态条件。

主从协调:中央Orchestrator维护主Loop,负责任务分解和分配;Worker Agent执行子任务,汇报结果后Orchestrator更新主状态。CrewAI和Magentic-One采用此模式。

层级Loop:策略与执行的嵌套

复杂系统常采用层级Loop架构

顶层策略Loop:低频率运行(如每5分钟),负责目标设定、资源分配、异常策略调整。维护长期记忆和全局状态。

底层执行Loop:高频率运行(如每秒),负责传感器数据处理、实时控制、即时反馈响应。状态生命周期短,上下文窗口有限。

层级Loop通过命令接口通信:顶层Loop生成目标或约束,写入共享内存;底层Loop读取并执行,定期汇报进度。

七、总结

当前局限:循环深度与上下文窗口的权衡

Agent Loop面临的核心矛盾是无限循环需求有限上下文窗口的冲突。随着迭代次数增加,历史记录线性增长,最终超过上下文限制。

当前解决方案包括:

  • 上下文压缩:摘要旧历史、剪枝冗余信息、向量化存储关键记忆
  • 滑动窗口:仅保留最近N轮交互,丢弃早期历史
  • 分层记忆:外循环维护长期记忆(向量数据库),内循环仅使用工作记忆

但这些方案都涉及信息损失,如何在压缩中保留关键决策线索,仍是待解决的问题。

未来方向

Agent Loop的演化将沿以下方向展开:

持久化Loop:Loop状态持久化到数据库,支持跨会话恢复。Agent可以"暂停"数小时甚至数天后"继续"执行,实现真正的长周期任务处理。这需要解决状态序列化、版本兼容、分布式锁等技术挑战。

学习增强的循环策略:当前Loop的控制逻辑(何时终止、如何重试、是否反思)由硬编码规则或启发式决定。未来将出现元Agent,通过强化学习或进化算法优化Loop控制策略本身,实现自适应的循环参数调整。

Agent Loop作为AI系统的核心引擎,其设计哲学超越了简单的代码结构——它是对智能本质的工程诠释:不是静态的知识储备,而是持续的感知-行动-适应过程。理解并掌握Agent Loop的设计与优化,是构建AI Agent系统的关键能力。


欢迎关注公众号【dev派】,获取最前沿Ai时代技术发展新动态。