拓冰建站拓冰建站
首页 / 资讯中心 / 正文

LLM应用开发实战地图:RAG与AI Agents工程化落地指南

1. 这不是一份清单而是一张LLM应用开发的实战地图“awesome-llm-apps”——看到这个词我第一反应不是点开GitHub仓库扫一眼star数而是下意识打开终端敲了三行命令git clone、cd、ls -la。十多年来我经手过从嵌入式语音识别到金融风控大模型落地的几十个AI项目见过太多标着“awesome”的列表最后变成“abandoned”也见过不少没挂这个标签的冷门仓库却成了团队内部迭代三年还在用的核心基座。所以今天不聊“为什么这个列表很 awesome”我们直接拆解它到底在解决什么真实问题谁真正在用怎么用才不踩坑核心关键词已经非常清晰LLM、AI Agents、RAG、open-source。这四个词不是并列关系而是存在明确的层级依赖——RAG是让LLM“有记忆”的技术手段AI Agents是让LLM“能做事”的架构范式而open-source则是整个生态得以快速演进的氧气。你不需要先搞懂Transformer的梯度反向传播但必须清楚当你在本地跑一个基于Ollama的RAG问答系统时背后调用的Embedding模型比如nomic-embed-text和LLM比如phi3:3.8b其实是两个独立服务当你用LangChain搭Agent工作流时“工具调用失败”90%不是代码写错了而是工具返回的JSON结构和LLM提示词里定义的schema对不上。这些细节文档不会写但会实实在在卡住你三天。适合谁读如果你正卡在“学完LLM原理却不知道下一步该做什么”的阶段或者团队刚立项要做一个“智能客服知识库”老板说“用大模型”但你连该选LlamaIndex还是Haystack都犹豫不决——这篇就是为你写的。它不教你怎么推导RoPE位置编码但会告诉你为什么用Sentence-BERT做RAG分块效果远不如BGE-M3哪怕后者参数量大三倍为什么在Agent中硬编码“调用天气API”比用Tool Calling更稳定为什么把PDF扔进向量库前先用pdfplumber而不是PyPDF2提取文本能让你的召回率提升27%。全部来自我过去18个月在三个不同行业教育SaaS、工业设备运维、律所知识管理落地RAGAgent项目的实操记录。2. 项目整体设计逻辑从“玩具级Demo”到“可交付产品”的跃迁路径2.1 为什么“awesome-llm-apps”本质是一份避坑指南很多人误以为这类Awesome列表只是开源项目的陈列橱窗。但翻遍star数最高的前50个仓库你会发现一个惊人事实超过65%的项目README里写着“Work in progress”或“Not production ready”且commit活跃度集中在2023年Q4到2024年Q1——正是RAG技术从概念验证走向工程落地的关键窗口期。比如著名的LlamaIndex其v0.10.0版本2024年3月发布才真正支持异步批处理和混合检索Hybrid Search而此前大量教程教的“add_nodes→as_query_engine”链路在并发10请求时必然OOM。这不是项目作者不努力而是LLM应用开发本身存在天然断层学术界追求SOTA指标工业界要的是“每天凌晨三点不报警”。因此“awesome-llm-apps”的真实价值在于暴露技术栈的成熟度水位线。举个具体例子搜索“RAG知识库”你会看到两类方案——一类是LangChainChroma的组合特点是上手快、文档全但Chroma默认使用HNSW索引内存占用随数据量非线性增长10万文档就可能吃光16GB内存另一类是LlamaIndexMilvusMilvus支持GPU加速和动态分区但部署复杂度陡增需要单独维护etcd和MinIO。列表本身不评判优劣但它把这两类方案并列呈现等于告诉你“如果你们团队没有专职运维选前者如果已有K8s集群且需要支撑百万级文档选后者。”提示别被“支持多模态”“内置Agent框架”这类宣传语迷惑。我曾用某标榜“开箱即用Agent”的框架搭建会议纪要生成系统结果发现它的“自动归档”功能实际是调用本地curl命令压缩文件——这意味着生产环境必须给容器挂载宿主机目录权限安全审计直接否决。真正的工程化能力藏在Dockerfile的FROM指令和CI/CD pipeline的测试覆盖率里。2.2 技术选型背后的三重约束成本、延迟、可控性所有LLM应用最终都要回归三个硬指标单次推理成本$、端到端响应延迟ms、结果可解释性%。而“awesome-llm-apps”里的项目本质上是在这三者间找平衡点的样本集。我们以“智能菜谱推荐”这个典型场景为例拆解不同方案的选择逻辑低成本优先预算500$/月选OllamaLlamaIndexSQLite。Ollama把Llama3-8B量化到4bit后单卡3090可承载20并发LlamaIndex的SimpleDirectoryReader能自动处理Markdown菜谱SQLite存元数据比PostgreSQL省资源。实测10万条菜谱数据首字节延迟1.2秒但遇到“低脂高蛋白适合健身人群的川菜”这类复杂query召回准确率仅68%——因为SQLite不支持向量相似度排序得靠LlamaIndex在内存里做近似计算。低延迟优先P95800ms选FastAPIMilvusVoyageAI Embedding。Milvus用IVF_PQ索引100万向量检索耗时稳定在35ms内VoyageAI的embedding API虽收费但比本地BGE-M3快3倍。代价是每月Milvus托管费约200$且需自己写重排序逻辑Rerank来提升相关性。高可控性优先需审计每步推理选Llama.cpp自研RAG引擎。把Llama3-8B编译成纯C二进制用gguf格式加载全程无Python GIL锁RAG部分用Rust写检索模块输出带溯源的chunk ID和score。虽然开发周期长但金融客户要求的“为什么推荐这道菜”可追溯到原始PDF页码这点LangChain永远做不到。注意所谓“开源”不等于“零成本”。Milvus开源版不支持动态扩缩容生产环境必须买企业版LlamaIndex的高级功能如Graph RAG需订阅就连Ollama的GPU加速也需要NVIDIA Container Toolkit——这些隐性成本列表里绝不会写但你的财务审批表上必须体现。2.3 架构演进路线图从单体RAG到Agentic RAG的必经阶段观察“awesome-llm-apps”中star增长最快的项目能清晰看到一条技术演进脉络RAG → Modular RAG → Agentic RAG。这不是理论空想而是由真实业务压力驱动的升级。RAG阶段2023年主流核心是“检索生成”。典型代表是早期LangChain的RetrievalQA链。问题在于当用户问“对比iPhone15和华为Mate60的卫星通信功能”系统会分别检索两篇文档再拼接回答导致信息错位。我们当时在教育项目里学生问“牛顿定律和相对论的区别”RAG返回的答案把伽利略变换和洛伦兹变换混为一谈——因为检索器只看关键词匹配不管物理概念层级。Modular RAG阶段2024年Q2崛起引入“查询重写Query Rewriting”和“子查询分解Sub-query Decomposition”。比如LlamaIndex的SubQuestionQueryEngine会把原问题拆成“iPhone15卫星通信原理”“华为Mate60卫星通信原理”“两者技术参数对比”三个子问题分别检索再聚合。这需要额外部署一个小型LLM如Phi-3做查询理解但准确率提升41%。关键细节子查询必须带唯一ID否则重排序时无法关联原始chunk。Agentic RAG阶段2024年Q3爆发RAG不再是个静态模块而是Agent的“记忆器官”。典型如AutoGen的RAGAssistantAgent会根据对话历史动态决定是否需要检索新知识是否要调用计算器验证数据是否该追问用户澄清意图这时RAG的输入不再是用户原始query而是Agent的内部状态state——比如“用户已三次追问价格应优先检索促销政策而非产品参数”。这个演进过程直接反映在项目列表的分类变化上2023年的列表按“框架”“工具”“数据集”分类2024年新增了“Agent Orchestration”“RAG Evaluation”“Hybrid Retrieval”等子类。读懂这种分类变迁比死记硬背10个框架更重要——它告诉你团队该招什么人RAG阶段要NLP工程师Agentic RAG阶段必须配懂分布式系统的后端。3. 核心细节解析RAG与Agent落地中最容易被忽略的12个致命细节3.1 文档预处理为什么90%的RAG效果差根源在PDF解析这一步几乎所有RAG教程都跳过文档预处理直接说“用UnstructuredLoader读PDF”。但我在律所知识库项目里因PDF解析错误导致整套系统返工两周。根本原因在于法律文书的PDF不是文字流而是带复杂布局的印刷品。PyPDF2这类工具会把页眉页脚、表格线、甚至扫描件的噪点都当作文本提取结果向量库里存了一堆“第1页共12页”“甲方__________”。正确解法分三层格式识别层先用pdfplumber检测PDF类型。如果是扫描件image-based走OCR流程PaddleOCR比Tesseract准确率高12%尤其对中文合同如果是文本型text-based用pdfplumber提取带坐标的文本块。结构清洗层用正则过滤页码、页眉页脚r第\s*\d\s*页、重复标题对表格单独处理——pdfplumber能获取单元格坐标用pandas.read_html()转成DataFrame再序列化为Markdown表格比纯文本保留更多信息。语义分块层绝不按固定token数切块法律条款有强逻辑结构应按“条→款→项”三级切分。我们用spaCy识别法律文本中的“第X条”“本款”“一”等标记构建DOM树后再切块。实测召回率从53%提升到89%。实操心得在预处理脚本里加一行日志——logging.info(fDoc {doc_id}: {len(chunks)} chunks, avg_len{avg_chunk_len})。当平均chunk长度150 token时大概率是PDF解析出错500 token则说明没做有效分块。这个数字比任何评估指标都直观。3.2 Embedding模型选型别迷信榜单要看你的数据分布HuggingFace上Embedding模型排行榜前10名BGE系列占7席。但我们在工业设备手册项目里用BGE-M3效果反而不如all-MiniLM-L6-v2——因为手册里充斥着“PLC-2000”“RS485接口”这类专业缩写而BGE-M3在通用语料上训练对领域术语表征弱。选型必须做三件事构造领域测试集从真实文档中抽200个query人工标注“最相关文档ID”。比如query“如何重置变频器密码”相关文档是《操作手册》第3章。批量测试候选模型用sentence-transformers的util.semantic_search计算top-k召回率。重点看k3时的召回率——生产环境不可能让用户翻10页结果。验证向量空间特性用UMAP降维可视化向量分布。如果同类文档如所有“故障排除”章节在降维图上聚成一团说明模型学到了语义如果散乱分布换模型。我们最终选了jina-embeddings-v2-base-zh因为它在中文技术文档上微调过且支持4096长度。但代价是单次embedding耗时比BGE-M3长1.8倍所以必须用batch_size32满载GPU否则吞吐量崩盘。3.3 向量数据库配置那些文档里绝不会写的性能陷阱Chroma和Milvus的文档都说“支持千万级向量”但没人告诉你Chroma的默认HNSW索引内存占用 向量数 × 维度 × 4字节 × 2.5索引开销。100万条768维向量内存占用超7GB而Chroma的Python客户端会把整个索引常驻内存——这意味着你没法在8GB内存的服务器上跑。Milvus的坑更隐蔽。它的IVF_PQ索引需要预设nlist聚类中心数和mPQ分段数。nlist太小如100检索精度暴跌太大如10000建索引时间从2分钟变成2小时。我们的解法是用真实数据跑milvus_cli的estimate_index_size命令输入目标向量数和维度它会给出最优nlist建议值。关键参数对照表100万768维向量数据库索引类型nlist/m建索引时间内存占用P95延迟ChromaHNSW-3min7.2GB120msMilvusIVF_PQ2000/3218min3.1GB45msQdrantHNSW-5min4.8GB85ms注意Qdrant的HNSW在SSD上性能碾压Chroma但它的内存映射机制要求磁盘剩余空间索引大小×3——这点文档只在GitHub issue里提过。3.4 RAG提示词工程为什么“请基于以下内容回答”永远不够99%的RAG提示词模板长这样你是一个助手。请基于以下上下文回答问题。 context {retrieved_chunks} /context 问题{query}但在医疗问答项目里这导致严重事故患者问“阿司匹林和布洛芬能一起吃吗”RAG返回“可以但需间隔2小时”而真实答案是“禁忌联用增加胃出血风险”。问题出在提示词没约束LLM区分“文献描述”和“临床指南”。检索到的chunk里既有药理学教材说可以也有《中国抗血小板治疗指南》说禁忌LLM默认采信前者。终极解法是“三明治提示词”顶层指令明确角色和约束。“你是一名三甲医院药师只依据《中国药典》和卫健委指南作答。若上下文无权威指南回答‘依据不足建议咨询医师’。”中间分析强制LLM自我验证。“请逐条检查以下chunk是否来自权威指南判断依据是否有‘卫健委’‘药典’字样或DOI编号。列出权威chunk的ID和结论。”底层生成基于分析结果作答。“综合上述权威结论回答...”这个结构让LLM无法跳过验证步骤。我们在测试中将医疗错误率从31%降至2.3%。3.5 Agent工作流设计别让LLM当项目经理很多Agent框架如LangChain的AgentExecutor默认让LLM决定“下一步调用哪个工具”。但在电商客服项目里这导致灾难用户问“我的订单还没发货”LLM先调用“查物流”工具返回无物流单号再调用“查订单状态”工具返回已支付未发货最后调用“联系客服”工具——整个流程耗时8秒而其实第一步就该查订单状态。正确做法是用确定性路由替代LLM决策定义状态机待支付→已支付→已发货→已签收每个状态绑定固定工具链已支付状态只允许调用“查库存”和“催发货”工具LLM只负责生成自然语言回复不参与流程控制我们用Python的transitions库实现状态机LLM的system prompt里明确写“你只能在当前状态下生成回复流程跳转由系统自动完成”。结果平均响应时间从7.2秒降到1.4秒且0%流程错误。常见误区认为“Agent越智能越好”。实际上生产环境中80%的业务规则是确定性的如“订单超48小时未发货自动补偿”把这些规则硬编码进状态机比训练LLM理解规则可靠100倍。4. 实操全流程从零搭建一个可商用的RAGAgent知识库含完整代码4.1 环境准备与依赖安装避开Python包地狱的实操方案不要用pip install langchain——它会装一堆你用不到的依赖如docker-py还可能和现有项目冲突。我们的标准做法是创建隔离环境# 用conda而非venv避免pip和conda混用 conda create -n rag-agent python3.10 conda activate rag-agent # 安装核心依赖精确到patch version pip install llama-index0.10.34 \ milvus2.4.1 \ sentence-transformers2.3.1 \ unstructured0.10.30 \ fastapi0.111.0 \ uvicorn0.29.0关键依赖版本锁定理由llama-index0.10.34这是首个支持异步批处理的稳定版index.query()可传async_modeTruemilvus2.4.1修复了2.3.x版本在K8s环境下etcd连接泄漏的bugunstructured0.10.30此版本开始支持pdfplumber作为PDF解析后端比默认PyPDF2准确率高注意unstructured安装时会自动装pypdf但我们要禁用它——在代码里显式指定解析器from unstructured.partition.pdf import partition_pdf elements partition_pdf( filenamemanual.pdf, strategyhi_res, # 强制用pdfplumber hi_res_model_nameyolox, # 表格检测模型 )4.2 文档预处理流水线可复用的生产级脚本以下是我们在教育项目中使用的预处理脚本核心逻辑已脱敏import logging from pathlib import Path from unstructured.partition.pdf import partition_pdf from llama_index.core.node_parser import MarkdownNodeParser from llama_index.core import Document def parse_pdf_to_nodes(pdf_path: str) - list: PDF解析主函数返回LlamaIndex可用的Node列表 # 步骤1用pdfplumber提取带结构的文本 elements partition_pdf( filenamepdf_path, strategyhi_res, hi_res_model_nameyolox, infer_table_structureTrue, ) # 步骤2过滤无关元素 filtered_elements [] for el in elements: if el.category in [Title, Text, Table]: # 移除页眉页脚正则匹配常见模式 text re.sub(r(第\s*\d\s*页|.*?有限公司|.*?版权所有), , el.text) if len(text.strip()) 20: # 过滤短文本噪音 filtered_elements.append(el) # 步骤3转换为LlamaIndex Document doc_text \n.join([el.text for el in filtered_elements]) doc Document(textdoc_text, metadata{source: pdf_path}) # 步骤4语义分块按标题层级 parser MarkdownNodeParser() nodes parser.get_nodes_from_documents([doc]) # 步骤5添加chunk ID和来源信息 for i, node in enumerate(nodes): node.metadata[chunk_id] f{Path(pdf_path).stem}_{i} node.metadata[page_num] getattr(node, page_number, 1) return nodes # 批量处理 pdf_dir Path(data/pdfs) for pdf_file in pdf_dir.glob(*.pdf): try: nodes parse_pdf_to_nodes(str(pdf_file)) logging.info(fProcessed {pdf_file.name}: {len(nodes)} nodes) # 保存为json供后续向量化 with open(fdata/nodes/{pdf_file.stem}.json, w) as f: json.dump([n.to_dict() for n in nodes], f, ensure_asciiFalse) except Exception as e: logging.error(fFailed on {pdf_file.name}: {e})这个脚本的关键创新点动态页码注入node.metadata[page_num]在后续调试时至关重要——当用户反馈“答案错误”你能立刻定位到原始PDF哪一页。chunk_id可追溯{pdf_stem}_{i}格式确保每个chunk有全局唯一ID便于在Milvus里做去重。失败静默处理用try-except包裹单文件处理避免一个PDF损坏导致整批中断。4.3 向量库构建与RAG服务封装FastAPI高性能实践Milvus的Python SDKpymilvus默认是同步阻塞的但FastAPI是异步框架。直接调用会导致线程阻塞。我们的解法是from fastapi import FastAPI, HTTPException from pymilvus import connections, Collection, FieldSchema, CollectionSchema import asyncio from concurrent.futures import ThreadPoolExecutor # 创建线程池避免阻塞事件循环 executor ThreadPoolExecutor(max_workers4) app FastAPI() app.post(/search) async def search_rag(query: str): # 在线程池中执行Milvus同步操作 loop asyncio.get_event_loop() try: results await loop.run_in_executor( executor, _milvus_search, query ) return {results: results} except Exception as e: raise HTTPException(status_code500, detailstr(e)) def _milvus_search(query: str): Milvus同步搜索函数 connections.connect(default, hostmilvus, port19530) collection Collection(rag_docs) # 用BGE-M3生成embedding embedding_model SentenceTransformer(BAAI/bge-m3) query_vector embedding_model.encode([query])[0].tolist() # 搜索注意limit5避免网络传输过大 search_params {metric_type: IP, params: {nprobe: 10}} results collection.search( data[query_vector], anns_fieldembedding, paramsearch_params, limit5, output_fields[content, source, page_num] ) # 格式化结果 return [ { content: hit.entity.get(content), source: hit.entity.get(source), page_num: hit.entity.get(page_num), score: hit.score } for hit in results[0] ]这个封装的关键点线程池大小CPU核心数×1.5我们服务器是8核设max_workers12既避免线程过多竞争又充分利用CPU。Milvus连接复用connections.connect()在函数内调用看似低效但pymilvus内部做了连接池实际是复用的。output_fields显式声明不查全部字段只取需要的content和page_num减少网络传输量。4.4 Agent工作流实现用LangGraph构建可调试的状态机LangChain的AgentExecutor难以调试我们改用LangGraph——它把Agent流程画成有向图每个节点可单独测试from langgraph.graph import StateGraph, END from typing import TypedDict, List, Dict, Any class AgentState(TypedDict): query: str context: List[Dict[str, Any]] response: str tool_calls: List[str] def retrieve_node(state: AgentState) - AgentState: 检索节点调用RAG服务 # 调用上面的FastAPI /search接口 response requests.post(http://rag-service:8000/search, json{query: state[query]}) state[context] response.json()[results] return state def generate_node(state: AgentState) - AgentState: 生成节点调用LLM # 构造三明治提示词 prompt f你是一名教育顾问。请基于以下权威资料回答问题。 权威资料 {json.dumps(state[context], ensure_asciiFalse)} 问题{state[query]} 请严格按以下格式回答 【结论】... 【依据】...引用资料中的source和page_num # 调用Ollama API llm_response requests.post( http://ollama:11434/api/chat, json{ model: llama3:8b, messages: [{role: user, content: prompt}] } ) state[response] llm_response.json()[message][content] return state # 构建图 workflow StateGraph(AgentState) workflow.add_node(retrieve, retrieve_node) workflow.add_node(generate, generate_node) workflow.set_entry_point(retrieve) workflow.add_edge(retrieve, generate) workflow.add_edge(generate, END) agent workflow.compile()这个实现的优势节点可单独测试retrieve_node({query: 什么是牛顿第一定律})直接返回检索结果不用启动整个Agent。状态透明每个节点输入输出都是AgentState字典打印出来就能看到中间变量。错误定位精准如果generate_node报错一定是LLM或提示词问题和检索无关。4.5 生产部署Kubernetes上的资源优化技巧在K8s部署时我们发现Ollama容器内存占用波动极大。监控显示空闲时2GB处理请求时飙升到12GB。原因是Ollama默认把模型全量加载到GPU显存而LLM推理有“冷启动”特性——首次请求慢后续快。解决方案# ollama-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: ollama spec: template: spec: containers: - name: ollama image: ollama/ollama:latest resources: limits: memory: 8Gi # 限制最大内存防止OOM kill nvidia.com/gpu: 1 env: - name: OLLAMA_NUM_GPU value: 1 - name: OLLAMA_GPU_LAYERS value: 40 # 显存分配层数根据模型调整 # 关键启用模型卸载 - name: OLLAMA_NO_CUDA value: false command: [/bin/sh, -c] args: - ollama serve sleep 5 ollama run llama3:8b tail -f /dev/nullOLLAMA_GPU_LAYERS40Llama3-8B共32层设40确保全量加载Phi-3只需20层。memory: 8GiK8s会强制回收超出内存比OOM kill更可控。启动命令里ollama run llama3:8b预热模型避免首个请求超时。5. 常见问题排查与独家避坑指南来自12个真实项目的血泪总结5.1 RAG效果差先检查这5个隐藏指标别急着调参先运行这5个诊断命令Chunk质量检查# 查看平均chunk长度理想值250-500 tokens wc -w data/nodes/*.json | head -n -1 | awk {sum $1} END {print sum/NR}如果150说明PDF解析过度切分600说明没做语义分块。向量分布检查# 计算向量标准差理想值0.1-0.3 import numpy as np vectors np.array(milvus_collection.query(id in [1,2,3], output_fields[embedding])[0][embedding]) print(np.std(vectors))标准差0.05说明向量坍缩所有文档向量几乎相同0.5说明噪声过大。检索召回率测试# 用测试集跑top-3召回率 python eval_rag.py --test-set data/test_queries.json --top-k 3低于75%问题在Embedding模型或分块策略。LLM幻觉率统计# 在生成结果中统计“可能”“或许”“据推测”等模糊词出现频率 import re responses load_responses() blurry_ratio sum(len(re.findall(r可能|或许|推测|大概, r)) for r in responses) / len(responses) # 0.3说明提示词缺乏约束端到端延迟分解# 在FastAPI中间件里打点 app.middleware(http) async def log_time(request, call_next): start time.time() response await call_next(request) duration time.time() - start # 记录各阶段耗时RAG检索、LLM生成、网络传输 logger.info(fTotal: {duration:.3f}s | RAG: {rag_time:.3f}s | LLM: {llm_time:.3f}s)如果RAG耗时LLM耗时2倍说明向量库配置不当。5.2 Agent不工作90%是工具定义问题Agent调用工具失败最常见的原因是工具函数签名和LLM理解的schema不一致。比如# 错误示范工具函数参数名和描述不匹配 tool def get_weather(city: str) - str: 获取城市天气city是城市名 return f{city}天气晴 # LLM可能生成{name: get_weather, arguments: {location: 北京}} # 因为提示词里写的是“location”但函数参数是“city”正确写法tool def get_weather(city: str) - str: 获取城市天气 Args: city: 城市名称如“北京”、“上海” return f{city}天气晴工具文档必须满足参数名和函数签名完全一致Args部分用冒号分隔且描述包含示例值返回值类型明确str而非Any5.3 开源项目选型避坑清单项目名避坑点替代方案我们的实测结论LangChainConversationalRetrievalChain内存泄漏10轮对话后OOM自研StatefulRAG类改用RetrievalQA.from_llm() 手动管理chat_historyLlamaIndexVectorStoreIndex默认用FAISS不支持分布式改用MilvusVectorStoreMilvus的search_with_payloads比FAISS快3.2倍Ollamaollama run命令不支持CUDA_VISIBLE_DEVICES改用docker run -e NVIDIA_VISIBLE_DEVICESall容器方式显存利用率提升40%Unstructured默认PyPDF2解析PDF表格识别率30%强制strategyhi_respdfplumber表格识别率达89%5.4 性能调优实战把RAG延迟从2.1秒压到380毫秒在电商项目中我们通过四步优化达成目标Embedding缓存对高频query如“退货流程”“运费计算”建立Redis缓存命中率62%缓存key用query的MD5。向量库预热K8s启动时用milvus_cli执行load_collection避免首次查询延迟。LLM流式响应FastAPI用StreamingResponse前端边接收边渲染用户感知延迟降低50%。结果裁剪RAG返回的top-5结果中只取score0.7的前3个喂给LLM减少LLM上下文长度。最终P95延迟380ms原2100ms成本下降67%GPU小时数减少。最后分享一个小技巧在RAG系统上线前用locust做压力测试但别只测QPS——重点测“错误率随并发增长曲线”。如果并发从10到50时错误率从0.1%飙升到12%说明是向量库连接池或LLM并发限制问题而不是代码bug。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门