MedMemory-RAG系统构建(一)----基于datawhale
首先需要进行初始化配置。
import os
# hugging face镜像设置,如果国内环境无法使用启用该设置
# os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
from dotenv import load_dotenv
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
load_dotenv()
使用 dotenv.load_dotenv() 从本地 .env 文件中加载诸如 OPENAI_API_KEY 等配置,供后续模型调用自动读取;接着依次引入 Markdown 文档加载器、递归文本切分器(将长文本按语义优先级拆分为适合向量化和检索的小块)、HuggingFace Embeddings(将文本编码为高维语义向量)、内存向量存储(用于保存向量并执行相似度检索)、Prompt 模板构建器(用于结构化拼接系统与用户指令),以及 OpenAI 聊天模型封装类(负责最终调用大模型生成回答)。
文本分块的本质目的是把“大段原始文本”切成“语义尽量完整、大小可控、适合向量化与检索的小片段。RecursiveCharacterTextSplitter()(递归分割策略)的思想可以理解为先尝试“语义上自然的切分方式”,如果块仍然过大,再降级到更细粒度的切分方式。
| 分隔符 | 语义层级 | 含义 |
|---|---|---|
| "\n\n" | 段落 | 最完整语义单元 |
| "\n" | 行 | 次级语义单元 |
| " " | 单词 | 最小语言单位 |
| "" | 字符 | 最后兜底 |
它的工作流程类似于
如果按段落切后仍然超过 chunk_size
↓
按行切
↓
还不行
↓
按空格切
↓
再不行
↓
按字符硬切
我们将块大小chunk_size和块重叠chunk_ovelap分别设置为4000和200,前者表示分块的大小,后者表示块B会“继承”块A末尾的 200 字符以防止语义分割。
在数据准备完成之后,构建向量索引,本质上是将已经分割好的文本块映射到一个可计算的语义空间中,并建立支持相似度检索的结构。
通过 HuggingFaceEmbeddings 加载嵌入模型,负责将文本转换为高维向量表示,设置 normalize_embeddings=True 则会对生成的向量做归一化处理,使其长度为 1,从而在后续计算余弦相似度时可以直接使用点积进行高效匹配,并保证数值稳定性。随后,使用 InMemoryVectorStore 初始化一个内存中的向量存储对象,并通过 add_documents(texts) 将分割后的文本块逐条转换为向量并存入索引结构中。
vectorstore.similarity_search(question, k=3),其内部流程是:将问题通过同一个嵌入模型编码为查询向量 vqv_qvq,然后与索引中所有文本向量 viv_ivi 计算相似度(通常是归一化后的点积或余弦相似度),最后选出相似度最高的前 k 个文本块返回。这里的 k=3 表示只取最相关的三个片段,用于后续生成。
检索得到的是若干 Document 对象,每个对象包含 page_content(文本内容)和 metadata(元信息)。随后通过 "\n\n".join(...) 将多个文本块拼接成一个大的上下文字符串。之所以使用双换行符而不是单换行,是为了在语义上形成明显的段落边界,让大模型在理解时能够区分不同来源的片段,从而减少语义混淆。这一步的本质是构造“可控上下文输入”。
接下来进入生成阶段。通过 ChatPromptTemplate.from_template 构建一个结构化提示模板,其中显式规定:回答必须基于提供的上下文,如果信息不足必须拒答。这是一种典型的“约束式提示设计”,目的是降低幻觉风险,强化基于检索内容作答的行为。模板中的 {context} 和 {question} 是占位符,后续会被实际内容替换。
然后初始化 ChatOpenAI 客户端,指定所用模型(例如 glm-4.7-flash-free)、温度参数 temperature=0.7(控制生成随机性,数值越高回答越发散)、最大生成长度 max_tokens=2048,以及 API 密钥和接口地址。这里本质上是配置一个符合 OpenAI 协议的对话模型推理客户端。
最后,通过 llm.invoke(prompt.format(...)) 将问题和检索到的上下文填充进提示模板,形成完整输入,并发送给大模型进行推理。模型根据提示约束和提供的上下文生成答案,返回结果后打印输出。
更多推荐


所有评论(0)