1. 原型跑通不等于能上线,问题到底出在哪
RAG 和 Agent 这两个词,过去一年在各种技术社区里几乎被聊烂了。单独拿 RAG 来说,用向量库加一个大模型,把文档切片、做嵌入、检索、拼 prompt,半天就能跑出一个能回答问题的 Demo。单独拿 Agent 来说,定义几个工具函数,写一个 ReAct 循环,让模型自己决定调哪个工具,也能很快看到效果。分开写的时候,每个模块都挺听话,测试用例也都能过。
但一旦把两者合到一起,准备上线,事情就开始不对劲了。我见过太多团队卡在这个阶段:本地跑得好好的,一上测试环境就开始超时;并发稍微高一点,检索结果就开始串;Agent 调了几轮工具之后,回答质量断崖式下跌;更离谱的是,同样的输入,两次请求返回的结果完全不一样,排查起来毫无头绪。
这个问题的本质,不是 RAG 不行,也不是 Agent 不行,而是两个独立系统在工程层面耦合时,暴露出了原型阶段被掩盖的所有问题。原型阶段我们关注的是“能不能跑通”,而生产环境关注的是“能不能稳定、可控、可观测地跑通”。这两者之间的差距,远比大多数人想象的要大。
这篇文章面向的是已经写过 RAG 或 Agent 原型、正准备往生产环境推进的开发者。我会从架构设计、核心细节、实操落地、问题排查四个维度,把从原型到生产这条路上踩过的坑和对应的工程化方案完整拆一遍。不管你是刚跑通第一个 Demo,还是已经在做上线前的压测,应该都能从中找到对自己有用的东西。
2. 合体上线的核心矛盾与架构思路拆解
2.1 为什么分开写容易,合起来就崩
先说清楚一个基本事实:RAG 和 Agent 分开写的时候,它们各自运行在一个相对封闭、边界清晰的环境里。RAG 的输入是用户问题,输出是一段拼接好的上下文和回答;Agent 的输入是一个目标,输出是一系列工具调用和最终结果。两者的状态管理、错误处理、超时控制都是独立的。
合体之后,情况变了。Agent 需要在推理过程中动态决定是否调用检索工具,检索回来的内容又要作为下一轮推理的输入,推理结果可能触发新的工具调用,新的工具调用又可能再次触发检索。这个循环一旦形成,状态空间就爆炸了。
我画一个简单的对比你就明白了。单独 RAG 的调用链是线性的:问题 → 检索 → 生成 → 返回。单独 Agent 的调用链是循环的,但循环的每一步都是确定的工具调用。合体之后,调用链变成了嵌套循环:外层是 Agent 的推理循环,内层是 RAG 的检索生成循环,而且内层的输出会影响外层的决策,外层的决策又会改变内层的检索策略。
这种嵌套结构带来的直接后果有三个。第一,延迟不可控。一次用户请求可能触发多轮检索和多轮工具调用,每轮都有网络开销和模型推理开销,累加起来很容易超过前端等待阈值。第二,错误会级联放大。检索返回了一条不相关的文档,Agent 基于这条文档做了一个错误决策,错误决策又触发了一个不该调用的工具,最终结果完全跑偏。第三,状态难以追踪。出了问题之后,你很难快速定位是哪一轮检索、哪一次工具调用、哪一个 prompt 拼接环节出了错。
2.2 合体架构的三种常见模式与选型逻辑
在实际工程中,RAG 和 Agent 的合体方式大致有三种模式,每种模式的复杂度和适用场景不同。
第一种是检索前置模式。用户问题先经过 RAG 检索,把检索结果作为上下文注入 Agent 的初始 prompt,然后 Agent 在这个上下文基础上进行推理和工具调用。这种模式的好处是检索只发生一次,延迟可控,状态简单。缺点是 Agent 在推理过程中无法动态调整检索策略,如果初始检索结果不理想,后续推理就会一直受限于这个不好的上下文。
第二种是检索即工具模式。把 RAG 封装成一个工具函数,Agent 在推理过程中可以随时调用。这种模式灵活度最高,Agent 可以根据当前推理状态决定什么时候检索、检索什么。但问题也很明显:Agent 可能频繁调用检索工具,导致延迟飙升;也可能该调用的时候不调用,完全依赖自身知识胡编。
第三种是混合模式。初始阶段做一次前置检索,保证 Agent 有一个基本的上下文底座,同时在 Agent 的工具集中保留检索工具,允许它在必要时进行补充检索。这种模式在灵活性和可控性之间取了一个平衡,也是我在实际项目中用得最多的方案。
选型的核心判断依据是你的业务场景对延迟和准确率的容忍度。如果是客服问答这类对延迟敏感、问题相对固定的场景,检索前置模式就够了。如果是复杂的研究分析类任务,需要多轮信息收集和推理,那检索即工具模式或者混合模式更合适。但无论选哪种,都需要在工程层面做好超时控制、重试策略和降级方案。
2.3 生产环境对系统的四个硬性要求
原型阶段我们只关心功能,生产环境必须同时满足四个要求,缺一不可。
可观测性是第一条。每一次请求的完整调用链,包括检索了哪些文档、Agent 做了哪些决策、调用了哪些工具、每步耗时多少,都必须有日志记录。没有可观测性,出了问题就是盲人摸象。
确定性是第二条。同样的输入,在同样的上下文条件下,系统应该给出稳定可预期的输出。大模型本身有随机性,但工程层面可以通过固定随机种子、缓存检索结果、限制工具调用轮数等手段来提高确定性。
容错性是第三条。检索服务挂了怎么办?模型接口超时怎么办?工具调用返回了异常格式怎么办?这些在原型阶段可以不管,在生产环境必须每一个都有对应的处理策略。
成本可控是第四条。Agent 多轮推理加上多轮检索,token 消耗可能是单次 RAG 的十倍甚至几十倍。如果不做成本控制,账单会教你做人。
3. 核心细节解析与实操要点
3.1 检索层的工程化改造:从“能搜到”到“搜得稳”
原型阶段的检索通常很简单:把文档切片,做嵌入,存向量库,查询时取 top-k。但生产环境里,检索层需要做大量工程化改造。
切片策略是第一道关。固定长度切片是最省事的做法,但效果往往不好。我一般会采用语义切片加滑动窗口的方式:先按段落或标题做语义分割,保证每个切片在语义上相对完整,然后在切片之间保留一定的重叠区域,避免关键信息被切断。切片长度控制在 300 到 500 个 token 之间,这个范围在检索精度和上下文完整性之间比较平衡。
多路召回是第二道关。单一向量检索在某些场景下召回率不够,我会同时使用向量检索和关键词检索,然后把两路结果做融合排序。向量检索擅长语义匹配,关键词检索擅长精确匹配,两者互补。融合排序可以用简单的加权分数,也可以用更复杂的重排序模型。
检索结果过滤是第三道关。不是所有检索回来的内容都值得塞进 prompt。我会设置一个相关性阈值,低于阈值的直接丢弃;同时做去重处理,避免相似内容重复占用上下文窗口。如果检索结果为空或者全部低于阈值,系统应该有一个明确的降级策略,比如返回“暂时无法回答该问题”而不是硬编。
# 检索层核心参数配置示例 RETRIEVAL_CONFIG = { "chunk_size": 400, # 切片长度(token) "chunk_overlap": 80, # 切片重叠长度 "top_k_vector": 10, # 向量检索召回数量 "top_k_keyword": 10, # 关键词检索召回数量 "final_top_k": 5, # 最终注入 prompt 的数量 "relevance_threshold": 0.65, # 相关性阈值 "rerank_model": "cross-encoder" # 重排序模型类型 }注意:相关性阈值的设定需要根据你的嵌入模型和业务数据做校准,不能直接照搬。建议先用一批标注数据跑一遍,看分数分布再定阈值。
3.2 Agent 决策循环的边界控制
Agent 的决策循环是合体系统中最容易失控的部分。原型阶段我们通常只限制一个最大轮数,比如 10 轮,但生产环境需要更精细的控制。
轮数限制要分层次。我会设置三个级别的限制:单次请求的总轮数上限、连续检索调用的次数上限、连续同一工具调用的次数上限。总轮数上限防止无限循环,连续检索上限防止 Agent 陷入“检索-不满意-再检索”的死循环,连续同一工具上限防止某个工具被反复调用。
超时控制要分阶段。整个请求有一个总超时时间,比如 30 秒;每一轮推理有一个单轮超时,比如 8 秒;每一次工具调用有一个调用超时,比如 5 秒。任何一级超时触发,都要有对应的处理逻辑:单轮超时可以重试一次,工具调用超时可以跳过该工具继续推理,总超时则直接返回当前已得到的最佳结果。
决策收敛要有引导。在 Agent 的 prompt 里,我会明确告诉它:如果已经收集到足够的信息,应该尽快给出最终答案,而不是继续调用工具。同时,在每一轮推理后,我会检查当前已收集的信息是否足以回答用户问题,如果足够,就主动终止循环,不再等待 Agent 自己决定。
# Agent 循环控制参数 AGENT_CONFIG = { "max_total_turns": 8, # 总轮数上限 "max_retrieval_calls": 3, # 连续检索调用上限 "max_same_tool_calls": 2, # 同一工具连续调用上限 "total_timeout": 30, # 总超时(秒) "per_turn_timeout": 8, # 单轮超时(秒) "tool_call_timeout": 5, # 工具调用超时(秒) "enable_early_stop": True # 启用提前终止 }3.3 上下文窗口的动态管理
合体系统里,上下文窗口是最稀缺的资源。Agent 的推理历史、检索回来的文档、工具调用的结果,全都要塞进上下文窗口。如果不做管理,很快就会超出模型的最大 token 限制。
我的做法是分层管理加动态裁剪。把上下文分成四个区域:系统指令区、检索文档区、推理历史区、当前轮输入区。系统指令区固定不变,占用最少;检索文档区按相关性排序,优先保留高分文档;推理历史区只保留最近几轮的关键信息,更早的历史做摘要压缩;当前轮输入区保证完整。
当总 token 数接近模型上限时,裁剪顺序是:先压缩推理历史,再减少检索文档数量,最后才动系统指令。系统指令是 Agent 行为的基石,任何时候都不应该被裁剪。
实操心得:我习惯在每轮推理后计算一次当前上下文的 token 占用,如果超过阈值就触发裁剪。这个计算可以用 tiktoken 之类的库来做,开销很小,但能避免很多“莫名其妙”的失败。
3.4 工具调用的幂等性与错误隔离
Agent 调用的工具可能是查询数据库、调用外部 API、执行计算任务等等。这些工具在生产环境里必须满足两个要求:幂等性和错误隔离。
幂等性意味着同一个工具用同样的参数调用多次,结果应该一致。这对于重试机制至关重要。如果工具不幂等,重试就可能导致数据重复写入或者其他副作用。对于不幂等的工具,我会在调用层做去重:记录本次请求中已经调用过的工具和参数组合,如果重复出现,直接返回缓存结果。
错误隔离意味着一个工具调用失败不应该导致整个请求崩溃。每个工具调用都应该被 try-catch 包裹,失败时返回一个结构化的错误信息给 Agent,让 Agent 决定是重试、换工具还是放弃。同时,工具调用失败要记录日志,方便后续排查。
# 工具调用封装示例 def safe_tool_call(tool_name, params, cache, timeout=5): cache_key = f"{tool_name}:{hash(str(params))}" if cache_key in cache: return cache[cache_key] try: result = execute_with_timeout(tool_name, params, timeout) cache[cache_key] = result return {"status": "success", "data": result} except TimeoutError: return {"status": "timeout", "message": "工具调用超时"} except Exception as e: return {"status": "error", "message": str(e)}4. 实操过程与核心环节实现
4.1 环境准备与依赖选型
在动手搭建之前,先把技术栈定下来。我的选型原则是:成熟优先、可替换、可观测。
向量库方面,原型阶段用内存版或者轻量级本地库就够了,生产环境建议用支持持久化和水平扩展的方案。嵌入模型可以选择 API 调用或者本地部署,API 调用省事但有网络延迟和成本,本地部署可控但需要 GPU 资源。大模型方面,推理能力强的模型适合做 Agent 的决策核心,响应快的模型适合做检索结果的初步筛选和摘要。
| 组件 | 原型阶段选择 | 生产环境建议 | 选型理由 |
|---|---|---|---|
| 向量库 | 内存向量索引 | 支持持久化的向量数据库 | 数据不丢失,支持并发查询 |
| 嵌入模型 | API 调用 | 本地部署或 API 加缓存 | 降低延迟和成本 |
| 大模型 | 单一模型 | 大小模型分工 | 决策用大模型,筛选用小模型 |
| 缓存 | 无 | Redis 或内存缓存 | 减少重复检索和重复推理 |
| 日志 | 结构化日志系统 | 支持调用链追踪 |
依赖安装这块,我建议用虚拟环境隔离,把 RAG 相关依赖和 Agent 相关依赖分开管理,避免版本冲突。特别是嵌入模型库和 Agent 框架可能对同一个底层库有不同版本要求,提前隔离能省很多事。
4.2 检索服务的搭建与参数调优
检索服务的搭建分四步:文档加载、切片、嵌入、入库。
文档加载阶段要注意格式兼容性。PDF、Word、Markdown、HTML 的解析方式不同,我一般会统一转成纯文本再处理。解析过程中要保留标题层级和段落结构,这些信息对后续切片很有帮助。
切片阶段的核心参数是切片长度和重叠长度。我的经验值是:技术文档切片长度 400 token 左右,重叠 80 token;对话记录切片长度 200 token 左右,重叠 40 token。切片太长会导致检索精度下降,太短会导致上下文不完整。
嵌入阶段要注意批量处理和错误重试。大批量文档嵌入时,分批发送,每批 100 条左右,失败的重试两次,仍然失败就记录到失败列表,不影响其他文档入库。
入库之后,用一批测试问题做检索效果评估。我会看三个指标:召回率(相关文档是否被检索到)、精确率(检索结果中相关文档的比例)、排序质量(最相关的文档是否排在前面)。根据评估结果调整切片参数和检索参数。
# 检索效果评估示例 def evaluate_retrieval(test_queries, ground_truth, retriever): metrics = {"recall": [], "precision": [], "mrr": []} for query, relevant_ids in zip(test_queries, ground_truth): results = retriever.search(query, top_k=10) retrieved_ids = [r.id for r in results] # 召回率 recall = len(set(retrieved_ids) & set(relevant_ids)) / len(relevant_ids) metrics["recall"].append(recall) # 精确率 precision = len(set(retrieved_ids) & set(relevant_ids)) / len(retrieved_ids) metrics["precision"].append(precision) # MRR(平均倒数排名) for i, rid in enumerate(retrieved_ids): if rid in relevant_ids: metrics["mrr"].append(1 / (i + 1)) break else: metrics["mrr"].append(0) return {k: sum(v) / len(v) for k, v in metrics.items()}4.3 Agent 推理循环的完整实现
Agent 推理循环的实现,我拆成六个步骤。
第一步,初始化上下文。把系统指令、用户问题、前置检索结果(如果采用混合模式)组装成初始 prompt。
第二步,调用模型推理。把当前上下文发给大模型,获取模型的输出。输出可能是最终答案,也可能是工具调用请求。
第三步,解析模型输出。如果是最终答案,进入第七步;如果是工具调用请求,提取工具名称和参数。
第四步,执行工具调用。根据工具名称路由到对应的工具函数,带上超时和错误处理。
第五步,处理工具结果。把工具返回的结果格式化后追加到上下文,同时更新调用计数和缓存。
第六步,检查循环条件。如果达到轮数上限、超时、或者检测到可以提前终止,就强制模型给出最终答案;否则回到第二步。
第七步,返回最终结果。把最终答案、调用链日志、耗时统计一起返回。
# Agent 推理循环核心逻辑 def agent_loop(user_query, tools, config): context = build_initial_context(user_query) call_history = [] for turn in range(config["max_total_turns"]): # 检查总超时 if time.time() - start_time > config["total_timeout"]: break # 模型推理 response = call_llm(context, timeout=config["per_turn_timeout"]) # 解析输出 parsed = parse_response(response) if parsed["type"] == "final_answer": return build_result(parsed["content"], call_history) # 工具调用 tool_name = parsed["tool_name"] tool_params = parsed["tool_params"] tool_result = safe_tool_call(tool_name, tool_params, cache) # 更新上下文 context = append_tool_result(context, tool_name, tool_result) call_history.append({"turn": turn, "tool": tool_name, "result": tool_result}) # 检查提前终止条件 if should_stop_early(context, call_history, config): context = append_stop_instruction(context) # 达到上限,强制生成最终答案 final = call_llm(context + "\n请基于以上信息给出最终答案。") return build_result(final, call_history)4.4 缓存策略与性能优化
缓存是合体系统性能优化的关键手段。我在三个层面做缓存。
检索结果缓存:同样的查询在短时间内重复出现时,直接返回缓存的检索结果。缓存键是查询文本的哈希,过期时间设置 5 到 10 分钟。这个缓存能大幅减少向量库的查询压力。
嵌入缓存:文档切片和查询文本的嵌入向量缓存起来,避免重复计算。嵌入计算是 CPU 或 GPU 密集型操作,缓存命中率通常很高。
模型推理缓存:对于完全相同的上下文,缓存模型的输出。这个缓存命中率相对较低,但在一些高频重复场景下很有用。需要注意的是,如果模型输出有随机性,缓存时要固定随机种子。
# 多级缓存配置 CACHE_CONFIG = { "retrieval_cache": {"ttl": 600, "max_size": 10000}, "embedding_cache": {"ttl": 3600, "max_size": 50000}, "llm_cache": {"ttl": 300, "max_size": 5000} }实操心得:缓存键的生成要小心。查询文本直接哈希可能因为标点或空格差异导致缓存不命中,我一般会先做标准化处理(去多余空格、统一标点),再哈希。
5. 常见问题与排查技巧实录
5.1 超时问题的分层排查方法
超时是合体系统上线后最常见的问题。排查超时要分层进行,不能笼统地看总耗时。
第一层,检索耗时。如果检索耗时超过 2 秒,说明向量库查询慢或者嵌入计算慢。检查向量库索引是否合理、嵌入模型是否在 CPU 上跑、是否有大量并发查询。
第二层,模型推理耗时。如果单轮推理超过 5 秒,说明模型响应慢或者上下文太长。检查模型服务负载、上下文 token 数是否接近上限、是否有不必要的长文本输入。
第三层,工具调用耗时。如果某个工具调用超过 3 秒,检查该工具依赖的外部服务是否正常、网络是否有抖动、是否有重试导致的额外延迟。
第四层,循环轮数。如果总轮数接近上限,说明 Agent 决策效率低。检查 prompt 是否清晰、工具描述是否准确、是否有工具反复调用但结果不理想的情况。
| 问题现象 | 可能原因 | 排查方法 | 解决方向 |
|---|---|---|---|
| 总耗时超 30 秒 | 循环轮数过多 | 查看调用链日志 | 收紧轮数上限 |
| 单轮推理超 8 秒 | 上下文过长 | 统计 token 数 | 启用上下文裁剪 |
| 检索超 2 秒 | 向量库负载高 | 查看向量库监控 | 加缓存或扩容 |
| 工具调用超 5 秒 | 外部服务慢 | 单独测试工具 | 加超时和降级 |
5.2 检索结果不相关的排查思路
检索结果不相关,Agent 基于不相关的上下文推理,最终答案自然好不了。排查这个问题,我一般按以下顺序来。
先看查询改写。用户原始问题可能很短或者很口语化,直接拿去做向量检索效果不好。我会在检索前加一步查询改写,用模型把用户问题改写成更适合检索的形式。这一步能显著提升召回质量。
再看切片质量。如果切片本身语义不完整,检索再准也没用。随机抽一批切片出来人工检查,看是否包含完整的信息单元。
然后看嵌入模型。不同的嵌入模型在不同领域的数据上表现差异很大。如果通用嵌入模型在你的专业领域数据上效果不好,考虑换一个在该领域微调过的模型,或者用领域数据做一次微调。
最后看重排序。如果召回阶段没问题但排序不好,加一个重排序模型。重排序模型对 top-20 的结果做精细打分,把最相关的排到前面,效果通常比单纯依赖向量相似度好很多。
5.3 Agent 决策跑偏的典型场景与修复
Agent 决策跑偏有几个典型场景,我逐个说。
场景一:该检索时不检索。Agent 觉得自己知道答案,直接编了一个。修复方法是在系统指令里明确要求:涉及事实性问题必须先检索,检索不到才能说不知道。
场景二:反复检索同一内容。Agent 对检索结果不满意,反复用相似的查询去检索。修复方法是设置连续检索次数上限,同时在 prompt 里告诉它:如果连续两次检索结果相似,应该基于现有信息给出答案。
场景三:工具参数错误。Agent 调用了正确的工具但参数格式不对。修复方法是把工具的参数 schema 写清楚,在 prompt 里给出参数示例,同时在工具调用层做参数校验和自动修正。
场景四:忽略工具返回的错误。工具返回了错误信息,Agent 当作正常结果继续推理。修复方法是在工具返回的错误信息里加上明确的标记,比如[ERROR],并在 prompt 里告诉 Agent 遇到错误标记时应该怎么处理。
5.4 成本失控的预警与优化
成本失控通常来得悄无声息。等发现的时候,账单已经翻了好几倍。我一般会设置三个预警线。
单请求 token 消耗预警:如果单个请求的 token 消耗超过阈值(比如 10000 token),记录日志并告警。这说明上下文管理可能出了问题。
日 token 消耗预警:设置日消耗上限,达到 80% 时告警,达到 100% 时触发限流。
单用户频率预警:如果某个用户在短时间内发起大量请求,触发频率限制。这可能是恶意使用,也可能是程序 bug 导致的循环调用。
优化方向主要是三个:减少不必要的检索调用、压缩上下文长度、用小模型做初步筛选。特别是第三点,用一个小模型先判断检索结果是否相关,只把相关的传给大模型,能省不少 token。
5.5 上线前的检查清单
上线前我会过一遍这个清单,每一项都确认无误才发布。
- 检索层:切片参数已校准、相关性阈值已评估、缓存已启用、降级策略已配置
- Agent 层:轮数上限已设置、超时已配置、工具幂等性已验证、错误处理已覆盖
- 上下文管理:token 计数已接入、裁剪策略已测试、系统指令不可裁剪
- 可观测性:调用链日志已接入、关键指标已监控、告警规则已配置
- 成本控制:token 消耗已监控、限流已配置、缓存命中率已统计
- 压测:并发 10 请求下无超时、无错误、结果稳定
注意:压测一定要用真实数据,不要用构造的简单问题。真实数据的分布和复杂度才能暴露真正的问题。
6. 从原型到生产的经验沉淀
6.1 我踩过的三个印象最深的坑
第一个坑是检索缓存键设计不当。早期我用查询文本的原始字符串做缓存键,结果用户输入多一个空格、少一个标点,缓存就不命中,缓存形同虚设。后来改成先标准化再哈希,命中率从 30% 提升到 70% 以上。
第二个坑是Agent 循环没有提前终止机制。有一次线上请求,Agent 连续检索了 12 轮,每轮都返回相似但不完全一样的结果,Agent 就一直觉得“还不够”,继续检索。总耗时 45 秒,用户早就关了页面。后来加了提前终止判断,连续两轮检索结果相似度超过阈值就强制收敛。
第三个坑是上下文裁剪把系统指令裁掉了。有一次裁剪逻辑写错了,优先裁掉了系统指令,结果 Agent 完全失去了行为约束,开始胡乱调用工具。这个 bug 排查了很久,因为日志里看不出明显异常,只是行为变得很奇怪。后来把系统指令设为不可裁剪,问题才解决。
6.2 关于合体系统的一些个人体会
做了几个合体项目之后,我最大的体会是:RAG 和 Agent 的合体,难点不在 AI 部分,而在工程部分。模型能力决定了上限,但工程实现决定了下限。一个工程做得好的系统,即使模型能力一般,也能稳定输出可用的结果;一个工程做得差的系统,即使模型再强,上线后也会问题百出。
另一个体会是不要追求一步到位。我见过一些团队,一开始就想做一个全能的 Agent,什么工具都接,什么场景都覆盖。结果复杂度失控,根本没法调试。我的建议是:先做检索前置模式,把 RAG 做稳;然后逐步引入一两个工具,观察 Agent 的决策行为;最后再开放更多工具和更复杂的检索策略。每一步都确保稳定之后再走下一步。
还有一个体会是日志和监控的投入永远不亏。合体系统的调用链很长,没有完善的日志,出了问题根本无从下手。我在项目初期就接入了结构化日志和调用链追踪,虽然前期花了一些时间,但后期排查问题的效率提升了好几倍。
6.3 后续可以继续优化的方向
如果基础版本已经稳定运行,可以考虑几个优化方向。
检索策略的自适应:根据用户问题的类型自动选择检索策略。事实型问题用精确检索,分析型问题用多路召回加重排序。
Agent 决策的缓存复用:把常见的决策路径缓存起来,相似问题直接复用之前的决策序列,减少模型推理次数。
多模型协作:用小模型做意图识别和工具路由,用大模型做复杂推理和最终生成,在成本和效果之间找更好的平衡。
用户反馈闭环:收集用户对回答的反馈,用反馈数据持续优化检索排序和 Agent 的 prompt。
这些方向不需要一次性全做,根据业务需求和资源情况逐步推进就行。关键是先把基础版本做稳,有了稳定的基线,后续优化才有意义。