智能体循环:感知、思考与行动

把一次调用变成多步自主——ReAct 推理与行动交替、多轮迭代累积上下文、循环何时该停。

为什么只记结论不足以掌握智能体循环:感知、思考与行动

学习“智能体循环:感知、思考与行动”时,第一步不是记住结论,而是冻结输入、上下文、版本和成功标准。只有这些条件明确,智能体循环:感知、思考与行动的含义才不会随着样例变化。正文已有的概念说明要与结构图、运行轨迹和失败样本互相印证,不能只凭最终输出看似正确就宣布完成。

结构分析从先打个比方开始:列出参与者、职责、连接方向、生命周期和所有权,再沿正常路径追踪数据或控制流。每一条边都要说明为什么存在、谁创建、谁消费、谁负责清理;如果边界被跨越,必须能在证据中找到第一处异常。

机制验证要把第一个概念:为什么一次调用不够,得让它转圈写成可以执行的条件。正常样本证明主路径,恰好边界样本验证等号和空值,单故障样本只破坏一个假设。三类样本使用同一份观察指标,避免因为测试口径变化而把偶然结果误认成规律。

成本分析同时记录时间、空间、延迟、耦合、可维护性和不可逆操作。第二个概念:ReAct——把一圈拆成「想、做、看」三段不是一句“可能失败”,而是可复现输入、预期停点、实际轨迹、错误分类与清理步骤。任何自动重试都要有次数、预算和幂等边界。

方案比较不能只列优点。需要给出直接实现、当前方案和至少一个替代方案,逐项比较复杂度、扩展点、故障隔离和团队认知成本。当问题规模很小或变化轴稳定时,更简单的实现往往更好;模式与框架必须由真实变化压力证明。

实现阶段把大结论拆成可检查的中间产物:配置快照、结构清单、状态转移、输入输出样本、日志摘要和测试结果。每个产物带来源与生成命令,下一阶段只消费已通过门禁的版本,避免旧缓存或隐式默认值污染结论。

解释结果时必须区分相关性与因果性、接口承诺与实现细节、设计意图与运行事实。对“第三个概念:多轮迭代——结果喂回,思考随之改变”的判断要由独立证据支持,并明确适用范围;一旦输入分布、版本、硬件或组织边界改变,就重新运行最小实验。

复盘从首个分叉开始,而不是从最后一个报错倒推。先比较冻结输入,再比较第一份结构化中间产物,随后检查状态、约束和副作用。这样可以把复杂系统的排错范围收缩到一个阶段,避免在多个层次同时修改造成新的不确定性。

迁移到真实项目时,先选择一个最小但有代表性的切片,保存改造前基线,再逐步引入“智能体循环:感知、思考与行动”中的机制。每一步只改变一个变量并保留回滚点;性能、正确性、安全性和可理解性至少各有一项可量化指标。

最终验收要求读者能脱离页面重新画出结构、口述关键链路、实现最小版本、构造一个反例并解释失败位置。若只能复述名词而不能预测中间状态,说明知识仍停留在识记层,需要回到图示和实验重新验证。

本页用、

、、、建立统一坐标。先预测这些概念在结构图和运行轨迹中的位置,再操作实验控件;如果结果与预测不一致,停止在首个分叉,不要用后续补丁掩盖早期错误。

权威目录与核心概念逐项对照

  • 智能体循环:感知、思考与行动
  • 先打个比方
  • 第一个概念:为什么一次调用不够,得让它转圈
  • 第二个概念:ReAct——把一圈拆成「想、做、看」三段
  • 第三个概念:多轮迭代——结果喂回,思考随之改变
  • 第四个概念:循环何时该停
  • 动手看:一个任务怎么转 3 轮才办成
  • 用代码看带刹车的循环骨架

可复现的最小实现

先把决策记录写成机器可读结构:

{
  "unit": "智能体循环:感知、思考与行动",
  "inputFrozen": true,
  "scenario": "normal | boundary | single-fault",
  "firstDivergence": null,
  "cleanupRequired": true
}

再用同一条执行链处理三类样本:

type Evidence = { stage: string; expected: string; actual: string };
 
function verify(sample: unknown, expected: readonly Evidence[]) {
  const trace = runFromCleanState(sample);
  return expected.find((item, index) => trace[index]?.actual !== item.expected);
}

最后保存回归门禁,禁止失败样本静默通过:

normal      -> complete, invariant preserved
boundary    -> complete or explicit rejection
singleFault -> stop at first divergence, no stale output

用统一评分解释实验结果:

Q=Ccorrect+Ctrace+CrecoverRresidualQ = C_{correct} + C_{trace} + C_{recover} - R_{residual} Ccoverage=NverifiedNofficialC_{coverage} = \frac{N_{verified}}{N_{official}} Rresidual=P(failure)×I(impact)R_{residual} = P(failure) \times I(impact) Accept=(Ccoverage0.90)(RresidualRbudget)Accept = (C_{coverage} \ge 0.90) \land (R_{residual} \le R_{budget})

本章回顾

  • 智能体循环:感知、思考与行动必须绑定冻结输入和明确成功标准。
  • 先打个比方必须能画成结构并沿边追踪责任。
  • 第一个概念:为什么一次调用不够,得让它转圈要由正常、边界和单故障样本共同验证。
  • 第二个概念:ReAct——把一圈拆成「想、做、看」三段必须保存第一处偏离和清理重建步骤。
  • 第三个概念:多轮迭代——结果喂回,思考随之改变决定方案是否可以进入下一阶段。

术语表

先打个比方

还是那位帮你办事的同事。你让他「订明天去上海最便宜的高铁票」——这事他没法一步办完:得先查有哪些车次、看哪趟最便宜,再去下单。

