第2课:Agent 循环与 ReAct 深入

核心机制⭐⭐⭐⭐⭐ 必问↔ Hermes 源码对照→ 第7课 Go 实现

← 上一章:第1课下一章 → 第3课

一、为什么需要 Agent Loop?

单次 LLM 调用做不了复杂任务——LLM 只能"思考",不能"行动"。一个 task 可能拆成多个步骤:查数据库 → 调用 API → 计算结果 → 综合分析 → 生成报告。

Agent Loop = 让 LLM 能"思考→行动→观察结果→再思考"的循环机制

看一个简单例子:用户说"帮我查上海天气,对比北京"

User: "上海和北京天气对比" Iteration 1: LLM 思考 → 决定调 get_weather("上海") → 拿到结果 → 反馈给 LLM Iteration 2: LLM 思考 → 决定调 get_weather("北京") → 拿到结果 → 反馈给 LLM Iteration 3: LLM 思考 → 已经拿到两地数据 → 生成对比回答 → 输出给用户 → 完成。3轮迭代,2次工具调用。

二、ReAct 模式(论文核心)

ReAct = Reasoning + Action(Yao et al., 2022, arXiv:2210.03629)

核心洞察:推理(思考)和行动(工具调用)交替进行,互相增强。

伪代码:最简单的 Agent Loop
def agent_loop(user_input, tools, llm, max_iterations=10):
    messages = [system_prompt, user_input]
    
    for i in range(max_iterations):
        # 1. 思考:让 LLM 决定下一步
        response = llm.chat(messages, tools=tools)
        
        # 2. 检查 LLM 是要说话还是要调用工具
        if response.has_tool_call():
            tool_name = response.tool_call.name
            tool_args = response.tool_call.args
            # 3. 执行工具
            result = execute_tool(tool_name, tool_args)
            # 4. 把结果放回对话
            messages.append(tool_result(tool_name, result))
        else:
            # 5. LLM 直接回复 → 结束
            return response.text
    
    # 6. 超出迭代上限 → 返回已生成的内容
    return "Max iterations reached"

就这么简单。6 步,就是所有 Agent 框架的核心骨架。

三、真实世界:Hermes Agent 的 Loop

我在 Hermes 源码里找到的生产级实现(conversation_loop.py),比上面的伪代码多了几层工程:

build_turn_context() # 组装环境:system prompt + memory + tools ↓ while api_call_count < max_iterations: # 默认最多 90 轮 ├── iteration_budget.consume() # 预算检查(防无限循环) ├── LLM.chat(messages, tools) # 调用模型 ├── if tool_call_found: # LLM 想调用工具 │ ├── handle_function_call(...) # 分派到工具注册表 │ ├── append result to messages # 工具结果拼回对话 │ └── continue # 继续下一轮 ├── else: # LLM 直接回复 │ └── return final_response ├── on_error: │ ├── classify_error() # 错误分类 │ ├── jittered_backoff() # 退避重试 │ └── failover_model() # 降级模型 └── compression_if_needed() # 上下文过长时压缩
和 Hermes 的对照:
max_iterations = 90(父 Agent),子 Agent 默认 50。
每一轮都通过 iteration_budget 计数,超限就 break。
execute_code(编程性工具调用)会被 refund(不计入预算)——因为那是你在写代码时的内部操作,不是 Agent 的思维迭代。
你和我对话的每一次工具调用就是一次 ReAct 步骤。算算看,刚才创建教学空间那轮,我调了多少次工具?→ 8 次。

四、三种场景:看 ReAct 在不同任务中的表现

场景 A:简单问答(无需工具)

用户:"法国的首都是哪?"

场景 B:需 1 次工具调用

用户:"现在上海几度?"

场景 C:复杂多步(面试常考)

用户:"帮我找今年 GitHub 上最火的 AI Agent 框架,发邮件给我"

五、ReAct 的局限性

面试必问:"ReAct 有什么缺点?"

✅ 优点
❌ 缺点

六、ReAct 的改进方案

面试中如果被问到"怎么解决 ReAct 的缺点",这是你的框架:

改进 1:Plan-and-Execute

不让 LLM 一步一想,而是先让 LLM 一次性输出完整计划,再逐步执行。

Phase 1(计划): LLM: "这个任务需要三步:①查GitHub ②筛选Top5 ③发邮件" Phase 2(执行,逐不执行,可能出错修正): Step 1 → 查GitHub Step 2 → 筛选Top5 Step 3 → 发邮件

好处:token 消耗更少(计划阶段一次推理),执行时 LLM 有大局观。
问题:计划可能过时(中间结果和预期不符)。

改进 2:Reflection(反思)

Agent 执行完每一步后,检查自己的结果是否合理,不满意就重做。

执行 → 生成结果 → LLM 检查结果 ↓(结果有问题) 重新执行 → 再检查 → 通过 → 继续

改进 3:Structured Output + 约束解码

不用等到 LLM 输出完再解析,而是强制 LLM 的每一步输出符合 JSON Schema。这样:

七、面试问答

🎯 Q1:"请手写一个最简单的 Agent Loop 伪代码"

回答框架(边写边说):

"Agent Loop 的核心是 6 步:
1. 构造函数组装(system prompt + 用户输入 + 历史)
2. while 循环,上限 max_iterations
3. 调用 LLM,传入消息 + 工具定义
4. 判断返回:如果有工具调用 → 解析 name + args → 执行 → 结果拼回消息
5. 如果没有工具调用 → 返回 LLM 的文本回复
6. 超限则返回超时提示"

然后可以直接写出上面"最简单的 Agent Loop"的伪代码。
🎯 Q2:"ReAct 和 Plan-and-Execute 有什么区别?各自适用场景?"

框架:
- ReAct = 边走边想,适合步骤不确定、需要中途调整的任务(如 Debug、研究)
- Plan-and-Execute = 先计划后执行,适合步骤明确的任务(如数据处理、报告生成)
- 也可以混合:先 Plan 再 Act,中间加 Reflection 检查
🎯 Q3:"一个 Agent 卡在无限循环里了,怎么设计防御机制?"

框架(5 层防御):
1. max_iterations 硬上限(Hermes 默认 90)
2. Token budget:每轮监控 token 消耗,超限 break
3. 同工具连续调用检测:如果同一个工具被调 3 次结果一样 → 跳出
4. 时间超时:总运行时间超过阈值 → 强制结束
5. Human-in-the-Loop:N 次工具调用后请求人工确认

八、本课和后续课程的连接

九、课后行动

  1. ✅ 打开 Hermes,观察我的一次完整操作——数一数我有多少轮迭代
  2. ✅ 用伪代码在纸上画一遍 Agent Loop(6 步)
  3. ✅ 准备 Q1(手写 Agent Loop)和 Q3(无限循环防御)的面试回答
  4. ⏭ 下节课:工具系统与通信协议——Function Calling 细节、MCP、A2A

📁 0002-agent-loop-reactor.html
📁 源码参考:Hermes /usr/local/lib/hermes-agent/agent/conversation_loop.py (4480行)
📁 源码参考:Hermes /usr/local/lib/hermes-agent/agent/iteration_budget.py (62行)
📝 有问题随时问。

← 上一章:第1课下一章 → 第3课