news 2026/4/18 12:55:01

基于 LangChain 的毕业设计实战:从零构建可扩展的智能问答系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 LangChain 的毕业设计实战:从零构建可扩展的智能问答系统


背景痛点:Demo 级项目的“三宗罪”

去年指导毕设答辩,最常被问到的一句话是:“如果 PDF 换成 10 万篇,你的系统还能跑吗?”
大多数同学的答案都是沉默。归结下来,问题集中在三点:

  1. 提示词写死在代码里,换一道题就要重新部署;
  2. 对话历史放在内存列表,重启即清零;
  3. 异常全靠try/except包一层,网络抖动直接 500。

这样的“玩具”在答辩现场一演示就翻车,更别提后续扩展。
我的毕设目标很明确:做一套能上线、能扩容、能溯源的智能问答系统,而 LangChain 提供了最接近“工业级”的脚手架。

技术选型:为什么不是原生 OpenAI SDK 或 LlamaIndex

维度原生 SDKLlamaIndexLangChain
链式抽象自己拼消息检索链封装更高阶的 LCEL
多模型切换手动改 base_url支持,但文档散一行model="gpt-3.5-turbo"
记忆组件自己维护列表有,但偏检索ConversationBufferWindowMemory即插即用
社区生态官方示例少偏学术工业案例多,Issue 回复快

一句话总结:LlamaIndex 像“研究版”,LangChain 像“工程版”
毕设时间有限,选 LangChain 能把“写论文”的时间省出来“写代码”。

核心实现:模块化 RAG 流程

系统架构图先奉上,方便对照代码阅读:

下面所有代码均放在src/目录,遵循 Clean Code 原则:

  • 一个类只做一件事
  • 函数长度 ≤ 30 行
  • 依赖注入,方便单测

1. 文档加载器:统一接口,支持 PDF / Markdown / Web

# src/loader.py from pathlib import Path from langchain.document_loaders import PyPDFLoader, UnstructuredMarkdownLoader class UniversalLoader: """根据后缀自动选 loader,返回 List[Document]""" def __init__(self, file_path: str): self.path = Path(file_path) def load(self): suffix = self.path.suffix.lower() if suffix == ".pdf": return PyPDFLoader(str(self.path)).load() if suffix in {".md", ".markdown"}: return UnstructuredMarkdownLoader(str(self.path)).load() raise ValueError(f"unsupported suffix: {suffix}")

2. 文本切分:按“章节”语义保留标题

# src/splitter.py from langchain.text_splitter import RecursiveCharacterTextSplitter class SemanticSplitter: def __init__(self, chunk_size=800, chunk_overlap=100): self.splitter = RecursiveCharacterTextSplitter( separators=["\n\n", "\n", "。", ". "], chunk_size=chunk_size, chunk_overlap=chunk_overlap, length_function=len, ) def split(self, docs): return self.splitter.split_documents(docs)

3. 向量存储:Qdrant + 本地磁盘双保险

# src/store.py from qdr import QdrantClient from langchain.vectorstores.qdrit import QdrantVectorStore from langchain.embeddings import OpenAIEmbeddings class VectorStoreFactory: @staticmethod def from_docs(docs, collection: str): client = QdrantClient(path="./qdrant_data") # 本地持久化 store = QdrantVectorStore( client=client, collection_name=collection, embedding=OpenAIEmbeddings(), ) store.add_documents(docs) return store

4. RAG 链:带溯源、带记忆、可并发

# src/chain.py from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferWindowMemory from langchain.chat_models import ChatOpenAI def build_chain(vectorstore): memory = ConversationBufferWindowMemory( memory_key="chat_history", return_messages=True, k=6, # 只保留最近 6 轮,防 token 爆炸 ) llm = ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0) chain = ConversationalRetrievalChain.from_llm( llm=llm, retriever=vectorstore.as_retriever(search_kwargs={"k": 5}), memory=memory, return_source_documents=True, # 溯源关键 verbose=True, ) return chain

5. 并发封装:FastAPI + 异步队列

# src/api.py from fastapi import FastAPI from chain import build_chain import asyncio app = FastAPI() chain = build_chain(...) # 省略初始化 @app.post("/ask") async def ask(question: str): # 使用 run_in_executor 把同步链跑在线程池,防止阻塞主事件循环 loop = asyncio.get_event_loop() ans = await loop.run_in_executor(None, chain, {"question": question}) return {"answer": ans["answer"], "sources": [s.metadata for s in ans["source_documents"]]}

至此,“加载-切分-存储-检索-生成”全链路打通,代码总量不到 300 行,却满足:

  • 换模型只需改配置
  • 换数据只需扔文件到./data
  • 重启服务历史不丢

