恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
30天从零开始学AI应用开发(Day 19):项目二完结:RAG 知识库问答系统,把自己攒的资料变成私人顾问
首页
资讯中心
/
30天从零开始学AI应用开发(Day 19):项目二完结:RAG 知识库问答系统,把自己攒的资料变成私人顾问
30天从零开始学AI应用开发(Day 19):项目二完结:RAG 知识库问答系统,把自己攒的资料变成私人顾问
发布时间:2026/10/7 8:34:34
这是系列的第 19 篇。整个系列写给零基础、想入行 AI 的朋友每天一篇30 天后你会做出 3 个能写进简历的项目。这篇解决什么问题Day 1 的时候我埋了一个包袱说 Day 19 会做 RAG 知识库问答系统让大家在评论区说说想用 AI 解决的具体问题说不定就是我的素材。今天来结账。四天的铺垫到这里收口Day 15 讲清了为什么需要 RAGDay 16 学会把文字变向量Day 17 用 ChromaDB 建库Day 18 解决了切分和检索。今天把它们组装成一个完整系统。和昨天的区别在哪昨天是个验证性的小脚本今天是一个有人愿意用的东西能导入一整批文档、能追问、能标出答案来自哪份文件的哪一页、资料里没有就老实说不知道。这就是你简历上的第二个项目也是当前招聘市场上最吃香的技能方向。一、先看系统长什么样先把结构画清楚写代码时就不会乱。我们分三层数据层文档进来切分向量化存进 ChromaDB检索层用户提问找出相关的片段回答层把片段和问题交给大模型生成带来源的回答对应的文件结构建议这样分别全堆在一个文件里rag-demo/ ├── config.py # 密钥和参数配置 ├── ingest.py # 文档导入读文件、切分、入库 ├── rag.py # 检索和回答的核心逻辑 └── app.py # 跑起来的入口分文件是专业习惯也是简历上的加分点面试官看到目录结构就知道你不是随手写的。二、第一步导入文档先写 config.py把配置集中管理# config.pyAPI_KEY你的密钥# 正式项目放 .env用 os.getenv 读BASE_URLhttps://api.deepseek.comCHAT_MODELdeepseek-chatEMBED_MODELembedding-model-name# 换成你平台的 embedding 模型名DB_PATH./chroma_dbCHUNK_SIZE500CHUNK_OVERLAP50再写 ingest.py负责把文档变成库里的片段# ingest.pyimportosimportchromadbfromopenaiimportOpenAIfromlangchain_text_splittersimportRecursiveCharacterTextSplitterfromconfigimportAPI_KEY,BASE_URL,EMBED_MODEL,DB_PATH oaiOpenAI(api_keyAPI_KEY,base_urlBASE_URL)defread_doc(path):读文档先支持 txt 和 md够用了withopen(path,r,encodingutf-8)asf:returnf.read()defingest_file(path,collection):把一份文档切分入库带来源信息textread_doc(path)filenameos.path.basename(path)# 记下文件名后面要标来源splitterRecursiveCharacterTextSplitter(chunk_size500,chunk_overlap50,separators[\n\n,\n,。,,, ,])chunkssplitter.split_text(text)print(f{filename}切成{len(chunks)}段)fori,chunkinenumerate(chunks):vecoai.embeddings.create(modelEMBED_MODEL,inputchunk).data[0].embedding collection.add(documents[chunk],embeddings[vec],ids[f{filename}_{i}],# 唯一编号metadatas[{source:filename,chunk:i}]# 元数据来源)关键在最后那行 metadatas。它把“这段内容来自哪个文件”一起存进了库后面回答时才能标来源。Day 18 讲切分时提过的“给片段贴身份证”就是用在这里。批量导入一个文件夹if__name____main__:clientchromadb.PersistentClient(pathDB_PATH)collectionclient.get_or_create_collection(nameknowledge)folder./docs# 把你的资料放这个文件夹fornameinos.listdir(folder):ifname.endswith((.txt,.md)):ingest_file(os.path.join(folder,name),collection)print(全部导入完成共,collection.count(),个片段)三、第二步检索加回答核心逻辑写在 rag.py# rag.pyimportchromadbfromopenaiimportOpenAIfromconfigimportAPI_KEY,BASE_URL,CHAT_MODEL,EMBED_MODEL,DB_PATH oaiOpenAI(api_keyAPI_KEY,base_urlBASE_URL)clientchromadb.PersistentClient(pathDB_PATH)collectionclient.get_or_create_collection(nameknowledge)defretrieve(question,n3):检索最相关的 n 个片段返回内容加来源vecoai.embeddings.create(modelEMBED_MODEL,inputquestion).data[0].embedding resultcollection.query(query_embeddings[vec],n_resultsn)hits[]fordoc,metainzip(result[documents][0],result[metadatas][0]):hits.append({text:doc,source:meta[source]})returnhitsdefanswer(question):检索 生成回答hitsretrieve(question)# 把资料拼起来标上编号方便让 AI 引用context\n\n.join(f[{i1}] 来自{h[source]}\n{h[text]}fori,hinenumerate(hits))promptf你是一个严谨的知识库助手。请只根据下面的资料回答问题。 要求 1. 资料里没有提到的内容直接回答资料中没有相关内容不要编造。 2. 回答时在句末标注依据来源格式如 [1]。 3. 回答用中文条理清晰不超过 300 字。 资料{context}问题{question}responseoai.chat.completions.create(modelCHAT_MODEL,messages[{role:user,content:prompt}])returnresponse.choices[0].message.content,hits四、第三步跑起来app.py 做个简单的命令行交互# app.pyfromragimportanswerprint(知识库助手已就绪输入问题开始输入 退出 结束)whileTrue:questioninput(\n你).strip()ifquestion退出:breakifnotquestion:continueresult,hitsanswer(question)print(\nAI,result)# 把用到的资料来源列出来方便用户核对print(\n--- 依据来源 ---)fori,hinenumerate(hits,1):print(f[{i}]{h[source]})运行起来问几个问题试试。重点验证三件事资料里有答案的问题答得准不准资料里没有答案的问题它有没有老实说不知道回答末尾有没有标出来源第三点特别重要。能标出来源的 AI用户才敢信。这是 RAG 相比普通聊天机器人的核心优势面试时也值得强调。五、这个项目强在哪对比一下你的项目一你会发现项目二的价值跨度项目一是“用 AI 处理文件”项目二是“让 AI 掌握你的私有知识”。后者能解决的问题更值钱企业知识库、客服机器人、产品文档助手、法律条文问答技术底座全是它。写在简历上大概是这个形态RAG 知识库问答系统个人项目背景个人积累的文档资料分散检索靠关键词经常搜不到需要语义级问答实现基于 LangChain 文本切分 ChromaDB 向量库 大模型构建 RAG 流程设计 chunk_size 与 overlap 双参数切分策略并通过问题改写提升召回质量增强回答强制标注来源文件提示词层面约束资料外不编造有效抑制大模型幻觉技术栈Python、LangChain、ChromaDB、向量检索、大模型 API明天还有接口化的内容把项目二的完成度再提一档到时候用更完整的话术写进简历。常见报错排查报错一检索出来的片段和问题完全不相关。先检查 embedding 是否用了同一个模型入库和查询必须一致Day 16 讲过的坑。再检查切分是否合理用 Day 18 的自测方法看片段是否完整。报错二AI 明明资料里有答案却说“资料中没有相关内容”。两种情况一是召回没命中加大 n_results 试试二是提示词约束过严AI 太保守了。可以改成“优先根据资料回答资料中确实没有的信息再说明没有”。报错三老是返回同样几条片段。检查 ids 是不是重复了导致重复内容覆盖。ids 里带上文件名和序号就不会出这个问题。报错四导入大文件时很慢。每条片段单独请求 embedding 确实慢。改成批量请求embedding 接口一般支持一次传多条速度能快好几倍。这个优化留给你自己动手做出来可以直接写进简历。报错五中文文件名入库后乱码。确认读取文件时用了 encoding“utf-8”写入时也一样。今天的作业用你自己的真实资料笔记、手册、行业文档都可以跑通整个系统然后做三个测试一个资料里有答案的问题、一个需要综合两段内容的问题、一个资料里完全没有的问题。把三个回答和来源截图贴到评论区。我最想知道的是资料里没有的那个问题它有没有老实交代。明天预告Day 20《用 FastAPI 给你的 RAG 穿上接口别人也能访问了》。现在你的系统只能自己在本机跑明天把它变成一个能被别人调用的服务。做完之后面试官可以打开链接直接问你的知识库这个体验完全不一样。而且 FastAPI 是后端开发的标配学一次受益很久。————————————————*系列目录30天从零开始学AI应用 开发