
最近不少做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_factorylist) pending_files: List[str] field(default_factorylist) current_focus: Optional[str] None step_count: int 0 max_steps: int 20 is_done: bool False状态对象是循环的“共享白板”每次Read把它的内容整理成文本每次Act之后更新它。所有信息都集中在一个地方别散落得到处都是。接着定义工具集。每个工具就是普通函数加一个描述和参数schemadef 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_observationEvaluate是llm_evaluateAct是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为Falseerror字段带上原因。评估层会把错误信息注入下一轮观察模型就能看到“刚才的创建文件夹操作失败因为路径已存在”然后自动调整策略。这就是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开发的人越早开始存轨迹后面你会越感谢自己当时的决定。