ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agentic RAG实战:建库-检索-生成闭环设计与工程落地

Agentic RAG实战:建库-检索-生成闭环设计与工程落地 1. 这不是“又一个RAG教程”而是一份Agent开发者亲手踩坑后整理的全流程实操手记我从2022年夏天开始写第一个能调用天气API的简单Agent到今天带团队落地三个企业级Agentic RAG系统中间重写了七版知识库 pipeline。这篇笔记标题里写的“建库→检索→生成”表面看是线性流程实际在真实项目里这三个环节像三股拧在一起的麻绳——建库质量差一毫检索就跑偏检索召回不准生成再强也是胡说而生成环节一旦没约束又会反向污染建库策略。所以别被标题骗了这不是教你怎么调用LangChain的load_and_split而是告诉你当用户扔给你10万页PDF、37个Excel表、还有几GB扫描件时你第一分钟该敲什么命令、第二分钟该删什么字段、第三分钟该盯住哪个指标。核心关键词全在标题里Agent、RAG、建库、检索、生成。但请注意这里说的“Agent”不是单次问答机器人而是具备记忆、工具调用、多步推理能力的智能体“RAG”也不是把文档扔进向量库就完事而是要让Agent在决策链路中像人类专家一样知道“此刻该查什么、去哪查、查到后怎么用”“建库”不是ETL流水线而是对原始数据做语义切片、结构清洗、元信息标注的深度预处理“检索”不是相似度排序而是结合查询意图、上下文状态、Agent历史行为的动态重排序“生成”更不是LLM自由发挥而是带约束、带溯源、带fallback机制的可控合成。适合三类人直接抄作业正在用LlamaIndex搭内部知识库却总被业务方吐槽“答非所问”的工程师想把现有客服Bot升级成能自主查手册、填工单、写报告的Agentic系统的产品经理以及刚学完Transformer原理、正卡在“知道向量是什么但不知道怎么让它真正有用”阶段的AI新人。下面所有内容都来自我们给某制造业客户部署RAGAgent系统时的真实日志、监控截图和回滚记录。2. 整体设计思路为什么必须放弃“先建库再检索最后生成”的线性幻想2.1 真实Agent工作流中的RAG不是插件而是呼吸系统很多初学者把RAG当成LLM的“外挂插件”用户提问→触发RAG→返回结果→LLM生成回答。这种理解在demo里能跑通在生产环境里必死。我们给客户做的第一个POC就是这么崩的——Agent需要根据设备故障代码如E204查维修手册但手册里“E204”同时出现在“电源模块故障”和“通信协议超时”两章纯向量检索召回Top3里混着两个矛盾答案LLM直接拼凑出“先断电再重启网关”的错误指令。问题出在哪不是Embedding模型不够好而是整个流程设计违背了Agent的本质Agent是状态机不是函数调用器。它每一步决策都依赖当前状态已知信息、历史动作、用户情绪、目标解决故障、约束安全规范、时效要求。RAG必须嵌入这个状态机而不是等它喊“我要查东西”才启动。所以我们重构了架构把RAG拆成三个可编程组件Query Planner查询规划器接收Agent当前state比如“用户说‘机器突然停机’上一步已确认型号为X3000”输出结构化查询请求包含主关键词“X3000 停机”、排除项“不查软件升级指南”、优先级字段“必须含‘急停按钮’‘继电器’”Adaptive Retriever自适应检索器不只用向量相似度还融合BM25关键词匹配、实体链接把“X3000”映射到数据库里的product_id、甚至用户画像老工程师偏好查电路图新员工倾向看操作视频Evidence Integrator证据整合器把召回的片段按可信度加权手册原文论坛帖子内部Wiki、去重同一故障的三种描述合并、补全上下文只召回“检查K1继电器”自动补上“K1位置在控制柜右下角”。这三者不是顺序执行而是形成反馈环Evidence Integrator发现召回内容矛盾会触发Query Planner生成修正查询Agent执行生成后发现用户追问“为什么不是A而是B”Evidence Integrator立刻调取A/B对比依据。建库阶段就为这个闭环埋点——比如在PDF解析时不仅存文本块还存“该段落属于手册第几章第几节”“是否含警告图标”“关联的故障代码列表”。2.2 建库策略决定80%的检索质量而90%的人在第一步就错了我见过太多团队花两周调优Embedding模型却用Python默认的pdfplumber.extract_text()处理技术手册。结果呢一页PDF里“表3-2 输入电压参数”被切成三段“表3-2”、“输入电压”、“参数”向量库里存了三个孤立向量。用户搜“X3000输入电压”检索器根本找不到完整表格。建库不是数据搬运而是语义结构重建。我们最终采用的分层建库法Layer 0原始数据归一化所有文件转为统一格式PDF用pdfminer.six保留字体/表格结构、Word用python-docx提取样式层级、Excel用pandas.read_excel强制dtypestr避免数字转科学计数法。关键动作删除页眉页脚、合并重复标题、修复扫描件OCR错字用Levenshtein距离比对同章节不同版本PDF。Layer 1语义切片Semantic Chunking拒绝固定长度切片。对技术文档按“标题层级”切H1→H2→H3形成树状结构每个叶子节点如“3.2.1 K1继电器测试步骤”作为独立chunk对FAQ按“问题-答案对”切对日志文件按“时间戳事件类型”切。每个chunk附带元数据{source: manual_v2.pdf, section: 3.2.1, type: procedure, entities: [K1, 继电器, X3000]}。Layer 2增强标注Enriched Annotation这步最耗时但回报最高。用规则引擎小模型做三件事实体标准化把“X3K”“X3000”“X-three-thousand”全映射到product_id: X3000关系注入在“K1继电器”chunk里自动添加related_to: [电源模块, 急停电路]置信度打标对OCR识别的文本用字符级置信度Tesseract输出标记ocr_confidence: 0.82低置信度chunk在检索时降权。建库完成后我们不做“向量化入库”而是先做质量探针测试随机抽100个真实用户问题如“X3000停机时如何复位”人工标注标准答案所在chunk ID然后测召回率。低于92%就退回Layer 1重新切片——这个阈值是客户现场故障平均处理时长倒推出来的92%意味着90%的故障能在2分钟内定位到正确手册章节。2.3 检索不是“找相似”而是“找相关”相关性由Agent目标定义传统RAG的检索评估用MRRMean Reciprocal Rank但在Agent场景里MRR毫无意义。我们曾用all-MiniLM-L6-v2模型MRR达0.85但Agent在实际对话中仍频繁出错。根因在于MRR假设“Top1就是正确答案”而Agent需要的是“支撑决策的最小证据集”。比如用户问“X3000停机是否需更换主板”正确答案不是“是/否”而是“先测K1继电器电压若12V则更换主板”。检索器必须召回“K1测试步骤”和“主板更换指南”两个chunk并明确它们的逻辑关系前者是判断条件后者是执行动作。因此我们的检索器设计遵循三个原则目标对齐Goal AlignmentQuery Planner输出的查询请求里包含intent: diagnosis诊断意图或intent: repair维修意图检索器据此调整权重。诊断意图下“故障现象描述”chunk权重30%而“备件清单”chunk权重-50%上下文感知Context AwarenessAgent当前state里有last_action: 测量K1电压检索器自动提升含“电压标准值”“异常电压处理”的chunk排名证据链构建Evidence Chaining不只返回孤立chunk而是返回带关系的chunk组。例如召回[chunk_A: K1电压标准12-24V, chunk_B: K1电压12V需更换主板]并标注relation: prerequisite前提关系。技术实现上我们放弃单一向量检索采用混合检索Hybrid Retrieval关键词层用Elasticsearch做BM25匹配确保术语精确如“K1”不会被“K12”干扰向量层用bge-reranker-base做重排序但输入不是原始query而是Query Planner生成的结构化请求含intent、exclude、priority图谱层对高频实体如“X3000”“K1”构建轻量知识图谱检索时走图遍历“X3000→has_part→K1→requires_test→电压”。这带来一个反直觉结论Embedding模型的选择不如Query Planner的设计重要。我们测试过text-embedding-3-large和bge-m3当Query Planner能精准表达意图时两者效果差距3%但Query Planner失效时再强的Embedding也救不了。2.4 生成不是“写答案”而是“编排证据”编排逻辑由Agent角色决定很多RAG项目失败是因为把生成环节当成“把召回文本喂给LLM”。结果LLM把三段冲突描述揉成一段看似合理实则错误的回复。在Agent系统里生成是证据驱动的编排Evidence-Guided Orchestration而非文本生成。我们定义了三种Agent角色对应三种生成模式Troubleshooter故障排查员生成必须严格遵循“现象→原因→验证→解决”四段式。召回的chunk被强制分类到四类槽位缺失任一槽位则触发fallback如无“验证步骤”则生成“建议使用万用表测量K1两端电压”Documentalist文档专员生成需保留原文出处。每句输出后自动追加[来源: manual_v2.pdf 第3.2.1节]且对引用内容做保真度校验若原文说“电压12V”生成不得写成“电压不低于12V”Advisor顾问生成需带风险提示。当召回内容含“警告”“注意”标签时生成开头必须加⚠️ 安全提示且禁止省略原警告内容。技术实现上我们不用prompt engineering硬编码而是用结构化输出模板Structured Output Schema{ role: Troubleshooter, evidence_slots: { phenomenon: [chunk_123, chunk_456], cause: [chunk_789], verification: [chunk_246], solution: [chunk_135] }, constraints: [禁用推测性语言, 所有数值保留原文单位] }LLM的输出被强制解析为JSON再由Orchestrator按schema填充。这样即使LLM胡说系统也能拦截如solution槽位为空时拒绝输出。3. 核心实操环节从零搭建可落地的Agentic RAG Pipeline3.1 建库实操用PythonUnstructured.io重建语义结构建库不是写个for循环调用split()而是构建一个能理解技术文档语义的解析流水线。我们基于Unstructured.io定制了三层处理器代码已开源在内部GitLab可提供简化版Step 1文档预处理Preprocessorfrom unstructured.partition.pdf import partition_pdf from unstructured.staging.base import convert_to_dict def preprocess_pdf(file_path): # 关键参数strategyhi_res启用OCRinfer_table_structureTrue识别表格 elements partition_pdf( filenamefile_path, strategyhi_res, infer_table_structureTrue, languages[zh], # 重点指定坐标系避免扫描件歪斜导致切片错乱 coordinatesTrue ) # 后处理合并相邻的TextElement修复被换行切断的句子 merged_elements [] for el in elements: if hasattr(el, text) and el.text.strip(): # 检查是否与前一个元素在同一逻辑段Y坐标差20px且字体相同 if (merged_elements and abs(el.metadata.coordinates.points[0][1] - merged_elements[-1].metadata.coordinates.points[0][1]) 20 and el.metadata.font_name merged_elements[-1].metadata.font_name): merged_elements[-1].text el.text else: merged_elements.append(el) return merged_elementsStep 2语义切片SemanticChunkerclass SemanticChunker: def __init__(self): self.section_pattern r^\d\.\d\.\d\s.*$ # 匹配3.2.1 测试步骤 def chunk_by_section(self, elements): chunks [] current_section None current_content [] for el in elements: if hasattr(el, text) and re.match(self.section_pattern, el.text.strip()): # 遇到新章节保存上一章节 if current_section and current_content: chunks.append({ text: \n.join(current_content), metadata: { section: current_section, source: el.metadata.filename, type: section } }) current_section el.text.strip() current_content [el.text] elif current_section: current_content.append(el.text) return chunks # 实际使用时对每个PDF调用 elements preprocess_pdf(manual_v2.pdf) chunks SemanticChunker().chunk_by_section(elements) # 输出示例{text: 3.2.1 K1继电器测试步骤\n1. 断开电源...\n2. 用万用表测量K1两端电压..., metadata: {section: 3.2.1, source: manual_v2.pdf, type: section}}Step 3元数据增强MetadataEnricherimport spacy from spacy.matcher import Matcher nlp spacy.load(zh_core_web_sm) matcher Matcher(nlp.vocab) # 定义产品型号匹配规则覆盖X3000/X3K/X-three-thousand等变体 pattern [{LOWER: {IN: [x3000, x3k]}}] matcher.add(PRODUCT_ID, [pattern]) def enrich_metadata(chunk): doc nlp(chunk[text]) matches matcher(doc) entities [] for match_id, start, end in matches: span doc[start:end] entities.append({ text: span.text, label: PRODUCT_ID, normalized: X3000 # 标准化ID }) # 注入关系从chunk文本中提取K1关联的部件 if K1 in chunk[text] and 继电器 in chunk[text]: entities.append({ text: K1, label: COMPONENT, relations: [电源模块, 急停电路] }) chunk[metadata][entities] entities return chunk # 对每个chunk调用 enriched_chunk enrich_metadata(chunks[0])关键参数说明与避坑点strategyhi_res必须开启普通OCR对技术文档表格识别率40%hi_res模式用LayoutParser检测版面再用PaddleOCR识别准确率达92%coordinatesTrue不可省略没有坐标信息无法判断“表3-2”和其下方表格是否属于同一逻辑单元切片正则^\d\.\d\.\d\s.*$要根据手册实际编号风格调整有的用“3.2.1.”有的用“3.2.1”Spacy模型必须用zh_core_web_sm而非en_core_web_sm中文分词错误会导致实体识别全盘崩溃元数据增强不是一次性的当新增手册版本时需用新旧版本diff自动更新关系映射如新版中“K1”改名为“RELAY_K1”。3.2 检索实操Hybrid Retrieval的工程化实现纯向量检索在Agent场景下必然失败必须用混合检索。我们用ElasticsearchSentence-Transformers构建双通道检索器核心代码如下Step 1Elasticsearch索引配置支持结构化查询PUT /rag_index { settings: { number_of_shards: 3, analysis: { analyzer: { my_analyzer: { type: custom, tokenizer: ik_max_word, filter: [lowercase] } } } }, mappings: { properties: { text: { type: text, analyzer: my_analyzer }, section: {type: keyword}, source: {type: keyword}, entities: {type: keyword}, intent_weights: { // 预存各意图下的权重系数 properties: { diagnosis: {type: float}, repair: {type: float} } } } } }Step 2向量库构建用BGE-M3做多语言Embeddingfrom sentence_transformers import SentenceTransformer import numpy as np model SentenceTransformer(BAAI/bge-m3, devicecuda) def embed_chunks(chunks): # BGE-M3支持多向量dense稠密向量、sparse稀疏向量、colbert多向量 # 我们用densecolbert双编码提升长文本匹配精度 dense_embeddings model.encode( [c[text] for c in chunks], batch_size32, show_progress_barTrue, convert_to_numpyTrue ) # colbert向量需单独计算用官方ColBERTv2 from colbert import Indexer indexer Indexer(checkpointcolbert-ir/colbertv2.0) colbert_embeddings indexer.encode([c[text] for c in chunks]) return dense_embeddings, colbert_embeddings # 存入FAISS向量库支持GPU加速 import faiss index faiss.IndexFlatIP(1024) # BGE-M3 dense维度为1024 index.add(dense_embeddings)Step 3混合检索执行Query Planner输出→双通道召回→融合排序def hybrid_retrieve(query_plan, top_k10): # Query Plan示例{intent: diagnosis, keywords: [X3000, 停机], exclude: [升级]} # 通道1Elasticsearch关键词检索 es_query { bool: { must: [{multi_match: {query: .join(query_plan[keywords]), fields: [text^3, section^2]}}], must_not: [{term: {section: exclude}} for exclude in query_plan.get(exclude, [])] } } es_results es.search(indexrag_index, queryes_query, sizetop_k*2) # 通道2向量检索用dense向量 query_embedding model.encode(query_plan[keywords][0]) # 简化用主关键词编码 D, I index.search(np.array([query_embedding]), top_k*2) # 融合排序ES得分 * intent_weights 向量相似度 * 0.7 fused_scores {} for hit in es_results[hits][hits]: doc_id hit[_id] es_score hit[_score] intent_weight hit[_source].get(intent_weights, {}).get(query_plan[intent], 0.5) fused_scores[doc_id] es_score * intent_weight for i, idx in enumerate(I[0]): doc_id fvec_{idx} vec_score D[0][i] fused_scores[doc_id] vec_score * 0.7 # 返回TopK融合结果 sorted_docs sorted(fused_scores.items(), keylambda x: x[1], reverseTrue)[:top_k] return [doc_id for doc_id, _ in sorted_docs] # 实际调用 query_plan {intent: diagnosis, keywords: [X3000, 停机], exclude: [软件升级]} top_chunks hybrid_retrieve(query_plan)关键参数与性能调优Elasticsearch的multi_match字段加权text^3确保正文匹配优先于标题BGE-M3的dense向量用于全局相似度colbert向量用于细粒度匹配如“K1电压”vs“K1两端电压”但我们发现colbert在工业文档上收益有限最终只用dense融合权重0.7是实测得出向量相似度对术语变化鲁棒但ES对精确术语匹配更强0.7平衡两者top_k*2召回再融合避免单通道漏召如ES可能漏掉“X3000突然停机”但向量库能召回“X3000无预警停机”。3.3 生成实操Evidence-Guided Orchestration的强制约束生成环节必须打破“LLM自由发挥”的幻觉用结构化Schema约束输出。我们基于Llama-3-70B-Instruct实现核心是Prompt JSON Schema Output Parser三重保险Step 1定义Orchestration SchemaPydantic v2from pydantic import BaseModel, Field from typing import List, Optional class EvidenceSlot(BaseModel): chunk_ids: List[str] Field(..., description召回的chunk ID列表) required: bool Field(True, description该槽位是否必须填充) class GenerationConfig(BaseModel): role: str Field(..., descriptionAgent角色Troubleshooter/Documentalist/Advisor) evidence_slots: dict[str, EvidenceSlot] Field(..., description证据槽位定义) constraints: List[str] Field(..., description生成约束列表) # 示例配置 config GenerationConfig( roleTroubleshooter, evidence_slots{ phenomenon: EvidenceSlot(chunk_ids[chunk_123], requiredTrue), cause: EvidenceSlot(chunk_ids[chunk_456], requiredTrue), verification: EvidenceSlot(chunk_ids[chunk_789], requiredFalse), solution: EvidenceSlot(chunk_ids[chunk_246], requiredTrue) }, constraints[禁用推测性语言, 所有数值保留原文单位] )Step 2构造结构化Promptdef build_prompt(config, retrieved_chunks): # 从retrieved_chunks中提取对应chunk内容 evidence_text for slot_name, slot in config.evidence_slots.items(): for chunk_id in slot.chunk_ids: # 从向量库或ES中获取chunk原文 chunk_content get_chunk_by_id(chunk_id) evidence_text f[{slot_name.upper()}]\n{chunk_content}\n\n prompt f你是一名专业的{config.role}请严格按以下要求生成回复 1. 输出必须为JSON格式包含字段phenomenon, cause, verification, solution 2. 每个字段内容必须直接来自[evidence]中的对应部分禁止添加、删减、改写 3. 遵守约束{; .join(config.constraints)} 4. 若[evidence]中缺少某字段所需内容该字段值设为null [evidence] {evidence_text} [output_format] {config.model_dump_json(indent2)} return prompt # 生成Prompt示例 prompt build_prompt(config, top_chunks) # 输出含明确字段要求和约束的PromptLLM无法自由发挥Step 3强制JSON输出与校验import json from llama_cpp import Llama llm Llama(model_path./llama-3-70b-instruct.Q4_K_M.gguf, n_ctx8192) def generate_with_schema(prompt, schema): # Llama.cpp支持grammar参数强制JSON输出 grammar froot :: {json.dumps(schema, ensure_asciiFalse)} output llm( prompt, max_tokens1024, temperature0.1, # 降低随机性 grammargrammar, # 关键强制JSON格式 stop[] # 防止LLM输出代码块 ) try: result json.loads(output[choices][0][text]) # 校验required字段 for slot_name, slot in schema.evidence_slots.items(): if slot.required and not result.get(slot_name): raise ValueError(fRequired slot {slot_name} is empty) return result except json.JSONDecodeError: # fallback用正则提取JSON片段 import re json_match re.search(r\{.*?\}, output[choices][0][text], re.DOTALL) if json_match: return json.loads(json_match.group()) else: raise RuntimeError(Failed to parse JSON output) # 调用 result generate_with_schema(prompt, config) # result是严格符合schema的dict可直接用于前端渲染关键经验与避坑点temperature0.1是底线高于0.3时LLM开始“创作”即使有grammar也会输出无效JSONgrammar参数比prompt里写“请输出JSON”有效10倍Llama.cpp底层用PEG语法树校验stop[]防止LLM在JSON后加代码块注释如json {...}导致解析失败必须做required字段校验LLM常把optional字段设为null但required字段缺失必须报错重试不要用OpenAI API其JSON mode在长文本生成时不稳定Llama.cppgrammar更可靠。4. 常见问题与排查技巧实录那些文档里不会写的血泪教训4.1 建库阶段90%的“检索不准”其实源于PDF解析失败问题现象用户搜“X3000输入电压”召回结果全是“输出电压”相关内容且MRR测试显示Top1准确率仅65%。排查路径检查原始PDF用pdfinfo manual_v2.pdf发现Creator为“Adobe Acrobat Pro DC 2023”确认是扫描件查看Unstructured日志INFO unstructured.partition.pdf - Using hi_res strategy with OCR但OCR日志为空提取单页图像用pdf2image.convert_from_path(manual_v2.pdf, first_page1, last_page1)导出PNG发现分辨率仅72dpi标准应≥300dpi验证OCR用PaddleOCR直接识别该PNG字符错误率高达42%正常5%。根本原因扫描件分辨率不足OCR识别错误导致“输入”被识别为“输出”。解决方案预处理增加DPI提升用pdfimages -list manual_v2.pdf查图像DPI若200则用ImageMagick重采样convert -density 300 -quality 100 manual_v2.pdf manual_v2_hi.pdfOCR后人工校验对高频术语如“输入电压”“K1”建立词典OCR结果匹配词典率95%时告警终极方案对关键手册采购专业OCR服务如ABBYY FineReader成本高但准确率99%。提示不要相信“PDF转Word再转文本”的方案Word会破坏表格结构技术文档中表格占比超30%丢失表格等于丢失核心参数。4.2 检索阶段向量相似度高≠语义相关警惕“假阳性召回”问题现象Agent查“X3000停机”召回Top3是“X3000正常运行时电压范围”相似度0.82“X2000停机处理流程”相似度0.79“X3000软件升级后停机”相似度0.75但正确答案“X3000硬件故障停机”相似度仅0.61排在第12位。根因分析Embedding模型在通用语料上训练对“停机”一词把“软件升级后停机”高频共现和“硬件故障停机”低频向量拉近Query未体现意图用户说“停机”但未说明是“突发”还是“升级后”Query Planner未区分。解决方案Query Planner增强在Agent state中加入context: 用户刚说‘机器突然停机’未提升级”生成查询时强制添加exclude: [升级, 重启后]向量库微调用客户真实QA对如“X3000突然停机→查硬件故障”做Contrastive Learning损失函数聚焦区分“突然停机”vs“升级停机”引入领域词典在Embedding前用规则替换同义词“突然停机”→“hardware_failure_shutdown”“升级停机”→“software_update_shutdown”让向量空间天然分离。实操心得我们用spaCy的Matcher构建了200条领域规则覆盖“停机/宕机/死机/黑屏”等12种表述替换后相似度差异从0.05扩大到0.32Top1召回率从65%升至89%。4.3 生成阶段LLM“一本正经胡说八道”如何让证据说话问题现象Agent生成“K1继电器电压标准为12-24V若低于12V需更换主板”但手册原文是“K1电压12V时检查电源模块若电源模块正常再更换主板”。LLM把两步操作压缩成一步且删除了关键前提。技术根因Prompt中“禁止改写”约束太弱LLM认为“压缩步骤”不算改写证据整合器未标注逻辑关系原文中“检查电源模块”是“更换主板”的前提条件。解决方案强化证据标注在建库阶段用规则引擎识别条件句“若...则...”“当...时...”自动标注relation: prerequisitePrompt显式要求在constraints中增加“必须保留原文逻辑连接词若/当/则/否则”并举例说明后处理校验用spaCy解析生成文本检查“若”字句是否都有对应“则”字句缺失则触发重生成。注意不要指望LLM理解逻辑关系。我们测试过GPT-4-turbo对“若A则B若B则C”链条30%概率漏掉中间环节。必须靠结构化约束后处理双重保险。4.4 Agent集成阶段RAG响应延迟高拖慢整个决策链路问题现象Agent单次交互平均耗时8.2秒其中RAG占6.5秒建库0.3s检索4.2s生成2.0s用户等待感强烈。性能瓶颈定位timeit测试显示Elasticsearch查询100msFAISS向量搜索50ms主要耗时在model.encode()BGE-M3编码单个query需1.8sCPU/0.3sGPU但Agent每轮需编码3-5个queryQuery Planner生成多个变体llm.generate()耗时1.5s但这是必要开销。优化方案Query缓存对相同意图关键词组合缓存Embedding结果Redis命中率70%异步检索Agent发起RAG请求后不阻塞继续执行其他动作如调用API查库存RAG结果返回后再整合Embedding模型蒸馏用BGE-M3蒸馏出轻量版768维→384维速度提升2.1倍相似度下降仅0.02可接受。经验不要盲目追求“端到端低延迟”。Agent的价值在于决策质量
返回列表