资讯详情

轻量级多智能体系统:用LlamaIndex RAG实现手机客服场景的端到端协作

📅 2026/10/13 15:26:44 | 华诺云谱 👁 阅读
轻量级多智能体系统:用LlamaIndex RAG实现手机客服场景的端到端协作
1. 项目概述为什么一份手机数据集能跑通从单点问答到多智能体协作的完整链路最近在某实验室带一个RAGAgent的实战训练营学员普遍卡在一个认知盲区总以为“多智能体”是高不可攀的架构设计得先搭好消息总线、定义好Agent通信协议、再搞状态同步——结果三个月还在画UML图。直到我们用一份只有327行JSON的国产手机机型数据集含品牌、发布时间、屏幕尺寸、主摄参数、电池容量、起售价、是否支持5G等12个字段在不到4小时里跑通了从“用户问‘哪款华为手机拍照最好’”这种单轮问答到“用户说‘我要买一台适合拍夜景的折叠屏预算6000以内今天下单能发货吗’”系统自动拆解为品牌专家路由→影像能力比对→形态筛选→库存与履约校验→生成订单摘要的全流程。这不是Demo是真实可部署的轻量级服务。核心关键词就三个LlamaIndex RAG、手机客服场景、多智能体Agent协作。它不碰大模型微调不依赖私有GPU集群全程用LlamaIndex 0.10.x OpenAI兼容接口实测本地Ollama跑Qwen2-7B也稳所有代码跑在一台16GB内存的MacBook Pro上。关键在于——我们把“多智能体”的复杂性全部压在了数据结构设计和任务分解逻辑上而不是基础设施上。比如“品牌专家路由”这个环节根本没用LangChain的RouterChain而是用LlamaIndex的VectorStoreIndex对品牌知识做向量化再让LLM基于query embedding的相似度打分选Top1品牌节点而“协作下单”也不是靠Agent间发消息而是用一个共享的OrderContext字典对象每个Agent只读写自己负责的字段影像专家填best_camera_model形态专家填form_factor履约专家填shipping_available_today最后由Coordinator Agent聚合输出。这种设计让整个系统像乐高一样可插拔明天要加个“环保材料顾问”只要新增一个Agent类实现analyze()方法往OrderContext里写eco_certification字段就行不用动其他模块。适合谁学三类人最受益一是刚学完LangChain基础、想落地真实业务场景的开发者这篇教程会告诉你RAG怎么从“查文档”升级为“驱动业务动作”二是客服系统产品经理你能看清一个“智能导购”背后真正的决策链条不是简单调API而是如何把模糊需求拆解成可验证的原子条件三是中小厂商的技术负责人你们没有资源养一支AI工程团队但这份方案证明用结构化数据轻量RAG明确分工的Agent就能做出比传统规则引擎更灵活、比纯大模型更可控的客服系统。接下来所有内容都围绕这份手机数据集展开——它不是玩具数据而是我们刻意设计的“最小可行业务域”字段覆盖用户决策关键点数据量小到能人工核对每条记录但又足够支撑多层推理。现在我们开始拆解这个系统的骨架。2. 整体架构设计为什么放弃“标准Agent框架”选择“数据流驱动”的极简协作模式2.1 标准Agent框架的三大隐性成本我们在第一轮POC就踩了坑刚开始我们按教科书走用LangChain的AgentExecutorTool体系给每个Agent配独立的LLM实例和工具集。结果三天调试下来发现三个致命问题提示第一个问题是上下文爆炸。当用户问“华为Mate60 Pro和小米14 Ultra哪个更适合拍星空”Router Agent先判断品牌再传给影像专家影像专家调用工具查参数再传给对比Agent……每跳一次就得把前面所有历史、工具返回、中间结论塞进新prompt。327行数据集的embedding向量才2KB但一个5轮交互的prompt轻松突破120KBOllama直接OOM。提示第二个问题是状态漂移。影像专家说“Mate60 Pro主摄1英寸传感器”形态专家却说“它不是折叠屏”但没人校验这两条信息是否来自同一数据源。我们发现不同Agent用的其实是同一份JSON但各自解析时用了不同key名有的用main_camera_sensor有的用camera_primary_size导致最终订单里camera_sensor字段为空。提示第三个问题是故障不可追溯。当订单生成失败日志里只有一串“Agent execution failed”根本不知道是路由错了品牌还是影像专家误判了长焦能力还是履约接口超时。传统框架把错误封装在AgentExecutor.run()里你只能看到“Exception in chain”看不到具体哪个环节、哪条数据出了问题。所以第二版我们彻底转向“数据流驱动”不设Agent运行时不搞消息总线所有Agent都是纯函数输入是UserQuery和SharedContext输出是更新后的SharedContext。整个流程变成一条清晰的数据管道UserQuery → RouterAgent → SharedContext(brandhuawei) → CameraExpert → SharedContext(brandhuawei, camera_rank1) → FormFactorExpert → SharedContext(brandhuawei, camera_rank1, form_factorfoldable) → FulfillmentExpert → SharedContext(brandhuawei, camera_rank1, form_factorfoldable, shipping_availableTrue) → Coordinator → FinalOrderSummary这个设计的核心优势是每个环节的输入输出完全可序列化、可断点调试、可单元测试。比如测试CameraExpert你直接构造一个SharedContext(brandhuawei)喂给它断言它是否正确设置了camera_rank和best_model字段。不需要启动LLM不需要mock网络请求。2.2 LlamaIndex RAG在这里不是“检索器”而是“领域知识编译器”很多人把LlamaIndex当搜索引擎用建完index就调query_engine.query()。但在本项目里我们把它用成了“静态知识编译器”。具体操作分三步预编译阶段加载手机数据集后不直接建VectorStoreIndex而是先用Pandas做清洗——统一price字段为整数去掉“¥”和“起”字标准化release_date为YYYY-MM-DD格式把5g_support布尔值从字符串yes转为True/False。这步耗时2秒但避免了90%的LLM幻觉。索引构建阶段我们建了三个独立索引而非一个大索引BrandIndex只索引brand和model字段用于品牌路由CameraIndex索引model、main_camera_sensor、ultra_wide_aperture、night_mode_rating人工标注的1-5分FulfillmentIndex索引model、stock_status、shipping_days、warranty_months。这样做的理由很实在品牌路由需要毫秒级响应用BrandIndex查xiaomi比在全量索引里筛快3倍而影像分析需要细粒度参数CameraIndex的embedding向量只包含相关字段噪声更少。查询增强阶段query_engine.query()返回结果后我们不直接喂给LLM而是用正则提取关键数值。比如query_engine.query(华为哪款手机夜景最强)返回一段文本“华为Mate60 Pro夜景评分4.8分主摄1英寸传感器”我们用re.search(r夜景评分(\d\.\d)分, text)抽取出4.8存入SharedContext.night_mode_score。这步看似多余实则是把LLM的“语言理解”能力降维成“结构化数据提取”能力准确率从82%提升到99.3%实测100次query仅1次因标点异常漏匹配。这种用法让LlamaIndex从“黑盒检索器”变成了“白盒数据管道”每个环节的输入输出都是确定的。当你发现订单里night_mode_score为空直接查CameraIndex的query日志就知道是原始数据里night_mode_rating字段缺失而不是怪LLM没理解“夜景”这个词。2.3 多智能体协作的本质是“字段所有权”的精确划分所谓“多智能体”在本项目里就是四个Python类RouterAgent、CameraExpert、FormFactorExpert、FulfillmentExpert。它们之间零耦合不import彼此不调用彼此方法只通过一个约定好的SharedContext对象通信。这个对象长这样class SharedContext: def __init__(self): self.user_query: str self.brand: Optional[str] None # RouterAgent写 self.camera_rank: Optional[int] None # CameraExpert写 self.best_camera_model: Optional[str] None # CameraExpert写 self.form_factor: Optional[str] None # FormFactorExpert写foldable, slate, flip self.shipping_available: Optional[bool] None # FulfillmentExpert写 self.shipping_days: Optional[int] None # FulfillmentExpert写 self.order_summary: Optional[str] None # Coordinator写关键设计原则是每个字段有且只有一个Agent有写权限。brand只能由RouterAgent设置camera_rank只能由CameraExpert计算。如果FormFactorExpert试图修改brand字段代码会抛出AttributeError我们重写了__setattr__做字段锁。这种设计带来两个好处第一调试成本归零。当form_factor为空你只看FormFactorExpert.analyze()方法就行不用翻10个文件找谁改了它。第二扩展性极强。要加“价格顾问”只需新增字段budget_friendly: bool写一个PriceExpert类在analyze()里根据SharedContext.brand和user_query里的“预算”关键词查FulfillmentIndex的price字段设置budget_friendlyTrue/False。整个过程不涉及任何架构调整。我们刻意避免使用LangGraph或AutoGen这类框架就是因为它们的“图编排”抽象在小规模业务中反而增加了心智负担。当你只有4个Agent时“顺序调用共享状态”比“定义节点配置边处理循环”更直观、更易维护。这就像写SQLSELECT * FROM phones WHERE brandhuawei AND night_mode_rating 4比写MapReduce作业清晰多了。3. 核心模块实现从数据准备到订单生成的逐行代码解析3.1 手机数据集的设计哲学327行JSON如何承载完整决策链这份数据集不是随便爬的而是按“用户决策树”反向设计的。我们访谈了23位真实购机用户总结出他们决策的6个关键节点品牌偏好→预算范围→核心功能影像/游戏/续航→形态偏好直板/折叠/翻盖→交付时效→售后保障。数据集字段严格对应这些节点字段名类型示例值设计意图brandstrhuawei品牌路由的唯一键小写无空格modelstrMate60 Pro所有Agent引用机型的统一IDpriceint6999用于价格顾问和预算校验main_camera_sensorstr1-inch影像专家的核心参数night_mode_ratingfloat4.8人工标注避免LLM主观评分form_factorstrfoldable形态专家的分类依据foldable,slate,flipbattery_capacity_mahint5000续航专家的输入shipping_daysint1履约专家的交付承诺warranty_monthsint24售后专家的保障依据特别说明night_mode_rating字段我们没让LLM自己评而是请3位手机摄影师用同一组夜景样张在暗光环境下对327款机型主摄进行盲评取平均分。为什么这么做因为LLM对“夜景强”的理解是模糊的可能认为像素高夜景强而真实用户要的是“拍出来不糊、噪点少、色彩准”。这个人工标注字段让CameraExpert的输出准确率从76%提升到94%。数据加载代码极简import json import pandas as pd def load_phone_dataset() - pd.DataFrame: with open(phones.json, r, encodingutf-8) as f: data json.load(f) df pd.DataFrame(data) # 强制类型转换避免后续LLM解析失败 df[price] df[price].astype(int) df[night_mode_rating] df[night_mode_rating].astype(float) df[battery_capacity_mah] df[battery_capacity_mah].astype(int) return df # 实测327行数据pandas加载类型转换耗时0.012秒这份数据集的价值不在于“大”而在于“准”和“全”。它让每个Agent的决策都有明确的数据锚点而不是在LLM的幻觉里打转。3.2 RouterAgent用向量相似度代替关键词匹配的品牌路由品牌路由看似简单但传统关键词匹配如if 华为 in query: brandhuawei在真实客服场景中会崩盘。用户说“国货之光”、“鸿蒙系统”、“麒麟芯片”关键词匹配全失效。我们的方案是用LlamaIndex的VectorStoreIndex对品牌知识做向量化让LLM自己学“什么是华为”。具体步骤构建品牌知识库不是直接用brand字段而是为每个品牌生成一段100字以内的描述包含其技术标签和用户心智词。例如华为的描述是“华为中国科技企业以鸿蒙操作系统、麒麟芯片、XMAGE影像技术著称用户常关联‘国产高端’、‘信号强’、‘商务旗舰’”。创建BrandIndexfrom llama_index.core import VectorStoreIndex, SimpleDirectoryReader from llama_index.core.node_parser import SentenceSplitter from llama_index.embeddings.huggingface import HuggingFaceEmbedding # 加载品牌描述文本 brand_docs [ Document(text华为中国科技企业以鸿蒙操作系统...), Document(text小米互联网科技公司以MIUI系统、徕卡影像...), # ... 其他品牌 ] # 使用bge-small-zh-v1.5中文嵌入模型128MBOllama可跑 embed_model HuggingFaceEmbedding(model_nameBAAI/bge-small-zh-v1.5) index VectorStoreIndex.from_documents( brand_docs, embed_modelembed_model, transformations[SentenceSplitter(chunk_size128)] )路由逻辑RouterAgent.analyze()接收user_query用同一embed_model生成query向量然后在BrandIndex中找最相似的品牌描述def analyze(self, user_query: str) - str: # 生成query embedding query_embedding self.embed_model.get_text_embedding(user_query) # 在BrandIndex中搜索最相似的品牌描述 retriever index.as_retriever(similarity_top_k1) nodes retriever.retrieve(query_embedding) if not nodes: return unknown # 提取品牌名从华为中国科技企业...中提取华为 brand_name nodes[0].text.split()[0] return brand_name.lower().replace( , )实测效果对100个含蓄表达的query如“想买个用鸿蒙系统的手机”、“信号最好的国产机”、“拍月亮的手机”路由准确率达92%远超关键词匹配的63%。关键是这个模型可以增量更新——新出一个品牌只需加一段描述重新index.insert()不用改任何代码。3.3 CameraExpert从参数到体验的语义映射影像专家的任务不是罗列参数而是回答“哪款最适合拍夜景”。难点在于参数如f/1.4光圈和体验“夜景明亮”之间没有直接映射。我们的解法是用LlamaIndex的SubQuestionQueryEngine做多跳推理。CameraExpert不直接查night_mode_rating而是把用户query拆成子问题用户要什么→ 提取核心需求“夜景”、“星空”、“暗光”哪些参数影响这个需求→ 查知识库“影响夜景成像的关键参数是光圈大小、传感器尺寸、夜景算法评分”当前品牌下哪些机型满足→ 用CameraIndex查brand匹配的机型按night_mode_rating排序代码实现from llama_index.core.query_engine import SubQuestionQueryEngine def analyze(self, shared_context: SharedContext) - None: # Step1: 提取需求关键词 demand_prompt f从以下用户问题中提取1个最相关的摄影场景关键词只输出关键词不要解释。问题{shared_context.user_query} demand_keyword self.llm.complete(demand_prompt).text.strip() # Step2: 查询该场景的关键参数预置知识 knowledge_map { 夜景: [night_mode_rating, main_camera_sensor, ultra_wide_aperture], 人像: [front_camera_mp, bokeh_effect_rating], 远景: [telephoto_focal_length, periscope_zoom] } params knowledge_map.get(demand_keyword, [night_mode_rating]) # Step3: 用SubQuestionQueryEngine查参数 query_engine SubQuestionQueryEngine.from_defaults( query_engine_tools[ QueryEngineTool.from_defaults( query_engineself.camera_index.as_query_engine(), metadataToolMetadata(namecamera_db, description手机影像参数数据库) ) ] ) sub_questions [f华为品牌下{param}值最高的机型是 for param in params] responses query_engine.query(\n.join(sub_questions)) # Step4: 解析响应取交集机型 best_models [] for resp in responses: model_match re.search(r机型是(.?)。, resp.response) if model_match: best_models.append(model_match.group(1).strip()) # 取交集得到综合最优机型 from collections import Counter most_common Counter(best_models).most_common(1) if most_common: shared_context.best_camera_model most_common[0][0] # 同时查该机型的night_mode_rating rating self._get_rating_by_model(shared_context.best_camera_model, night_mode_rating) shared_context.night_mode_score rating这个设计的精妙之处在于它把LLM的“推理”能力限定在“子问题生成”和“结果聚合”两个环节而参数查询、排序、交集计算全部由代码完成。这样既利用了LLM的语义理解又规避了它的幻觉风险。实测中对“拍星空”这种长尾需求准确率比单次query高37%。3.4 FormFactorExpert与FulfillmentExpert用结构化查询替代LLM自由发挥形态专家和履约专家的任务最“枯燥”但也最容错。用户说“我要折叠屏”FormFactorExpert只需查form_factor foldable用户问“今天能发货吗”FulfillmentExpert只需查shipping_days 1。但我们发现直接让LLM执行这种布尔查询准确率只有68%LLM常把shipping_days2误判为“能发货”。解决方案把LLM降级为“查询生成器”把执行交给Pandas。def analyze(self, shared_context: SharedContext) - None: # 让LLM生成Pandas查询字符串不是直接回答 query_prompt f 用户问题{shared_context.user_query} 请生成一个pandas DataFrame查询字符串用于筛选手机数据。 要求 1. 只用df[...]语法不要用for循环 2. 字段名必须是form_factor, shipping_days, stock_status 3. 输出格式df[条件1 条件2] 示例用户问“折叠屏”输出df[df[form_factor] foldable] pandas_query self.llm.complete(query_prompt).text.strip() # 安全执行查询白名单校验 if not self._is_safe_pandas_query(pandas_query): raise ValueError(Unsafe pandas query detected) # 执行查询取第一条 result_df self.phone_df.eval(pandas_query) if not result_df.empty: shared_context.form_factor result_df.iloc[0][form_factor] shared_context.shipping_available result_df.iloc[0][shipping_days] 1 shared_context.shipping_days result_df.iloc[0][shipping_days]_is_safe_pandas_query()做白名单校验只允许df[...]、、、、字段名、字符串和数字。这样LLM再怎么胡说也执行不了恶意代码。实测100次查询安全校验拦截了7次无效输入如LLM生成df.drop()但所有有效查询100%准确执行。3.5 Coordinator用模板填充替代LLM自由创作的订单摘要最后一步Coordinator把所有字段拼成自然语言订单摘要。很多教程让LLM“根据上下文生成摘要”结果输出千奇百怪“您选择了华为Mate60 Pro它很棒”——这根本不能用。我们的方案预定义模板用字段值填充。ORDER_TEMPLATES { full: 已为您匹配到最佳机型{best_camera_model}{brand}品牌夜景评分{night_mode_score}分{form_factor}形态预计{shipping_days}天内发货。, short: {best_camera_model}{brand}{night_mode_score}分{shipping_days}天发货。 } def generate_summary(self, shared_context: SharedContext) - str: # 检查必填字段是否齐全 required_fields [best_camera_model, brand, night_mode_score, shipping_days] missing [f for f in required_fields if getattr(shared_context, f) is None] if missing: return f订单生成失败缺少字段{missing} # 选择模板并填充 template ORDER_TEMPLATES[full] if len(shared_context.user_query) 20 else ORDER_TEMPLATES[short] try: summary template.format(**shared_context.__dict__) return summary except KeyError as e: return f模板填充失败字段{e}未设置这个设计的好处是摘要格式100%可控字段缺失立刻报错运维人员一眼看出哪个Agent没干活。上线后订单摘要的NPS用户满意度从72分升到94分因为用户终于看到了确定的机型、分数、发货时间而不是“可能”“大概”“推荐”。4. 实操避坑指南那些文档里不会写的血泪教训4.1 LlamaIndex版本陷阱0.10.x的as_query_engine()和0.9.x的query()行为完全不同这是我们在第三天凌晨2点发现的致命坑。旧教程全用index.query(xxx)但升级到LlamaIndex 0.10.x后query()方法被废弃必须用index.as_query_engine().query(xxx)。表面只是方法名变实际行为差异巨大0.9.x的query()返回Response对象.response属性是纯文本.source_nodes是原始chunk。0.10.x的as_query_engine().query()返回StreamingResponse默认是流式输出.response是generator必须用str(response)才能拿到完整文本。我们一开始没注意CameraExpert里直接写# 错误0.10.x下response是generator.response是generator object result self.camera_index.as_query_engine().query(华为夜景最强机型) print(result.response) # 输出generator object ...结果result.response被当成字符串传给正则re.search()永远返回None所有影像分析失败。修复方案# 正确强制转为字符串 result self.camera_index.as_query_engine().query(华为夜景最强机型) full_text str(result) # 或 result.get_response()更坑的是这个bug在本地Ollama上不报错因为流式输出很快但部署到云服务器时网络延迟导致generator卡住整个Agent挂死。建议所有as_query_engine().query()调用后立即加str()或.get_response()并在日志里打印len(str(result))确保返回了预期长度的文本。4.2 中文嵌入模型的选择别迷信“SOTA”小模型才是生产力项目初期我们试了bge-large-zh-v1.51.2GB向量质量确实好但Ollama在MacBook上加载要47秒每次重启服务都要等半分钟。后来换成bge-small-zh-v1.5128MB加载只要3秒而品牌路由准确率只降了1.2%92.1%→90.9%。关键洞察在结构化数据场景嵌入模型的“绝对精度”不如“相对稳定性”重要。bge-small对“鸿蒙”和“HarmonyOS”的向量距离和bge-large几乎一致但它的推理速度是后者的8倍。我们做了AB测试用bge-small跑1000次路由平均耗时83msbge-large是672ms。对客服系统83ms意味着QPS每秒查询数从12提升到120这才是真实业务价值。所以选型口诀数据量1万行字段20个优先选small模型只有当你需要跨语言检索如中英混合query才考虑large。4.3 SharedContext的并发安全多用户请求下的字段污染上线压力测试时我们发现一个诡异现象用户A问“华为拍照”返回Mate60 Pro用户B同时问“小米续航”返回的却是“华为Mate60 Pro电池5000mAh”。查日志SharedContext对象被两个请求共用了。根源在于我们最初把SharedContext做成全局单例。修复方案很简单每个请求创建独立实例。# 错误全局单例 context SharedContext() def handle_request(query: str): context.user_query query router_agent.analyze(context) # ... 其他Agent return coordinator.generate_summary(context) # 正确请求级实例 def handle_request(query: str): context SharedContext() # 每次新建 context.user_query query router_agent.analyze(context) # ... 其他Agent return coordinator.generate_summary(context)但要注意SharedContext里不能存大对象如整个DataFrame否则内存暴涨。我们把phone_df作为Agent的成员变量SharedContext只存轻量字段。实测单实例内存占用从12MB降到0.3MB。4.4 LLM提示词的“防抖”设计如何让输出格式100%稳定RouterAgent曾出现过这样的输出“华为中国品牌”导致brand字段存了华为中国品牌后续所有查询都失败。原因是LLM喜欢加括号解释。解决方案在提示词末尾加“输出格式强制约束”。router_prompt f 你是一个手机品牌路由专家。请从用户问题中识别出最可能的品牌。 要求 1. 只输出品牌名小写字母无空格无标点无解释 2. 例如“华为Mate60 Pro拍照怎么样” → 输出huawei 3. “想买个用鸿蒙系统的手机” → 输出huawei 4. 如果无法确定输出unknown 用户问题{user_query} 输出 重点是第1条和第4条。我们还加了后处理def clean_brand_output(raw: str) - str: # 移除所有非字母数字字符转小写 cleaned re.sub(r[^a-zA-Z0-9], , raw).lower() # 白名单校验 valid_brands [huawei, xiaomi, apple, samsung, oppo, vivo] return cleaned if cleaned in valid_brands else unknown这套组合拳让品牌输出准确率从89%提升到100%。记住LLM不是神它是工具你要用代码给它画牢笼。4.5 数据集版本管理如何避免“线上跑着V1开发改着V2”的灾难我们吃过亏开发环境用phones_v2.json新加了eco_certification字段但生产环境还是phones_v1.json结果PriceExpert一查eco_certification就报KeyError整个服务雪崩。现在强制执行数据集文件名带版本号代码里硬编码版本。# config.py PHONE_DATASET_VERSION v2 PHONE_DATASET_PATH fphones_{PHONE_DATASET_VERSION}.json # load_phone_dataset()函数里加校验 def load_phone_dataset() - pd.DataFrame: path PHONE_DATASET_PATH if not os.path.exists(path): raise FileNotFoundError(fDataset {path} not found! Check version.) # ... 加载逻辑每次数据更新必须改PHONE_DATASET_VERSION常量提交新数据文件CI流水线跑pytest检查所有Agent能否用新数据正常运行。这个流程让数据变更从“风险操作”变成“可审计事件”。上线半年零数据相关故障。5. 场景延展与工程化建议从教程到生产系统的最后一公里5.1 如何把这份教程变成可交付的产品三个必须做的工程化动作这份教程的代码离生产环境还有三步第一步增加健康检查端点。别等用户投诉才知服务挂了。加一个/health接口返回各模块状态app.get(/health) def health_check(): return { status: ok, components: { router_agent: _test_router(), camera_index: _test_camera_index(), shared_context: ready }, dataset_version: PHONE_DATASET_VERSION }_test_router()不是调LLM而是查BrandIndex能否返回至少一个品牌。这样K8s的liveness probe能秒级发现索引损坏。第二步为每个Agent加超时熔断。LLM调用可能卡死必须设timeoutfrom tenacity import retry, stop_after_attempt, wait_fixed retry(stopstop_after_attempt(3), waitwait_fixed(2)) def safe_analyze(self, context: SharedContext): # 原analyze逻辑 pass三次失败后返回兜底值如brandunknown保证服务不雪崩。第三步日志结构化。别再用print()用structlog记录每个Agent的输入输出import structlog logger structlog.get_logger() def analyze(self, context: SharedContext): logger.info(router_agent_start, user_querycontext.user_query) brand self._route(context.user_query) logger.info(router_agent_end, brandbrand, user_querycontext.user_query) return brand这样ELK里能直接查“所有brandunknown的请求”快速定位问题。5.2 业务场景的自然延伸从“下单”到“售后”的闭环设计这份教程止于订单生成但真实客服要闭环。我们已在某客户项目中落地了两个延伸售后咨询Agent当用户问“我的Mate60 Pro屏幕碎了保修吗”它查warranty_months和purchase_date用户提供的购买日期用FulfillmentIndex查该机型的warranty_policy字段如“屏幕碎裂不在保修范围但可享8折维修”生成明确答复。竞品对比Agent用户说“iPhone15和Mate60 Pro哪个好”它不自己评而是并行查两个model的night_mode_rating、battery_capacity_mah、price生成对比表格。关键点所有对比字段必须来自同一数据源避免“苹果官网参数 vs 华为论坛爆料”的混乱。这两个延伸都没新增架构只是加了两个Agent类和几个字段。这印证了我们最初的设计哲学用数据定义能力边界用Agent定义职责边界。5.3 给技术决策者的务实建议什么时候该用什么时候不该用最后分享一个血泪经验别为了用RAG而用RAG别为了多Agent而多Agent。该用的场景你的数据是结构化的如本项目的JSON且字段能覆盖用户决策点你的业务流程可拆解为明确步骤路由→分析→筛选→确认你有明确的“成功标准”如订单摘要必须含机型、分数、发货时间。不该用的场景数据是纯PDF扫描件OCR后全是乱码——先做数据清洗再谈R
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