🎯 项目目标

用LangChain + 向量数据库搭建一个医学文献智能问答系统,用户输入问题,AI从文献库中检索相关内容并生成专业回答。

核心流程

用户提问 → 向量检索 → 召回相关文献 → LLM生成答案 → 返回结果+引用

🛠️ 技术栈

pip install langchain openai chromadb pypdf sentence-transformers
技术 作用
LangChain RAG框架
ChromaDB 向量数据库
OpenAI API GPT-4生成答案
SentenceTransformers 文本向量化

🚀 完整代码实现

步骤1:文献加载与切分

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os

def load_medical_papers(pdf_folder: str):
    """
    加载医学文献PDF文件
    
    Args:
        pdf_folder: PDF文件夹路径
        
    Returns:
        文档片段列表
    """
    documents = []
    
    # 遍历文件夹中的所有PDF
    for filename in os.listdir(pdf_folder):
        if filename.endswith('.pdf'):
            pdf_path = os.path.join(pdf_folder, filename)
            print(f"📄 加载文献: {filename}")
            
            # 加载PDF
            loader = PyPDFLoader(pdf_path)
            pages = loader.load()
            documents.extend(pages)
    
    print(f"✅ 共加载 {len(documents)} 页文献")
    
    # 切分文档(避免单个片段过长)
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,        # 每个片段1000字符
        chunk_overlap=200,      # 片段间重叠200字符
        length_function=len
    )
    
    splits = text_splitter.split_documents(documents)
    print(f"✅ 切分为 {len(splits)} 个片段")
    
    return splits

步骤2:构建向量数据库

from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma

def create_vector_db(documents, persist_directory="./chroma_db"):
    """
    创建向量数据库
    
    Args:
        documents: 文档片段列表
        persist_directory: 数据库持久化路径
        
    Returns:
        向量数据库对象
    """
    print("🔄 生成文本向量...")
    
    # 使用开源embedding模型(也可用OpenAI Embeddings)
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2"
    )
    
    # 创建向量数据库
    vectordb = Chroma.from_documents(
        documents=documents,
        embedding=embeddings,
        persist_directory=persist_directory
    )
    
    # 持久化到磁盘
    vectordb.persist()
    print(f"✅ 向量数据库已保存至 {persist_directory}")
    
    return vectordb

# 也可以加载已有数据库
def load_vector_db(persist_directory="./chroma_db"):
    """加载已有向量数据库"""
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2"
    )
    
    vectordb = Chroma(
        persist_directory=persist_directory,
        embedding_function=embeddings
    )
    
    return vectordb

步骤3:构建RAG问答链

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

def create_qa_chain(vectordb, openai_api_key: str):
    """
    创建问答链
    
    Args:
        vectordb: 向量数据库
        openai_api_key: OpenAI API密钥
        
    Returns:
        问答链对象
    """
    # 初始化LLM
    llm = ChatOpenAI(
        model_name="gpt-4",
        temperature=0.2,  # 降低随机性,提高准确度
        openai_api_key=openai_api_key
    )
    
    # 自定义Prompt模板
    prompt_template = """你是一位专业的医学AI助手。请基于以下医学文献内容回答问题。

文献内容:
{context}

问题:{question}

要求:
1. 回答必须基于提供的文献内容
2. 如果文献中没有相关信息,明确说明
3. 提供具体的引用来源
4. 使用专业但易懂的语言

回答:"""

    PROMPT = PromptTemplate(
        template=prompt_template,
        input_variables=["context", "question"]
    )
    
    # 创建检索器(返回top-4相关文档)
    retriever = vectordb.as_retriever(
        search_type="similarity",
        search_kwargs={"k": 4}
    )
    
    # 构建RAG链
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True,  # 返回引用文档
        chain_type_kwargs={"prompt": PROMPT}
    )
    
    return qa_chain

def ask_question(qa_chain, question: str):
    """
    提问并获取答案
    
    Args:
        qa_chain: 问答链
        question: 用户问题
    """
    print(f"\n❓ 问题: {question}")
    print("🔍 检索相关文献...")
    
    # 执行问答
    result = qa_chain({"query": question})
    
    # 输出答案
    print(f"\n💡 答案:\n{result['result']}")
    
    # 输出引用来源
    print(f"\n📚 参考文献:")
    for i, doc in enumerate(result['source_documents'], 1):
        source = doc.metadata.get('source', 'Unknown')
        page = doc.metadata.get('page', 'N/A')
        print(f"{i}. {source} (第{page}页)")
        print(f"   内容摘要: {doc.page_content[:100]}...\n")

步骤4:完整使用示例

