ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Rea循环:让AI从“回答问题”到“动手做事”的Agent核心机制

Rea循环:让AI从“回答问题”到“动手做事”的Agent核心机制

最近不少做AI应用的朋友都在聊一个词:Rea。这个缩写拆开是Read、Evaluate、Act,翻译过来就是“观察-评估-行动”。它既不是某个具体的工具,也不是某个模型的名字,而是设计自主智能体(Agent)时最核心、最常用的一套循环结构。

一句话概括:让AI不再回答完问题就结束,而是给定一个目标之后,自己反复执行“看一眼现状、想一下下一步、动手做一件事、再看结果”的闭环,直到完成任务为止。

我最早接触Rea是为了做某文档整理Agent。当时的需求很简单:给一堆零散笔记,让AI自动归类、生成摘要、按目录归档。用单次对话的方式试过,模型只能给出整理建议,不能真正动手。后来改造成Rea循环之后,它才能真正完成整个流程:读取文件、分析内容、创建目录、移动文件、生成索引。整个过程像是一个有经验的新员工:先看,再想,再做,再复查。

这篇文章会把Rea循环的原理、实现步骤、参数设计、常见坑全部摊开讲一遍。适合正在做Agent开发、或者对AI自动化感兴趣、但还没完全摸清底层逻辑的人。尽量用大白话,把每一步都拆到能直接照着做。

1. 为什么需要Rea这个循环

1.1 单次推理撑不起真实任务

大语言模型本质上是一个“单次预测”系统。你给它一段输入,它生成一段输出,然后结束。这在写文案、写代码片段、翻译这种场景下完全够用,因为任务是“一次性”的。

但真实世界的任务几乎都是多步骤的。比如“把我网盘里的照片按拍摄地点分类”,理论上可以做,实际操作里你需要:

  • 列出网盘文件
  • 识别每张照片的拍摄信息
  • 判断地点
  • 创建对应文件夹
  • 移动文件
  • 遇到无法识别的照片要单独处理
  • 最后还要生成一个分类报告

如果只调一次模型,让它在一次回复里完成以上所有步骤,几乎不可能。因为模型在生成第一句时并不知道后面文件列表是什么样的,更不可能在执行中途根据实际情况调整方案。

Rea解决的就是这个“多步骤、不可预知、需要动态决策”的问题。它把一个大任务拆成一个又一个的小循环,每个循环只做一件具体的事。模型在每个循环里只面对当前这一步的信息,决策压力小得多,正确率也成倍上升。

1.2 Rea让AI的“思考过程”可见

传统单次调用里,模型的推理过程藏在一大段文本里,很多时候我们根本不知道它是怎么得出答案的。出了问题也没法查,只能重新试。

Rea循环天然把推理过程拆开了:每次评估环节都会输出“当前状态是什么”“距离目标还差什么”“我准备选哪个动作”“为什么选它”。这些信息可以一行一行打印出来,甚至存进日志。

我调试某图像处理Demo时就是靠这个特性找回状态的。当时AI连续三次对一张图片做了错误的裁剪参数,我翻看评估记录,发现它每一次都认为自己“已经获取到了图片尺寸信息”,但实际上读取尺寸的动作压根没有执行成功。如果不是Rea循环把每一轮决策都留了痕,这种问题根本没法定位。

这就是Rea最大的隐形价值:它让AI从“黑盒”变成了“白盒”。哪怕模型最终做错了,你也能一步步回溯错在哪,而不是对着一个结果干瞪眼。

1.3 生活化类比:做饭不放盐的AI

你想一下人是怎么做饭的:腌肉、热锅、下料、翻炒、尝一口、不够咸再加盐、出锅。这一整个过程没有人是闭着眼睛一口气做完的,大家都是一边做一边看,根据实际情况调整火候和调料。

Rea就是让AI学会“边做边看”。每次Action之后都会产生一个新状态,下一轮Read就会观察到这个变化,Evaluate再据此判断下一步。跟人做饭的逻辑完全一致。

2. Rea的核心设计拆解

Rea看起来只是三个字母,但每个部分都有自己的设计要点。我在实际项目里从“跑得通”到“跑得稳”,中间反复迭代了很多轮,这里把每个模块的关键点讲透。

2.1 Read:怎么把状态喂给模型

Read模块的任务是:把当前环境里跟任务相关的信息收集起来,整理成模型能理解的文本。

这个环节最常犯的错是“什么都往里塞”。文件多的时候,把所有内容都堆到上下文里,不仅浪费token,还会让模型抓不住重点。我的做法是做一个状态摘要函数,每次只提取跟当前目标最相关的信息。

