尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从零构建生产级RAG系统:LangGraph编排与LoRA微调实战

从零构建生产级RAG系统:LangGraph编排与LoRA微调实战 如果你正在构建一个基于大语言模型LLM的智能应用比如一个能回答公司内部文档问题的客服机器人或者一个能总结长篇技术报告的工具你很可能已经遇到了一个核心难题大模型无法记住它没“见过”的信息更无法保证回答的准确性。直接向模型提问它可能会基于训练数据“一本正经地胡说八道”幻觉问题。而简单地将文档内容全部塞进提示词Prompt又会迅速耗尽模型的上下文窗口导致成本飙升且效果不佳。RAG检索增强生成正是解决这一痛点的关键技术范式。它让大模型能够“外接”一个专属知识库在回答前先进行精准检索从而生成有据可依、准确可靠的答案。然而从“知道RAG概念”到“搭建一个稳定、高效、可用的RAG系统”中间隔着巨大的工程鸿沟。网上教程往往只讲单一环节比如如何调用一个Embedding API或者如何连接向量数据库。但一个生产级的RAG系统是一个复杂的工程拼图涉及数据预处理、向量化、检索、重排、生成以及流程编排等多个环节任何一个环节的短板都会导致最终效果大打折扣。本文将从零开始为你拆解一个完整、可落地的RAG系统全流程。我们将不仅介绍每个核心组件Embedding模型、向量数据库、检索优化策略更会使用LangGraph这一新兴的AI应用编排框架将整个流程串联成一个具备状态管理和复杂决策能力的智能体Agent。最后我们还会探讨如何通过LoRA微调来低成本地定制专属的Embedding模型进一步提升检索精度。读完本文你将能清晰地回答RAG系统的核心流程与关键组件是什么如何根据场景选择合适的Embedding模型和向量数据库超越简单相似度检索有哪些高级优化策略如何用LangGraph构建一个具备自我修正和路由能力的智能RAG流程何时以及如何对Embedding模型进行LoRA微调1. RAG系统全景图不止是“检索生成”在深入代码之前我们必须建立一个正确的认知RAG不是一个简单的“向量搜索提示词拼接”的公式。一个健壮的RAG系统是一个精心设计的管道Pipeline。1.1 核心流程拆解一个典型的RAG流程包含以下关键阶段文档加载与预处理从PDF、Word、网页、数据库等来源获取原始文本。文本分割Chunking将长文档切割成适合模型处理的小片段。这是影响检索精度的首要因素。分割策略按字符、按句子、按语义、重叠窗口需要根据文档类型精心设计。向量化Embedding使用Embedding模型将文本块转换为高维空间中的向量一组数字。这个向量的质量直接决定了后续检索的准确性。向量存储与索引将向量及其对应的原始文本元数据存入向量数据库并建立高效的索引如HNSW, IVF以加速检索。查询转换与检索将用户问题也转换为向量在向量数据库中执行相似度搜索如余弦相似度找出最相关的K个文本块。后处理与重排Rerank初步检索的结果可能包含相关性不高但向量相似的噪音。使用一个更精细的重排模型对Top K结果进行二次排序筛选出最相关的几个片段。提示工程与生成将筛选后的文本片段作为“上下文”与用户问题一起构造成最终的提示词提交给大语言模型LLM生成最终答案。评估与迭代通过人工评估或自动化指标如检索命中率、答案忠实度来衡量系统效果并持续优化上述各个环节。1.2 为什么需要LangGraph传统的RAG实现如使用LangChain通常是线性的、静态的管道。而LangGraph引入了“图”的概念允许你定义具有状态State和循环Cycle的复杂工作流。状态管理可以记住多轮对话的历史、中间结果和决策路径。条件路由可以根据检索结果的质量如相似度分数过低决定下一步动作例如触发一个“联网搜索”节点或直接告知用户“知识库中未找到相关信息”。自我修正可以设计一个“验证”节点检查LLM生成的答案是否与提供的上下文一致如果不一致则重新检索或生成。多智能体协作可以轻松编排多个“专家”智能体如一个负责检索一个负责总结一个负责校验协同工作。将RAG构建在LangGraph之上意味着你的系统从一个“问答机”进化成了一个具备基本推理和决策能力的“智能体”。2. 环境准备与工具选型在开始实战前我们需要搭建开发环境并选择合适的技术栈。本文将以Python为核心选择目前主流且易用的开源工具。2.1 基础环境Python 3.10确保你的Python版本在3.10或以上。包管理工具使用pip或conda。IDEVSCode、PyCharm等均可。2.2 核心库安装我们将使用以下库请通过pip安装# 核心AI应用框架与向量数据库客户端 pip install langgraph langchain langchain-community # 文本分割与加载 pip install unstructured[all-docs] tiktoken pypdf # Embedding模型以BGE为例和重排模型 pip install sentence-transformers FlagEmbedding # 向量数据库以Chroma为例轻量易用 pip install chromadb # LLM调用以Ollama本地运行Llama3为例也可替换为OpenAI等 pip install ollama # 可选用于LoRA微调 pip install transformers datasets peft accelerate torch2.3 关键组件选型建议Embedding模型通用场景BAAI/bge-large-zh-v1.5中文优、BAAI/bge-base-en-v1.5英文优、thenlper/gte-large。本文选用BAAI/bge-base-en-v1.5。追求最新可关注BAAI/bge-m3它支持多语言、多粒度但资源消耗更大。向量数据库轻量开发/原型ChromaDB无需服务API简单。生产级、高并发Qdrant、Milvus、Weaviate。它们支持分布式、持久化、更丰富的过滤条件。LLM本地部署隐私、成本通过Ollama运行llama3、qwen2.5、mistral等模型。云API便捷、强大OpenAI GPT-4o、Anthropic Claude、DeepSeek等。编排框架LangGraph用于构建有状态的智能工作流。3. 实战第一步构建基础RAG管道让我们先构建一个最基础的、线性的RAG管道理解每个环节。3.1 文档加载与分割假设我们有一个sample.pdf的技术文档。# file_path: data_loader.py from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter def load_and_split_pdf(pdf_path): 加载PDF并分割文本 loader PyPDFLoader(pdf_path) documents loader.load() # 加载出Document对象列表 # 创建文本分割器 # chunk_size: 每个文本块的最大字符数 # chunk_overlap: 块之间的重叠字符数防止语义被切断 # separators: 分割符优先级列表 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , , , , ] ) # 执行分割 chunks text_splitter.split_documents(documents) print(f原始文档页数: {len(documents)}) print(f分割后文本块数量: {len(chunks)}) print(f示例块内容 (前200字符): {chunks[0].page_content[:200]}...) return chunks if __name__ __main__: chunks load_and_split_pdf(./docs/sample.pdf)关键点chunk_size需要根据Embedding模型的最大输入长度和文档特点调整。重叠overlap对于保持上下文连贯性至关重要。3.2 向量化与存储接下来我们将文本块转换为向量并存入ChromaDB。# file_path: vector_store.py from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma import os def create_vector_store(chunks, persist_directory./chroma_db): 创建并持久化向量数据库 # 1. 初始化Embedding模型 # 这里使用开源的BGE模型无需API密钥 model_name BAAI/bge-base-en-v1.5 model_kwargs {device: cpu} # 或 cuda encode_kwargs {normalize_embeddings: True} # 归一化便于计算余弦相似度 embeddings HuggingFaceEmbeddings( model_namemodel_name, model_kwargsmodel_kwargs, encode_kwargsencode_kwargs ) # 2. 创建向量数据库并持久化到本地目录 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directorypersist_directory ) # 显式持久化 vectorstore.persist() print(f向量数据库已创建并保存至: {persist_directory}) return vectorstore def load_vector_store(persist_directory./chroma_db): 加载已存在的向量数据库 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-base-en-v1.5) vectorstore Chroma( persist_directorypersist_directory, embedding_functionembeddings ) print(向量数据库加载成功。) return vectorstore if __name__ __main__: # 假设已有chunks # vectorstore create_vector_store(chunks) vectorstore load_vector_store()关键点normalize_embeddingsTrue意味着向量会被归一化为单位长度此时向量点积就等于余弦相似度是推荐做法。3.3 检索与生成现在我们可以进行检索并调用LLM生成答案了。# file_path: basic_rag.py from langchain.chains import RetrievalQA from langchain_community.llms import Ollama from vector_store import load_vector_store # 导入上一步的函数 def setup_basic_rag_chain(): 设置基础的RAG问答链 # 1. 加载向量数据库 vectorstore load_vector_store() # 2. 创建检索器可以配置搜索参数 retriever vectorstore.as_retriever( search_typesimilarity, # 相似度搜索 search_kwargs{k: 4} # 返回最相关的4个片段 ) # 3. 初始化LLM (使用本地Ollama) # 确保已通过 ollama pull llama3 下载了模型 llm Ollama(modelllama3, temperature0.1) # temperature调低答案更确定 # 4. 创建RetrievalQA链 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 最简单的方式将所有上下文塞进prompt retrieverretriever, return_source_documentsTrue, # 返回源文档便于调试 verboseTrue # 打印详细日志 ) return qa_chain if __name__ __main__: qa_chain setup_basic_rag_chain() query RAG系统的主要优势是什么 result qa_chain.invoke({query: query}) print(问题, query) print(答案, result[result]) print(\n--- 参考来源 ---) for i, doc in enumerate(result[source_documents]): print(f[{i1}] {doc.page_content[:150]}...)运行这个脚本你将得到一个基于本地知识库的答案。这就是一个最基础的RAG系统。4. 进阶优化让检索更精准基础RAG的检索可能不够精准。我们引入两个关键优化重排Rerank和元数据过滤。4.1 集成重排模型重排模型是一个更精细的文本匹配模型它会对初步检索出的Top K个结果进行二次打分和排序。# file_path: advanced_retriever.py from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.cross_encoders import HuggingFaceCrossEncoder from vector_store import load_vector_store def create_advanced_retriever(): 创建带重排功能的高级检索器 vectorstore load_vector_store() base_retriever vectorstore.as_retriever(search_kwargs{k: 10}) # 第一步多检索一些 # 初始化交叉编码器重排模型例如BGE的Reranker # 注意rerank模型通常与embedding模型不同专用于对(query, doc)对进行精细打分 model_name BAAI/bge-reranker-large cross_encoder HuggingFaceCrossEncoder(model_namemodel_name) compressor CrossEncoderReranker(modelcross_encoder, top_n4) # 重排后保留Top 4 # 组合成上下文压缩检索器 compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverbase_retriever ) return compression_retriever if __name__ __main__: retriever create_advanced_retriever() query 如何优化RAG中的文本分割 docs retriever.invoke(query) print(f检索到 {len(docs)} 个相关文档片段:) for i, doc in enumerate(docs): print(f[{i1}] Score (if any): {doc.metadata.get(score, N/A)} | Content: {doc.page_content[:100]}...)关键点重排模型计算量大通常只在初步检索k10~20后进行筛选出最相关的少量top_n3~5片段送入LLM在精度和延迟间取得平衡。4.2 利用元数据过滤在存入向量数据库时我们可以为每个文本块添加元数据如来源文件、章节、页码。检索时可以利用这些元数据进行过滤。# file_path: metadata_filter.py from langchain.vectorstores import Chroma from langchain.embeddings import HuggingFaceEmbeddings def search_with_metadata_filter(): 使用元数据过滤进行检索 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-base-en-v1.5) vectorstore Chroma(persist_directory./chroma_db, embedding_functionembeddings) # 假设我们在存储时为每个文档添加了 source 和 page 元数据 # 检索时我们可以添加过滤器 results vectorstore.similarity_search( query神经网络结构, k5, filter{source: deep_learning_book.pdf} # 只从这本书里找 # 更复杂的过滤: filter{$or: [{source: book1}, {page: {$gte: 10}}]} ) for doc in results: print(fSource: {doc.metadata.get(source)}, Page: {doc.metadata.get(page)}) print(fContent: {doc.page_content[:150]}\n)关键点合理的元数据设计如文档类型、日期、作者、重要性标签能极大提升检索的针对性和准确性。5. 用LangGraph构建智能RAG工作流现在我们将使用LangGraph把基础RAG和优化策略组合成一个更智能的、有状态的工作流。这个工作流将具备条件路由能力如果检索结果置信度低则转向其他处理方式。5.1 定义状态与节点首先定义整个图的状态State和各个节点Node的功能。# file_path: langgraph_rag.py from typing import TypedDict, List, Annotated from langgraph.graph import StateGraph, END from langchain_core.documents import Document from langchain_community.llms import Ollama from langchain.prompts import ChatPromptTemplate from langchain.schema.output_parser import StrOutputParser import operator from advanced_retriever import create_advanced_retriever # 导入我们之前写的高级检索器 # 1. 定义状态结构 class GraphState(TypedDict): 图的状态在节点间传递。 question: str # 用户原始问题 retrieved_docs: List[Document] # 检索到的文档 generation: str # LLM生成的最终答案 decision: str # 路由决策例如 proceed, web_search, clarify # 2. 初始化组件 llm Ollama(modelllama3) retriever create_advanced_retriever() # 使用带重排的检索器 # 3. 定义各个节点函数 def retrieve(state: GraphState): 检索节点从知识库中获取相关文档 print(f---执行检索: {state[question][:50]}...) docs retriever.invoke(state[question]) # 简单计算平均相关性分数假设文档有score元数据 avg_score sum([d.metadata.get(score, 0) for d in docs]) / len(docs) if docs else 0 print(f检索到 {len(docs)} 个文档平均相关性分数: {avg_score:.3f}) return {retrieved_docs: docs, decision: proceed if avg_score 0.5 else low_confidence} def generate_answer(state: GraphState): 生成节点基于检索到的文档生成答案 print(---执行生成---) context \n\n.join([doc.page_content for doc in state[retrieved_docs]]) prompt ChatPromptTemplate.from_messages([ (system, 你是一个专业的助手请严格根据以下上下文回答问题。如果上下文不包含答案请明确说根据已知信息无法回答。), (human, 上下文\n{context}\n\n问题{question}) ]) chain prompt | llm | StrOutputParser() answer chain.invoke({context: context, question: state[question]}) return {generation: answer} def decide_route(state: GraphState): 决策节点根据检索结果的质量决定下一步 print(---执行路由决策---) # 这里可以根据 retrieved_docs 的长度、分数、内容等做更复杂的判断 if state[decision] low_confidence: # 例如可以路由到一个“联网搜索”节点或者直接返回一个提示 # 这里我们简单返回一个提示 return {generation: 抱歉我的知识库中关于这个问题的信息不足无法给出准确答案。} else: # 质量合格继续到生成节点 return {decision: to_generate} def web_search_fallback(state: GraphState): 示例联网搜索回退节点 # 这里可以集成 Tavily、SerpAPI 等搜索工具 print(---执行联网搜索---) # simulated_search_result 这是模拟的联网搜索到的信息... # return {retrieved_docs: [Document(page_contentsimulated_search_result)], decision: proceed} return {generation: 此示例未实现真实搜索建议您尝试使用搜索引擎获取最新信息。}5.2 构建并运行图将节点连接起来并定义路由逻辑。# file_path: langgraph_rag.py (续) # 4. 构建图 workflow StateGraph(GraphState) # 添加节点 workflow.add_node(retrieve, retrieve) workflow.add_node(generate, generate_answer) workflow.add_node(decide, decide_route) workflow.add_node(web_search, web_search_fallback) # 设置入口点 workflow.set_entry_point(retrieve) # 定义边连接 workflow.add_edge(retrieve, decide) # 条件路由根据 decision 字段的值决定下一步 workflow.add_conditional_edges( decide, lambda state: state.get(decision), { to_generate: generate, # 去生成答案 low_confidence: web_search, # 去联网搜索 } ) workflow.add_edge(generate, END) workflow.add_edge(web_search, END) # 编译图 app workflow.compile() # 5. 运行图 if __name__ __main__: # 初始化状态 initial_state GraphState(questionLangGraph在RAG中起什么作用) # 运行 final_state app.invoke(initial_state) print(\n *50) print(f最终答案\n{final_state[generation]}) print(*50)这个工作流实现了检索 - 判断置信度 - 高则生成答案低则转向备用方案如联网搜索。你可以通过扩展decide_route函数和添加更多节点如答案验证、多步查询改写来构建更复杂的智能体。6. 终极定制使用LoRA微调Embedding模型当通用Embedding模型在你的专属领域如医疗病历、法律条文、金融报告上表现不佳时微调是提升效果的关键手段。LoRALow-Rank Adaptation是一种参数高效的微调方法只需训练极少量参数就能让大模型适配新任务。6.1 LoRA微调Embedding模型原理我们不是微调整个庞大的模型可能数亿参数而是为模型中的线性层注入可训练的“低秩适配器”。训练时原模型权重冻结只更新适配器参数从而大幅降低计算和存储成本。6.2 准备训练数据你需要一个由(query, positive_doc, negative_doc)组成的三元组数据集。positive_doc是与query相关的正例negative_doc是不相关或相关性弱的负例。# file_path: prepare_lora_data.py from datasets import Dataset import json # 示例构造一个简单的训练数据 train_data [ { query: 什么是Transformer架构, positive: Transformer是一种基于自注意力机制的深度学习模型架构广泛应用于NLP领域..., negative: 卷积神经网络CNN主要用于计算机视觉任务如图像分类... }, { query: 如何配置ChromaDB的持久化路径, positive: 在创建Chroma客户端时通过persist_directory参数指定本地目录即可实现持久化。, negative: MySQL数据库的连接字符串通常包含主机、端口、用户名和密码。 } ] # 保存为JSON文件 with open(./data/train_data.jsonl, w) as f: for item in train_data: f.write(json.dumps(item, ensure_asciiFalse) \n) # 使用datasets库加载 dataset Dataset.from_json(./data/train_data.jsonl, splittrain) print(dataset)6.3 使用PEFT进行LoRA微调以下是一个简化的微调脚本框架展示了核心步骤。# file_path: train_lora_embedding.py from transformers import AutoModel, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, TaskType from trl import SFTTrainer from datasets import load_dataset import torch # 1. 加载模型和分词器 model_name BAAI/bge-base-en-v1.5 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModel.from_pretrained(model_name) # 2. 配置LoRA lora_config LoraConfig( task_typeTaskType.FEATURE_EXTRACTION, # 特征提取任务 r8, # LoRA的秩越小参数量越少 lora_alpha32, lora_dropout0.1, target_modules[query, key, value] # 针对Transformer的QKV矩阵 ) model get_peft_model(model, lora_config) model.print_trainable_parameters() # 查看可训练参数占比通常1% # 3. 定义数据预处理函数 def preprocess_function(examples): 将三元组数据转换为模型输入 queries examples[query] positives examples[positive] negatives examples[negative] # 这里需要根据你选择的损失函数如对比损失、三元组损失来构造批次 # 以下是一个简化示例实际训练需要更复杂的数据整理和损失计算 batch tokenizer(queries, paddingmax_length, truncationTrue, max_length512) return batch dataset load_dataset(json, data_files./data/train_data.jsonl, splittrain) tokenized_dataset dataset.map(preprocess_function, batchedTrue) # 4. 定义训练参数 training_args TrainingArguments( output_dir./bge-lora-finetuned, per_device_train_batch_size4, num_train_epochs3, logging_dir./logs, save_strategyepoch, evaluation_strategyno, # 示例中未准备验证集 ) # 5. 创建Trainer (此处使用SFTTrainer实际需自定义损失函数) # 注意Embedding模型的微调通常需要自定义Trainer和损失函数如CosineEmbeddingLoss, TripletLoss # 这里仅为流程展示直接运行可能不工作。 trainer SFTTrainer( modelmodel, argstraining_args, train_datasettokenized_dataset, tokenizertokenizer, ) print(开始训练...此示例需要自定义损失函数才能有效训练Embedding模型) # trainer.train()关键点微调Embedding模型的核心在于损失函数的设计如对比损失、三元组损失这需要你根据数据格式自定义训练循环。上述代码主要展示了使用PEFT库注入LoRA适配器的流程。6.4 使用微调后的模型训练完成后你可以像使用原模型一样加载并使用它。from peft import PeftModel from transformers import AutoModel base_model AutoModel.from_pretrained(BAAI/bge-base-en-v1.5) fine_tuned_model PeftModel.from_pretrained(base_model, ./bge-lora-finetuned/checkpoint-xxx) # 合并LoRA权重到基础模型便于部署 merged_model fine_tuned_model.merge_and_unload() merged_model.save_pretrained(./bge-finetuned-merged)然后在LangChain中指定新的模型路径即可。embeddings HuggingFaceEmbeddings(model_name./bge-finetuned-merged)7. 常见问题与排查思路在构建RAG系统的每个阶段你都可能遇到以下典型问题问题现象可能原因排查方式解决方案检索结果完全不相关1. Embedding模型与领域不匹配。2. 文本分割不合理chunk过大或过小。3. 查询与文档语言不一致。1. 检查Embedding模型名称。2. 打印出文本块内容看分割是否破坏了语义。3. 计算查询与几个已知相关文档的相似度。1. 更换或微调Embedding模型。2. 调整chunk_size和chunk_overlap尝试按段落或句子分割。3. 确保查询与文档语言一致。LLM回答“根据上下文无法回答”但明明有相关文档1. 检索到的文档未有效送入Prompt。2. Prompt指令不清晰。3. 上下文过长超出模型窗口。1. 检查source_documents确认检索是否成功。2. 查看构建的完整Prompt。3. 检查上下文总长度。1. 修复检索器连接。2. 优化System Prompt使用更明确的指令。3. 减少k值或使用map_reduce等链式类型处理长上下文。系统响应速度慢1. Embedding模型在CPU上运行。2. 向量数据库索引未优化。3. 重排模型计算开销大。1. 监控各阶段耗时。2. 检查向量数据库索引类型如HNSW。3. 检查重排模型的top_n设置。1. 使用GPU运行Embedding和重排模型。2. 为向量数据库创建合适的索引。3. 调整初步检索的k和重排的top_n或在特定场景下关闭重排。Ollama LLM服务连接失败1. Ollama服务未启动。2. 模型未下载。1. 命令行运行ollama serve检查服务状态。2. 运行ollama list检查模型是否存在。1. 确保Ollama服务在运行。2. 使用ollama pull llama3下载指定模型。ChromaDB持久化后加载失败1. 保存和加载时使用的Embedding函数不一致。2. 持久化目录被破坏。1. 确认两次使用的model_name完全相同。2. 检查目录下是否有chroma.sqlite3等文件。1. 统一Embedding模型配置。2. 尝试重新生成向量数据库。8. 最佳实践与工程建议要将一个实验性的RAG管道升级为生产系统请关注以下方面数据预处理是根基清洗去除无关字符、乱码、页眉页脚。标准化统一日期、单位、缩写格式。高质量分割尝试不同的分割策略语义分割、固定长度、递归字符并通过评估选择最佳方案。检索策略组合拳混合检索结合密集向量检索语义相似和稀疏检索如BM25关键词匹配取长补短。多路召回使用不同的Embedding模型或检索参数进行多次检索然后合并去重。查询改写/扩展在检索前使用LLM对原始查询进行改写或扩展以提高召回率。提示工程优化清晰的系统指令明确要求模型“基于给定上下文回答”并定义无法回答时的行为。上下文结构化在Prompt中清晰分隔不同来源的上下文可附加来源信息。少样本示例Few-Shot在Prompt中提供一两个问答示例引导模型输出格式和风格。评估与监控定义评估指标至少包括检索命中率检索到的文档是否相关和答案忠实度答案是否基于上下文。构建测试集收集一批真实用户问题及标准答案用于定期回归测试。记录日志记录每次问答的查询、检索到的文档ID、生成的答案、耗时和置信度分数便于分析和优化。安全与成本输入过滤对用户查询进行敏感词过滤和恶意指令检测。输出审查对模型生成的内容进行安全性审查。成本控制监控LLM API调用次数和Token消耗设置用量告警。对于内部应用优先考虑本地模型。从理解RAG的核心价值到动手搭建一个包含Embedding、向量数据库、检索优化和LangGraph智能编排的完整流程再到探索通过LoRA微调进行深度定制你已经走完了构建一个现代RAG应用的关键路径。记住RAG不是一个一劳永逸的解决方案而是一个需要持续迭代优化的系统工程。成功的核心在于对业务数据的深刻理解以及对每个技术环节的精心调优。建议你从一个小而具体的场景开始构建最小可行产品MVP然后依据评估数据逐个环节地进行优化和升级。
返回列表