
简介本资源是一套面向高校计算机及相关专业学生的毕业设计/课程设计实战项目聚焦大语言模型与RAG检索增强生成技术落地以LangChain框架为核心构建智能情感识别与知识检索应用。项目覆盖从环境配置、数据预处理含多份CSV情感标注语料、LangChain链式调用开发多个Python主程序及模块化脚本、Prompt工程优化到可视化模型结构drawio图、PDF与JPG图示的完整流程配套设计文档、README说明及详细Markdown技术解析。压缩包共27个文件含7个核心Python脚本、10个CSV数据集、2个Markdown文档、2个PNG/JPG图表、1个PDF模型图、1个DOC设计文档等整体6.12MB结构清晰、模块解耦便于学习复现或二次开发。已有147人下载学习提供稳定可运行代码、完整测试数据与远程技术支持特别适合AI方向初学者系统入门也适合作为毕设选题参考或教学演示案例。1. 毕业设计选 LangChain RAG 不是堆概念而是用最小闭环验证「知识可检索、回答可溯源、逻辑可调试」的三重能力很多同学把毕业设计做成“调 API 出结果”的演示幻灯片上传 PDF → 输入问题 → 返回一段文字。但答辩时被问“为什么召回了这篇文档”“重排序依据是什么”“LLM 是怎么把 chunk 拼成答案的”立刻卡壳。本项目标题里的「LangChainRAG应用示例开发」核心不在“用了什么框架”而在构建一个可拆解、可干预、可验证的 RAG 流水线——从原始文本切片、嵌入向量化、多路召回关键词向量元数据、重排序cross-encoder rerank到 LLM 提示工程与引用标注每一步都有明确输入输出、可替换模块、可观测日志。它适合两类人一是需要在 46 周内交付可运行、可讲解、可扩展的毕设系统的学生二是想跳过“Hello World”式教程直接上手调试真实 RAG 痛点如召回漂移、幻觉抑制、上下文截断的初阶开发者。源码不是黑盒打包而是按ingest/retrieval/generation/eval/四个目录组织每个.py文件对应一个可独立测试的组件。2. 用 LangChain 搭建 RAG 最小可行流水线从文档加载到向量存储的 5 步实操RAG 的根基不在大模型而在数据管道的确定性。LangChain 提供了模块化组装能力但直接套用VectorstoreIndexCreator会掩盖关键决策点。我们采用显式分步法确保每步可查、可调、可替换。2.1 文档加载与结构化切片避免“一刀切”导致语义断裂PDF、Word、Markdown 等格式需差异化处理。本项目使用UnstructuredPDFLoader需pip install unstructured[all-docs]保留标题层级而非简单按字符数切分from langchain_community.document_loaders import UnstructuredPDFLoader from langchain_text_splitters import MarkdownHeaderTextSplitter loader UnstructuredPDFLoader( file_pathdata/thesis_guidelines.pdf, modeelements, # 保留段落、标题、表格等结构信息 strategyfast # 平衡速度与精度对毕业设计文档足够 ) docs loader.load() # 按 Markdown 标题层级切片比固定 token 切分更符合语义 headers_to_split_on [ (#, Header 1), (##, Header 2), (###, Header 3), ] splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) splits splitter.split_text(docs[0].page_content) # 注意此处传入单个 Document 的 content提示modeelements会将 PDF 解析为带category如Title,NarrativeText,Table的元素列表MarkdownHeaderTextSplitter能识别这些结构并按标题嵌套关系切分。若用RecursiveCharacterTextSplitter默认按\n\n、\n、 、四级切分易在公式、代码块中间断开。2.2 嵌入模型选型与本地部署避开 OpenAI 依赖用 sentence-transformers 实现离线向量化毕业设计常受限于网络环境与 API 配额。本项目默认使用all-MiniLM-L6-v2384维CPU 推理 100ms/文档通过HuggingFaceEmbeddings封装from langchain_huggingface import HuggingFaceEmbeddings embeddings HuggingFaceEmbeddings( model_namesentence-transformers/all-MiniLM-L6-v2, model_kwargs{device: cpu}, # 显存不足时强制 CPU encode_kwargs{normalize_embeddings: True} # 向量单位化提升余弦相似度计算稳定性 )参数说明normalize_embeddingsTrue是关键。LangChain 默认不归一化而 FAISS 等向量库的余弦相似度计算要求向量模长为 1若未归一化高维空间下欧氏距离与余弦距离偏差显著导致召回结果不可靠。此参数必须显式设置。2.3 向量存储选型对比FAISS 适合单机调试Chroma 支持元数据过滤FAISS 是 Facebook 开源的高效近似最近邻库零依赖、纯 Python 加载适合毕业设计本地调试from langchain_community.vectorstores import FAISS vectorstore FAISS.from_documents( documentssplits, embeddingembeddings, # 可选添加元数据用于后续过滤 metadatas[{source: thesis_guidelines.pdf, page: 5}] * len(splits) ) vectorstore.save_local(faiss_index) # 保存为本地文件下次直接加载若需支持按source、date等字段过滤如“只检索 2023 年后的政策文件”则切换为 Chromafrom langchain_community.vectorstores import Chroma vectorstore Chroma.from_documents( documentssplits, embeddingembeddings, persist_directorychroma_db, # 自动持久化 collection_namethesis_docs ) # 查询时可加 metadata filter retriever vectorstore.as_retriever( search_kwargs{filter: {source: thesis_guidelines.pdf}} )2.4 检索器配置启用多路召回Multi-Query HyDE提升泛化能力基础向量检索易受 query 表达偏差影响。本项目集成两种增强策略Multi-Query让 LLM 生成 3 个变体 query分别检索后合并结果HyDEHypothetical Document Embeddings先让 LLM 生成“假设答案”再对该答案向量化检索from langchain.retrievers.multi_query import MultiQueryRetriever from langchain.chains import create_history_aware_retriever from langchain_core.prompts import ChatPromptTemplate # 使用本地 LLM如 Ollama 的 llama3生成多 query llm ChatOllama(modelllama3, temperature0) multi_retriever MultiQueryRetriever.from_llm( retrievervectorstore.as_retriever(), llmllm, promptChatPromptTemplate.from_messages([ (system, 你是一个学术助手请基于用户问题生成 3 个不同角度的搜索 query用换行符分隔。), (human, {question}) ]) )注意MultiQueryRetriever默认返回去重后的 top-k 文档但未做重排序。实际使用中需配合reranker见 3.3 节。2.5 构建可追溯的 RetrievalQA 链让 LLM 引用来源而非自由发挥避免“幻觉式回答”关键在提示词约束与输出解析from langchain.chains import create_retrieval_chain from langchain.chains.combine_documents import create_stuff_documents_chain from langchain_core.prompts import ChatPromptTemplate # 系统提示强制引用 system_prompt ( 你是一个严谨的毕业设计指导助手。请严格基于以下检索到的文档片段回答问题。 每个答案必须标注来源【{source} P{page}】。 若文档中无相关信息回答根据提供的资料无法确定。 \n\n{context} ) prompt ChatPromptTemplate.from_messages([ (system, system_prompt), (human, {input}), ]) document_chain create_stuff_documents_chain(llm, prompt) retrieval_chain create_retrieval_chain( retrievermulti_retriever, combine_docs_chaindocument_chain ) # 执行查询 response retrieval_chain.invoke({input: 毕业论文查重率要求是多少}) print(response[answer]) # 输出含【thesis_guidelines.pdf P3】的引用答案关键点create_retrieval_chain将retriever与document_chain绑定response中context字段即为被选中的 Document 列表answer字段由 LLM 生成。通过解析response[context]可验证召回质量这是答辩时展示“可解释性”的核心证据。3. RAG 流程详解从数据注入到答案生成的 7 个关键节点与调试方法一个完整 RAG 流程不是线性执行而是包含数据预处理、检索、重排序、生成、评估五个阶段。本节以ingest.py→retrieval.py→generation.py→eval.py四个脚本为线索逐节点说明输入输出、常见失败现象及定位命令。3.1 数据注入阶段ingest.py验证切片质量与向量一致性该阶段输出faiss_index/目录。调试重点是确认切片是否保留关键信息# 查看切片数量与平均长度 python -c from langchain_community.vectorstores import FAISS v FAISS.load_local(faiss_index, embeddingsNone) print(f总切片数: {len(v.docstore._dict)}) print(f平均长度: {sum(len(d.page_content) for d in v.docstore._dict.values()) / len(v.docstore._dict):.0f} 字符) 典型问题PDF 表格被转为乱码、公式丢失、页眉页脚混入正文。解决方案UnstructuredPDFLoader的strategyhi_res需安装unstructured[pdf]和pdfminer.six可提升表格识别但速度下降 3 倍或预处理 PDF 用pdfplumber提取文本表格分离存储。3.2 检索阶段retrieval.py用 similarity_score_threshold 过滤低置信召回默认as_retriever()返回 top-k 文档但可能包含相似度仅 0.2 的噪声。显式设置阈值retriever vectorstore.as_retriever( search_typesimilarity_score_threshold, search_kwargs{ k: 5, score_threshold: 0.5 # 余弦相似度 0.5 才返回 } )调试命令直接调用vectorstore.similarity_search_with_score()查看原始分数docs_and_scores vectorstore.similarity_search_with_score(查重率标准, k5) for doc, score in docs_and_scores: print(fScore: {score:.3f} | {doc.page_content[:50]}...)若最高分 0.4说明嵌入模型或 query 表达需优化。3.3 重排序阶段rerank.py用 cross-encoder 替代 BM25 提升相关性向量检索后用sentence-transformers/ms-marco-MiniLM-L-6-v2对 query-doc pair 进行精排from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_huggingface import HuggingFaceCrossEncoder compressor CrossEncoderReranker( modelHuggingFaceCrossEncoder(model_namecross-encoder/ms-marco-MiniLM-L-6-v2), top_n3 ) compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverretriever )参数说明top_n3表示从向量检索返回的 5 个文档中选出最相关的 3 个送入 LLM。ms-marco-MiniLM-L-6-v2是专为重排序微调的模型在 TREC-DL 数据集上 MRR10 达 0.32显著优于 BM25 规则。3.4 生成阶段generation.py控制 token 截断与引用锚点LLM 上下文窗口有限需精确控制context长度。本项目用TokenTextSplitter动态截断from langchain.text_splitter import TokenTextSplitter def truncate_context(context_docs, max_tokens2000): 按 token 数截断 context保留完整 Document splitter TokenTextSplitter(chunk_sizemax_tokens, chunk_overlap0) full_text \n\n.join([d.page_content for d in context_docs]) truncated splitter.split_text(full_text)[0] # 取第一个 chunk return [Document(page_contenttruncated, metadatacontext_docs[0].metadata)] # 在 prompt 中插入截断后 context prompt ChatPromptTemplate.from_messages([ (system, 基于以下资料回答引用格式【{source}】\n{context}), (human, {input}) ])注意TokenTextSplitter比字符切分更准确其chunk_size单位为 token经 tiktoken 计算。若用llama3模型1 个中文字符 ≈ 1.3 token故max_tokens2000约容纳 1500 字中文。3.5 评估阶段eval.py用 answer relevancy 和 faithfulness 指标量化效果毕业设计需量化指标。本项目集成langchain-community的评估链from langchain.evaluation import load_evaluator # 评估答案相关性Answer Relevancy relevancy_evaluator load_evaluator( labeled_score_string, criteria{answer_relevancy: 答案是否直接回应问题1-5 分} ) # 评估忠实度Faithfulness答案是否被 context 支持 faithfulness_evaluator load_evaluator( labeled_score_string, criteria{faithfulness: 答案中每个事实是否能在 context 中找到依据1-5 分} ) # 示例评估 result faithfulness_evaluator.evaluate( predictionsresponse[answer], input毕业论文查重率要求是多少, reference_contexts[doc.page_content for doc in response[context]] ) print(fFaithfulness Score: {result[score]})指标含义faithfulness低于 3 分说明 LLM 编造内容answer_relevancy低于 3 分说明 context 未覆盖问题核心。这两个指标比 BLEU、ROUGE 更贴合 RAG 场景。3.6 元数据过滤实战按文档类型/时间/章节精准召回Chroma 支持复杂过滤例如“只检索‘格式规范’章节中的内容”# 在 ingest 阶段为每个 Document 添加结构化元数据 for doc in splits: if 格式规范 in doc.metadata.get(header, ): doc.metadata[section] format # 检索时过滤 retriever vectorstore.as_retriever( search_kwargs{filter: {section: format}} )调试技巧用vectorstore.get()查看某 Document 的完整元数据doc vectorstore.docstore._dict[list(vectorstore.docstore._dict.keys())[0]] print(doc.metadata) # 输出 {source: thesis_guidelines.pdf, page: 5, section: format}3.7 故障排查速查表5 类高频报错与修复命令报错现象根本原因修复命令ValueError: max() arg is an empty sequence向量库为空ingest.py未成功执行ls faiss_index/确认存在index.faissindex.pkltorch.cuda.OutOfMemoryErrorembedding 模型加载到 GPU 但显存不足在HuggingFaceEmbeddings中添加model_kwargs{device: cpu}KeyError: sourceprompt 中引用{source}但 Document 无该 metadataprint(docs[0].metadata)检查字段名或统一设doc.metadata[source] xxxContext length exceededLLM 输入超限在create_stuff_documents_chain前调用truncate_context()No module named langchain_huggingfaceLangChain 0.1 版本拆分包pip install langchain-huggingface4. RAG 多路召回与 Agent 开发进阶用 LangGraph 实现动态路由与人工审核回路当毕业设计需体现“智能性”而非“自动化”应引入决策逻辑。LangGraph 作为 LangChain 的有状态图框架可构建带人工审核节点的 RAG 工作流这比单纯调用AgentExecutor更可控。4.1 构建带审核节点的 RAG 图定义 state 与 conditional edge核心是定义State数据结构包含question、context、answer、needs_review四个字段from typing import TypedDict, Annotated, Sequence import operator from langgraph.graph import StateGraph, END class RAGState(TypedDict): question: str context: Sequence[str] answer: str needs_review: bool # 是否需人工审核 def retrieve_node(state: RAGState) - RAGState: docs retriever.invoke(state[question]) return { question: state[question], context: [d.page_content for d in docs], answer: , needs_review: False } def generate_node(state: RAGState) - RAGState: # 调用 LLM 生成答案 answer llm.invoke(f基于以下资料回答{state[context]}\n问题{state[question]}) # 规则若答案含“可能”、“大概”、“建议咨询”等模糊词标记审核 needs_review any(word in answer.content for word in [可能, 大概, 不确定, 建议]) return { question: state[question], context: state[context], answer: answer.content, needs_review: needs_review } def human_review_node(state: RAGState) - RAGState: print(f【人工审核】问题{state[question]}\n答案{state[answer]}) user_input input(是否通过(y/n): ) return {needs_review: False if user_input.lower() y else True} # 定义图 workflow StateGraph(RAGState) workflow.add_node(retrieve, retrieve_node) workflow.add_node(generate, generate_node) workflow.add_node(review, human_review_node) workflow.set_entry_point(retrieve) workflow.add_edge(retrieve, generate) # 条件边根据 needs_review 决定是否进入 review def should_review(state: RAGState) - str: return review if state[needs_review] else END workflow.add_conditional_edges(generate, should_review) workflow.add_edge(review, END)优势说明此图将“是否审核”决策外置答辩时可演示输入“答辩PPT模板在哪里下载”因答案含“建议访问教务处网站”触发needs_reviewTrue暂停等待人工确认而输入“查重率上限是多少”直接输出带引用的答案。这体现了对不确定性边界的认知远超静态 RAG。4.2 集成外部工具用 requests 调用校内教务系统 API 补充实时数据RAG 的知识库是静态的但毕业流程涉及动态信息如查重系统维护时间。LangGraph 支持工具调用import requests def fetch_maintain_time() - str: 调用校内 API 获取查重系统维护时间 try: resp requests.get(https://jwxt.xxx.edu.cn/api/maintenance, timeout5) return resp.json().get(next_maintenance, 暂无维护计划) except: return 网络连接失败 # 在 generate_node 中调用 def generate_node(state: RAGState) - RAGState: # ... 先用 RAG 生成答案 if 查重系统 in state[question]: maintain_info fetch_maintain_time() answer f\n【实时信息】{maintain_info} # ...安全实践生产环境需添加requests.Session()复用连接、retry重试机制并将 API 地址存于.env文件避免硬编码。4.3 可视化执行轨迹用langgraph.checkpoint.sqlite记录每步 state答辩时需展示“系统如何思考”。启用 SQLite 检查点from langgraph.checkpoint.sqlite import SqliteSaver checkpointer SqliteSaver.from_uri(checkpoints.db) app workflow.compile(checkpointercheckpointer) # 执行后查询历史 import sqlite3 conn sqlite3.connect(checkpoints.db) cursor conn.cursor() cursor.execute(SELECT * FROM checkpoints ORDER BY thread_ts DESC LIMIT 5) for row in cursor.fetchall(): print(fStep: {row[2]}, State: {row[3][:100]}...) # row[2] 是 step, row[3] 是 state json输出示例Step: 2, State: {question: 查重率要求, context: [根据《本科生毕业论文管理规定》第5条..., 附件1查重率阈值表...], answer: 本科毕业论文查重率不得高于15%。, needs_review: false}—— 这就是可写进毕设论文“系统实现”章节的原始日志。5. 毕业设计落地技巧3 个让答辩老师眼前一亮的细节处理答辩不是功能演示而是展现工程思维。以下三个细节无需额外代码但能极大提升专业感。5.1 在 UI 层显示召回依据用 HTML 渲染带高亮的 source 文本Streamlit 前端中不只显示答案还展开被引用的原文片段import streamlit as st def render_source(doc, query): # 用正则高亮 query 在 doc 中的匹配位置 highlighted re.sub(f({re.escape(query)}), rmark\1/mark, doc.page_content[:200]) return fdiv styleborder-left: 3px solid #4CAF50; padding-left: 10px; margin: 10px 0; smallb来源/b{doc.metadata.get(source, 未知)} P{doc.metadata.get(page, ?)}/smallbr {highlighted}... /div # 在 st.write() 中渲染 for i, doc in enumerate(response[context]): st.markdown(render_source(doc, user_question), unsafe_allow_htmlTrue)效果老师能看到“答案来自哪一页、哪一段”且关键词自动高亮。这比单纯打印doc.metadata更直观证明你理解了 RAG 的“可追溯”本质。5.2 用 requirements.txt 锁定版本避免答辩现场环境不一致requirements.txt必须精确到 patch 版本而非langchain0.1.0langchain0.1.16 langchain-huggingface0.0.1 sentence-transformers2.3.0 faiss-cpu1.9.0 pymupdf1.23.22 unstructured0.10.22验证命令答辩前在干净虚拟环境中测试python -m venv env source env/bin/activate pip install -r requirements.txt python main.py若报错立即修正——这是答辩时最不该出现的失误。5.3 设计可复现的测试用例覆盖边界场景在tests/目录下提供 3 个.md测试用例每个含question、expected_source、expected_answer_fragment!-- tests/test_highlight.md -- ## 测试用例查重率阈值 - **Question**: 本科毕业论文查重率上限是多少 - **Expected Source**: thesis_guidelines.pdf P3 - **Expected Answer Fragment**: 不得超过15%执行脚本test_runner.py自动遍历测试用例调用 RAG 链比对response[answer]是否含expected_answer_fragment并检查response[context][0].metadata[source]是否匹配。答辩时可当场运行python test_runner.py证明系统鲁棒性。最后打开main.py确认if __name__ __main__:下的入口函数调用顺序为ingest()→retrieval()→generation()→eval()且每个函数有清晰的print(✅ Ingest completed)日志。这便是毕业设计最扎实的收尾——没有炫技只有可验证、可讲解、可复现的工程闭环。本文还有配套的精品资源点击获取