具体来说要处理三件事:

  • 信息范围:是全部文件内容,还是只要文件名列表?是完整的日志,还是只看最近几条?范围越大,模型越迷糊。
  • 信息结构:输出得结构化,比如用markdown、JSON、列表,让关键信息一目了然。
  • 信息去重:上一轮Action已经处理过的东西,这一轮就没有必要再原样给模型看一遍,可以替换成“已经完成”的标记。

状态摘要的原则是:给模型看“仪表盘”,而不是把整台设备的零件清单全倒出来。

2.2 Evaluate:决定“下一步做什么”

Evaluate是整个循环的大脑。模型读完状态之后,要回答三个问题:

  • 当前目标完成了吗?
  • 如果没完成,还差哪些步骤?
  • 下一步应该采取哪个具体动作?

我建议这一层不要用自由文本输出,而是强制输出结构化数据。常见格式:

{ "reasoning": "当前已扫描12个文件,其中3个无法识别类型,剩余任务需要创建归档目录并移动文件", "next_action": "create_folder", "action_args": { "path": "/archive/未分类" }, "is_finished": false }

强制结构化有几个好处:第一,代码可以稳定解析,不用去猜自然语言;第二,模型在生成答案时会被迫梳理逻辑,输出质量更高;第三,遇到格式错误能快速发现并重试。

Evaluate环节的另一个重点是“进度感知”。模型需要知道自己已经走到哪一步了,否则容易出现重复劳动。我通常会让状态对象里维护一个进度列表,每完成一步就往里追加一条记录,Evaluate读状态时自然能看到该做什么、不该做什么。

2.3 Act:模型不直接动手,而是调工具

关键设计原则是:模型永远不直接操作系统,它只负责“发出动作指令”,真正动手的是一个个工具函数。

这样做有两个好处:

  • 安全可控。模型每次动作都经过工具层校验,权限被限制在白名单里,不会出现模型胡来导致系统崩溃的情况。
  • 工具可以复用。同一个Read、Evaluate逻辑,只要换一套工具集,就能适配完全不同的任务。

举例,知识库整理项目的工具集是:

  • list_files(path)
  • read_file(file_path)
  • create_folder(folder_path)
  • move_file(src, dst)
  • write_summary(file_path, summary)
  • search_keyword(keyword)

每一个工具都有一段清晰的描述,包括功能、参数、返回值。模型根据工具描述来决定调用哪个。工具描述的清晰度直接决定了模型选错工具的概率。

工具这层还应该做输入校验。模型输出的action_args偶尔会缺字段或者类型不对,工具层要在执行前做schema检查,不合法就返回错误信息,让模型自己修正重试。

3. 从零实现一个Rea循环

这一部分我会直接演示一个最小可运行的方案,并解释每一步为什么这样写。你可以把它当成模板,替换成自己的任务逻辑。

3.1 定义状态与动作空间

先把数据结构和工具集定义好。以“整理文档”任务为例:

from dataclasses import dataclass, field from typing import List, Optional @dataclass class TaskState: task_description: str steps_completed: List[str] = field(default_factory=list) pending_files: List[str] = field(default_factory=list) current_focus: Optional[str] = None step_count: int = 0 max_steps: int = 20 is_done: bool = False

状态对象是循环的“共享白板”,每次Read把它的内容整理成文本,每次Act之后更新它。所有信息都集中在一个地方,别散落得到处都是。

接着定义工具集。每个工具就是普通函数加一个描述和参数schema:

def list_files(directory: str) -> List[str]: ... def read_file(file_path: str) -> str: ... def create_folder(folder_path: str) -> bool: ... def move_file(source: str, destination: str) -> bool: ...

工具的具体实现取决于你的业务场景,这里不展开。核心是:工具越单一职责越好,一个工具只做一件事,描述写得清清楚楚。

3.2 实现read、evaluate、act主循环

主循环的伪代码大致是这样:

state = init_state("将downloads目录下的文档按主题分类归档") history = [] while not state.is_done and state.step_count < state.max_steps: # Read: 构造当前状态摘要 observation = build_observation(state, history) # Evaluate: 让模型决策 decision = llm_evaluate(observation) if decision.is_finished: state.is_done = True break if not validate_decision(decision): history.append("无效动作,请检查参数") continue # Act: 执行动作并更新状态 result = execute_action(decision.next_action, decision.action_args) update_state(state, decision, result) # 记录本轮轨迹,供下一轮观察以及人工审计 history.append(format_trace(decision, result)) state.step_count += 1

