技术选型
LangChain
- LangChain 是专门为开发基于 LLM 应用而设计的全面框架
- LangChain 的核心目标是简化开发者的构建流程,使其能够高效地创建 LLM 驱动的应用
索引
文档解析
- pypdf 专门用于处理 PDF 文档
- pypdf 支持 PDF 文档的创建、读取、编辑和转换,能够有效地提取和处理文本、图像及页面内容
文档分块
- RecursiveCharacterTextSplitter 是 LangChain 默认的文本分割器
- RecursiveCharacterTextSplitter 通过层次化的分隔符(从双换行符到单字符)拆分文本
- 旨在保持文本的结构和连贯性,优先考虑自然边界(如段落和句子)
索引 + 检索
向量化模型
- bge-small-zh-v1.5 是由北京智源人工智能研究院(BAAI)开发的开源向量模型
- bge-small-zh-v1.5 的模型体积较小,但仍能提供高精度和高效的中文向量检索
- bge-small-zh-v1.5 的向量维度为 512,最大输入长度同样为 512
向量库
- Faiss - Facebook AI Similarity Search
- Faiss 由 Facebook AI Research 开源的向量库,非常稳定和高效
生成
LLM
- Qwen 是阿里云推出的一款超大规模语言模型
- Qwen 支持多轮对话、文案创造、逻辑推理、多模态理解和语言处理
- Qwen 在模型性能和工程应用中表现出色
- Qwen 支持云端 API 服务
RAG
- LangChain
- 索引流程
- 使用 pypdf 对文档进行解析并提取信息
- 采用 RecursiveCharacterTextSplitter 对文档内容进行分块(Chunk)
- 使用 bge-small-zh-v1.5 将 Chunk 进行向量化处理,并将生成的向量存储到 Faiss 向量库中
- 检索流程
- 使用 bge-small-zh-v1.5 对 Query 进行向量化处理
- 通过 Faiss 向量库对 Query 向量和 Chunk 向量进行相似度匹配
- 从而检索出与 Query 最相似的 Top K 个 Chunk
- 生成流程
- 设定提示模板(Prompt)
- 将 Query 与 Chunk 填充到提示模板,生成增强提示,输入到 Qwen LLM,生成最终的 RAG 回答
开发环境
虚拟环境
1 2
| $ python3 -m venv rag_env $ source rag_env/bin/activate
|
安装依赖
1 2
| $ pip install --upgrade pip $ pip install langchain langchain_community pypdf sentence-transformers faiss-cpu dashscope
|
向量化模型
bge-small-zh-v1.5 - 95.8MB - pytorch_model.bin
1 2 3 4 5 6 7 8
| $ git clone https://huggingface.co/BAAI/bge-small-zh-v1.5
$ du -sh * 367M bge-small-zh-v1.5 332K corpus.pdf 4.0K LICENSE 1.1G rag_env 4.0K README.md
|
核心代码
依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from sentence_transformers import SentenceTransformer import faiss import numpy as np import dashscope from http import HTTPStatus
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"
qwen_model = "qwen-turbo" qwen_api_key = "your_api_key"
|
索引
Embedding
SentenceTransformer - map sentences / text to embeddings
1 2 3 4 5 6 7 8 9 10
| def load_embedding_model(): """ 加载bge-small-zh-v1.5模型 :return: 返回加载的bge-small-zh-v1.5模型 """ print(f"加载Embedding模型中") embedding_model = SentenceTransformer(os.path.abspath('bge-small-zh-v1.5')) print(f"bge-small-zh-v1.5模型最大输入长度: {embedding_model.max_seq_length}") return embedding_model
|
Indexing
chunk_size + chunk_overlap
- chunk_size
- 对输入文本序列进行切分的最大长度
- GPT-3 的最大输入长度为 2048 个 Token
- chunk_overlap
- 相邻的两个 Chunk 之间的重叠 Token 数量
- 为了保证文本语义的连贯性,相邻 Chunk 会有一定的重叠
- chunk_size = 1024,chunk_overlap = 128,对于长度为 2560 Token 的文本序列,会切分成 3 个 Chunk
- 1 ~ 1024 = 1024
- 897~1920 = 1024
- 1793~2560 = 768
索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| def indexing_process(pdf_file, embedding_model): """ 索引流程:加载PDF文件,并将其内容分割成小块,计算这些小块的嵌入向量并将其存储在FAISS向量数据库中。 :param pdf_file: PDF文件路径 :param embedding_model: 预加载的嵌入模型 :return: 返回FAISS嵌入向量索引和分割后的文本块原始内容列表 """ pdf_loader = PyPDFLoader(pdf_file, extract_images=False) text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=128)
pdf_content_list = pdf_loader.load() pdf_text = "\n".join([page.page_content for page in pdf_content_list]) print(f"PDF文档的总字符数: {len(pdf_text)}")
chunks = text_splitter.split_text(pdf_text) print(f"分割的文本Chunk数量: {len(chunks)}")
embeddings = [] for chunk in chunks: embedding = embedding_model.encode(chunk, normalize_embeddings=True) embeddings.append(embedding)
print("文本块Chunk转化为嵌入向量完成")
embeddings_np = np.array(embeddings)
dimension = embeddings_np.shape[1]
index = faiss.IndexFlatIP(dimension) index.add(embeddings_np)
print("索引过程完成.")
return index, chunks
|
- 使用 PyPDFLoader 加载并预处理 PDF 文档,将其内容提取并合并为完整文本
- 利用 RecursiveCharacterTextSplitter 将文本分割为每块 512 字符(非 Token)、重叠 128 字符(非 Token)的文本块
- 通过预加载的 bge-small-zh-v1.5 嵌入模型将文本块转化为归一化的嵌入向量
- 将嵌入向量存储在基于余弦相似度的 Faiss 向量库中,以支持后续的相似性检索和生成任务
余弦相似度 - Cosine Similarity - 在 N 维空间中,两个 N 维向量之间角度的余弦 - 值越大越相似
$$
Similarity(A,B) = \frac{A\cdot{B}}{|A|\times|B|} = \frac{\sum_{i=1}^n(A_i\times{B_i})}{\sqrt{\sum_{i=1}^n{A_i^2}}\times\sqrt{\sum_{i=1}^n{B_i^2}}}
$$
待优化项
- 多文档处理
- 嵌入模型的效率优化与并行处理
- Faiss 采用持久化存储 - 目前仅在内存中存储
检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| def retrieval_process(query, index, chunks, embedding_model, top_k=3): """ 检索流程:将用户查询Query转化为嵌入向量,并在Faiss索引中检索最相似的前k个文本块。 :param query: 用户查询语句 :param index: 已建立的Faiss向量索引 :param chunks: 原始文本块内容列表 :param embedding_model: 预加载的嵌入模型 :param top_k: 返回最相似的前K个结果 :return: 返回最相似的文本块及其相似度得分 """ query_embedding = embedding_model.encode(query, normalize_embeddings=True) query_embedding = np.array([query_embedding])
distances, indices = index.search(query_embedding, top_k)
print(f"查询语句: {query}") print(f"最相似的前{top_k}个文本块:")
results = [] for i in range(top_k): result_chunk = chunks[indices[0][i]] print(f"文本块 {i}:\n{result_chunk}")
result_distance = distances[0][i] print(f"相似度得分: {result_distance}\n")
results.append(result_chunk)
print("检索过程完成.") return results
|
- Query 被预加载的 bge-small-zh-v1.5 嵌入模型转化为归一化的嵌入向量
- 进一步转换为 numpy 数组以适配 Faiss 向量库的输入格式
- 利用 Faiss 向量库中的向量检索功能
- 计算 Query 与存储向量之间余弦相似度,从而筛选出与 Query 最相似的 Top K 个文本块
- 相似文本块存储在结果列表中,供后续生成过程使用
生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| def generate_process(query, chunks): """ 生成流程:调用Qwen大模型云端API,根据查询和文本块生成最终回复。 :param query: 用户查询语句 :param chunks: 从检索过程中获得的相关文本块上下文chunks :return: 返回生成的响应内容 """ llm_model = qwen_model dashscope.api_key = qwen_api_key
context = "" for i, chunk in enumerate(chunks): context += f"参考文档{i + 1}: \n{chunk}\n\n"
prompt = f"根据参考文档回答问题:{query}\n\n{context}" print(f"生成模型的Prompt: {prompt}")
messages = [{'role': 'user', 'content': prompt}]
try: responses = dashscope.Generation.call( model=llm_model, messages=messages, result_format='message', stream=True, incremental_output=True ) generated_response = "" print("生成过程开始:") for response in responses: if response.status_code == HTTPStatus.OK: content = response.output.choices[0]['message']['content'] generated_response += content print(content, end='') else: print(f"请求失败: {response.status_code} - {response.message}") return None print("\n生成过程完成.") return generated_response except Exception as e: print(f"大模型生成过程中发生错误: {e}") return None
|
- 结合 Query 与检索到的文本块组织成 LLM Prompt
- 调用 Qwen 云端 API,将 Prompt 发送给 LLM
- 利用流式输出的方式逐步获取 LLM 生成的响应内容,实时输出并汇总成最终的生成结果
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 生成过程开始: 参考文档中涉及的案例及其面临的挑战如下:
1. **金融业**: - **挑战**:银行面临的主要挑战包括客户服务模式过时(主要依赖实体网点,服务效率低、客户体验差),金融科技企业的竞争压力(凭借创新技术和便捷服务吸引大量客户,尤其是年轻一代),以及数据孤岛和风险管理滞后(各业务部门缺乏数据共享机制,信息无法整合,风险管理效率低)。
2. **制造业**: - **挑战**:制造业面临的主要挑战包括生产效率低、易出错,供应链管理复杂(涉及多个国家和地区,信息传递不及时,造成库存管理困难,甚至存在供应链断裂的风险),以及无法满足市场对个性化定制产品的需求(传统大规模生产方式无法适应)。
3. **零售业**: - **挑战**:零售业面临的主要挑战是线上线下渠道割裂(导致库存管理不统一、客户体验不一致,难以提供无缝购物体验),以及数据利用率低(尽管拥有大量消费者和销售数据,但缺乏先进的数据分析工具,未能转化为可操作的商业洞察)。
对于每个行业,数字化转型解决方案通常包括: - **金融业**:构建数字化银行平台,推出移动银行应用、在线服务、虚拟客服和智能理财顾问,同时引入人工智能和大数据分析技术以提升服务便捷性、客户满意度和风险管理能力。 - **制造业**:引入工业4.0技术(如物联网、人工智能、大数据分析和机器人自动化)以优化生产线,构建基于云计算的智能供应链管理系统实现供应链的端到端可视化管理。 - **零售业**:构建全渠道零售平台实现线上与线下购物渠道的无缝整合,引入大数据和人工智能驱动的分析平台以精准预测需求、优化库存,并提供个性化产品推荐和营销活动。 生成过程完成.
|