跳到主要内容
ReAct 不是 React,但蹭这个名字是它最值得聊的一步

ReAct 不是 React,但蹭这个名字是它最值得聊的一步

破壁人
破壁人

· 阅读约 7 分钟

我第一次看到 ReAct 的时候,脑子里的第一反应估计跟大多数人一样:React?大写的 A?哪家会议论文这么皮。等发现它跟前端那个库没有半毛钱关系之后,那种感觉就像你看到菜单上写着“招牌牛排”,端上来一盘豆腐——东西也许不差,那口气就是顺不下去。

8 月有人在 dev.to 上发了一篇文章,标题是《Who Named This ReAct? I'd Like to Speak to the Manager.》。这个标题我看了就乐。作者是个真做过前端的人,写过 React、上过 Next.js,从 Vite 加 TypeScript 搭起来的项目里一路走过来。然后她进 AWS AI & ML Scholars 那个 Agentic Engineer Nanodegree 的课,第一次撞见 ReAct 这个词,第一反应跟我们一样——React 前端那个库?等搞明白 ReAct 是 reasoning 加 acting 的合成词,她只憋出一句“我想找经理谈谈”。这个情绪太真实了。但我想说的是,这个情绪本身就是 ReAct 这个名字最成功的地方——它让你记住了,而且几年之后还能在社区里回头吐槽它。命名,尤其是模式命名,威力就在这。

先把这个名字放下,拆正事。论文全名是“ReAct: Synergizing Reasoning and Acting in Language Models”,Yao 那批人写的。它不是一篇有漂亮核心公式的推导型论文,是方法和实验论文。但要拆的承重墙很明确:让语言模型把“推理”和“动作”放在同一个循环里,交错进行。每一步推理都以可观测的文本形式存在,下一步动作由这段文本推理决定。不是先想完再去做,也不是做完再想——是边想边做、做完看一眼结论、再接着想、接着做。原文管这个叫 reasoning traces 和 task-specific actions 以 interleaved 的方式运行。

听上去抽象,画出来就是一个圈:

    Reason ──► Act ──► Observe
       ▲                   │
       └───────────────────┘
          (observation 回来,触发下一轮 reason)

这个 loop 是不是很熟悉?任何一个真正写过 agent 的人,哪怕没读过这篇论文,多半也写过这个东西。工具调用的 agent 是这么写的:模型先产出一段文本思考,决定调用一个函数;函数执行完返回结果;把结果拼回上下文;模型再看结果继续想下一步。代码里就是一个 while 循环在跑,但你跑的就是 ReAct loop。所以从工程上看,ReAct 不是全新发明。它是对“当时已经到处都是的靠大模型走流程、调工具”这个做法的一次正视和命名。论文的贡献不是凭空造出“边想边做”,而是把“想”那一步强制成文本、跟“做”放在同一个可被外部观测的上下文里。

这是关键一步,得讲透。在 ReAct 之前,语言模型也会“想”,但很多设计里那个“想”是隐式的——模型直接输出下一格动作,中间经过哪些念头,你在外面看不出来。ReAct 把它拽出来了:模型必须把每个决策前的推理用自然语言写出来,比如“这个法院网站可能有 api 子路径,我先抓首页看有哪些链接”,然后才调用抓取工具。写出来的这段推理,成了上下文的一部分,下一轮模型读到自己之前写下的“api 子路径”这几个字,才有依据继续往下走。好处是双重的:对外部开发者来说,这段推理轨迹可读、可调试、可审计;对模型自己来说,它把该注意什么写进上下文,等于给自己一个外接缓存,避免在隐向量里把重要线索弄丢。没有这个文本化推理轨迹,ReAct 就只是一个“调工具的老式 agent”;有了它,reasoning 和 acting 才能在同一个 token 流里咬合。这就是这篇论文的承重墙。没有它,整个架构站不住。