这个循环的每一步都对应前面讲的三个字母。Read是build_observation,Evaluate是llm_evaluate,Act是execute_action加update_state。

3.3 关键参数的选择与计算逻辑

模型调用参数直接决定循环质量。我实测后比较稳的建议:

  • 温度:评估环节调到0到0.3之间。温度太高模型会随机换动作,导致重复劳动或来回跳步。
  • 最大轮数max_steps:按任务复杂度估算。简单任务5到8轮足够,常规自动化任务10到15轮比较合理,超过20轮大概率是出问题了,再给轮数只是浪费token。
  • 模型选择:优先选支持function calling或工具调用能力的模型。这类模型输出动作的格式更规范,解析成功率明显高。用普通对话模型硬顶也能跑,但解析成本高、失败率高。
  • 上下文管理:不可能每轮都把全部历史塞进去。我用的规则是:完整历史只保留最近3轮,更早的合并成一段摘要,保存在history_summary字段里。

关于轮数和token的关系,可以简单估算一下:假设每轮输入输出合计2000 token,跑10轮就是2万token,再叠加历史累积,实际消耗可能翻倍。如果一个任务正常只需要5轮,你设的max_steps是30,一旦模型陷入死循环,成本会非常难看。所以max_steps宁小勿大,宁可任务没跑完重试,也不要让它无限烧钱。

3.4 动作验证与失败重试

有一个很多人会忽略的细节:模型输出的动作描述可能不合法,比如参数名拼错、文件路径不存在、文件夹已存在。这种问题不能直接抛异常结束,要让工具返回明确错误信息,回传给模型让它自己修正。

我的工具层有一个统一返回结构:

{ "success": True, "data": "...", "error": None }

失败时success为False,error字段带上原因。评估层会把错误信息注入下一轮观察,模型就能看到“刚才的创建文件夹操作失败,因为路径已存在”,然后自动调整策略。这就是Rea循环的自愈能力来源。

4. 常见问题与排查技巧实录

这部分分享我在实际项目里踩过的一些坑,都是文档里不会写但几乎每个做Rea循环的人都会遇上的问题。

4.1 模型陷入死循环,同一个动作反复执行

这是我最早遇到的严重问题。某次测试里,模型连续五轮执行“list_files”动作,每轮拿到的文件列表都一样,它却一直重复扫描,就是不进入下一步。

排查思路:

  • 检查工具执行后有没有真正改变状态。如果list_files的结果没被存储或没影响后续决策,模型每次看到的都是同一画面,自然只能给同一个答案。
  • 检查观察文本里有没有加入“最近一次已执行动作”。如果没有,模型可能不记得自己刚刚扫过文件。

解决办法:一是每次动作完成后,强制把动作和结果摘要写进状态历史;二是给模型加一条提示规则:如果当前状态的已完成列表中已经包含“扫描文件”,就不要再重复扫描。三是增加“连续相同动作检测”,一旦识别到连续3轮动作完全一致,直接终止本轮循环并报错。

4.2 模型假装已经完成任务

这是在评估环节最无语的情况:模型的reasoning里写着“我已经完成了所有操作”,但实际工具记录显示它根本还没创建归档文件夹。

原因在于模型可能被训练数据影响,倾向于在推理结束时输出一个“完美的结论”,而不是严格对照工具反馈。尤其是对话模型,特别容易顺着你的问题语境自我圆场。

我的解决办法有两个:

  • 在提示词里弱化“完成”的表述,改成“只有当所有工具执行结果都确认成功后,才允许输出is_finished”。
  • 在代码层加一道硬校验:is_finished为true时,强制执行一个检查工具,核对状态对象的step_completed是否包含所有必需项。不通过就让模型继续动作。

有一次我甚至让模型自己写一个“任务完成自检清单”,然后再对照清单逐项确认,发现模型比预想中更认真。这种做法适合对准确性要求很高的场景。

4.3 上下文越来越长,最终超过模型窗口

如果不做压缩,每轮叠加完整对话历史,十轮之后可能就已经逼近窗口上限,后面模型甚至直接报错或丢失早期信息。

解决思路:

  • 历史压缩:每完成一个子步骤,就把该步骤的详情合并成一行摘要,只保留最近几轮原始内容。
  • 状态替换:状态对象里维护“已完成步骤列表”和“当前未完成任务列表”,每次观察时只生成这两个列表,不把所有历史动作细节回放。

