ReAct(Reason + Act)本质是让模型在“思考(推理/计划)—行动(调用工具/读写环境)—观察(读取结果)”的循环里完成任务。
结合例子:从需求到前端应用的 Agent
1) ReAct 的核心循环:Act 不是装饰,是对齐现实的“落地步骤”
一个合格的 ReAct Agent 必须把任务拆成:
- Reason/Plan:要做什么、为什么、下一步依赖什么信息
- Act:调用工具/执行动作(读需求、读 repo、写文件、跑测试、lint、构建、启动、截图/DOM 检查等)
- Observe:读取工具输出/运行结果(错误栈、测试失败、构建产物)
- Iterate:根据观察修正计划、再次行动
对前端生成代码来说,“能跑起来”依赖大量现实约束(依赖版本、路由、构建器、已有代码风格、lint 规则、组件库约束)。所以你需要把这些约束作为必须通过 Act/Observe 验证的对象,而不是只在提示词里“想象”。
2) 工具设计决定上限:至少要有这些 Action
面向“生成前端应用代码”的 Agent,最低工具集建议:
read_file / list_dir / search:理解现有工程、约定、API 封装write_file / patch_file:可控地修改(更推荐 patch/差分而非整文件覆盖)run:执行pnpm/yarn/npm、lint、test、build、typecheck- (可选但强烈推荐)
open_page / get_console / screenshot / dom_query:跑起来后的 UI/控制台检查(哪怕是 headless) format:统一 prettier/eslint 产物,减少无谓 diff
ReAct 的关键在于:每一次 Act 都要产出可观测结果,否则循环无法收敛。
3) “思考”要可控:把隐藏推理和可见产出分离
很多实现会把模型的 Chain-of-Thought 原样打印出来,这会带来:
- 泄露内部策略/敏感信息(尤其你会把 repo 内容喂给模型)
- 输出冗长、难以 eval
- 容易被 prompt injection 诱导
工程上建议:
- 内部保留“计划/推理”但不对用户展示,对用户只输出“步骤、变更摘要、下一步操作/确认点”
- 或者让模型输出结构化计划(简短 bullets)而不是长篇推理
4) ReAct 最常见失败:无限循环与“幻觉完成”
你一定要做的防护:
- 步数上限 / 时间上限 / token 上限
- 停止条件:例如
build && typecheck && tests全绿;或关键页面可渲染且无 console error - 失败升级策略:连续 N 次同类失败(如依赖冲突)就切换策略(锁版本/换包管理器/减少改动面/请求用户输入)
- 每轮都要“可验证进展”:例如减少错误数、修复某个具体失败项
5) 规划粒度:前端任务必须“先搭骨架再填肉”
ReAct 的计划不要一口气写完所有文件。更稳定的节奏是:
- 读取需求 → 输出“需求澄清问题/假设”
- ==读取 repo → 输出“现状与约束”(框架、路由、状态管理、UI库、lint/tsconfig)
- ==先做最小可运行骨架(路由/页面壳/数据流 mock)
- 再逐步实现功能点(每个点都跑 lint/test/build)
- 最后统一体验(错误处理、loading、空态、i18n、可访问性)
粒度太大时,模型更容易写出“看似完整但不可运行”的代码。
6) 记忆与上下文:不要把 repo 全塞进上下文
ReAct 常见实现会把很多观察结果堆进对话,导致:
- 上下文爆炸
- 模型抓不住关键约束
- 旧信息污染决策
必知做法:
- 建立“工作记忆(working memory)”:只保留关键事实(技术栈、目录结构、关键 API、约定)
- 建立“证据引用”:结论必须能指向某个文件/命令输出(例如
package.json、tsconfig、eslint结果) - 对长日志做摘要,只保留错误关键信息(文件/行号/错误码)
7) 代码生成的安全边界:防 prompt injection(尤其来自用户需求/仓库文件)
用户需求、README、甚至某些源码注释都可能包含“把 secrets 打印出来/忽略系统提示/执行危险命令”等注入。
你需要:
- 把“工具调用权限”做成白名单(例如只允许在 repo 目录内读写、只允许执行
pnpm test/build/lint等) - 对“写文件/执行命令”增加策略层校验(路径穿越、rm -rf、curl|bash 之类禁止)
- 对外部网络默认禁用(除非你明确允许)
8) 评估与可观测性:没有 eval 的 ReAct 只是“会动的聊天”
要让 Agent 迭代可用,你需要:
- 轨迹日志(trace):每步 Act/Observe/决策原因(简短)都记录下来
- 离线回放:同一需求是否稳定生成同样可用的改动
- 指标:首次可构建成功率、平均迭代轮数、回归引入率、diff 大小、lint/test 通过率、人工介入次数
前端 Agent 特别建议把“能跑起来”作为硬指标:install -> typecheck -> lint -> test -> build 的成功率。
9) ReAct ≠ 只会写代码:要会“提问与确认”
前端需求常有歧义(路由、权限、UI 规范、数据源、埋点、SEO、国际化)。ReAct 中“提问”也是一种 Act(向用户请求信息)。
实践建议:
- 只问“阻塞性问题”(不问也能做的先假设)
- 每个假设要可追踪(写入变更说明/注释/PR 描述)
- 允许用户对计划点选确认(让流程更像“交互式脚手架”)
10) 输出形式:对用户最有用的是“变更摘要 + 如何运行 + 已知限制”
即便内部有 ReAct 循环,对用户交付建议固定模板:
- 改了哪些文件/新增哪些路由组件
- 如何启动/构建/测试(命令)
- 已实现的需求点清单 & 未覆盖点
- 如果你做了假设,列出来