资讯动态

LlamaIndex实战:从RAG问答到多智能体协作,构建手机客服Agent

发布时间:2026/10/17 8:58:08 来源:尧图企业网站定制
做这个项目之前我其实已经用 LlamaIndex 写过不少 RAG Demo但基本都是单问答机器人那种数据扔进去、检索出来、生成回答链路很短成就感来得快但离真正能用到业务里还差一大截。这次这个手机客服场景不一样——标题里写得很清楚一份手机机型数据集要贯穿单机器人问答、品牌专家路由、多智能体协作下单三个阶段。说白了就是从能对话一路干到能办事这中间涉及的不只是代码量变多而是架构思路要整体升级。这篇文章我会把项目从数据设计、链路搭建、路由策略到多智能体协作的完整实现过程拆开讲重点是每一步为什么要这么选、参数为什么这么调、哪些地方是文档里不会写但实操必踩的坑。如果你正在用 LlamaIndex 做 RAG 应用或者刚接触多智能体 Agent 想找一个能落地的参考案例这篇值得看完。1. 项目拆解一份数据集如何撑起三层复杂度1.1 场景定位从能答到会分工再到能办事这个项目最巧妙的地方就是没有为了炫技去堆多智能体而是先想清楚三个递进的业务场景第一个场景是单机器人问答。用户问某款手机支持无线充电吗电池容量多少系统需要基于手机机型数据集做检索增强生成回答要准确、有依据。这个阶段解决的问题是知识库能不能被有效检索和引用。第二个场景是品牌专家路由。用户说我想看看 Alpha 品牌 3000 元以内的手机系统应该判断这个问题涉及 Alpha 品牌然后把这个请求交给专门负责 Alpha 品牌数据的专家机器人处理而不是让一个通用机器人去检索全部数据。这个阶段解决的问题是不同品牌的知识能不能隔离处理、精准路由。第三个场景是多智能体协作下单。用户说我需要一台适合玩游戏的手机预算 4000帮我下单。这句话里包含需求分析、产品推荐、价格计算、库存确认、下单执行等多个动作单个机器人很难一口气搞定需要多个 Agent 各司其职、协作完成。这个阶段解决的问题是复杂任务能不能被拆解并由多个智能体协同执行。这三个场景如果各用各的数据集、各写各的代码其实也能跑通但那就失去了这个项目的真正价值。我选择用同一份手机机型数据集贯穿全部流程原因很朴素业务里的数据永远是同一套不同场景只是以不同方式在使用它。单机器人问答用全量数据做召回品牌专家路由用品牌维度做过滤多智能体协作则是在前两者基础上叠加工具调用能力。数据不统一后面的路由和协作就没有可信的根基。1.2 数据集设计的统一约定手机机型数据集是这个项目的地基。我没有用那些公开的爬虫数据因为字段不可控而是自己构造了一份结构化 JSON 数据集每条记录包含以下字段phone_id机型唯一标识如A1001brand品牌这里用虚构品牌名 Alpha、Beta、Gamma 代替避免商标问题model具体型号名如Alpha X5 Proprice价格元specs屏幕、处理器、内存、存储、电池、摄像头、充电功率features如无线充电、NFC、防水等级、指纹识别stock当前库存数量warranty保修政策描述为什么用 JSON 而不是纯文本因为后续的路由和下单环节需要结构化字段做精确过滤和参数传递。比如路由阶段要按brand字段匹配下单阶段要检查stock字段是否充足如果数据是一大段自然语言文本这些操作会非常吃力。这里有个细节值得注意我在每个机型描述里都保留了完整的自然语言文本描述而不只是结构化字段。原因是 RAG 检索依赖语义相似度用户提问哪款手机拍照好看并不包含摄像头像素这样的显式关键词如果只靠结构化字段检索效果会很差。所以数据集的设计是结构化字段 自然语言描述双轨并行结构化字段服务路由和下单自然语言描述服务语义检索。我测试下来这种混合结构比纯字段或纯文本都稳。数据量方面我构造了 90 款机型每个品牌 30 款覆盖 1000-8000 元价格区间。90 这个数量对于跑通整个流程足够了再多也会拖慢索引构建和检索速度不利于调试。2. 基础对话落地单机器人的 RAG 问答链路2.1 选型与架构取舍单机器人问答是整个项目的地基后面的路由和 Agent 协作都是在这个基础上叠加。这个环节我踩过一个比较大的坑一开始图省事直接用了 LlamaIndex 的默认配置向量索引建完就开测结果检索效果惨不忍睹——用户问3000 块能买到什么手机返回的结果里混着大量 8000 元旗舰机的信息。后来我认真分析了原因问题出在三个地方一是文本切分粒度太大二是检索器没有结合结构化字段做过滤三是指令模板没有引导模型聚焦机型参数。这三个问题其实是 RAG 落地中最常见的三座大山逐个解决之后效果才真正可用。LlamaIndex 在这个场景下的优势是它把加载-索引-检索-生成封装得非常顺手尤其支持多种索引类型混用。我做这个项目时用了VectorStoreIndex做主要的语义检索配合自带的retriever和query_engine几百行代码就能跑起来一个基础问答服务。相比从零写向量检索和上下文组装效率高太多了。2.2 LlamaIndex 构建检索问答的具体实现基础问答链路其实可以拆成四步加载数据、解析文档、构建索引、创建查询引擎。我用SimpleDirectoryReader读取 JSON 文件然后用NodeParser做文本切分再用VectorStoreIndex构建向量索引最后通过as_query_engine暴露问答接口。这里有一个我强烈建议不要略过的细节自定义NodeParser的切分逻辑。LlamaIndex 默认按固定 chunk_size 切分但手机机型文档的结构化特征很强默认切分很容易把一个机型的卖点描述和参数列表切开导致检索时上下文不完整。我推荐的最小化配置大概是这样的from llama_index.core.node_parser import JSONParser from llama_index.core import SimpleDirectoryReader, VectorStoreIndex documents SimpleDirectoryReader( input_dir./data/, required_exts[.json], ).load_data() parser JSONParser() nodes parser.get_nodes_from_documents(documents) index VectorStoreIndex(nodes) query_engine index.as_query_engine( similarity_top_k4, response_modecompact, )response_modecompact这点很重要。默认的response_mode会把所有检索到的文本块全部塞给大模型回答很啰嗦而且容易跑题。compact模式会尽量压缩有用的上下文后再给模型回答更聚焦。我在实测中对比过回答准确率更高而且响应时间明显缩短。2.3 检索质量提升的关键参数调节如果你只是按照上面的代码跑大概率会遇到检索结果不精准的问题。我调试的时候重点盯三个参数similarity_top_k这个参数控制召回几条相关片段。我建议先设 4-6 之间测试如果回答经常漏信息就调大如果经常塞入无关内容就调小。手机问答场景比较特殊用户问这款手机支持 5G 吗答案往往只藏在某一个片段里top_k 设得太大反而会把不相关的片段混进来干扰模型判断。chunk_size和chunk_overlap这两个参数决定了切分粒度。手机机型数据文本大部分在 200-500 字之间我测试下来chunk_size300、chunk_overlap50效果比较好。chunk 太小容易把关键参数切断chunk 太大检索精度下降overlap 可以保证跨段信息不丢。还有一个容易被忽略的是 Embedding 模型选择。LlamaIndex 默认的text-embedding-ada-002效果不错但需要付费 API离线场景下我推荐BAAI/bge-small-zh-v1.5这类中文 Embedding 模型。手机问答场景很多描述是中文中文 Embedding 模型的语义理解更准。切换方式是在Settings.embed_model里指定本地模型路径。提示similarity_top_k和检索精度的关系不是线性的。我在这个项目里测试过从 2 调到 6召回率提升明显但调到 8 以后回答开始出现轻微跑题。建议在验证集上多测几组找到你的数据集专属的甜点值。3. 品牌专家路由从一个客服到品牌专家组3.1 为什么需要路由而不是一把抓单机器人问答跑通之后我开始思考一个实际业务问题如果客服系统只服务一个品牌那一个机器人确实够了。但现实的手机客服通常要同时服务多个品牌而不同品牌的定位、参数侧重点、卖点差异很大。让一个通用机器人回答所有品牌的问题至少有两个明显问题第一检索范围过大导致精度下降。当用户问Alpha 品牌的旗舰机用的什么处理器如果系统在全量 90 款机型里做语义检索很可能把 Beta、Gamma 品牌的高端机也召回进来因为它们同样有旗舰处理器这些词回答时模型就容易混淆品牌信息。第二不同品牌需要不同的回答口径。举个例子Alpha 品牌主打性价比推荐话术偏重同价位配置最高Gamma 品牌主打高端影像推荐话术偏重影像系统调教。如果所有品牌共用一个系统提示词回答风格会非常统一但失去品牌特色。路由机制解决的就是这两个问题先判断用户问题属于哪个品牌然后只在该品牌的数据范围内做检索和生成。3.2 路由方案实现基于元数据过滤的 RouterQueryEngineLlamaIndex 官方提供了多种路由方案其中最常用的是RouterQueryEngine它本质上是一个先选择、再查询的引擎。它内置了一个选择器会对每个候选查询引擎打分选中最合适的那个执行。但光有 RouterQueryEngine 还不够关键在于如何构造品牌专属查询引擎。这一步我用了 LlamaIndex 的元数据过滤能力from llama_index.core.query_engine import RouterQueryEngine from llama_index.core.tools import QueryEngineTool alpha_engine index.as_query_engine( similarity_top_k4, filtersMetadataFilters( filters[ ExactMatchFilter(keybrand, valueAlpha) ] ) ) beta_engine index.as_query_engine( similarity_top_k4, filtersMetadataFilters( filters[ ExactMatchFilter(keybrand, valueBeta) ] ) ) router RouterQueryEngine.from_defaults( query_engine_tools[ QueryEngineTool.from_defaults( query_enginealpha_engine, description回答 Alpha 品牌手机的问题, ), QueryEngineTool.from_defaults( query_enginebeta_engine, description回答 Beta 品牌手机的问题, ), ], select_multiFalse, )用MetadataFilters配合ExactMatchFilter做品牌字段的精确过滤是这里最核心的一步。这样每个品牌引擎在检索时就直接被限制在对应品牌的数据子集内从源头避免了跨品牌混淆。description字段也别小看。RouterQueryEngine 的选择器本质上是把每个引擎的 description 和用户问题一起送给大模型做意图判断所以 description 写得越清晰路由准确率越高。我测试时把 description 从查询 Alpha 品牌手机改成回答与 Alpha 品牌手机相关的任何问题包括参数、价格、推荐、对比之后路由准确率肉眼可见地提升了。3.3 路由效果评估与兜底路由不是配置完就完事的一定要做效果评估。我从测试集里抽了 45 个问题15 个问 Alpha15 个问 Beta15 个跨品牌或通用问题然后逐个看路由结果和最终回答。评估下来准确率大概在九成以上但这个数字背后有个重要的补充设计——兜底引擎。当用户问题不涉及具体品牌比如4000 块以内的手机推荐一下路由选择器会硬着头皮选一个品牌引擎但这样回答就会偏心。我增加了第三个兜底引擎使用全量数据专门处理跨品牌和通用问题general_engine index.as_query_engine( similarity_top_k6, ) router RouterQueryEngine.from_defaults( query_engine_tools[ alpha_tool, beta_tool, QueryEngineTool.from_defaults( query_enginegeneral_engine, description回答不限定品牌的通用手机问题或跨品牌对比问题, ), ] )这样一个三层路由结构就能覆盖大多数实际问答场景。品牌问题找品牌专家通用问题找全量引擎结构简单又不会让检索顾此失彼。注意路由方案还有一个变体叫FunctionToolAgent原理是把每个品牌引擎封装成一个函数工具让Agent自主决定调用哪个。这种方式更灵活但可控性不如 RouterQueryEngine。如果你的业务路由规则很稳定用 RouterQueryEngine 就够了如果问题复杂到需要多轮工具组合再上 Agent 方案。4. 多智能体协作下单让 Agent 真正动起来4.1 多智能体分工设计与工具定义到了这个阶段项目开始真正进入多智能体 Agent的范畴。我的设计思路是不再用一个超级 Agent 包办所有事而是拆分出三个职责单一、边界清晰的智能体。第一个是客服导购 Agent负责理解用户需求、推荐合适机型、回答参数问题。它本质上就是前面已经做好的 RAG 问答引擎只是被封装成了可调用工具。第二个是订单处理 Agent负责创建订单、查询库存、生成订单详情它不直接访问知识库而是通过工具函数操作一个内存中的订单系统。第三个是协调者 Agent也可以理解为调度中枢负责理解用户意图决定把任务交给哪个下游 Agent并把最终结果汇总给用户。我之所以采用这种调度者 执行者的架构而不是把所有事情塞给一个 Agent是因为下单流程有严格的状态依赖必须先确定机型 - 检查库存 - 计算价格 - 创建订单。如果让一个 Agent 用工具自由发挥它可能在库存不足时仍然创建订单或者在用户还没确认机型时就去查价格。协调者架构让每个环节有明确的归属Agent 之间的协作是通过工具调用来实现的。具体来说协调者 Agent 手上有一组工具recommend_phone调用客服导购 Agent 的 RAG 引擎、create_order调用订单处理 Agent、check_stock工具函数直接查库存。它根据用户意图选择工具然后把多个工具调用串起来。LlamaIndex 里实现多智能体协作有不少方式我这个项目用的是最直观的一种把下游能力全部封装成工具注册给协调者 Agent。这本质上是一种工具型多智能体模式虽然不如那种 Agent 之间互相通信的模式花哨但胜在稳定、可调试、易监控。4.2 协作流程从咨询到下单的完整链路以一个真实的对话流程来演示可能会更清楚。用户输入我想买一台适合玩游戏的手机预算 4000 左右帮我下单。协调者 Agent 的第一步是调用客服导购 Agent推荐引擎把用户这句话切片理解玩游戏对应高刷屏幕、强处理器、好散热预算 4000 左右对应价格区间过滤帮我下单对应后续动作。推荐引擎返回符合条件的机型比如 Alpha X5 Pro3999 元骁龙级别处理器144Hz 高刷屏库存 15 台。协调者 Agent 的第二步是调用check_stock确认该机型库存充足再调用create_order创建订单。create_order工具接收三个参数用户ID、机型 ID、数量。工具内部会再校验一次库存然后生成一条订单记录返回订单号。第三步协调者 Agent 把订单结果整理成自然语言响应给用户已为您下单 Alpha X5 Pro订单号 A1001202401预计 48 小时内发货。这个流程的关键在于RAG 推荐结果和下单操作之间的衔接。很多教程到这里就断了只演示推荐不演示下单但真实业务里光有推荐没有下单动作项目价值就少了一大半。实现上我会把工具函数定义为 LlamaIndex 的FunctionToolfrom llama_index.core.tools import FunctionTool def create_order(user_id: str, phone_id: str, quantity: int) - str: # 校验库存 stock get_stock(phone_id) if stock quantity: return f库存不足当前仅剩 {stock} 台 order_id make_order(user_id, phone_id, quantity) return f订单创建成功订单号 {order_id} order_tool FunctionTool.from_defaults( fncreate_order, description为指定用户创建手机订单需要用户ID、机型ID、数量 )这里有个细节description写得越详细Agent 正确调用工具的概率越高。我发现如果不写清参数含义Agent 经常会把手机型号名称当机型ID传进去导致工具内部查不到库存。加上机型ID是数据集中 phone_id 字段不要用型号名称这类说明后调用成功率会显著提升。4.3 状态管理与错误恢复多智能体协作最容易翻车的环节是状态管理。用户说下单但并没有指定具体机型这时候如果 Agent 直接创建订单就可能下错单。解决思路是让订单工具内部做前置条件校验不满足条件就拒绝执行并给出明确反馈。我在create_order里加了一个规则必须从推荐结果中携带机型 ID不允许凭空指定机型。如果协调者 Agent 没有先调用推荐工具就直接下单工具会返回未找到有效的机型推荐记录请先选择机型。这个设计让下单链条有清晰的依赖关系也避免 Agent 在信息不足时瞎操作。还有一类问题是模型幻觉导致的工具参数错误比如机型 ID 写错、数量写负数。我的做法是在工具函数里做参数校验非法输入直接返回错误信息而不是抛异常。大模型很擅长从错误信息里自我纠正只要反馈明确它下一轮往往就能修正调用参数。我整理了个比较实用的状态流转初始状态用户表达购机意向需求分析调用推荐引擎获取候选机型列表机型确认确认用户选定的机型 ID 和数量创建订单校验库存后生成订单完成返回订单号协调者 Agent 在这个状态机里担任大脑下游工具是手脚。这样一个环节一个环节走完下单流程的稳定性和可解释性都有了保障。5. 踩坑实录与排查技巧5.1 高频问题速查表做这个项目过程中我整理了一份高频问题速查表基本都是 LlamaIndex 和 Agent 开发中常见的坑症状根因解决办法检索结果出现其他品牌机型没有加MetadataFilters或过滤器字段名不对检查索引节点有无brand元数据过滤器使用ExactMatchFilter路由选择器频繁选错引擎description描述含糊重写description明确品牌边界和可处理的问题类型Agent调用工具时参数错误工具description未说明参数规则在工具的description里写清参数格式、取值来源、示例回答内容大段重复response_mode默认模式导致切换到compact或refine模式下单时库存为0但依然创建订单业务校验放在Agent提示词里而非工具内把库存校验写入工具函数内部不满足条件直接拒绝中文检索效果差Embedding模型不擅长中文切换BAAI/bge-small-zh-v1.5等中文Embedding模型JSON数据加载后节点过少JSONParser解析逻辑不符合预期自定义解析逻辑把每个机型变成一个独立节点这张表里的每一条都是我在调试过程中实际遇到过的有一个算一个都花了不少时间去定位。5.2 我印象最深的三个坑第一个坑是路由阶段元数据过滤失效。我在索引里明明设置了 brand 字段但用了MetadataFilters就是过滤不掉其他品牌的数据。排查了一下午发现是加载 JSON 数据时LlamaIndex 默认把 JSON 的键值对结构当成文档内容处理并没有自动生成brand元数据字段。我后来手动在节点构造时补充了元数据问题才解决。第二个坑是 Agent 工具调用陷入死循环。协调者 Agent 在推荐完机型后一直重复检查库存而不创建订单原因是check_stock工具和create_order工具的 description 区分度不够模型搞不清楚该用哪个。我把 description 改成更明确的只检查库存数量不创建订单和创建订单前必须已经确认过库存模型就不再困惑了。第三个坑是响应时间太慢。加了多智能体之后一次完整下单流程可能需要调用多次大模型总耗时经常超过 30 秒。优化思路有两个方向一是把路由和推荐这类子任务用as_query_engine的缓存特性缓存结果二是把协调者 Agent 的模型换成推理速度更快的小模型专业任务才用大模型。实测下来首响应时间从 30 秒降到了 10 秒以内。提醒多智能体项目调试时一定要保留完整的中间日志包括路由选择结果、工具调用参数、工具返回结果。这不是可有可无的细节而是定位问题的唯一线索。6. 实操心得与扩展建议整个项目做下来我个人最深的体会是RAG 应用从能用到好用瓶颈往往不在模型能力而在工程设计的细致程度。同一份数据集单机器人问答阶段它只是检索对象品牌路由阶段它变成了分而治之的知识切片多智能体协作阶段它又成了工具调用的数据源头。数据始终没变变的是我们处理数据、组织知识、编排任务的方式。如果你也想复现这个项目我的建议是严格按照单问答 - 路由 - 多智能体的顺序推进不要一上来就跳到多智能体。没有把单机器人问答的检索质量调到满意后面的路由和 Agent 协作会在同样的数据问题上反复踩坑而且排查难度成倍增加。最后再分享一个小技巧这个项目的数据集虽然是虚构的 90 款机型但你可以很容易地替换成真实业务的数据来做改造。手机机型数据的核心结构——品牌、型号、价格、参数、库存、自然语言描述——很多电商和客服场景的数据都能对齐到这个结构上。拿一套真实数据直接套用这个链路很快就能验证多智能体 Agent 在你自己的业务场景里是不是真的有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