更现实的是,事情常常不顺:他挑中最便宜那趟去订,结果一看售罄了。这时靠谱的同事会怎么办?他不会卡死,也不会傻乎乎再订一遍同一趟,而是改订次便宜的那趟,订成了才回来交差。

这一章讲的就是:怎么让 AI 像这位同事一样,试一下、看结果、不行再换个法子,反复转到办成为止。少了这种「反复转圈」的本事,AI 就只能一锤子买卖——查一次、订一次,一遇到「没票了」就当场卡住,啥也办不成。

动手看:一个任务怎么转 3 轮才办成

前面讲了「多轮迭代」和「结果喂回」,但只有亲眼看它一圈圈转,才能真正抓住循环的精髓。下面这个动画用「帮我订明天去上海最便宜的高铁票」把它拆成 10 步——这事一次调用搞不定,得转整整 3 轮。

猜一猜:动画第 ⑥ 步,第 2 轮订 G1 失败(「G1 已售罄」)。那么第 3 轮 LLM 的思考会变成什么?是再订一次 G1,还是改主意?先想一想——它能看到上一轮的结果吗?再单步播放,看它是不是基于「G1 没票」改订了 G7。

可交互
🧠 上下文(记忆)每轮观察喂回,越滚越长📌 第 1 条轮1 看到:G1/G7 都 ¥553📌 第 2 条轮2 看到:G1 已售罄📌 第 3 条轮3 看到:G7 订票成功🔁 第 1 轮循环🔁 第 2 轮循环🔁 第 3 轮循环💭 思考 Thought先查有哪些车次,再挑最便宜的G1 最便宜,就订 G1G1 没票了 → 改订次便宜的 G7🛠️ 行动 Actionsearch_trains(明天, 上海)book(G1)book(G7)👀 观察 ObservationG1 ¥553(最便宜)· G7 ¥553 · G11 ¥667 …❌ G1 已售罄✓ 订票成功,G7 已出票🎯 任务完成 → 跳出循环

第 1 / 10 步 · ① 目标进入:「帮我订明天去上海最便宜的高铁票」—— 这事一次调用搞不定,得多转几轮

点击播放,看「订高铁票」这件事如何转 3 轮才办成:每轮观察喂回左侧上下文(越滚越长),下一轮的思考都基于上一轮看到的结果调整。可暂停、单步、拖进度逐帧看。

多轮迭代:一次工具调用不够时,智能体反复转圈——每轮的观察都喂回上下文(左侧越滚越长),下一轮的思考基于上一轮的结果随机应变。这是「循环」区别于单轮问答(第 1 章)、一次增强调用(第 2 章)的精髓。

盯住两件事:一是左侧的上下文栏每轮加一条、越滚越长——每轮看到的结果都被喂了回去;二是右侧的思考每轮都在变,而且变化都源自上一轮看到的结果(看到 G1 最便宜 → 想订 G1;看到 G1 售罄 → 改订 G7)。是它自己根据结果一轮轮调整路线,最后自己判定「订成了」跳出循环。这一圈圈相关地转下去,就是「多轮迭代」的全部秘密。

用代码看带刹车的循环骨架

上面的动画,落到代码里就是一个带步数上限的 for 循环。下面这段精简伪代码,是几乎所有智能体框架的共同骨架——把它读懂,多轮迭代和终止条件就都通了:

def run_agent(user_goal, max_steps=10):
    # 上下文 = 智能体的记忆,先放进系统提示和用户目标
    context = [system_prompt, user_goal]
 
    for step in range(max_steps):        # 最多转 max_steps 圈(刹车在此)
        # ① 思考(Thought):LLM 看着当前上下文,决定下一步
        thought = llm(context)
 
        # ② LLM 自己判断:任务办完了吗?办完就正常退出
        if thought.is_done:
            return thought.answer        # 出口一:LLM 判定完成
 
        # ③ 行动(Action):照 LLM 选的工具和参数真去执行
        observation = act(thought.tool, thought.args)
 
        # ④ 观察(Observation):把结果喂回上下文,进入下一圈
        context.append(thought)
        context.append(observation)
 
    # 转满 max_steps 还没收尾 → 强制停,给兜底回答
    return "抱歉,我尝试了多轮仍没能完成这个任务。"  # 出口二:max steps

逐句对照动画看:for step in range(max_steps) 就是那个会转多圈、但最多转 max_steps 圈的环;llm(context) 是每一轮的思考if thought.is_done 是 LLM 自己判断何时收尾(出口一);act(...)行动context.append(observation)观察喂回——下一圈 llm(context) 就能看到它了,这正是「上下文越滚越长、下一轮基于上一轮」的代码落点。

最后那行 return "抱歉……" 是第二个出口:for 转满 max_steps 还没等到 is_done,就强制停、返回兜底回答。两个出口缺一不可——少了第一个它不会主动收尾,少了第二个它可能无限转。至于第三个「出错兜底」出口,只需把 act(...) 包进 try / except,工具报错时也跳出循环即可,这里为聚焦先省略。

容易踩的坑

小结

  • 步数不定的开放任务一次调用搞不定,得靠循环一圈圈转着把活干完
  • ReAct 把一圈拆成三段:Thought 想下一步、Action 真去做、Observation 看结果
  • 多轮迭代的精髓:观察逐轮喂回(上下文越滚越长)、下一轮思考基于上一轮结果
  • 循环靠三条出口停下:LLM 判完成正常退出、转够 max steps 强制停、出错兜底退出
  • 没有 max steps / 不喂回结果 → 死循环或原地打转,是循环最常见的两个坑

名词解释

讨论

评论区加载中…