回到地面。那篇 dev.to 文章里最值得被拆的不是吐槽名字,是她写到一个细节:她在 OpenAI Build Week 做了一个叫 Verity Lex 的东西,目标是判断一个法院机构是不是“准备好用 AI”。法院网站天然是 agent 探索的噩梦:证据可能藏在 PDF 里、网页标题语焉不详、一个文档的内容会改变你下一步该搜什么。这种情况你没法预先写好一个固定的抓取序列,只能让模型自己边看边决定下一步。于是她的系统架构里,controller 就是一个 model-directed ReAct loop:reasoning、acting、observing、repeating。这里有一个边界选择值得强调——她让模型主导“发现”这个过程,但不让模型决定最终的 readiness 分数。原因两个字:可复现。模型可以决定下一步看哪个页面,但最后打分必须是确定性规则算出来的,不然同一个法院你今天打分 86、明天打分 74,产品没法用。这个边界太关键了。她把“探索”和“结论”拆开,让模型去跑那个不可预测的 loop,让代码去守那个必须可审计的结论。这才是把 ReAct 用到生产系统里该有的分寸感。

ReAct 太常用了,以至于你很可能已经在写 ReAct 而不自知。评论区里有个人说得更直白:他写过 Telegram bot,后来重构得模块化一点,回头才发现自己重新发明了 RAG,还自己写了一套简陋的记忆机制,做完之后才知道这些都有名字、都有论文。这话我信。做 agent 的人往往先写出来后知道名字——顺序反过来的,反而是少数在学校里按教科书顺序学下来的人。所以名字这件事,对一线工程师来说从来不是“你发明得早不早”,而是“你有没有一个把手,能把手上正在做的事讲给团队、讲给投资人、讲给下一个接手的人听”。ReAct 这个把手很好用,但它依然只是个把手,不是库。

这里我承认一句,我对 ReAct 这篇论文的感情是分裂的。一边我认为它把一个真实存在的 pattern 讲清楚了,而且用“文本推理轨迹”这个承重墙把 agent 的可解释性往前推了一步;另一边,这个名字——ReAct——你必须承认这里头有借力。2013 年 5 月 React 开源,那是十几年积累下来的公共记忆。到 2022 年这篇论文出世,React 已经几乎成了“前端”的代名词。你叫 ReAct,后面一堆做 web 的人一眼扫过去就记住了,哪怕记岔了,他也记住了。这种被名字吸进去、后来发现自己记岔了的传播,本身就在给论文涨声势。这不是贬低作者——命名起个能抓人的名字,是论文写作里最被低估的一个环节。ReAct 这名字至少比什么“Interleaved Reasoning and Acting Framework for Language Models”这种一听就想关掉标签页的东西强一万倍。

但我还是想去找经理聊聊。为什么?因为这个名字蹭得聪明,但它偷走了一层意思。在 React 那个世界里,一个名字对应一个具体的库、一套语法、一个运行时、一批工具链。你把 React 装进项目里,它能跑,它有官方维护者,有版本号。ReAct 不是。ReAct 是一篇论文和一个后来被所有人念叨的 pattern,它没有给你那个 while 循环之外的任何实现;你还是要自己管上下文长度、管工具返回的脏数据、管什么时候停、管循环死锁。这就是 pattern 命名论文最大的问题——它霸占了对话,但交付物几乎为零。评论区那个 Nyx533 说得好:pattern-naming 论文常常主导讨论,但真正需要的是可复用工具,而且他建议给工具起 boring names。这个评价放在 2022 年以来整个 agent 论文生态里看,几乎是照妖镜级别。

搞清楚它的承重墙是什么——让推理以文本形式暴露出来、跟动作交错在同一循环里——比纠结它跟 React 有没有关系重要得多。这个 pattern 你八成已经在自己的 while 循环里写过,而你现在知道它有个名字,不代表你原来的代码就比论文落后。知道名字真正的价值,是让你能站在 Yao 他们那个视角重新看自己写的循环:哪一段是 reasoning,哪一段是 acting,我是不是把 reasoning 藏起来了、导致整个系统变成黑盒。答案如果是“藏起来了”,那你从 ReAct 里真正学到的第一课就有了。

这堵墙我替你拆了,但循环能不能改对,还得回到你自己的代码里看。别让摘要替你下结论。

破壁人
破壁人

把高冷论文拆成中文开发者能懂的:核心 idea、公式推导、示意图、工程联系。

查看主页 →