def main():
    """主函数:完整工作流"""
    
    # 配置OpenAI API密钥
    OPENAI_API_KEY = "sk-your-openai-api-key"
    
    # === 第一次运行:构建向量数据库 ===
    print("=== 步骤1: 加载文献 ===")
    documents = load_medical_papers("./medical_papers")
    
    print("\n=== 步骤2: 构建向量数据库 ===")
    vectordb = create_vector_db(documents)
    
    # === 后续运行:直接加载数据库 ===
    # vectordb = load_vector_db()
    
    # === 步骤3: 创建问答系统 ===
    print("\n=== 步骤3: 初始化问答系统 ===")
    qa_chain = create_qa_chain(vectordb, OPENAI_API_KEY)
    
    # === 步骤4: 开始提问 ===
    print("\n=== 步骤4: 医学文献问答 ===")
    
    # 示例问题1
    ask_question(qa_chain, "糖尿病的主要治疗方法有哪些?")
    
    # 示例问题2
    ask_question(qa_chain, "二甲双胍的作用机制是什么?")
    
    # 示例问题3
    ask_question(qa_chain, "1型糖尿病和2型糖尿病的区别?")

if __name__ == "__main__":
    main()

📊 运行效果

$ python medical_rag.py

=== 步骤1: 加载文献 ===
📄 加载文献: diabetes_treatment.pdf
📄 加载文献: metformin_mechanism.pdf
✅ 共加载 45 页文献
✅ 切分为 128 个片段

=== 步骤2: 构建向量数据库 ===
🔄 生成文本向量...
✅ 向量数据库已保存至 ./chroma_db

=== 步骤3: 初始化问答系统 ===

=== 步骤4: 医学文献问答 ===

❓ 问题: 糖尿病的主要治疗方法有哪些?
🔍 检索相关文献...

💡 答案:
根据文献内容,糖尿病的主要治疗方法包括:

1. **药物治疗**:
   - 二甲双胍作为一线用药
   - 胰岛素治疗(适用于1型和部分2型患者)
   - SGLT-2抑制剂
   
2. **生活方式干预**:
   - 饮食控制:低糖低脂饮食
   - 规律运动:每周150分钟中等强度运动
   
3. **血糖监测**:定期监测血糖水平,调整治疗方案

📚 参考文献:
1. diabetes_treatment.pdf (第3页)
   内容摘要: Treatment of diabetes includes pharmacological and lifestyle interventions...

2. diabetes_treatment.pdf (第12页)
   内容摘要: Metformin is recommended as first-line therapy...

🔧 进阶优化

1. 混合检索(提升准确度)

from langchain.retrievers import EnsembleRetriever
from langchain.retrievers import BM25Retriever

# 结合向量检索和关键词检索
vector_retriever = vectordb.as_retriever(search_kwargs={"k": 4})
bm25_retriever = BM25Retriever.from_documents(documents)

ensemble_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.7, 0.3]  # 向量检索权重更高
)

2. 添加对话记忆

from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain

memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

qa_chain = ConversationalRetrievalChain.from_llm(
    llm=llm,
    retriever=retriever,
    memory=memory
)

3. 评估检索质量

def evaluate_retrieval(vectordb, test_questions):
    """评估检索准确率"""
    retriever = vectordb.as_retriever(search_kwargs={"k": 4})
    
    for q in test_questions:
        docs = retriever.get_relevant_documents(q)
        print(f"问题: {q}")
        print(f"召回文档数: {len(docs)}")
        print(f"相关性得分: {docs[0].metadata.get('score', 'N/A')}\n")

💡 实际应用场景

场景1:科研辅助
研究人员快速查询文献中的关键信息

场景2:临床决策支持
医生查询疾病治疗方案和药物信息

场景3:医学教育
学生通过问答学习医学知识

🆚 与现有工具对比

如果你需要更专业的医学文献问答,可以参考 suppr超能文献 的深度研究功能:

  • ✅ 已接入海量医学文献库
  • ✅ 针对医学术语优化的检索
  • ✅ 25分钟生成综述报告

当然,自建RAG系统的优势是完全可控可定制,适合有特定需求的研发团队。

📚 完整代码

GitHub: medical-rag-langchain
包含完整代码、测试数据和部署文档

📝 总结

  • 核心技术: LangChain + 向量数据库 + LLM
  • 关键步骤: 文档加载 → 向量化 → 检索 → 生成答案
  • 优化方向: 混合检索、对话记忆、准确性评估
  • 适用场景: 私有知识库问答、领域专家系统

从零搭建只需30分钟,代码可直接运行!

更多推荐