大语言模型(LLM)虽然功能强大,但在处理实时信息、企业私有数据或特定领域知识时存在固有的局限性,如知识截止日期和“幻觉”现象。为了克服这些挑战,检索增强生成(RAG)技术应运而生。RAG 允许 LLM 在生成回答前,从外部知识库中检索最相关的信息作为上下文,从而显著提高回答的准确性、时效性和可靠性。本章将深入探讨知识库的类型、构建方法,以及如何优化 RAG 策略,以赋能 Agent 更智能、更可靠地响应用户请求。
6.1 知识库类型:结构化数据(商品信息、FAQ)、非结构化文档(用户手册、政策文件)
要让 Agent 具备更强的问答能力和更广泛的问题解决范围,它必须能够访问和利用大量的外部知识。大型语言模型(LLM)虽然拥有海量的训练数据,但它们存在知识截止日期、容易产生“幻觉”、以及无法访问实时或私有数据的局限性。为了弥补这些不足,我们需要构建专门的知识库,并通过检索增强生成(RAG)技术,将相关知识注入到 LLM 的上下文中。
知识库中的数据形式多种多样,主要可以分为两大类:结构化数据和非结构化文档。理解这些不同类型的数据及其特点,是高效构建和利用知识库的第一步。
6.1.1 结构化数据 (Structured Data)
定义: 结构化数据是指那些以预定义格式组织的数据,它们通常存储在关系型数据库、电子表格或特定的数据结构中,具有明确的行、列、表等Schema。这种数据易于查询、排序和分析。
在电商客服 Agent 中的应用场景:
- 商品信息 (Product Information):
- 示例数据:商品ID、商品名称、价格、库存、SKU、颜色、尺码、品牌、商品描述(关键属性如材质、功能)、上架时间、好评率等。
- Agent 应用:当用户询问“那款红色T恤多少钱?”、“黑色牛仔裤M码有货吗?”时,Agent 可以查询结构化的商品数据库,快速获取准确信息。这通常通过调用数据库查询 API 或封装的工具来实现。
- 特点:数据准确性要求极高,实时性要求也较高(尤其库存)。更新频率可能随着商品上下架和库存变化而调整。
- 常见问题解答 (FAQ - Frequently Asked Questions):
- 示例数据:问题分类、标准问题、标准答案、相关链接、关键词等。
- Agent 应用:当用户问到“如何申请退款?”、“你们支持哪些支付方式?”这类高频且有标准答案的问题时,Agent 可以从 FAQ 知识库中检索并给出权威答复。FAQ 可以是数据库中的问答对,也可以是简单的 JSON 或 CSV 列表。
- 特点:问题与答案的对应关系明确,数据更新频率相对较低。是构建 RAG 最常见且直接的知识源之一。
- 订单信息 (Order Information):
- 示例数据:订单ID、用户ID、商品列表、订单状态、支付状态、物流单号、收货地址、下单时间等。
- Agent 应用:虽然订单查询通常通过工具调用(如 5.4 节的
query_order_status
工具)来实时获取,但对于历史订单的分析或某些特定状态的统计,也可以视为结构化数据。 - 特点:高度结构化,实时性要求极高,通常不作为RAG的直接检索对象,而是作为工具调用的参数来源或验证依据。
6.1.2 非结构化文档 (Unstructured Data)
定义: 非结构化文档是指那些没有预定义模型或组织结构的数据,它们通常以文本形式存在,例如文档、PDF、网页、邮件、图片等。从这些数据中提取信息需要更复杂的处理,如自然语言处理(NLP)技术。
在电商客服 Agent 中的应用场景:
- 用户手册与产品说明 (User Manuals & Product Descriptions):
- 示例数据:详细的产品使用说明书、组装指南、故障排除手册、特定功能的操作步骤等。这些通常是 PDF 或长篇网页文章。
- Agent 应用:当用户遇到产品复杂功能的使用问题(“我的智能音箱怎么连接蓝牙?”)、或寻求详细技术规格时,Agent 可以从这些文档中检索相关的段落或句子来回答。
- 特点:信息量大,细节丰富,但缺乏明确的键值对。需要复杂的文本处理和语义理解才能有效检索。
- 政策文件与服务条款 (Policy Documents & Terms of Service):
- 示例数据:退换货政策、隐私政策、会员协议、积分规则、保修条款、物流配送说明等。
- Agent 应用:当用户询问“退货的运费谁承担?”、“积分有效期是多久?”、“遇到物流问题怎么办?”时,Agent 需要从这些法律或规范性文件中提取精确的条款来回答,确保合规性和权威性。
- 特点:内容通常严谨、正式,涉及法律和业务规则,对准确性要求极高,错误理解可能导致严重后果。
- 客户服务日志与对话记录 (Customer Service Logs & Chat Transcripts):
- 示例数据:历史客服对话记录、电话录音转录、邮件往来等。
- Agent 应用:Agent 可以分析这些历史数据,从中学习常见问题、用户表达方式以及有效解决方案。在某些高级场景下,Agent 甚至可以检索相似的历史对话来辅助回答复杂问题。
- 特点:数据量巨大,高度口语化、非标准化,包含噪音和不规范表达。是训练模型、发现新问题和优化回答的重要来源。
- 商品评论与用户反馈 (Product Reviews & User Feedback):
- 示例数据:用户对商品的文字评论、社交媒体上的讨论、问答社区内容。
- Agent 应用:Agent 可以通过分析这些非结构化文本,了解用户对商品的实际体验、常见疑问和抱怨点,从而在推荐或问题解决时提供更具洞察力的信息。
- 特点:主观性强,情感色彩丰富,但包含大量用户真实使用的场景和问题。
6.1.3 结合使用:混合知识库
在实际的电商客服 Agent 系统中,通常不会只依赖单一类型的知识库。一个强大的 Agent 会结合使用结构化数据和非结构化文档,形成一个混合知识库。
- 结构化数据:提供准确、实时的事实性信息(如价格、库存、FAQ标准答案)。通常通过工具调用或直接数据库查询来获取。
- 非结构化文档:提供丰富、详细的解释性、指导性信息(如产品说明、政策条款)。这些通常需要进行文本分块、向量化,并通过 **RAG(Retrieval-Augmented Generation)**技术进行检索和上下文增强。
例如,当用户询问“这款手机的电池续航怎么样?”时:
- Agent 可以首先查询结构化商品信息,看是否有明确的电池容量参数(事实)。
- 然后,如果问题更深入(“使用过程中续航表现如何?”),Agent 可能会检索非结构化文档(如用户手册、专业评测文章),从中提取用户实际使用场景下的续航表现描述,并通过 RAG 生成更全面的答案。
理解并有效地管理这些不同类型的知识,是构建一个能够处理复杂、多样用户查询的智能 Agent 的基石。在接下来的章节中,我们将深入探讨如何对这些知识进行切分、向量化、以及优化 RAG 策略。
6.2 知识切分与向量化:如何有效处理和索引大量的电商知识数据
构建知识库并实现检索增强生成(RAG)不仅仅是收集数据,更重要的是如何将这些数据转化为 LLM 可以理解和高效检索的格式。这个过程主要涉及两个核心步骤:知识切分(Chunking)和向量化(Vectorization)。
6.2.1 知识切分 (Knowledge Chunking)
定义: 知识切分是将原始、通常较长的文档或数据(无论是结构化的还是非结构化的)分解成更小、更易于管理和检索的独立单元(或称“块”、“片段”)。
为什么需要切分?
- LLM 上下文窗口限制:LLM 有输入长度限制(称为“上下文窗口”)。原始文档可能非常长,远超 LLM 的处理能力。切分可以确保每个块都能放入 LLM 的上下文窗口,并与用户的查询一同提交。
- 提高检索效率和准确性:
- 减少噪音:如果检索到整个大文档,LLM 可能被迫处理大量不相关的信息,增加理解难度,降低答案质量。小而相关的块可以减少这种“噪音”。
- 聚焦相关性:用户通常只关心特定问题的答案。切分后,检索系统可以更精确地找到与查询最相关的具体信息片段,而不是整个文档。
- 降低计算成本:处理更小的文本块,无论是向量化还是后续的 LLM 推理,都比处理整个文档的成本更低,效率更高。
切分策略:
没有一劳永逸的最佳切分方法,它通常取决于数据类型、内容结构和应用场景。
- 固定大小切分 (Fixed-Size Chunking):
- 方法:按固定字符数或Token数进行切分,通常带有一些重叠(overlap)以保留上下文连续性。
- 优点:简单易实现,适用于各种文本。
- 缺点:可能截断句子或段落,导致语义不完整。
- 电商应用:对于大量的、没有明显结构的长文本(如商品评论、论坛讨论),可以作为初步切分方法。例如,每 500 个字符一个块,重叠 50 个字符。
- 基于语义结构切分 (Semantic Chunking):
- 方法:根据文档的自然语言结构进行切分,例如按段落、章节、标题、句子甚至更小的逻辑单元。
- 优点:保留了语义完整性,切分后的块更具意义,对 LLM 更友好。
- 缺点:实现更复杂,需要 NLP 技术识别结构。
- 电商应用:
- FAQ:每个问答对自然就是一个块。
- 用户手册/政策文件:按小节(如“退货流程”、“保修条款”)或段落进行切分,确保每个块包含一个独立的概念。可以利用Markdown、HTML的标题标签或PDF的结构信息来辅助切分。
- 递归切分 (Recursive Chunking):
- 方法:先尝试大粒度切分(如按章节),如果章节仍然过大,再递归地按段落、句子进行切分,直到满足大小要求。
- 优点:结合了语义和大小的优势,灵活且能适应不同粒度的内容。
- 缺点:实现复杂度较高。
- 电商应用:处理结构复杂的长篇文档,如详细的产品规格书或法律条款。
- 特殊切分规则:
- Markdown/HTML解析器:利用文档本身的格式(如Markdown标题、HTML标签)来识别语义边界进行切分。
- 代码分割器:对于包含代码的文档,可以专门按代码块进行切分。
切分后处理:
切分后,每个块通常会加上一些元数据(metadata),如文档来源、标题、页码、作者、创建日期等。这些元数据在检索时非常有用,可以用于过滤或增强检索结果。
6.2.2 向量化 (Vectorization / Embedding)
定义: 向量化是将文本(切分后的知识块)转换为**高维数值向量(embeddings)**的过程。这些向量能够捕捉文本的语义信息,使得语义相似的文本在向量空间中距离更近。
为什么需要向量化?
- 语义相似度匹配:传统的关键词匹配无法理解语义。例如,用户问“怎么换手机”,关键词匹配可能找不到“更换设备”的文档。向量化后,"换手机"和"更换设备"在向量空间中距离很近,可以被有效检索。
- 高效检索:在高维向量空间中,可以通过计算向量距离(如余弦相似度)快速找到与查询向量最相似的知识块,这比全文扫描或复杂的关键词索引要快得多。
- RAG 基础:向量化是 RAG 架构的核心。检索器通过比较用户查询的向量和知识库中所有知识块的向量来找到最相关的上下文。
向量化模型 (Embedding Models):
用于生成文本向量的模型通常被称为嵌入模型(Embedding Models)。这些模型可以是:
- 通用嵌入模型:如 OpenAI 的
text-embedding-ada-002
,Google 的text-embedding-004
或 Sentence-Transformers 系列模型(如all-MiniLM-L6-v2
)。它们在大量通用文本上训练,对各种主题都有很好的泛化能力。 - 领域特定嵌入模型:在特定领域(如电商、医疗、法律)的文本上进行微调或专门训练的模型。它们在特定领域的语义理解上可能表现更优。
向量化流程:
- 选择嵌入模型:根据应用场景和预算选择合适的嵌入模型。
- 批量向量化:将所有切分后的知识块批量输入到嵌入模型中,生成对应的向量。
- 存储到向量数据库:将生成的向量及其对应的原始文本块、元数据一同存储到**向量数据库(Vector Database)**中。
主流向量数据库:
向量数据库是专门用于高效存储、索引和检索高维向量的数据库。它们通常基于近似最近邻(ANN)算法来加速查询。
- 开源方案:
- Faiss (Facebook AI Similarity Search): 专为高效相似性搜索设计,适合作为本地或私有部署的向量索引库。
- ChromaDB:轻量级、易于使用的开源向量数据库,适合小型项目或快速原型开发。
- Weaviate:支持更多数据类型和复杂查询的向量搜索引擎。
- Milvus/Zilliz:为大规模向量相似性搜索设计,高可用、高扩展性。
- 云服务/托管方案:
- Pinecone:流行的托管向量数据库服务,提供高可扩展性和高性能。
- Qdrant:另一个流行的开源且提供托管服务的向量相似性搜索引擎。
- Elasticsearch (with Vector Search plugin):传统搜索引擎增加了向量搜索能力,适合混合搜索场景。
6.2.3 知识处理流程示例(电商客服场景)
以电商客服 Agent 为例,其知识库构建的知识切分与向量化流程可能如下:
- 数据来源:
FAQ.csv
(结构化,问题-答案对)退换货政策.pdf
(非结构化,长文本)iPhone15_用户手册.pdf
(非结构化,长文本)商品描述数据库
(结构化,通常通过API查询,但部分长描述可切分向量化)
- 切分:
- FAQ.csv:直接按行(问答对)切分,每个问答对作为一个语义块。
- 退换货政策.pdf:使用 PDF 解析库提取文本,然后按章节或段落递归切分。
- iPhone15_用户手册.pdf:类似政策文件,按章节或功能模块切分。
- 商品描述:对于长篇商品详情页文本,可以按描述中的小标题(如“产品特点”、“技术规格”)切分。
- 向量化:
- 将切分后的所有文本块(包括FAQ答案、政策条款段落、用户手册片段等)输入到选定的嵌入模型(例如,
text-embedding-ada-002
)。 - 模型为每个文本块生成一个高维向量。
- 将切分后的所有文本块(包括FAQ答案、政策条款段落、用户手册片段等)输入到选定的嵌入模型(例如,
- 存储:
- 将这些文本块的向量,连同原始文本内容和相关的元数据(如
source_document: "退换货政策.pdf"
,chapter: "退货运费说明"
,faq_id: "FAQ001"
等),一起存储到向量数据库(例如 ChromaDB 或 Pinecone)。
- 将这些文本块的向量,连同原始文本内容和相关的元数据(如
当用户提问时,用户的查询也会通过同样的嵌入模型转化为一个向量。然后,检索器会在向量数据库中进行相似度搜索,找到与用户查询向量最接近的知识块,并将这些知识块作为上下文传递给 LLM,用于生成最终的答案。
总结:
知识切分与向量化是构建高效 RAG 系统的核心。通过将原始知识库分解为语义完整且大小合适的知识块,并将其转化为能够捕捉语义信息的向量,我们能够极大地提升 LLM 在特定领域知识上的问答能力、准确性和实时性,有效克服其固有的知识局限性。
6.3 RAG 策略优化:提高检索准确性,确保 Agent 能获取到最相关的上下文
检索增强生成(RAG)的核心思想是,在大型语言模型(LLM)生成回答之前,先从一个外部知识库中检索最相关的信息,然后将这些信息作为上下文提供给 LLM,引导其生成更准确、更具时效性且更少“幻觉”的答案。然而,RAG 的效果并非一蹴而就,它高度依赖于检索的准确性和上下文的质量。本节将探讨多种 RAG 策略优化方法,旨在提高检索准确性,确保 Agent 能够获取到最相关的上下文。
6.3.1 检索阶段优化 (Retrieval Optimization)
检索阶段的目标是尽可能找到与用户查询语义最相关的知识块。
- 优化嵌入模型 (Embedding Model Optimization):
- 选择高质量模型:使用表现优秀的通用嵌入模型(如 OpenAI 的
text-embedding-3-large
、Google 的text-embedding-004
或 Cohere 的embed-english-v3.0
),它们能够更好地捕捉文本的语义信息。 - 领域微调 (Domain-Specific Fine-tuning):如果你的知识库属于特定领域(如电商、医疗、法律),可以考虑使用在类似领域数据上预训练或微调过的嵌入模型。这能让模型更好地理解领域内的专业术语和上下文语义。
- 动态嵌入 (Dynamic Embeddings):对于某些复杂查询,可以考虑使用结合了用户意图、对话历史等信息的动态嵌入方法,使查询向量更精确。
- 选择高质量模型:使用表现优秀的通用嵌入模型(如 OpenAI 的
- 优化向量搜索 (Vector Search Optimization):
- 近似最近邻 (ANN) 算法调优:向量数据库使用的 ANN 算法(如 HNSW、IVF_FLAT)有各种参数(如搜索效率与准确性的权衡参数),合理配置这些参数可以优化检索性能和结果质量。
- 混合搜索 (Hybrid Search / RRF):结合关键词搜索(如 BM25)和向量相似度搜索的优势。
- 关键词搜索能有效处理精确匹配的专有名词或ID。
- 向量搜索能处理语义相似但关键词不同的查询。
- RRF (Reciprocal Rank Fusion):一种融合不同搜索结果排序的方法,将关键词搜索和向量搜索的结果进行加权合并,通常能取得更好的整体效果。
- 查询扩展 (Query Expansion):在进行向量搜索之前,对用户原始查询进行扩展,生成多个语义相近的查询变体、同义词或相关概念,用这些扩展后的查询进行多次检索,然后合并结果。这有助于捕捉用户可能隐含的意图。
- 元数据过滤 (Metadata Filtering):
- 在向量搜索时,结合知识块的元数据进行过滤。例如,如果用户查询“关于退货政策中电子产品的说明”,可以在检索时只选择来源为“退货政策文档”且包含“电子产品”标签的知识块。这能有效缩小搜索范围,提高相关性。
- 预过滤:先根据元数据过滤出一部分候选块,再进行向量搜索。
- 后过滤:先进行宽泛的向量搜索,再根据元数据对检索结果进行过滤。
6.3.2 上下文构建优化 (Context Construction Optimization)
检索到的知识块需要被组织成高质量的上下文,才能有效传递给 LLM。
- 冗余信息去除 (Redundancy Removal):
- 检索到的多个知识块之间可能存在大量重叠或重复信息。在将它们传递给 LLM 之前,进行去重和冗余信息消除,可以减少 LLM 处理的负担,避免上下文窗口浪费。
- 聚类与摘要:将语义相似的多个检索到的知识块进行聚类,并针对每个聚类生成一个简要摘要,作为LLM的上下文。
- 上下文重排序 (Context Re-ranking):
- 即使通过向量搜索得到了相关的知识块,它们的排序可能不完全符合 LLM 的最佳利用习惯。使用一个小型排序模型 (Re-ranker),对初步检索到的结果进行二次排序。这个排序模型可以更细致地评估每个知识块与查询的相关性,甚至考虑其在文档中的位置、信息密度等。
- 交叉编码器 (Cross-encoders):一类常用于重排序的模型,它们同时接收查询和知识块作为输入,并输出一个相关性分数,比独立的嵌入模型更准确。
- 滑动窗口与局部增强 (Sliding Window & Local Context Expansion):
- 当检索到的知识块本身较小但其周围的上下文很重要时,可以扩展该块,包含其原始文档中相邻的句子或段落。
- 例如,如果检索到一个关键句子,可以自动将其前后各 N 个句子也加入到上下文中。这有助于 LLM 更好地理解该信息的完整语境。
- 动态上下文大小 (Dynamic Context Sizing):
- 根据用户查询的复杂性和检索到的相关性分数,动态调整传递给 LLM 的上下文大小。对于简单问题,少量精确上下文即可;对于复杂问题,可能需要更多上下文。
- 摘要上下文:如果相关信息过多,可以通过另一个 LLM 对检索到的多个知识块进行摘要,生成一个更简洁但信息更密集的上下文。
6.3.3 生成阶段优化 (Generation Optimization)
尽管 RAG 的主要优化在检索和上下文构建阶段,但 LLM 的生成策略也会影响最终答案的质量。
- Prompt 工程 (Prompt Engineering):
- 精心设计 LLM 的 Prompt,明确指示它如何使用提供的上下文。例如,要求它“仅根据提供的上下文进行回答”、“如果上下文无法回答,请告知用户并提供转人工选项”。
- 在 Prompt 中强调权威性、准确性,并要求 LLM 引用上下文中的来源(例如“根据您提供的退换货政策,第3条规定…”)。
- CoT (Chain-of-Thought) / ReAct 提示:引导 LLM 逐步思考,先分析上下文,再生成答案,有助于提高逻辑性和准确性。
- 答案可靠性评估 (Answer Reliability Assessment):
- 让 LLM 自己评估其生成的答案是否完全基于提供的上下文。如果答案包含上下文之外的信息,可以要求 LLM 重新生成或标记为不可靠。
- 置信度评分:训练一个小型模型来评估 LLM 生成答案的置信度,低置信度的答案可以转交给人工。
- 安全性和合规性检查 (Safety & Compliance Checks):
- 在答案生成后,进行敏感词过滤和合规性检查,确保不泄露敏感信息,不提供错误或误导性建议(特别是法律、医疗或金融相关)。
6.3.4 持续迭代与评估
RAG 策略优化是一个持续的过程。
- A/B 测试:部署不同的 RAG 策略进行 A/B 测试,比较用户满意度、任务完成率、错误率等指标。
- 人工标注与反馈:收集用户反馈和人工标注数据(例如,哪些答案是错误的、哪些检索结果不相关),用于迭代优化嵌入模型、切分策略或重排序模型。
- 离线评估指标:
- 召回率 (Recall):模型能否找到所有相关的知识块。
- 精度 (Precision):找到的知识块中有多少是真正相关的。
- 上下文相关性 (Context Relevance):传递给 LLM 的上下文与用户查询的匹配程度。
- 答案忠实度 (Answer Faithfulness):LLM 生成的答案有多少是忠实于提供的上下文的。
- 答案相关性 (Answer Relevance):LLM 生成的答案与用户查询的匹配程度。
6.3.5 案例实践中的 RAG 策略优化
在电商客服 Agent 中,RAG 策略优化至关重要:
- 商品信息查询:当用户问“这款手机拍照怎么样?”。
- 优化:除了通过
query
检索商品描述,还可以通过元数据过滤(例如,仅检索“相机”或“拍摄”相关的段落),并结合产品评论数据(可能存储在另一个向量索引中)来提供更全面的回答。
- 优化:除了通过
- 政策问题:用户问“退货运费规则是什么?”
- 优化:可以优先使用针对政策文档微调的嵌入模型,确保对法律术语的准确理解。同时,采用递归切分和局部上下文扩展,确保 LLM 获得完整的条款上下文。如果检索到多个相似条款,使用重排序模型来选择最权威或最新的版本。
- FAQ问答:
- 优化:虽然 FAQ 本身就结构化,但当用户提问方式多样时,通过查询扩展和混合搜索,可以更精准地匹配到标准答案。例如,用户问“怎么换货”,查询扩展可以增加“退货”、“商品更换”等关键词,以提高召回率。
总结:
RAG 策略优化是一个多维度、系统性的工程。它涉及到对嵌入模型的选择与调优、向量搜索技术的精进(如混合搜索、元数据过滤)、上下文的智能构建(如去重、重排序、扩展)以及 LLM 生成阶段的 Prompt 工程。通过持续的实验、评估和迭代,可以显著提升 Agent 在复杂问答场景下的表现,使其能够为用户提供更准确、更可靠的帮助。
6.4 案例实践:构建电商客服 FAQ 知识库,并与 Agent 进行 RAG 集成
在本节中,我们将通过一个具体的案例来展示如何为电商客服 Agent 构建一个 FAQ(常见问题解答)知识库,并将其与 Agent 通过 RAG(检索增强生成)技术进行集成。FAQ 知识库是 RAG 最常见且效果显著的应用场景之一,因为它通常包含结构清晰的问题和标准答案。
6.4.1 知识库的准备:收集与整理 FAQ 数据
首先,我们需要一个 FAQ 数据源。在实际业务中,这可能是从客服的历史记录中提取、由运营团队整理,或从现有官网 FAQ 页面爬取。
数据格式: 为了便于处理,我们假设 FAQ 数据以 CSV 文件的形式存在,包含“问题” (question) 和“答案” (answer) 两列。
ecommerce_faq.csv
示例:
代码段
question,answer
如何申请退款?,您好!申请退款请前往“我的订单”页面,找到对应订单后点击“申请退款”按钮,并填写退款原因。商品寄回后,我们将在2-3个工作日内为您处理。
退货运费谁承担?,非商品质量问题(如:不喜欢、尺码不符)产生的退货,运费需由您自行承担。如属商品质量问题,运费将由我们承担。
我的订单发货了吗?,订单发货后您会收到短信通知,也可在“我的订单”中查看物流状态。
支持哪些支付方式?,我们支持支付宝、微信支付、银联在线支付和部分银行的网银支付。
商品多久能到货?,商品通常在发货后3-5个工作日内送达,具体时效取决于您的收货地址和物流公司。
如何修改收货地址?,订单发货前,您可在“我的订单”页面自行修改。发货后如需修改,请尽快联系在线客服处理。
6.4.2 知识切分与向量化:构建向量数据库
我们将使用 Python 和 LangChain 来实现知识库的加载、切分、向量化和存储到向量数据库。这里我们选择一个轻量级的向量数据库 ChromaDB
进行本地演示。
步骤:
- 加载数据:使用 LangChain 的
CSVLoader
加载 FAQ 文件。 - 文本切分:由于 FAQ 每个问答对本身就是逻辑单元,我们可以将每个问答对视为一个文档,不需要复杂的递归切分。这里我们将
question
和answer
字段合并为一个块。 - 选择嵌入模型:使用 HuggingFace
SentenceTransformers
的一个通用嵌入模型,例如all-MiniLM-L6-v2
。 - 向量化并存储:将文本块转换为向量并存储到 ChromaDB。
# 导入所需库
from langchain_community.document_loaders import CSVLoader
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
import os
# 确保 FAQ 文件存在
# 创建一个模拟的 CSV 文件,如果它不存在
faq_csv_content = """question,answer
如何申请退款?,您好!申请退款请前往“我的订单”页面,找到对应订单后点击“申请退款”按钮,并填写退款原因。商品寄回后,我们将在2-3个工作日内为您处理。
退货运费谁承担?,非商品质量问题(如:不喜欢、尺码不符)产生的退货,运费需由您自行承担。如属商品质量问题,运费将由我们承担。
我的订单发货了吗?,订单发货后您会收到短信通知,也可在“我的订单”中查看物流状态。
支持哪些支付方式?,我们支持支付宝、微信支付、银联在线支付和部分银行的网银支付。
商品多久能到货?,商品通常在发货后3-5个工作日内送达,具体时效取决于您的收货地址和物流公司。
如何修改收货地址?,订单发货前,您可在“我的订单”页面自行修改。发货后如需修改,请尽快联系在线客服处理。
会员积分如何计算?,会员积分根据您的消费金额按比例累计,每消费1元可获得1积分。
忘记密码怎么办?,请在登录页面点击“忘记密码”,通过手机验证或邮箱验证进行重置。
"""
faq_file_path = "ecommerce_faq.csv"
if not os.path.exists(faq_file_path):
with open(faq_file_path, "w", encoding="utf-8") as f:
f.write(faq_csv_content)
print(f"Loading FAQ data from {faq_file_path}...")
loader = CSVLoader(file_path=faq_file_path, csv_args={'delimiter': ','})
data = loader.load()
# 对于 FAQ 这种问答对,我们可以简单地将问题和答案合并作为文档内容
# 如果是更复杂的文档,这里会使用 RecursiveCharacterTextSplitter 等
documents = []
for doc in data:
# 假设 CSVLoader 已经将每一行解析为 Document 对象,page_content是整个行
# 这里我们手动提取question和answer,并将其组合成一个更适合RAG的文本块
question = doc.metadata.get('question')
answer = doc.metadata.get('answer')
if question and answer:
# 将问题和答案组合起来,让模型能理解上下文
combined_content = f"问题: {question}\n答案: {answer}"
doc.page_content = combined_content
documents.append(doc)
print(f"Loaded {len(documents)} FAQ documents.")
# 初始化嵌入模型
# 建议使用本地或可访问的Sbert模型
# 第一次运行时可能需要下载模型文件
print("Initializing embedding model...")
model_name = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" # 支持多语言的较小模型
# model_name = "sentence-transformers/all-MiniLM-L6-v2" # 英文模型
embeddings = HuggingFaceEmbeddings(model_name=model_name)
# 构建向量数据库
# persistent_directory 指定存储路径,以便下次运行时直接加载,无需重新构建
persist_directory = "chroma_faq_db"
print(f"Building/Loading ChromaDB from {persist_directory}...")
vectordb = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory=persist_directory
)
print("ChromaDB built/loaded successfully.")
# 确保数据库被持久化
vectordb.persist()
print("Vector database persisted.")
# 验证检索功能
query = "怎么退货?"
retrieved_docs = vectordb.similarity_search(query, k=2) # 检索最相似的2个文档
print(f"\n--- Test Retrieval for '{query}' ---")
for i, doc in enumerate(retrieved_docs):
print(f"Document {i+1} (Score: {doc.metadata.get('relevance_score', 'N/A')}):") # ChromaDB 默认不返回分数,需要额外处理
print(doc.page_content)
print("---")
代码解释:
CSVLoader
: 用于从 CSV 文件加载数据,每行成为一个Document
对象。- 文档组合: 我们将 CSV 的“问题”和“答案”字段合并到一个文档的
page_content
中,这样在检索时,LLM 能同时看到问题和对应的答案,更容易理解上下文。 HuggingFaceEmbeddings
: 加载预训练的 Sentence Transformer 模型来生成文本嵌入。选择paraphrase-multilingual-MiniLM-L12-v2
是因为它支持多语言且模型相对较小。Chroma.from_documents
: 将处理后的documents
和embeddings
传递给Chroma
,创建或加载一个向量数据库实例。persist_directory
参数确保了数据库可以被持久化到磁盘,下次无需重新计算嵌入。vectordb.similarity_search(query, k=N)
: 这是向量数据库的核心功能,通过计算用户查询向量与知识库中所有文档向量的相似度,返回最相关的N
个文档。
6.4.3 RAG 与 Agent 的集成
现在我们有了向量数据库和检索器,下一步就是将其集成到 Agent 的工作流中,实现 RAG。
集成方式:
- 将检索器作为工具:Agent 可以将 RAG 检索器视为一个特殊的“工具”。当 LLM 判断用户的问题可以通过知识库回答时,它会“调用”这个检索工具,将用户问题作为输入,获得相关知识块作为输出。
- RAG Chain:在更常见的 LangChain 模式中,检索器通常直接集成到 Retrieval Chain 中,LLM 在生成答案前自动进行检索。对于 Agent,这个过程可以在 Agent 的
Thought
阶段之后,Action
决策之前进行,或者直接封装为一个智能化的 RAG 工具。
修改 Agent Prompt (简化示例):
为了清晰起见,我们可以修改 Agent 的 Prompt,使其明确知道有一个“知识库检索”的能力。虽然在 LangChain 等框架中,RAG 通常是作为 LLM 的附加能力隐式实现的,但从概念上理解,我们可以将其视作一个特殊的“工具”。
# 导入 LangChain 相关组件
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI # 假设使用OpenAI模型,也可以是其他兼容模型
# 定义一个用于RAG的Prompt模板
rag_prompt_template = """你是一个电商客服Agent。根据用户问题和提供的上下文信息,准确、简洁地回答用户。
如果上下文无法回答问题,请告知用户你不知道,并引导用户提供更多信息或转接人工客服。
请始终保持友好和专业的态度。
上下文信息:
{context}
用户问题:
{question}
你的回答:
"""
# 创建RAG Prompt
rag_prompt = ChatPromptTemplate.from_template(rag_prompt_template)
# 初始化LLM
# 请替换为你的OpenAI API Key,或者使用其他LLM
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) # temperature=0 追求确定性
# 将检索器、Prompt 和 LLM 组合成一个 RAG Chain
# 首先,定义一个 Retriever,它能够从 vectordb 中检索文档
retriever = vectordb.as_retriever(search_kwargs={"k": 3}) # 检索最相似的3个文档
def format_docs(docs):
# 将检索到的文档格式化为LLM可以理解的字符串
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| llm
| StrOutputParser()
)
# 模拟用户对话
def ask_agent(question):
print(f"\n--- 用户提问: {question} ---")
response = rag_chain.invoke(question)
print(f"--- Agent 回复: {response} ---")
return response
# 测试 RAG Agent
ask_agent("我买的东西怎么退?")
ask_agent("发货时间是多久?")
ask_agent("我忘记登录密码了怎么办?")
ask_agent("你们的会员制度是什么?") # 知识库中没有,看Agent如何应对
ask_agent("我能用花呗支付吗?") # 知识库中有“支付方式”,但可能不完全匹配花呗,测试相似度
代码解释:
retriever = vectordb.as_retriever()
: 这是 LangChain 中将向量数据库转换为一个“检索器”的标准方法。检索器负责接收查询并返回相关的文档。search_kwargs={"k": 3}
表示每次检索返回最相似的 3 个文档。format_docs
函数: 辅助函数,将检索到的 LangChainDocument
对象列表格式化为一个单一的字符串,以便作为上下文传递给 LLM。rag_chain
: 构建 RAG 链的核心。{"context": retriever | format_docs, "question": RunnablePassthrough()}
: 这是一个并行处理的结构。context
部分会通过retriever
检索文档并用format_docs
格式化;question
部分则直接传递用户原始问题。| rag_prompt
: 将格式化后的上下文和用户问题注入到 RAG 专用的 Prompt 模板中。| llm
: 将完整 Prompt 传递给 LLM 进行生成。| StrOutputParser()
: 将 LLM 的输出解析为纯字符串。
ask_agent
函数: 模拟用户与 Agent 的交互,调用rag_chain
获取回答。
6.4.4 结果分析与优化方向
通过运行上述代码,我们可以观察到 Agent 的行为:
- 对于“我买的东西怎么退?”:Agent 能够检索到“如何申请退款?”和“退货运费谁承担?”等相关 FAQ,并基于这些信息生成准确的退款流程说明。
- 对于“发货时间是多久?”:Agent 能够检索到“商品多久能到货?”的 FAQ,并给出相应的物流时效信息。
- 对于“我忘记登录密码了怎么办?”:Agent 也能正确检索并告知密码重置流程。
- 对于“你们的会员制度是什么?”:由于我们的 FAQ 知识库中没有直接关于“会员制度”的信息,Agent 可能会:
- 如果 LLM 被严格限制在上下文内回答:它会回答“抱歉,我无法在当前信息中找到关于会员制度的详细信息。”或者类似表示不知道的回复。
- 如果 LLM 有一定的泛化能力:它可能会尝试结合其他信息或默认回答“抱歉,我不知道您想了解的具体信息,请问您想了解哪方面的会员制度?或者请提供更多信息。”
- 这正是 RAG 优化的方向:当知识库不足时,Agent 应该能识别并引导用户,或者触发转人工流程。
未来优化方向:
- 知识库扩充:不断收集和添加新的 FAQ、产品手册、政策文件等,丰富知识库。
- 切分策略优化:对于更复杂的文档(如产品说明书),可以尝试更精细的语义切分或递归切分,确保每个块包含的信息既完整又独立。
- 嵌入模型升级:考虑使用更强大的嵌入模型(如 OpenAI 的
text-embedding-3-large
)或针对电商领域进行微调的嵌入模型,以提高语义匹配的准确性。 - RAG 策略高级优化:
- 混合检索:结合关键词搜索(如 BM25)和向量搜索,尤其对于专有名词或产品型号的查询。
- 重排序 (Re-ranking):在检索到初始文档后,使用一个独立的排序模型对这些文档进行二次排序,确保最相关的文档排在最前面。
- 查询改写 (Query Rewriting):让 LLM 根据对话历史和用户意图,改写原始查询,使其更适合向量检索。
- 多跳推理:对于需要从多个知识块中提取信息并进行整合才能回答的问题,设计 Agent 进行多步检索和合成。
通过本案例实践,我们理解了从原始数据到可用于 RAG 的向量数据库的完整流程,以及如何将 RAG 能力集成到 Agent 中,使其能够智能地回答基于知识库的问题。