性能与安全:让答辩老师挑不出刺

  1. 冷启动延迟

    • chain对象在应用启动时初始化,做成单例;
    • 向量库预加载到内存,Qdrant 的memmap模式可省 40% 时间。
  2. Token 消耗控制

    • 记忆窗口k=6+chunk_size=800,实测 90% 问答 < 4k token;
    • 采用gpt-3.5-turbo-16k而非gpt-4,成本降 90%,效果可接受。
  3. 用户输入过滤

    • 先过一遍re黑名单,屏蔽脚本注入;
    • 再用 OpenAI Moderation API 做二次校验,违规直接 403。

生产环境避坑指南

  1. 异步调用陷阱
    LangChain 早期版本很多链没加arun,直接await chain.acall会阻塞。
    解决:用run_in_executor或升级到0.1+的 LCEL 语法,官方已补全异步支持。

  2. 缓存策略

    • 向量库层:Qdrant 自带on_disk索引,重启秒级加载;
    • 大模型层:对高频 FAQ 配置RedisCache,key 用question的向量哈希,TTL 1 h,命中率 35%,节省 20% 预算。
  3. 日志追踪
    采用structlog+trace_id透传,每一行日志都带user_idquestion_hash,方便回滚审计;
    再接入 Grafana Loki,答辩现场可直接展示“本月问答 1.2 万次,异常率 0.3%”,瞬间拉高印象分。

效果评估与扩展思考

指标数值
平均响应1.8 s
检索准确率(Top-1)0.91
生成事实性(人工抽 100 条)0.87
并发 50 请求 95th 延迟4.2 s

如果想再进一步:

  • 多模态:把课件的配图用CLIP做向量,一并扔进 Qdrant,支持“图+文”混合问答;
  • 微调:收集历年答辩问题,用 LoRA 训一个 7B 小模型,替代gpt-3.5,成本再降 70%;
  • 插件化:把loadersplitter做成PlugIn接口,毕设结束后可无缝迁移到实习项目。

结尾:把“能跑”变成“能扛”

毕设不是写 Demo,而是写一份“能扛 1 万用户”的简历。
本文的代码仓库已开源(见文末链接),你可以直接fork后换上自己的数据集,再按“性能与安全”章节调优。
下一步不妨思考:如果给系统加上语音输入,或把检索结果画成知识图谱,你的答辩 PPT 会不会更酷?
动手吧,让 LangChain 成为你毕业设计里的“工程加分项”,而不是“玩具减分项”。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 11:02:34

5个突破性步骤:3D模型跨软件无缝协作让设计师告别格式障碍

5个突破性步骤&#xff1a;3D模型跨软件无缝协作让设计师告别格式障碍 【免费下载链接】import_3dm Blender importer script for Rhinoceros 3D files 项目地址: https://gitcode.com/gh_mirrors/im/import_3dm 问题诊断&#xff1a;跨软件协作的隐形壁垒 作为一名从业…

作者头像 李华
网站建设 2026/4/18 8:41:13

智能分析工具赋能社区互动:用户行为洞察新范式

智能分析工具赋能社区互动&#xff1a;用户行为洞察新范式 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分&#xff0c;支持动态和关注识别以及手动输入 UID 识别 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-comment-checker 在当今UGC内…

作者头像 李华
网站建设 2026/4/18 8:40:42

Nexus Mods App 效率提升指南:从基础操作到高级管理

Nexus Mods App 效率提升指南&#xff1a;从基础操作到高级管理 【免费下载链接】NexusMods.App Home of the development of the Nexus Mods App 项目地址: https://gitcode.com/gh_mirrors/ne/NexusMods.App 基础认知&#xff1a;构建插件管理体系 建立游戏识别机制&…

作者头像 李华
网站建设 2026/4/18 5:40:41

从安装到应用:Qwen3-Embedding-0.6B完整使用路径

从安装到应用&#xff1a;Qwen3-Embedding-0.6B完整使用路径 你是否试过在本地快速跑通一个真正好用的嵌入模型&#xff0c;不依赖API、不卡配额、还能直接集成进自己的RAG系统&#xff1f;Qwen3-Embedding-0.6B就是这样一个“小而强”的选择——它不是玩具模型&#xff0c;而…

作者头像 李华
网站建设 2026/4/18 11:04:38

SiameseUIE中文-base部署教程:支持HTTPS反向代理的企业级接入方案

SiameseUIE中文-base部署教程&#xff1a;支持HTTPS反向代理的企业级接入方案 1. 为什么需要企业级接入方案 你可能已经试过直接运行 python app.py&#xff0c;打开 http://localhost:7860 就能用上 SiameseUIE 中文-base 模型——界面清爽&#xff0c;抽取效果扎实&#xf…

作者头像 李华
网站建设 2026/4/18 8:20:44

OFA-VE惊艳效果展示:OFA-Large在真实场景中的高精度视觉蕴含推理

OFA-VE惊艳效果展示&#xff1a;OFA-Large在真实场景中的高精度视觉蕴含推理 1. 什么是OFA-VE&#xff1a;不只是一个工具&#xff0c;而是一次视觉理解的跃迁 你有没有试过这样一种场景&#xff1a;看到一张照片&#xff0c;心里冒出一个判断——“这图里肯定有只猫在窗台上…

作者头像 李华