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/npmlinttestbuildtypecheck
  • (可选但强烈推荐)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 的计划不要一口气写完所有文件。更稳定的节奏是:

  1. 读取需求 → 输出“需求澄清问题/假设”
  2. ==读取 repo → 输出“现状与约束”(框架、路由、状态管理、UI库、lint/tsconfig)
  3. ==先做最小可运行骨架(路由/页面壳/数据流 mock)
  4. 再逐步实现功能点(每个点都跑 lint/test/build)
  5. 最后统一体验(错误处理、loading、空态、i18n、可访问性)

粒度太大时,模型更容易写出“看似完整但不可运行”的代码。

6) 记忆与上下文:不要把 repo 全塞进上下文

ReAct 常见实现会把很多观察结果堆进对话,导致:

  • 上下文爆炸
  • 模型抓不住关键约束
  • 旧信息污染决策

必知做法:

  • 建立“工作记忆(working memory)”:只保留关键事实(技术栈、目录结构、关键 API、约定)
  • 建立“证据引用”:结论必须能指向某个文件/命令输出(例如 package.jsontsconfigeslint 结果)
  • 对长日志做摘要,只保留错误关键信息(文件/行号/错误码)

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 循环,对用户交付建议固定模板:

  • 改了哪些文件/新增哪些路由组件
  • 如何启动/构建/测试(命令)
  • 已实现的需求点清单 & 未覆盖点
  • 如果你做了假设,列出来