上一课找到了相关片段;这次把片段交给 DeepSeek,让它回答并标注依据。继续使用同一文件夹里的bookstore.txt。
保存为unit3_lesson6_rag_with_sources.py:
import os from pathlib import Path from langchain_core.documents import Document from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.vectorstores import InMemoryVectorStore from langchain_huggingface import HuggingFaceEmbeddings from langchain_openai import ChatOpenAI from langchain_text_splitters import RecursiveCharacterTextSplitter # 1. 读取、切分文档 file_path = Path(__file__).with_name("bookstore.txt") document = Document( page_content=file_path.read_text(encoding="utf-8"), metadata={"source": file_path.name}, ) splitter = RecursiveCharacterTextSplitter( chunk_size=120, chunk_overlap=20, add_start_index=True, separators=["\n\n", "\n", "。", ",", ""], ) chunks = splitter.split_documents([document]) # 2. 建立向量库并检索 embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={"device": "cpu", "local_files_only": True}, encode_kwargs={"normalize_embeddings": True}, ) vector_store = InMemoryVectorStore.from_documents(chunks, embeddings) retriever = vector_store.as_retriever(search_kwargs={"k": 2}) question = "买书后可以退货吗?" found = retriever.invoke(question) context = "\n\n".join( f"[{number}] {doc.page_content}" for number, doc in enumerate(found, start=1) ) # 3. 根据检索到的片段回答 model = ChatOpenAI( model="deepseek-v4-flash", base_url="https://api.deepseek.com", api_key=os.environ["DEEPSEEK_API_KEY"], ) prompt = ChatPromptTemplate.from_messages([ ( "system", "只根据提供的资料回答。每个结论后标注资料编号,如 [1]。" "资料没有说明的事情,回答“资料中没有相关信息”。" ), ("human", "资料:\n{context}\n\n问题:{question}"), ]) chain = prompt | model | StrOutputParser() answer = chain.invoke({"context": context, "question": question}) print("回答:", answer) print("\n检索到的原文片段:") for number, doc in enumerate(found, start=1): print( f"[{number}] {doc.metadata['source']}," f"起始位置 {doc.metadata['start_index']}" ) print(doc.page_content)在已激活虚拟环境、已设置 DeepSeek Key 的 CMD 中运行:
python unit3_lesson6_rag_with_sources.py这次把“模型的回答”和“实际检索到的原文”一起打印出来。检查[1]、[2]是否真的支持回答,是验证 RAG 结果的重要一步。
小练习:把问题改成“书店提供送货吗?”。向量库仍会返回最相近的片段,但资料里没有送货规则;观察模型能否正确回答“资料中没有相关信息”。