烟台h5网站建设公司移动设备网站开发注意点
2026/4/15 21:45:15 网站建设 项目流程
烟台h5网站建设公司,移动设备网站开发注意点,京东门户网站怎么做,数据库网页制作教程【本期目标】 理解 RAG 系统中常见的挑战#xff08;如幻觉、上下文冗余、检索不精确#xff09;及其原因。掌握多种高级检索策略#xff0c;覆盖检索前、检索中、检索后全流程#xff0c;以提升召回率和相关性。学习如何通过Prompt工程、输出解析等方法优化LLM的生成质量。…【本期目标】理解 RAG 系统中常见的挑战如幻觉、上下文冗余、检索不精确及其原因。掌握多种高级检索策略覆盖检索前、检索中、检索后全流程以提升召回率和相关性。学习如何通过Prompt工程、输出解析等方法优化LLM的生成质量。探讨RAG幻觉问题的成因与多种防范策略。通过案例实践这些高级优化方法并观察其对RAG性能的影响。引言RAG的“痛点”与优化之路即便构建了基本的RAG系统你可能仍然会遇到以下问题 幻觉: LLM根据检索到的信息编造事实或者生成与上下文不符的回答。检索不精确 检索器未能召回最相关或最完整的文档块导致LLM无法获得足够信息。上下文冗余 检索到的文档块虽然相关但包含了大量与问题不直接相关的细节浪费LLM的Token。生成质量不高 LLM的回答不够简洁、不符合用户期望格式或引用不明确。查询理解不足 用户提问模糊、复杂LLM难以准确理解其检索意图。本期我们将重点解决这些痛点让RAG系统更智能、更可靠优化的核心思路是围绕检索前、检索中和检索后三个阶段展开的系统性工程。检索前优化 —— 从源头提升质量在我们将问题喂给检索系统之前可以做的优化是最基础、也最有效的。这部分主要包括两个方面优化我们拥有的知识库数和优化用户提出的问题。数据索引优化问题 基础RAG通常使用固定大小的文本块进行切割这容易导致语义信息被割裂或者相关的上下文分散在不同的块中。a.智能分块原理 不同于按字符数或Token数切块智能分块会根据文本的内在结构如段落、标题或语义完整性来切分文档。语义分块: 尝试在语义的断点处进行分割确保每个块包含一个完整的思想或概念单元。句子窗口检索: 一种更精细的策略。索引时以单个句子为单位创建嵌入。当检索到某个句子时将该句子及其前后的句子一并作为上下文提供给LLM。这样既能保证检索的精准度又能提供丰富的上下文。优点最大程度地保留了每个文本块的语义完整性。句子窗口策略能实现精确检索和充分上下文的平衡。b.元数据与图谱构建原理 在索引时为每个文档块添加丰富的元数据如文档来源、创建日期、章节标题、作者等。对于高度结构化的知识甚至可以构建知识图谱后面会专门写一篇关于知识图谱的文章。优点元数据可以用于检索时的精确过滤例如只检索2024年之后的文档。LangChain的SelfQueryRetriever就擅长利用元数据。知识图谱能处理多跳、复杂的关系查询为RAG提供结构化的先验知识。查询优化更好地理解用户意图用户的原始问题可能很模糊或者包含多个意图。直接用它检索效果可能不佳。a.查询扩展 : MultiQueryRetriever问题 用户原始的查询可能不够全面或精准导致检索器遗漏相关文档。原理 使用LLM根据原始查询生成多个语义相近或不同角度的查询。 然后用所有这些查询并行地执行检索并将所有结果合并去重。优点 显著提升召回率尤其适用于模糊或多义性查询。缺点 增加LLM调用次数和检索成本。from langchain.retrievers import MultiQueryRetriever from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_chroma import Chroma import os from dotenv import load_dotenv; load_dotenv() llm ChatOpenAI( modelos.environ.get(OPENAI_MODEL), temperature0.9, base_urlos.environ.get(OPENAI_BASE_URL), openai_api_keyos.environ.get(OPENAI_API_KEY), ) embeddings_model OpenAIEmbeddings( modelos.environ.get(EMBEDDING_MODEL), base_urlos.environ.get(EMBEDDING_BASE_URL), openai_api_keyos.environ.get(EMBEDDING_API_KEY), ) persist_directory ./chroma_db_rag_basic vectorstore Chroma(persist_directorypersist_directory, embedding_functionembeddings_model) # 2. 创建 MultiQueryRetriever # 它需要一个 LLM 来生成多个查询 multiquery_retriever MultiQueryRetriever.from_llm( retrievervectorstore.as_retriever(search_kwargs{k: 2}), # 基础检索器 llmllm, # 用于生成新查询的LLM # promptChatPromptTemplate.from_template(Generate 3 different ways to ask the question: {question}), # 也可以自定义生成查询的Prompt # parser_keytext, # LLM输出中包含查询的键 ) print(\n--- MultiQueryRetriever 示例 ---) query_mq LangChain的优势是什么 retrieved_docs_mq multiquery_retriever.invoke(query_mq) print(f对查询 {query_mq} 的 MultiQueryRetriever 检索结果 ({len(retrieved_docs_mq)} 个文档):) for i, doc in enumerate(retrieved_docs_mq): print(f文档 {i1} (来源: {doc.metadata.get(source, N/A)}):\n{doc.page_content}) print(- * 30)b. RAG-Fusion原理 这是MultiQueryRetriever的一种演进。它同样会利用LLM生成多个相似的子查询并分别执行检索。关键区别在于合并结果时它使用一种名为“倒数排序融合”Reciprocal Rank Fusion, RFF的算法对所有检索结果进行智能的重新排序选出在多次查询中都排名靠前的文档。优点 相比简单地合并结果RFF算法能更有效地将最核心、最相关的文档提升到前排提高结果质量。c. 后退一步提示方法原理 面对一个非常具体的问题直接检索可能找不到答案。此方法利用LLM先从具体问题中抽象出一个更高层次、更宽泛的“后退一步”的问题。然后同时对“原始问题”和“后退一步的问题”进行检索。优点 能同时召回包含高层概念的背景知识和针对具体问题的细节信息为LLM提供更全面的视角。d. 假设性文档嵌入原理首先让LLM根据用户问题生成一个假设性的、理想的回答。然后使用这个理想的回答的嵌入去进行向量检索。优点因为这个假设性文档在语义上与理想的答案高度相关所以它的嵌入能更有效地找到真正相关的原始文档。有些时候可以把问题和答案拼接一起检索效果可能更好些。检索中优化 —— 融合多种检索优势问题 单一的向量搜索语义搜索虽然强大但有时会忽略关键词的精确匹配尤其是在处理专有名词、代码函数或特定术语时。混合搜索Hybrid Search不同向量库有不同实现方式比如milvus内置BM25、关键词匹配、模糊查询匹配等原理 将传统的关键词搜索如BM25算法稀疏检索与现代的向量搜索密集检索结合起来。关键词搜索确保专有名词等能被精确匹配而向量搜索负责理解语义上的相似性。两者结合取长补短。优点 显著提升检索的鲁棒性在需要精确匹配和语义理解的场景下效果都很好。缺点 实现上需要同时维护两种索引如BM25索引和向量索引架构略微复杂。LangChain 提供了 EnsembleRetriever 来实现此功能。它可以将多个不同的检索器组合在一起并可以为各自的检索结果设置权重。# 示例: 使用 EnsembleRetriever 实现混合搜索 from langchain.retrievers import EnsembleRetriever from langchain_chroma import Chroma from langchain_community.retrievers import BM25Retriever # all_splits 是你的所有文档块列表, vectorstore 也已创建,这些都可从前面或之前代码找到 # 1. 初始化关键词检索器 bm25_retriever BM25Retriever.from_documents(all_splits) bm25_retriever.k 3 # 2. 初始化向量检索器 vector_retriever vectorstore.as_retriever(search_kwargs{k: 3}) # 3. 初始化 EnsembleRetriever ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.5, 0.5] # 可以给不同检索器设置不同权重 ) # 4. 使用 query LangChain中的LCEL是什么? retrieved_docs ensemble_retriever.invoke(query) print(f混合搜索召回了 {len(retrieved_docs)} 个文档。)检索后优化 —— 对检索结果精加工从数据库检索到初步的文档列表后直接将它们全部交给LLM并非最佳选择。精细的后处理是提升质量、降低成本的关键一步。重排序 (Re-ranking): 提升相关性原理 检索器通常返回的文档是基于向量相似度的初步排序。 重排序模型是独立的机器学习模型它们接收原始查询和每个检索到的文档对然后输出一个更精细的相关性分数。优点 显著提升检索结果的相关性尤其是在基础检索器表现不佳时。缺点 引入额外的模型调用和延迟。实现方式 通常作为 ContextualCompressionRetriever 的一部分或独立作为一个后处理步骤。这里为了方便找了使用Flashrank演示工作中我们是使用vllm部署rerank模型使用# 示例: 使用Flashrank 进行重排序 from langchain.retrievers.document_compressors import FlashrankRerank from langchain.retrievers import ContextualCompressionRetriever # 1. 定义一个重排序压缩器 # top_n 是重排序后返回多少个文档 rerank_compressor FlashrankRerank( modelminiReranker_arabic_v1, top_n3 ) # 2. 创建一个 ContextualCompressionRetriever, 使用 Flashrank 作为压缩器 rerank_retriever ContextualCompressionRetriever( base_compressorrerank_compressor, base_retrievervectorstore.as_retriever(search_kwargs{k: 5}) # 先检索5个再重排 ) # 3. 使用 print(\n--- Reranking (Flashrank) 示例 ---) query_rerank LangChain的最新功能是什么? retrieved_reranked_docs rerank_retriever.invoke(query_rerank) print(f对查询 {query_rerank} 的重排序检索结果({len(retrieved_reranked_docs)} 个文档):) for i, doc in enumerate(retrieved_reranked_docs): print(f文档 {i1} (分数: {doc.metadata.get(relevance_score, N/A)}):\n{doc.page_content[:100]}...) print(- * 30)上下文压缩: 减少冗余问题 检索到的文档块可能包含大量冗余信息浪费Token影响LLM的理解。原理 在检索到文档块之后利用LLM或专门的压缩器对每个文档块进行精简只保留与用户查询直接相关的部分。优点 减少LLM的输入Token数降低成本提高LLM对关键信息的聚焦能力。缺点 压缩过程可能损失少量上下文或引入偏差。from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor from langchain_openai import ChatOpenAI # 1. 定义一个基础检索器(先多检索一些,再压缩) base_retriever_for_comp vectorstore.as_retriever(search_kwargs{k: 5}) # 2. 定义一个 LLMChainExtractor (压缩器) compressor LLMChainExtractor.from_llm(llmllm) # 3. 创建 ContextualCompressionRetriever compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverbase_retriever_for_comp ) # 4. 使用 print(\n--- ContextualCompressionRetriever 示例 ---) query_comp LangChain的调试工具叫什么?它的主要作用是什么? retrieved_compressed_docs compression_retriever.invoke(query_comp) print(f对查询 {query_comp} 的ContextualCompressionRetriever 检索结果:) for i, doc in enumerate(retrieved_compressed_docs): original_len len(doc.metadata.get(original_content, doc.page_content)) compressed_len len(doc.page_content) print(f文档 {i1}(原始长度: {original_len}, 压缩后长度: {compressed_len}):) print(doc.page_content) print(- * 30)拐点法则: 减少冗余问题 检索到的文档块可能包含大量冗余信息浪费Token影响LLM的理解。假设我们已经检索并重排序了20个文档它们的分数从高到低排列。通常情况下最前面的几个文档与查询高度相关分数很高但随着排名的下降文档的相关性会急剧降低然后趋于平缓进入一个低相关性的“长尾”。“拐点”就是这个相关性分数急剧下降到平缓的转折点。这个点被认为是“高价值信息”和“噪音信息”的分界线。我们的目标就是自动找到这个点这个过程通常在检索和重排序之后进入LLM生成步骤之前进行。原理获取排序文档和分数 首先从检索器最好是经过重排序的获取一个相对较长的文档列表例如top 20或top 30以及它们对应的相关性分数。寻找拐点 有几种计算拐点的方式其中一种经典的方法是画一条直线在相关性分数图上从第一个点排名第1的文档到最后一个点排名第20的文档画一条直线。计算距离计算中间每个点到这条直线的垂直距离。找到最大距离点距离这条直线最远的点就是“弯曲”得最厉害的地方即我们寻找的“拐点”。动态截断 假设通过计算我们发现拐点出现在第4个文档上。那么我们就只选择前4个文档作为最终的上下文传递给LLM而忽略后面的16个文档。优点动态、自适应的K值这是最大的好处。对于一个简单、明确的问题系统可能会发现有8个高度相关的文档拐点在第8位而对于一个模糊、小众的问题可能只有2个相关文档拐点在第2位。系统可以自动适应而不是僵化地使用固定的top_k5。有效降噪能够动态地滤除那些相关性不高的“噪音”文档防止它们干扰LLM的判断从而降低产生“幻觉”的风险。优化成本和延迟通过向LLM提供更少、但更精炼的上下文可以显著减少Token消耗降低API调用成本并可能加快最终答案的生成速度。缺点与挑战依赖分数质量该方法的效果高度依赖于重排序Reranking分数的质量和区分度。如果分数本身不能很好地反映相关性那么找到的拐点也是无意义的。可能不存在明显拐点在某些情况下相关性分数可能是平滑下降的没有一个清晰的“拐点”这会导致该方法失效或选择一个次优的截断点。实现稍复杂相比简单的top_k它需要在检索后增加一个额外的计算步骤。from typing import List, Tuple import numpy as np from langchain_core.documents import Document def find_elbow_point(scores: np.ndarray) - int: 使用点到直线最大距离的纯几何方法。 返回的是拐点在原始列表中的索引。 n_points len(scores) if n_points 3: return n_points -1 # 返回最后一个点的索引 # 创建点坐标 (x, y)x是索引y是分数 points np.column_stack((np.arange(n_points), scores)) # 获取第一个点和最后一个点 first_point points[0] last_point points[-1] # 计算每个点到首末点连线的垂直距离 # 使用向量射影的方法 line_vec last_point - first_point line_vec_normalized line_vec / np.linalg.norm(line_vec) vec_from_first points - first_point # scalar_product 是每个点向量在直线方向上的投影长度 scalar_product np.dot(vec_from_first, line_vec_normalized) # vec_parallel 是投影向量 vec_parallel np.outer(scalar_product, line_vec_normalized) # vec_perpendicular 是垂直向量它的模长就是距离 vec_perpendicular vec_from_first - vec_parallel dist_to_line np.linalg.norm(vec_perpendicular, axis1) # 找到距离最大的点的索引 elbow_index np.argmax(dist_to_line) return elbow_index def truncate_with_elbow_method_final( reranked_docs: List[Tuple[float, Document]] ) - List[Document]: if not reranked_docs or len(reranked_docs) 3: print(文档数量不足3个无法进行拐点检测返回所有文档。) return [doc for _, doc in reranked_docs] scores np.array([score for score, _ in reranked_docs]) docs [doc for _, doc in reranked_docs] # 调用我们验证过有效的拐点检测函数 elbow_index find_elbow_point(scores) # 我们需要包含拐点本身所以截取到 elbow_index 1 num_docs_to_keep elbow_index 1 final_docs docs[:num_docs_to_keep] print(f检测到分数拐点在第 {elbow_index 1} 位。截断后返回 {len(final_docs)} 个文档。) return final_docs print(\n--- 拐点检测示例 ---) # 假设 reranked_docs 是你的输入数据 reranked_docs [ (0.98, 文档1), (0.95, 文档2), (0.92, 文档3), (0.75, 文档4), (0.5, 文档5), (0.48, 文档6) ] final_documents truncate_with_elbow_method_final(reranked_docs) print(final_documents)QA对优化 —— 检索过程将检索从“问题-文档”匹配升级为“问题-问题”匹配。因为用户提出的问题在语义上与另一个相似的问题通常比与一个包含答案的长篇文档段落更接近。这大大降低了语义匹配的难度从而提升了检索的精准度。方法一将QA对作为知识库直接索引这是最直接的一种方式特别适用于有现成FAQ文档的场景。操作流程数据准备将现有的知识库整理成一系列高质量的“一问一答”对。例如Q: “LangChain的LCEL是什么”A: “LCEL全称LangChain Expression Language是一种用于…”索引构建在构建向量索引时只对问题部分进行嵌入。而将对应的答案作为元数据存储与该问题向量绑定。检索过程当用户提出一个新问题时例如“请介绍一下LCEL”对这个新问题进行嵌入。相似度匹配在向量数据库中搜索与新问题向量最相似的已索引问题向量。返回答案一旦找到最匹配的旧问题系统直接从元数据中提取并返回其对应的答案。优点极高的精度由于是问题匹配问题语义对齐非常精准召回的结果相关性极高。答案质量可控返回的答案是预先编写和审核的质量有保证可以完全避免LLM的幻觉问题。响应速度快在某些情况下如果匹配度足够高甚至可以跳过最后调用LLM生成答案的步骤直接返回预设答案从而降低延迟和成本。缺点知识范围有限系统只能回答那些已经预设了QA对的问题。对于超出范围的新问题它将无法回答。制作成本高需要人工或半自动地创建和维护高质量的QA对知识库工作量较大。# --- 1. 数据准备高质量的QA对 --- qa_pairs [ { question: LangChain的LCEL是什么它有什么用, answer: LCEL全称LangChain Expression Language是一种用于声明式地链式组合AI组件的语言。它简化了复杂链的构建并原生支持流式处理、异步和并行执行等高级功能。 }, { question: 什么是RAG系统中的“幻觉”问题, answer: RAG系统中的“幻觉”指的是大型语言模型在生成答案时捏造了事实或者生成了与提供的上下文不符的、看似有理有据的错误信息。 }, { question: 如何提升RAG系统的检索精度, answer: 提升RAG检索精度的方法有很多包括使用更先进的嵌入模型、对文档进行智能分块、采用混合搜索、以及在检索后进行重排序Re-ranking等。 } ] # --- 2. 索引构建只对问题进行嵌入 --- # 创建一个文档列表每个文档的内容是“问题”元数据包含“答案” question_documents [] for pair in qa_pairs: # 将答案作为元数据存储 metadata {answer: pair[answer]} doc Document(page_contentpair[question], metadatametadata) question_documents.append(doc) # 使用OpenAI的嵌入模型 embeddings_model OpenAIEmbeddings( modelos.environ.get(EMBEDDING_MODEL), base_urlos.environ.get(EMBEDDING_BASE_URL), openai_api_keyos.environ.get(EMBEDDING_API_KEY) ) # 创建一个临时的Chroma向量数据库来存储问题向量 vectorstore_qa Chroma.from_documents( documentsquestion_documents, embeddingembeddings_model ) # --- 3. 检索与问答 --- def answer_from_qa_pairs(user_question: str): 通过在QA对知识库中搜索最相似的问题来回答。 print(f\n用户问题: {user_question}) # 在向量数据库中搜索与用户问题最相似的“已索引问题” similar_question_docs vectorstore_qa.similarity_search(user_question,k1) if similar_question_docs: # 提取最相似问题的预设答案 most_similar_question similar_question_docs[0].page_content retrieved_answer similar_question_docs[0].metadata[answer] print(f匹配到的最相似问题: {most_similar_question}) print(f系统回答 (来自预设答案): {retrieved_answer}) else: print(抱歉在知识库中没有找到相关问题。) # --- 测试 --- answer_from_qa_pairs(langchain的LCEL是做什么的) answer_from_qa_pairs(大模型幻视是什么意思)方法二从文档中生成QA对进行多路召回这是一种更灵活的策略它不是抛弃原始文档而是用QA对为原始文档增加新的元数据。操作流程文档分块像往常一样先将原始文档切分成块。QA对生成对每一个文档块使用LLM来反向生成几个可能指向这个文档块的问题。例如对于一个讲述LCEL优点的文档块LLM可能会生成“LCEL有哪些好处”“为什么应该使用LangChain表达式语言”“LCEL是如何支持流式处理的”混合索引现在我们有原始的文档块和新生成的问题。接下来有两种索引策略策略A推荐只索引生成的问题但将它们全部链接到同一个原始文档块的ID。当任何一个问题被匹配到时我们召回的是它们共同指向的那个原始文档块。策略B将每个生成的问题与文档块内容拼接后再进行索引。检索过程用户提问时他的问题现在有多条路径可以找到正确的信息路径1直接与原始文档块的语义匹配。路径2与某个LLM生成的代理问题”相似从而间接定位到原始文档块。优点召回率极大提升为单个知识点创建了多个不同的语义入口即使用户的提问方式千奇百怪只要能和其中一个代理问题对上就能找到正确答案。完美结合了两种模式的优点既保留了原始文档的完整上下文又利用了“问题匹配问题”的高精度优势。自动化程度高QA对的生成可以由LLM自动完成减少了人工成本。缺点索引成本增加需要额外调用LLM来生成问题并且索引的规模会变大。对生成质量有要求LLM生成的问题质量直接影响最终的检索效果。from langchain.storage import InMemoryStore from langchain_core.documents import Document from langchain.retrievers.multi_vector import MultiVectorRetriever from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate # --- 1. 原始文档准备 --- original_docs [ Document(page_contentLCEL全称LangChain Expression Language它通过操作符重载如|符号提供了一种声明式的、流畅的方式来构建AI链。它的关键优势包括开箱即用的流式处理、异步和并行执行能力以及对整个链的生命周期管理如日志、调试提供了极大的便利。, metadata{doc_id: lcel_intro}), Document(page_content混合搜索Hybrid Search结合了传统关键词搜索如BM25和现代向量搜索的优点。关键词搜索能精确匹配术语和缩写而向量搜索擅长理解语义和意图。二者结合能显著提升检索的鲁棒性和准确性。, metadata{doc_id: hybrid_search_intro}), ] # --- 2. 从文档生成“代理问题”的链 --- question_gen_prompt_str ( 你是一位AI专家。请根据以下文档内容生成3个用户可能会提出的、高度相关的问题。\n 只返回问题列表每个问题占一行不要有其他前缀或编号。\n\n 文档内容:\n ----------\n {content}\n ----------\n ) question_gen_prompt ChatPromptTemplate.from_template(question_gen_prompt_str) question_generator_chain question_gen_prompt | llm | StrOutputParser() # --- 3. MultiVectorRetriever 设置 --- # a. 向量数据库 vectorstore_mv Chroma(collection_namemultivector_retriever, embedding_functionembeddings_model) # b. 文档存储器用于根据ID存储和查找原始文档 doc_store InMemoryStore() # c. 生成的子文档问题列表 sub_docs [] # d. 原始文档ID列表 doc_ids [doc.metadata[doc_id] for doc in original_docs] # 遍历每个原始文档生成问题并存储 for i, doc in enumerate(original_docs): doc_id doc_ids[i] # 生成问题 generated_questions question_generator_chain.invoke({content: doc.page_content}).split(\n) # 清理可能存在的空字符串 generated_questions [q for q in generated_questions if q.strip()] # 将每个问题包装成一个Document并链接到原始文档的ID for q in generated_questions: sub_docs.append(Document(page_contentq, metadata{doc_id: doc_id})) # 将原始文档和生成的子文档问题都添加到存储中 doc_store.mset(list(zip(doc_ids, original_docs))) # 存储原始文档 vectorstore_mv.add_documents(sub_docs) # 只索引问题 # 初始化MultiVectorRetriever # - search_typesimilarity: 表示用向量搜索来查找问题 # - a. 它会在vectorstore_mv中搜索最相似的问题 # - b. 然后根据问题的metadata[doc_id]去doc_store中取回原始文档 retriever MultiVectorRetriever( vectorstorevectorstore_mv, docstoredoc_store, id_keydoc_id, search_typesimilarity ) # --- 4. 检索测试 --- user_query 混合检索的好处是什么 retrieved_results retriever.invoke(user_query) print(f\n用户问题: {user_query}) print(\n--- 检索到的原始文档 ---) print(retrieved_results[0].page_content)方法三利用QA对微调嵌入模型直接优化嵌入模型。操作流程构建训练集创建一个包含(查询, 正向文档, [负向文档])三元组的高质量数据集。这个数据集可以通过人工标注或使用现有QA对来构建。模型微调使用这个数据集对一个基础的嵌入模型进行微调。训练的目标是让查询和正向文档的嵌入在向量空间中尽可能接近同时让它们与“负向文档”的嵌入尽可能远离。应用新模型在整个RAG系统中使用这个经过微调、针对特定领域数据进行优化的嵌入模型。优点根本性提升直接从源头提升了嵌入的质量使得模型能更好地理解您所在领域的特定术语和语义关系。效果天花板高对于特定领域的应用一个精调的嵌入模型其效果通常会远超通用的预训练模型。缺点技术门槛最高需要高质量的标注数据并且微调过程需要专业的机器学习知识和计算资源。成本极高数据标注和模型训练的成本非常昂贵。以下是一个伪代码只能作为参考展示如何使用sentence-transformers库来微调一个嵌入模型。实际场景下需要一个规模大得多的高质量数据集。import torch from sentence_transformers import SentenceTransformer, losses from sentence_transformers.readers import InputExample from torch.utils.data import DataLoader # --- 1. 准备微调数据集--- # 实际应用中你需要成千上万这样的样本 # 每个样本是一个“查询”和“一个相关的正面段落” train_examples [ InputExample(texts[LCEL的优点有哪些, LCEL全称LangChain Expression Language...提供了极大的便利。]), InputExample(texts[什么是混合搜索, 混合搜索Hybrid Search结合了传统关键词搜索和现代向量搜索的优点...]), InputExample(texts[如何解决RAG幻觉, RAG系统中的“幻觉”指的是...可以通过多种方式缓解例如提高检索质量...]), ] # --- 2. 定义模型和数据加载器 --- # 选择一个强大的预训练模型作为基础 model_name moka-ai/m3e-base model SentenceTransformer(model_name) # 准备数据加载器 # MultipleNegativesRankingLoss 是一种非常适合此类任务的损失函数它会智能地将一个batch内的其他“正向段落”作为当前查询的“负向样本” batch_size 4 # 实际应用中可以更大如32或64 train_dataloader DataLoader(train_examples, shuffleTrue, batch_sizebatch_size) # --- 3. 定义损失函数 --- train_loss losses.MultipleNegativesRankingLoss(model) # --- 4. 模型微调 --- num_epochs 3 # 实际应用中可能需要更多轮次 warmup_steps int(len(train_dataloader) * num_epochs * 0.1) # 10%的预热步数 print(开始微调嵌入模型...) model.fit( train_objectives[(train_dataloader, train_loss)], epochsnum_epochs, warmup_stepswarmup_steps, output_path./finetuned_embedding_model, # 微调后模型的保存路径 show_progress_barTrue ) print(\n微调完成模型已保存至 ./finetuned_embedding_model)模型调优与Prompt工程——提升回答质量即使检索到高质量的上下文LLM的生成质量也依赖于优秀的Prompt工程。清晰的指令与角色设定在Prompt的 system 消息中明确LLM的角色如“你是一名专业的知识库助手”、语气如“友好且简洁”。明确回答的约束“如果信息不足请说明你不知道”。明确要求LLM引用其答案的来源例如文档编号、文件名这有助于用户验证信息也是防范幻觉的有效手段。强制结构化输出原理如果希望LLM输出特定格式如JSON、列表使用 llm.with_structured_output() 是最有效方法。强制LLM遵循定义的Pydantic模型输出格式。优点这对于后续的答案解析和整合非常重要能保证输出的稳定可靠。引用与来源在Prompt中明确要求LLM引用其答案的来源例如文档编号、文件名。这有助于用户验证信息也是防范幻觉的有效手段。实现方式通常在Prompt中添加“请在回答中引用你使用的上下文文档的编号或文件名”类似的指令。这需要你的 Document 块的 metadata 中包含这些信息。from pydantic import BaseModel, Field from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough class AnswerFormat(BaseModel): answer: str Field(description针对问题的回答) references: list[str] Field(description答案引用的文档块ID或来源信息列表) confidence_score: float Field(description对答案的自信度评分,0.0到1.0之间) # 绑定 LLM, 强制其输出 AnswerFormat 结构 llm_structured_output llm.with_structured_output(AnswerFormat) # 定义一个 Prompt,鼓励LLM输出结构化数据 structured_rag_prompt ChatPromptTemplate.from_messages([ (system, 你是一个知识问答机器人。请根据提供的上下文,以JSON格式回答问题。如果信息不足,请将answer字段设置为不知道,confidence_score为0.0。), (user, 问题:{question}\n\n上下文:\n{context}) ]) # 创建一个只负责生成结构化答案的链 structured_answer_chain ( {question: RunnablePassthrough(), context: vectorstore.as_retriever()} | structured_rag_prompt | llm_structured_output ) print(\n--- 结构化输出 RAG 示例 ---) query_structured LangChain的优势是什么? response_structured structured_answer_chain.invoke(query_structured) print(f问题: {query_structured}) # pydantic模型可以方便地转为JSON print(f回答:\n{response_structured})RAG“幻觉”的防范策略“幻觉”是LLM在没有足够信息时或者错误地解释信息时编造出不存在的事实。在RAG中这尤其危险因为它可能给出有理有据的错误信息。防范策略是一个系统工程它依赖于前面所有阶段的优化优质的检索这是最重要的。如果检索到的上下文本身就是不准确、不完整或不相关的那么LLM就更容易产生幻觉。高级检索策略是解决这个问题的核心。提高召回率 确保能找到所有相关文如 MuiQueryRetriever。提高相关性: 确保召回的文档都是高度相关的 (如使用 Hybrid Search、Re-ranking)。减少冗余: 确保上下文简洁明了 (如使用 Contextual Compression)。严格的Prompt工程明确的指令 “只根据提供的上下文回答如果不知道就说不知道不要编造。”负面约束 “不要包含个人意见”、“不要给出超出上下文范围的信息”。引用要求 要求LLM在回答中引用来源强制其与提供的上下文关联。答案验证/事实核查用于检查答案中的陈述是否都能在提供的上下文中找到证据。RAGAS 等评估工具 后续章节会讲到专门的RAG评估框架。Post-processing 在LLM生成答案后可以设计一个额外的LLM链或传统NLP模块模型选择与微调某些LLM天生就比其他模型更不容易产生幻觉通常是较新的、性能更好的模型。如果条件允许可以针对特定领域数据对LLM进行微调可以提高其在该领域的准确性和一致性从而减少幻觉。但微调成本较高通常是最后考虑的选项。将优化策略整合到RAG链中from langchain.chains import create_retrieval_chain from langchain.chains.combine_documents import create_stuff_documents_chain from langchain_core.prompts import ChatPromptTemplate from langchain.retrievers.document_compressors import FlashrankRerank from langchain.retrievers import ContextualCompressionRetriever # --- 1. 构建多阶段优化检索器 --- # 基础检索器: 混合搜索 bm25_retriever BM25Retriever.from_documents(split_documents) bm25_retriever.k 5 vector_retriever vectorstore.as_retriever(search_kwargs{k: 5}) ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.5, 0.5] ) # 查询扩展: 将混合搜索作为基础 multi_query_hybrid_retriever MultiQueryRetriever.from_llm( retrieverensemble_retriever, llmllm ) # 重排序: 将查询扩展和混合搜索的结果进行精排 rerank_compressor FlashrankRerank( modelminiReranker_arabic_v1, top_n3 ) # 创建一个 ContextualCompressionRetriever, 使用 Flashrank 作为压缩器 final_optimized_retriever ContextualCompressionRetriever( base_compressorrerank_compressor, base_retrievervectorstore.as_retriever(search_kwargs{k: 5}) # 先检索5个再重排 ) # --- 2. 构建最终的RAG链 --- # a. 定义RAG的Prompt optimized_rag_prompt ChatPromptTemplate.from_messages([ (system, 你是一名专业的知识库助手。请根据提供的上下文**简洁明了**地回答以下问题。\n**如果上下文没有足够信息请明确说明你不知道不要凭空捏造。**\n\n上下文:\n{context}), (user, {input}) ]) # 创建文档处理链 optimized_document_chain create_stuff_documents_chain(llm, optimized_rag_prompt) # 创建完整的检索链 optimized_retrieval_chain create_retrieval_chain( final_optimized_retriever, optimized_document_chain ) # --- 3. 调用测试 --- print(\n--- 完整优化后的RAG链示例 ---) query_final LangChain的优势是什么 response optimized_retrieval_chain.invoke({input: query_final}) print(f用户: {query_final}) print(fAI: {response[answer]}) # 查看检索到的上下文验证其高质量 print(\n--- 检索到的上下文(Context) ---) for i, doc in enumerate(response[context]): print(f文档 {i1} (来源: {doc.metadata.get(source, N/A)}, 分数: {doc.metadata.get(relevance_score, N/A)}):) print(doc.page_content) print(- * 20)代码解析:我们将 EnsembleRetriever 作为 MultiQueryRetriever 的基础。然后将 MultiQueryRetriever 的输出再送入 ContextualCompressionRetriever 中使用 FlashrankRerank (重排序) 进行最后的精简和排序。这个 final_optimized_retriever 就是一个实现了“先扩展查询 - 再混合搜索 - 最后重排序结果”的检索器。最终我们将这个优化后的检索器整合到标准的 create_retrieval_chain 中构建出一个非常强大的RAG应用。注意 如果你的 example.txt 内容较少优化的效果可能不明显。它们在大规模、多样化的知库中表现更佳。本期小结本期教程中我们可以掌握 RAG 系统的高级优化策略通过一个系统化的框架检索前、中、后来思考RAG优化。掌握数据索引智能分块、查询优化查询扩展、RAG-Fusion、检索混合搜索和后处理重排序、压缩等多个维度的具体技术。理解Prompt工程和结构化输出对生成质量的提升作用。最重要的是深入理解了RAG中“幻觉”问题的成因并掌握了如何通过全流程的优化来系统性地防范它。现在我们的RAG系统已经不仅仅是能用而是具备了迈向生产级应用的潜力。在下一期教程中我们将探讨 LangChain 应用的调试、评估与部署确保系统能够稳定运行、持续改进并最终上线代码仓库https://github.com/lgy1027/ai-tutorial普通人如何抓住AI大模型的风口领取方式在文末为什么要学习大模型目前AI大模型的技术岗位与能力培养随着人工智能技术的迅速发展和应用 大模型作为其中的重要组成部分 正逐渐成为推动人工智能发展的重要引擎 。大模型以其强大的数据处理和模式识别能力 广泛应用于自然语言处理 、计算机视觉 、 智能推荐等领域 为各行各业带来了革命性的改变和机遇 。目前开源人工智能大模型已应用于医疗、政务、法律、汽车、娱乐、金融、互联网、教育、制造业、企业服务等多个场景其中应用于金融、企业服务、制造业和法律领域的大模型在本次调研中占比超过30%。随着AI大模型技术的迅速发展相关岗位的需求也日益增加。大模型产业链催生了一批高薪新职业人工智能大潮已来不加入就可能被淘汰。如果你是技术人尤其是互联网从业者现在就开始学习AI大模型技术真的是给你的人生一个重要建议最后只要你真心想学习AI大模型技术这份精心整理的学习资料我愿意无偿分享给你但是想学技术去乱搞的人别来找我在当前这个人工智能高速发展的时代AI大模型正在深刻改变各行各业。我国对高水平AI人才的需求也日益增长真正懂技术、能落地的人才依旧紧缺。我也希望通过这份资料能够帮助更多有志于AI领域的朋友入门并深入学习。真诚无偿分享vx扫描下方二维码即可加上后会一个个给大家发【附赠一节免费的直播讲座技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等欢迎大家~】大模型全套学习资料展示自我们与MoPaaS魔泊云合作以来我们不断打磨课程体系与技术内容在细节上精益求精同时在技术层面也新增了许多前沿且实用的内容力求为大家带来更系统、更实战、更落地的大模型学习体验。希望这份系统、实用的大模型学习路径能够帮助你从零入门进阶到实战真正掌握AI时代的核心技能01教学内容从零到精通完整闭环【基础理论 →RAG开发 → Agent设计 → 模型微调与私有化部署调→热门技术】5大模块内容比传统教材更贴近企业实战大量真实项目案例带你亲自上手搞数据清洗、模型调优这些硬核操作把课本知识变成真本事‌02适学人群应届毕业生‌无工作经验但想要系统学习AI大模型技术期待通过实战项目掌握核心技术。零基础转型‌非技术背景但关注AI应用场景计划通过低代码工具实现“AI行业”跨界‌。业务赋能突破瓶颈传统开发者Java/前端等学习Transformer架构与LangChain框架向AI全栈工程师转型‌。vx扫描下方二维码即可【附赠一节免费的直播讲座技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等欢迎大家~】本教程比较珍贵仅限大家自行学习不要传播更严禁商用03入门到进阶学习路线图大模型学习路线图整体分为5个大的阶段04视频和书籍PDF合集从0到掌握主流大模型技术视频教程涵盖模型训练、微调、RAG、LangChain、Agent开发等实战方向新手必备的大模型学习PDF书单来了全是硬核知识帮你少走弯路不吹牛真有用05行业报告白皮书合集收集70报告与白皮书了解行业最新动态0690份面试题/经验AI大模型岗位面试经验总结谁学技术不是为了赚$呢找个好的岗位很重要07 deepseek部署包技巧大全由于篇幅有限只展示部分资料并且还在持续更新中…真诚无偿分享vx扫描下方二维码即可加上后会一个个给大家发【附赠一节免费的直播讲座技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等欢迎大家~】

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询