Agentic ReAct 3.0:多模态记忆与成本控制的工程实践 老规矩先交代一下背景。我手头维护着一个内部的智能Agent框架前后拖了大半年最近终于把两个最头疼的短板补上了一个是多模态记忆一个是成本控制。新的版本我内部编号叫Agentic ReAct 3.0底子还是2022年那套ReAct范式的思路但加入Multimodal Memory和Cost Control之后整个系统的行为方式和原来完全不一样了。这篇文章不是概念介绍是我自己从零改造、踩坑、调优的真实记录。如果你也在做Agent类应用或者正打算把一个只会对话完就忘的ReAct循环升级成能记住、能省钱的生产级系统那这篇应该对你有点用。1. 从ReAct到Agentic ReAct 3.0这次升级到底动了哪根筋1.1 ReAct范式为什么还值得留在2026年先说清楚ReAct是什么。这个名字是Reasoning和Acting的合成词核心思路就是让模型在一个循环里交替做三件事先思考当前情况Thought再决定调用什么工具或执行什么动作Action然后观察工具返回的结果Observation接着再思考、再行动直到任务完成。我见过很多刚接触这个概念的开发者第一反应是这不就是个while循环吗。确实从代码层面看它就是循环但这个循环的价值在于它把推理过程显式地暴露出来每一步的思考都成为下一步行动的输入。用修水管来类比的话传统一次性问答是你直接凭经验报一个维修方案ReAct则是先拧开水龙头看看水流情况再决定是换垫圈还是换整根管子换完之后再开一次水确认是否还漏。这个观察-行动-再观察的闭环让模型在处理多步骤、需要工具协作的任务时可靠性和可控性都上了一个台阶。所以尽管这几年Agent的编排方式层出不穷我个人还是把ReAct当作默认骨架。它不花哨但足够稳定出了问题也容易定位。框架的选择上稳定比新颖重要得多。1.2 传统ReAct在实际项目里的三个老毛病我并不是直接就把老版本推倒重来的。促使我升级的是它在实际生产环境中暴露出来的三个问题。第一个问题是失忆。老版本的ReAct每次任务开始都是一张白纸上一个任务里总结出来的经验、查到的资料、用户的偏好全部随着会话结束被丢进回收站。哪怕用户反复问同一个领域的知识模型每一次都要重新理解、重新搜索、重新推理。这也是ReAct循环最常见的抱怨——它是个很能干的临时工但永远记不住自己昨天干过什么。第二个问题是眼睛和耳朵是摆设。原始的ReAct是基于纯文本的。可现实业务里用户发来的往往是一张截图、一段录音、一个表格文件。你让模型去处理这些它要么说我看不了图片要么得靠人工把内容转成文本再喂进去。这个体验放在2026年已经说不过去了——主流多模态模型的能力早就具备只是你的Agent框架根本没有设计把多模态信息存下来、用起来的通道。第三个问题也是让运维同学最头疼的就是烧钱。一个复杂任务往往要循环十几轮每一轮都要调用大模型。而且模型经常会在同一个问题上反复思考生成一堆意义不大的中间草稿。更麻烦的是因为没记忆同样的信息换个问法又得重新调用一次。月底看账单的时候你才会意识到绝大部分成本都花在了重复获取早已获取过的信息上。1.3 3.0版本的设计目标补系统不是推翻重来所以在设计Agentic ReAct 3.0的时候我给自己定了两条原则第一不推翻ReAct的循环骨架因为它没坏第二把记忆和成本从外围功能提升为框架的核心组件。Multimodal Memory的定位是让Agent在处理文本、图像、音频之后能把这些信息按照统一的方式存下来并在后续任务中主动检索使用。Cost Control的定位是从单次token消耗到整个任务预算建立三层防线让每一分钱都花得明明白白。这两个系统不是互不相关的——记忆存得好重复调用就少成本自然下降成本控制得好又能给记忆系统留出更充裕的调用额度。接下来我分别展开讲。2. Multimodal Memory不是多模态记忆的拼接2.1 统一向量空间让文本、图像、音频进同一个索引多模态记忆的第一步是解决表示问题。文本好办用文本编码器转成向量就行但图像、音频怎么和文本放同一个索引里比较相似度我一开始想过维护三套独立的向量库图像一套、文本一套、音频一套检索的时候分别查再合并。实测下来这个方案的问题很明显跨模态检索几乎不可用你想知道之前用户提过的那张产品截图和当前这段文字描述是否相关两套向量库之间根本没有可比性。后来我改成了统一向量空间的做法给多模态模型加一个向量输出头让文本、图像、音频经过各自的编码器之后映射到同一维度我用的是1536维的向量空间里。这样做的本质是让模型在训练阶段就学会把不同模态的信息对齐到语义相近的位置。比如用户上传一张红色跑鞋的照片和一句用户想买红色跑鞋的文字它们在向量空间里的距离就会比较接近。检索的时候只查一个索引不管是文本还是图片都能用同一套相似度计算逻辑。这里要提醒一句并不是所有的多模态模型都适合直接拿来生成检索向量。我试过一些模型文本向量和图像向量虽然能输出但分布差异很大强行塞进同一个索引之后检索精度很惨。最后我用的方案是拿一个专门的对比学习模型来做向量化再把它嵌入到Agent的工具层里。如果你们也想走这条路先拿一批文本-图像对验证一下跨模态检索的召回率再决定要不要上。2.2 记忆写入不是所有信息都值得记确定了表示层之后第二个问题是什么样的信息要被写入记忆我见过很极端的做法——把Agent每一步的思考过程、每一个工具的返回结果全量塞进记忆库。这样做的后果是记忆库飞快膨胀检索时返回一堆无关信息反而把真正关键的记忆淹没了。我采用的策略是设卡。只有满足以下条件之一的信息才会被写入记忆库工具返回中包含用户明确的偏好或事实性结论比如用户说我只用安卓手机模型在思考中明确标注了这条信息对后续任务可能有价值某个操作踩了坑、报了错并且最终通过修正解决了问题这类经验型记忆最值钱。每条记忆记录都会附带元信息包括时间戳、来源哪一次会话、哪个工具、模态类型、重要性评分1到5以及一个可选的过期时间。这些元信息在检索阶段会发挥非常大的作用千万别省。这里我犯过一个很典型的错误一开始把重要性评分交给模型自己打结果模型倾向于把一切东西都打高分。后来我干脆把评分规则硬编码进框架——凡涉及用户身份、支付、公司政策的关键词自动加权一般性的闲聊内容固定给低分。宁可保守一点也比记忆库变成垃圾场好。2.3 记忆检索让Agent自己决定何时翻记忆记忆系统接入ReAct循环的方式决定了整个框架的可用性。我见过两种实现路线。一种叫自动注入也就是每一轮都把记忆库中和当前输入相关的Top-K条记忆直接塞进上下文。这个方案实现简单但问题很明显上下文很快被历史记忆填满留给当前推理的空间越来越小而且那些看起来相关但已经过时的记忆会干扰当前判断。这在后面踩坑环节里我还会详细说。另一种是主动检索也就是把记忆检索本身做成一个工具。Agent在Thought阶段如果发现自己缺少某方面的背景信息就调用一个search_memory工具传入查询词返回相关记忆。这个方案的好处是记忆只在需要的时候进入上下文成本可控也不容易干扰当前推理。我最终选择的就是这个方案。主动检索的核心是实现一个混合检索策略。纯靠向量相似度不够因为多模态数据里经常有专有名词、编号、型号这类词向量检索对精确匹配不友好。所以我在向量检索之外叠加了一个基于关键词的BM25检索然后把两组结果做融合排序取最终的Top-K。融合规则其实不复杂两边都命中的记录直接加权排前面单边命中的按得分各取一半再按时间戳做轻微降权避免老记忆一直压着新记忆。还有一个细节值得注意检索结果必须带来源和可信度标识。一个记忆如果来自官方文档截图可信度就比来自用户随口一句话高得多。我在返回结果里给每条记忆加了一个source_type字段Agent在后续推理时可以参考这个字段判断该信几分。2.4 遗忘与压缩记忆库不能无限膨胀记忆系统的最后一个大问题是容量管理。你不可能让记忆库无限膨胀下去——向量索引的检索延迟会越来越高存储成本也会变成一笔糊涂账。我给记忆库设计了两个清理机制。第一个是主动遗忘每条记忆都有一个重要度分数定期把低于阈值的记忆归档到冷存储对于设置了过期时间的临时候信息到期直接清除。第二个是摘要压缩当同一个主题下的记忆条数超过5条时触发一次压缩任务把这几条记忆交给大模型生成一段摘要替换掉原始多条记录。压缩会损失细节所以我现在只在记忆条数达到阈值时做并且压缩前的原始记录会保留在冷存储里可以被追溯。到这里你会发现Multimodal Memory其实是一个独立的子系统它有自己的写入策略、检索接口和生命周期管理。这个子系统一旦稳定运行Agent的行为会有一个肉眼可见的变化它不再是每次都从零开始理解问题而是像一个有经验的老员工那样带着之前的积累来处理新任务。3. 成本控制的三层防线从每一次调用到整个任务3.1 Token级控制把每一轮对话都精打细算多模态记忆解决的是重复获取信息的问题但即使有记忆每一轮ReAct循环里的token消耗仍然有大量可压缩空间。Token级的成本控制是我在整个3.0改造中最先做的、也是见效最快的一层。第一个手段是精简系统提示词和工具描述。我们经常习惯性地把工具描述写得非常详细生怕模型不理解。但实际上主流模型对工具描述的理解能力比我们想象中强得多。我把每个工具的description从平均120个词压缩到40个词左右只保留工具名称、输入参数、何时使用、返回数据类型这四个要素。实测下来工具调用的准确率几乎没有下降但每轮循环的输入token少了将近四分之一。第二个手段是结果缓存。ReAct循环里经常出现连续几轮调用同一个工具、查询同一个资源的情况。我在工具调用层加了一个缓存封装对幂等的、返回结果可预期的工具比如查询型工具做LRU缓存缓存key是参数序列化后的哈希值。遇到同样的查询直接返回缓存结果完全不需要再调用一次大模型去理解工具输出。这一项在我们实际项目里省下的token非常可观因为我们的Agent经常要反复查同一个知识库条目。第三个手段是动态上下文截断。ReAct循环跑久了历史observation会越来越长。我原来用的策略是全部保留导致上下文窗口经常报警。后来我改成动态滑动窗口只保留最近3轮完整的Thought/Action/Observation更早的轮次压缩成一段摘要摘要由模型生成存为短文本。需要追溯完整过程时再查记忆库。这个策略要把握好度——截断太狠会让模型丢失关键推理链条太轻则省不了钱。3.2 任务级控制模型路由与降级策略Token级控制解决的是每一轮怎么省但真正让成本结构发生质变的是任务级的模型路由。说白了不是所有问题都需要动用旗舰级的最大参数模型。我把任务按照难度分成三档。第一档是简单任务比如格式化输出、查询返回、单轮知识问答交给一个响应快、价格低的小参数模型就行。第二档是中等任务需要多步骤推理或工具调用用中等规模的模型。第三档才是复杂推理任务比如涉及多模态记忆融合、跨领域分析、需要长程规划的才动用旗舰模型。路由的判断逻辑我一开始用的是硬规则关键词匹配、用户意图分类、上下文长度阈值。但硬规则很容易误判。后来改成了两段式路由先让小模型做一个极简的意图分类只输出一个分类标签根据分类结果再决定是否升级到高一级模型。小模型做分类的token成本极低但准确率足够——毕竟让模型把这事难不难分个类比让它直接解决这个问题简单得多。和路由配套的是降级策略。我给每个任务设了一个预算上限当某个任务的实际消耗快逼近上限时Agent会自动进入降级模式停止调用推理开销大的工具改用更轻量的检索方式或者干脆生成一条当前任务复杂度超出预算建议简化问题的提示。这听起来有点粗暴但在生产环境里这种强制性的刹车能避免账单爆炸。3.3 架构级控制预算追踪器与记忆替换上下文架构级成本控制的核心是给整个Agent运行时加一个全局的预算追踪器。它的作用类似于信用卡账单每一轮循环结束把输入token、输出token、工具调用次数、缓存命中率全部记录到一个结构化日志里同时更新当前任务的剩余预算。追踪器不只是记账它还会主动干预。当发现某类工具调用频繁超预算时追踪器会把这类工具的优先级调低优先返回更便宜的替代工具。当整个任务的成本消耗超过预设的80%时追踪器会触发一次成本告警要求Agent在当前预算内输出一个尽量完整的结论而不是继续无休止地探索。架构级的另一个省钱点是用记忆替换重复上下文。这一点和我的多模态记忆系统是联动的老版本ReAct的上下文里往往重复出现同一大段背景信息新版本里这些背景信息只需要第一次从记忆库检索出来之后每次需要时再检索一次而不是一直塞在上下文窗口里。本质上是把上下文窗口从Agent自身切换到外部记忆系统。这样做不仅省钱还能让上下文窗口释放出更多空间给当下的关键信息。最后一项是异步批处理。有些场景下Agent要处理一批相对独立的请求比如批量审核一批图片。我把这些请求的推理调用合并成批次走批量API而不是逐个实时调用。批处理虽然会牺牲一点响应速度但单价能便宜不少特别适合非交互式的后台任务。4. 一个知识助手项目的完整改造记录与踩坑复盘4.1 项目背景从纯文本问答到多模态知识助手为了让上面的设计有一个具体的落点我拿团队内部的一个知识助手项目做了次完整改造。这个项目我们内部叫它某跨平台知识助手原来就是最原始的ReAct循环用户问一个问题Agent在几个内部知识库里检索、总结、回答。纯文本无记忆成本没有监控。用户一直在抱怨两件事第一我给它发一张软件报错的截图它说看不了第二同样的问题今天问完明天问它还是一副第一次见的样子。改造目标很明确第一支持用户上传截图、录音、文档摘要并把这些信息存入多模态记忆第二跨会话记住用户偏好和历史疑难问题第三把单次问答的平均成本压到原来的50%以下。4.2 踩坑一跨模态检索的排序漂移第一个踩的坑出现在记忆检索环节。我的知识助手上线多模态记忆之后出现了一个怪现象用户明明上传过一张截图截图内容在向量空间里也和当前问题很接近但检索结果总是排在很后面Agent压根看不到。排查下来发现问题不在向量相似度本身而在融合排序。我的BM25关键词检索主要针对文本面对图像时几乎没有有效命中而向量检索虽然命中了却在融合时被BM25的低分拖了后腿。也就是说一个只有向量命中、没有关键词命中的结果和一个两边都有命中的结果相比被严重不公平地压低了。修复方案是在融合算法里加入模态感知权重当查询本身包含图片或查询文本中出现截图照片这张图这类线索时向量检索的权重由0.5提升到0.75关键词权重相应调低。同时图片类记忆在展示给Agent时附上由多模态模型生成的文字描述这样即使后续不直接传图片Agent也能从描述里获取足够信息。4.3 踩坑二记忆污染——旧信息带偏了新判断第二个坑比较隐蔽而且我差点没发现。有一天测试同事反馈用户之前问过某软件新版本支持安卓吗当时的答案是不支持过了一周新版本上线了用户又来问同样的问题。按理说知识库已经更新Agent应该答支持。但我们的Agent在记忆库里检索到了上一次的旧回答把它作为高权重证据得出了不支持的错误结论。这是典型的记忆污染记忆系统本身没错错在它缺少时效性这个维度。我在元信息里其实记录了时间戳但检索策略没有把它用起来。修复分两步。第一步在检索排序中加入时间衰减因子对于事实型记忆如果发布时间距今超过7天相关性得分乘以0.6超过30天乘以0.4。第二步在记忆返回结果中标记obsolete字段——凡是被知识库新版本覆盖过的旧事实置为不可用。这一步需要定期做一次记忆对账拿知识库里最新的权威信息去批量扫描记忆库中同一主题的历史记录发现冲突就把旧记录标为失效。这也是我目前认为最有效的防污染手段。4.4 踩坑三模型路由本身也在烧钱而且烧得毫无知觉成本控制上线后的第一版我预期单任务成本能降一半。实际跑了一周成本确实降了但远没有达到预期。一查日志才发现模型路由模块自己捅了个大篓子——我用小模型做意图分类的那一步每天被调用了上万次单独看单次很便宜但架不住量大而且很多任务是同一类意图重复分类毫无价值。这里的教训是路由分类也要做缓存和去重。我在路由层加了一个意图结果缓存同一个任务的前缀文本比如前100个字符哈希后作为key如果最近30分钟内已经有同类路由结果直接复用不再调用分类模型。这个改动之后路由模块自身的成本几乎可以忽略不计。另外还有一个细节路由判断的时间点也很关键。我原来在每一轮循环都做一次路由判断这本身就会增加额外的token消耗。后来改成首轮路由、后续保持的策略——第一轮决定用哪个档次的模型后续轮次除非任务难度显著变化否则不再改变模型档次。这样既避免了频繁切换带来的成本也保证了推理风格的连贯性。4.5 最终配置参考与实测数据改造完成后的配置我整理了一份简化版本。你们可以根据自己的情况调整参数。agent: name: knowledge-assistant framework: react version: 3.0 memory: enabled: true embedding_dim: 1536 vector_store: local-hnsw hybrid_search: vector_weight: 0.75 keyword_weight: 0.25 top_k: 5 write_policy: min_importance_score: 3 expire_after_days: 90 compress_after_count: 5 retrieval: tool_name: search_memory time_decay_days: 7 decay_factor: 0.6 cost_control: level: token: cache_enabled: true cache_size: 512 description_compress: true task: router_small_model: fast-classifier-v2 router_medium_model: general-v4 router_flagship_model: flagship-v1 intent_cache_ttl: 30m budget: max_cost_per_task: 0.5 warn_at_percent: 80 fallback_mode: soft-stop最终实测一组数据供参考。改造之前我们处理一个典型的用户截图文字描述的知识问答任务平均需要12轮ReAct循环消耗约18万token成本大约0.8美元。改造完成后同样场景平均只需6轮token消耗降到约7万成本降到0.2美元。成本降了75%而回答准确率还提升了几个百分点——因为Agent不再重复踩同样的坑记忆让它越用越聪明。代价也很明确框架复杂度上来了记忆库需要定期维护遗忘和压缩任务本身会吃掉一些后台资源。但权衡下来这点代价换来的是跨会话的经验积累和可控的预算值。最后再分享一个小技巧如果你也想给现有Agent加记忆系统别一上来就搞多模态先跑通纯文本的记忆写入、检索、遗忘闭环让团队看到同样的老问题不用再问第二遍的效果再逐步把图像和音频接进来。循序渐进的节奏无论是从技术风险还是从团队接受度来看都顺畅得多。