我实测过,一个整理120个文件的任务,如果完整保留每轮历史,在第十轮就会超出窗口。改用摘要策略之后,跑完整个任务也只用掉不到一半的窗口。

4.4 工具调用格式频繁解析失败

模型输出的JSON偶尔会多一个逗号、字段名拼错、漏掉一个引号。如果用严格要求结构化输出的模型还好,普通模型会经常翻车。

正解是“解析容错+重试机制”组合:

  • 解析失败时,不直接抛弃,先尝试用正则提取JSON片段、补齐缺失字段。
  • 如果补不齐,把错误提示回传给模型,要求它重新生成合法JSON。错误信息越具体,重新生成的成功率越高。
  • 连续三次解析失败,要触发降级策略:切换成更小、更适合结构化输出的模型重试。

有些框架支持json_schema强制约束,那就尽量用上。没有的话就在提示词里把JSON格式示例放进去,模型模仿能力强,效果也不错。

5. Rea的进阶玩法与扩展方向

基础循环跑通之后,项目就开始“像样了”。但真实场景的任务复杂度往往远超简单循环能覆盖的范围,我总结出几个经过实践验证的扩展方向。

5.1 加入长期记忆

基础Rea循环里,状态信息只在单次任务中存续。如果你希望AI在不同任务之间积累经验,就要引入记忆模块。

做法是把每次任务的关键决策存到本地库或向量数据库里:

  • 任务的类型
  • 遇到的难点
  • 采用的解决动作
  • 最终结果

当下次遇到相似任务时,把相关历史经验注入初始提示,让模型从已有经验起步,而不是从零开始摸索。

我有一个场景是让AI定期整理项目周报,每次生成前它都会读取自己上次的总结经验和格式偏好。这样连续几次运行后,生成的周报风格越来越统一,几乎不需要人工修改。这就是记忆复用的好处。

5.2 规划器与执行器分离

纯Rea循环有一个弱点:它只会着眼于当前这一步的局部最优,缺少全局规划。任务步骤超过十五步时,模型容易在中间阶段迷路。

解法是拆成两层:

  • 规划层:任务开始时先让模型生成一份完整计划,列出所有子任务和预期成果。
  • 执行层:Rea循环只负责执行当前子任务,每完成一个就回到规划层勾选进度,再领取下一个子任务。

这样做的效果类似于“先画图纸再施工”。全局方向由规划层把控,局部操作由执行层灵活处理。我在一个较复杂的多数据源同步项目里用到这个架构,任务完成率从基础的60%左右提升到95%以上。

5.3 多Rea循环协作

单个Agent能力有限的时候,可以用多个Agent,每个Agent跑自己的Rea循环,通过消息机制协作。

比如整理一个技术资料库的任务,可以拆成三个Agent:

  • 采集Agent:负责扫描和下载资料,跑一个Rea循环。
  • 整理Agent:负责分类和去重,跑另一个Rea循环。
  • 索引Agent:负责生成目录和检索索引,跑第三个Rea循环。

三个Agent之间通过任务队列通信,采集Agent完成后把任务交到整理Agent的队列里,整理完再传给索引Agent。

这里有一个实用经验:消息不要直接传大量原始内容,尽量传引用或摘要。Agent之间传100个文件的路径列表没问题,直接把100个文件全文传过去就不明智了。等未来内容变多,你会感谢自己一开始就设计了这个约定。

这套方案带来的好处是可扩展性。新增一个需求时,只需要增加一个新Agent和对应的工具集,不需要改已有逻辑。

5.4 人机协同节点

最后一个建议是不要追求全程无人干预。有些决策点风险高或难以自动判断,比如“是否删除这个文件”“是否对这个客户发消息”,在Rea循环里加入一个人工审批节点,会稳妥很多。

实现方式不复杂:Rea的Act环节遇到特定动作时,先把请求发到审批队列,暂停循环,等人工确认后继续执行。虽然引入了等待时间,但换来了安全性。

我现在的习惯是:高风险操作默认加审批,低风险操作全程自动。这套规则让我的Agent既能干活又不会闯祸。

最后说一个我自己的习惯:每次跑完Rea循环,我都会把完整的一轮轨迹(状态、决策、动作、结果)单独存一份。一开始只是为了排查问题方便,后来发现积累多了,能做轨迹回放去分析模型的稳定性和行为偏向,也能把这些数据拿来做后续调优。这个习惯想分享给每一个正在做Agent开发的人,越早开始存轨迹,后面你会越感谢自己当时的决定。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进