基于LlamaIndex的RAG多智能体实战:手机客服从检索到下单全流程

发布时间:2026/10/14 5:03:22
基于LlamaIndex的RAG多智能体实战:手机客服从检索到下单全流程 1. 从一份手机机型数据集说起为什么客服场景是RAG多智能体的最佳练兵场手机客服这个场景看起来简单实际上是个麻雀虽小五脏俱全的典型。用户问的问题五花八门有人想知道某款机型的电池容量有人要对比两个品牌的拍照参数有人直接说我要买那个最薄的还有人问完参数之后紧接着就要下单。这四种需求分别对应了信息检索、对比分析、意图理解和任务执行四个层次的能力单一机器人很难全部覆盖。我最初做这个项目的时候想法很朴素拿一份手机机型数据集用LlamaIndex搭个RAG问答就完事了。结果实际跑起来才发现用户的问题一旦涉及帮我对比一下A和B或者我要买C单纯的向量检索就力不从心了。检索能返回相关文档片段但它不知道怎么组织对比逻辑更不知道怎么触发下单流程。这就是多智能体架构切入的地方。我的思路是把客服场景拆成三层能力——底层是RAG检索负责从手机机型数据集里精准捞出参数信息中层是品牌专家路由根据用户提到的品牌把请求分发给对应的专家Agent上层是多智能体协作处理下单这种需要多个步骤、多个角色配合的复杂任务。整个项目的数据基础是一份结构化的手机机型数据集包含品牌、型号、价格、屏幕尺寸、处理器、摄像头参数、电池容量、重量等字段。这份数据集既是RAG的知识库也是路由判断的依据还是下单流程中确认商品信息的来源。一份数据贯穿三种能力这是我觉得这个项目最值得分享的地方。适合谁来参考这篇内容如果你已经了解LlamaIndex的基本用法知道什么是Index、Retriever、QueryEngine但不知道怎么把这些组件组织成一个能处理真实业务场景的系统那这篇就是写给你的。如果你还没接触过LlamaIndex也没关系我会在关键步骤解释原理你跟着走一遍也能跑通。接下来我会按照实际搭建的顺序从数据集准备开始一步步走到多智能体协作下单把每个环节的设计理由、踩过的坑、以及实测有效的参数配置都讲清楚。2. 手机机型数据集的准备与索引策略选择2.1 数据集字段设计与结构化处理手机机型数据集的质量直接决定了整个系统的上限。我一开始图省事只放了品牌、型号、价格三个字段结果用户问哪个手机拍照好的时候检索出来的结果完全没法用。后来我把字段扩展到了十几个维度包括基础信息品牌、型号、发布时间、价格区间屏幕参数尺寸、分辨率、刷新率、材质性能参数处理器型号、运行内存、存储容量影像参数主摄像头像素、超广角、长焦、光学防抖续航参数电池容量、充电功率、无线充电支持物理参数重量、厚度、材质、防水等级每个字段我都尽量用自然语言描述而不是纯数值。比如电池容量不写5000而是写5000mAh大电池支持67W有线快充。这样做的好处是向量检索时能匹配到更丰富的语义用户问续航久的手机也能命中。数据处理上我把每条机型记录转成一个Document对象metadata里保留品牌和型号两个关键字段。metadata在后面做路由和过滤的时候会派上大用场。from llama_index.core import Document doc Document( text某品牌X12024年发布6.7英寸AMOLED屏幕分辨率2712x1220120Hz刷新率..., metadata{brand: 某品牌, model: X1, category: 旗舰} )注意metadata的字段不要太多否则索引体积会膨胀。我只保留了品牌、型号、类别三个其他信息全部放在text里让向量去处理。2.2 为什么选VectorStoreIndex而不是其他索引LlamaIndex提供了多种索引类型常用的有VectorStoreIndex、SummaryIndex、KeywordTableIndex等。这个项目我选的是VectorStoreIndex理由有三个第一客服场景中用户的提问方式高度多样化。同一个意思可能有十几种问法比如电池多大续航怎么样能用多久电量耐不耐用关键词索引很难覆盖全但向量索引可以。第二我需要做语义相似度排序。用户问最薄的手机向量检索能返回厚度参数最接近的几条记录而关键词索引只能做精确匹配。第三VectorStoreIndex和后续的RouterQueryEngine、SubQuestionQueryEngine配合得最好这是LlamaIndex生态里最成熟的组合。分块策略上我试过按固定长度切分和按字段切分两种方案。固定长度切分的问题是会把一个机型的参数拆到两个chunk里检索时可能只召回一半信息。按字段切分又太碎一个机型产生十几个chunk检索结果冗余严重。最后我的方案是一个机型一个Document不切分。因为单条机型记录的文本长度通常在300-500字之间完全在embedding模型的最佳处理范围内。这样检索出来的每条结果都是完整的机型信息不会出现信息碎片。2.3 Embedding模型的选择与实测对比Embedding模型我对比了三个方案默认的text-embedding-ada-002、开源的BGE-large-zh、以及多语言版本的E5-large。实测下来在中文手机参数这个场景里BGE-large-zh的检索准确率最高。我用了50条测试query做验证BGE-large-zh的Top-3命中率是92%ada-002是86%E5-large是88%。差距主要出现在一些中文特有的表达上比如拍照神器续航怪兽这类口语化描述BGE对中文语义的捕捉更准。不过BGE-large-zh需要本地部署对硬件有要求。如果你只是做原型验证用ada-002也够用等验证完再换也不迟。from llama_index.embeddings.huggingface import HuggingFaceEmbedding embed_model HuggingFaceEmbedding( model_nameBAAI/bge-large-zh-v1.5, max_length512 )提示换embedding模型之后一定要重建索引不同模型的向量空间不兼容混用会导致检索结果完全错乱。3. 单机器人问答RAG检索链的搭建与调优3.1 基础检索链的组装单机器人问答是整个系统的地基。我用的组合是VectorStoreIndex RetrieverQueryEngine这是LlamaIndex里最经典的RAG链路。from llama_index.core import VectorStoreIndex from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.retrievers import VectorIndexRetriever index VectorStoreIndex.from_documents(documents, embed_modelembed_model) retriever VectorIndexRetriever(indexindex, similarity_top_k3) query_engine RetrieverQueryEngine(retrieverretriever)similarity_top_k设成3是我反复测试后的选择。设成1的话遇到需要对比的问题信息不够设成5的话无关信息太多会干扰LLM的判断。3是一个平衡点既能覆盖大部分问题又不会引入太多噪声。3.2 检索质量调优从能查到到查得准基础链路跑通之后我发现检索质量还有很大提升空间。主要问题有两个一是相似机型之间的参数容易混淆比如同一品牌的两个型号检索时经常把A的参数安到B头上二是用户问最XX的手机这种比较级问题时向量检索返回的不一定真的是最值。第一个问题我通过metadata过滤解决。在检索时加上品牌或型号的过滤条件缩小检索范围from llama_index.core.vector_stores import MetadataFilters, MetadataFilter filters MetadataFilters(filters[ MetadataFilter(keybrand, value某品牌) ]) retriever VectorIndexRetriever( indexindex, similarity_top_k3, filtersfilters )第二个问题需要引入后处理。LlamaIndex提供了SimilarityPostprocessor和KeywordNodePostprocessor但对付比较级问题不太够。我的做法是在Prompt里明确要求LLM做数值比较并且把检索结果里的关键参数用结构化格式呈现给LLM。3.3 Prompt模板的设计心得Prompt模板我改了七八版才稳定下来。最初用的是LlamaIndex默认模板回答经常跑偏要么答非所问要么把检索到的所有信息一股脑倒出来。核心问题在于默认模板没有约束LLM的回答范围。我的改进思路是第一明确角色定位。在system prompt里写清楚你是一个手机客服助手只回答与手机参数相关的问题。第二要求引用来源。让LLM在回答时标注信息来自哪个机型这样用户能验证准确性我也能通过日志排查问题。第三处理不知道的情况。如果检索结果里没有相关信息要求LLM明确说暂时没有找到相关机型的这个参数而不是编造一个答案。from llama_index.core import PromptTemplate qa_prompt PromptTemplate( 你是一个专业的手机客服助手。根据以下机型信息回答用户问题。\n 如果信息中没有相关内容请直接说暂时没有找到相关信息不要编造。\n 回答时请注明信息来自哪个机型。\n ---------------------\n {context_str}\n ---------------------\n 用户问题{query_str}\n 回答 )实测下来加了不要编造这句约束之后幻觉率从大概15%降到了3%以下。这个改动成本极低但效果显著强烈建议加上。4. 品牌专家路由让对的Agent回答对的问题4.1 为什么需要路由而不是一个大而全的Agent单机器人跑通之后我试着把所有品牌的机型数据都塞进一个索引里。结果检索准确率明显下降因为不同品牌的参数命名习惯不同向量空间里互相干扰。比如某品牌用影像系统描述摄像头另一个品牌用相机模组用户问拍照的时候检索结果在两个品牌之间摇摆。更关键的是不同品牌的用户问题侧重点不同。有的品牌用户更关心性价比有的更关心影像能力有的更关心游戏性能。用一个统一的Prompt去回答所有品牌的问题效果必然打折扣。路由架构的核心思想就是让每个品牌有自己的索引、自己的Retriever、自己的Prompt模板Router负责根据用户问题里的品牌信息把请求分发到对应的专家Agent。4.2 RouterQueryEngine的配置与路由逻辑LlamaIndex的RouterQueryEngine支持多种选择器我用的是LLMSingleSelector让LLM根据用户问题判断应该走哪个品牌专家。from llama_index.core.query_engine import RouterQueryEngine from llama_index.core.selectors import LLMSingleSelector from llama_index.core.tools import QueryEngineTool brand_a_tool QueryEngineTool.from_defaults( query_enginebrand_a_engine, description某品牌手机参数查询专家擅长回答该品牌机型的屏幕、性能、影像等问题 ) brand_b_tool QueryEngineTool.from_defaults( query_enginebrand_b_engine, description另一品牌手机参数查询专家擅长回答该品牌机型的续航、快充、性价比等问题 ) router_engine RouterQueryEngine( selectorLLMSingleSelector.from_defaults(), query_engine_tools[brand_a_tool, brand_b_tool] )这里的关键是每个tool的description要写清楚这个专家擅长什么。LLM Selector就是靠这段描述来做路由判断的。我一开始description写得太笼统就写某品牌手机专家结果路由准确率只有70%左右。后来把擅长的领域加进去准确率提升到了95%以上。4.3 路由失败的兜底策略路由不是万能的。用户可能不提品牌直接问哪个手机拍照最好这时候LLM Selector就不知道该选哪个专家了。我的兜底策略是加一个通用查询工具它会在所有品牌的索引里做检索然后汇总结果。虽然准确率不如专家Agent但至少不会让用户收到我不知道这种回答。general_tool QueryEngineTool.from_defaults( query_enginegeneral_engine, description通用手机查询当用户没有指定品牌或需要跨品牌对比时使用 )另外我在路由层加了一个日志记录每次路由决策都记下来。跑一段时间之后回顾日志能发现哪些问题被路由错了然后针对性地优化description或者补充训练样本。经验路由的description不要写得太短也不要写得太长。我的经验是30-60个字最合适把品牌名、擅长领域、典型问题类型都涵盖进去。5. 多智能体协作下单从参数查询到完成交易的完整链路5.1 下单场景的任务拆解下单这个动作看起来简单实际上涉及多个步骤确认机型、确认配置、确认价格、确认库存、生成订单、确认支付。每一步都需要不同的信息而且步骤之间有依赖关系——不确认机型就没法确认配置不确认配置就没法确认价格。单Agent处理这种多步骤任务时很容易在中间某一步丢失上下文。比如确认完机型之后下一步问配置的时候Agent忘了用户选的是哪个机型。多智能体协作的思路是把每个步骤拆成一个独立的Agent每个Agent只负责自己的环节通过共享的上下文对象传递信息。这样每个Agent的逻辑都很简单组合起来却能处理复杂流程。5.2 用AgentWorker和Task编排下单流程LlamaIndex提供了AgentWorker和Task的抽象适合做这种流程编排。我定义了四个Worker机型确认Worker从用户输入中提取机型信息如果用户没指定就推荐几款配置确认Worker确认颜色、存储容量等配置选项价格确认Worker查询价格如果有优惠活动也一并告知订单生成Worker汇总所有信息生成订单确认信息from llama_index.core.agent import AgentRunner from llama_index.core.agent.workflow import FunctionAgent def confirm_model(model_name: str) - str: 确认用户选择的机型 # 查询数据集验证机型是否存在 return f已确认机型{model_name} def confirm_config(model_name: str, color: str, storage: str) - str: 确认配置选项 return f已确认配置{model_name}{color}{storage} agent FunctionAgent( tools[confirm_model, confirm_config], llmllm )每个Worker本质上是一个FunctionAgent它有自己的工具集和Prompt。AgentRunner负责按照任务依赖关系依次调用这些Worker。5.3 上下文传递与状态管理多智能体协作最容易出问题的地方就是上下文传递。我的做法是维护一个共享的state字典每个Worker完成自己的任务后把结果写进去下一个Worker从state里读取需要的信息。state { user_input: , selected_model: None, selected_config: None, price: None, order_status: pending }这样做的好处是每个Worker不需要知道完整的对话历史只需要从state里拿自己需要的那几个字段。既减少了token消耗也避免了上下文过长导致的注意力分散。踩坑记录我一开始把完整对话历史传给每个Worker结果token消耗巨大而且Worker经常被历史信息干扰。改成共享state之后token消耗降了60%任务完成率反而提升了。5.4 下单流程中的异常处理实际跑的时候异常情况比正常流程还多。用户可能在确认配置阶段突然改主意要换机型也可能在价格确认阶段说太贵了不买了。这些都需要Agent能识别并做出响应。我的处理方式是给每个Worker加上意图识别能力。如果检测到用户意图偏离了当前任务就把state重置到对应阶段重新走流程。比如用户在配置确认阶段说要换机型就把state里的selected_model清空回到机型确认阶段。def detect_intent(user_input: str) - str: 识别用户意图返回对应的流程阶段 # 用LLM判断用户是想继续当前流程还是切换 ...这套机制跑下来下单流程的完成率从最初的40%提升到了85%左右。剩下的15%主要是用户主动放弃或者提出了数据集覆盖范围之外的需求。6. 实测中的性能表现与几个值得注意的细节6.1 响应延迟的构成与优化整个系统的响应延迟主要来自三个部分路由判断、检索、LLM生成。实测数据如下环节平均耗时优化手段路由判断0.8s缓存常见品牌关键词命中缓存时跳过LLM调用向量检索0.3s使用本地embedding模型避免网络请求LLM生成2.5s控制context长度只传Top-3检索结果总计3.6s加缓存后可降到2.8s左右路由判断的缓存优化效果最明显。我统计了一下用户问题里提到品牌名的比例大概占60%这部分请求可以直接通过关键词匹配路由不需要调用LLM。只有剩下40%的模糊请求才需要LLM判断。6.2 多轮对话中的上下文保持客服场景天然是多轮对话。用户问完参数之后可能接着问那它的配件贵不贵这时候需要把上一轮的机型信息带入到当前轮的检索中。我的做法是在state里维护一个last_mentioned_model字段每次检索时如果当前问题没有明确提到机型就用这个字段作为过滤条件。这样即使用户说它系统也能知道指的是哪个机型。6.3 数据集更新与索引重建手机机型数据是不断更新的新机型发布、旧机型降价、参数修正这些都需要同步到索引里。我的方案是增量更新新机型直接插入索引参数修正则先删除旧Document再插入新的。# 增量插入 index.insert(new_document) # 更新已有文档 index.delete_ref_doc(doc_id) index.insert(updated_document)注意频繁的insert和delete会导致索引碎片化检索性能下降。我的经验是每积累50次更新就重建一次全量索引这样能保持检索性能稳定。6.4 几个容易忽略的细节第一个细节是机型名称的别名问题。用户可能说某品牌X1也可能说X1或者某品牌那个X1检索时需要做名称归一化。我在检索前加了一个别名映射表把常见别名统一映射到标准名称。第二个细节是价格信息的时效性。手机价格变动频繁我在metadata里加了last_updated字段检索时优先返回最近更新的记录。第三个细节是敏感信息的过滤。下单流程中会涉及用户信息我在state里只保留必要的字段其他信息用完即弃不落盘不日志。这套系统从最初的原型到稳定运行前后迭代了大概两个月。最大的体会是RAG多智能体系统的难点不在单个组件的搭建而在于组件之间的衔接和异常情况的处理。把每个环节的边界情况都想清楚系统才能真正可用。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询