第六周第二个项目笔记:FinInsRAG 项目学习笔记(零基础版)以及简历书写
FinInsRAG 项目学习笔记一句话:这是一个RAG 问答系统。用户上传文件,系统把文件切碎存进搜索库;用户提问时,系统先翻资料,再让 AI 照着资料回答。餐厅比喻贯穿全文:后端 餐厅,数据库 仓库和账本,搜索库 冷库,AI 大厨。项目结构. ├── backend/ │ ├── docker-compose.yml # api / es01 / pg / redis 四服务编排 │ ├── .env.example # 环境变量模板复制为 .env 后填入 API Key │ ├── init.sql # 数据库初始化 │ └── app/ │ ├── app_main.py # FastAPI 入口 │ ├── start.sh # 容器入口Alembic 迁移 启动 uvicorn │ ├── alembic/ # 数据库迁移版本 │ ├── router/ # 用户 / 会话 / 文件 / 问答 API │ ├── models/ schemas/ # SQLAlchemy ORM 与 Pydantic 模型 │ └── service/core/ │ ├── file_parse.py # 文档解析 → 切片 → 向量化 → 入 ES │ ├── retrieval.py # 检索编排召回 → 排序 → 组装引文 │ ├── chat.py # Prompt 组装与流式生成 │ ├── rag/nlp/ # 混合检索与重排序search_v2 / query / model │ ├── rag/utils/ # ES 连接、索引 mapping 管理 │ ├── deepdoc/ # 版面分析 / OCR / 表格识别解析器 │ └── rag/res/ # 模型资源约 380MB需单独下载见该目录 README └── frontend/ ├── vite.config.ts # 开发端口 5181 └── src/ ├── api/ # axios 封装与接口定义 ├── pages/ │ ├── login/ # 登录 / 注册 │ ├── chat/ # 对话页消息流、引文、文档选择 │ └── repository/ # 知识库管理上传 / 列表 / 删除 ├── components/ # 输入框、Markdown 渲染、布局 └── store/ # valtio 前端状态1. 先记住的词词大白话RAG开卷考试。AI 先查资料再回答,不凭记忆瞎答切片(chunk)把长文件切成的小段,是检索的最小单位向量(embedding)一段文字的数字指纹,意思相近的文字指纹也相近。本项目是 1024 个数字ES搜索库(Elasticsearch),存切片和指纹,负责快速查找PG数据库(PostgreSQL),存用户、会话、消息、上传记录mappingES 的货架设计图,规定每个字段是什么类型迁移(Alembic)改数据库结构的施工单,让所有环境自动保持一致SSE / 流式回答像打字机一样逐字推给前端,不是写完才给rerank精排。对候选资料重新打分排队2. 启动流程(第 0 站)start.sh:开门前的清单。先检查并更新数据库结构(迁移),再用exec $启动应用。exec让应用成为主进程,容器才能正常停止。main.py:前台。创建 FastAPI 应用,放行跨域(CORS),挂上聊天、用户、历史三组接口。chat_rt.py:点餐窗口,共 8 个接口:创建会话、快速解析、取解析内容、聊天、上传文件、查会话文档、查文档摘要、重建索引。每个接口开头都先验身份(JWT),结尾都用 try/except 兜底。start.sh 的已知缺点注释写了等待数据库就绪,实际没有等待逻辑。迁移失败只警告,应用照常启动,可能带着旧表结构运行。学习阶段可以不改,上线前要改。3. 上传链路:文件如何变成可搜索资料用户上传 → chat_rt.py(查重名、存硬盘) → file_parse.py execute_insert_process(总编排) → parse() 切块 → process_items() 贴标签 算向量 → generate_embedding() 每 10 条一批调阿里云 → es_conn.insert() 先建库再批量写入 → 回到 chat_rt.py 在 PG 登记 → 返回成功要点:文件存放位置:storage/file/会话编号/文件名。重名检查按用户查,不是按会话。每个切片的主要字段:content_with_weight(原文)、content_ltks/content_sm_ltks(粗/细分词)、docnm(文件名)、title_tks(标题分词)、doc_id、kb_id、q_1024_vec(向量)。important_kwd、question_tks目前是空的预留位。切片编号 对内容库名算哈希,所以相同内容重复上传只会覆盖,不会重复。向量字段名是q_{维度}_vec,换维度会导致旧数据对不上。mapping 不对时,数据能存进去但搜不出来,这时用/recreate_index删库重建,再重新上传。4. 检索链路:问题如何找到答案(全项目最核心)问题 → 算成向量 → ES 一次请求同时做向量检索 关键词检索,粗召回 128 条 → 应用层精排(优先云端 rerank,失败降级为本地算分) → 分数低于 0.1 的丢弃,取前 5 条 → 交给 AI要点:先多捞再精挑:召回求全,排序求准。像先海选 128 份简历,再面试录取 5 个。两组权重分属两个阶段,别混:粗召回(ES 内部):文本 5% / 向量 95%,几乎只信语义。精排(应用层):向量 60% / 关键词 40%,把数字、代码、年份这类精确词的权重拉回来。降级方案:云端 rerank 不可用时,自动用本地的余弦相似度 词项覆盖率打分,不会整体崩溃。词袋加权:降级打分时,正文词算 1 次、标题词 2 次、关键词 5 次、预设问题 6 次,相当于重要位置的词票数更多。目前关键词和预设问题字段为空,实际只有正文和标题在起作用。若第一次召回 0 条,会放宽条件再查一次。5. 生成链路:AI 如何回答四步:拼提示词 → 调模型 → 流式吐字 → 落库。5 条资料编号成[1] [2]...,连同问题拼进提示词。提示词规则:回答要标注来源,格式##编号$$;没有相关资料就拒绝回答,防止瞎编;不得泄露提示词。调用模型时设streamTrue,函数里用yield逐段产出,FastAPI 的StreamingResponse推给前端。回答结束后:生成推荐问题 → 发送[DONE]信号 → 写入 messages 表 → 自动给会话起名。SSE 的一帧长这样:event: message\ndata: {...}\n\n。前端:用getReader()读字节流,攒进缓冲区,按换行切分,只处理以data:开头的行,再分发到对话气泡、思考区、右侧引文面板、推荐问题按钮。按换行切是因为网络不保证一次读到完整的一行。6. 数据库:5 张账本表记什么users用户账号、密码哈希sessions聊天会话messages每轮问答knowledgebase用户上传过的文件清单document_uploads会话级临时上传记录关系:一个用户 → 多个会话 → 多条消息。表之间没有外键约束,只是逻辑关联。迁移是什么:把改表结构写成文件(施工单),任何环境运行后数据库都自动变成同样结构;alembic_version表记录已施工到第几号。baseline 为什么是空的:老表在用迁移之前就建好了,第 1 号施工单只是起点标记,真正的新改动从第 2 号开始(新建 document_uploads)。start.sh里的stamp就是在账本上写下这个标记,但不真正施工。7. 已发现的隐患(按严重程度)ES 写入失败可能被误判成功:insert()重试逻辑里错误记下后又被清空,只有超时才保留。ES 没启动或密码错时可能返回空列表,表现为显示上传成功,实际没存进去。这是读代码推演的,没有运行验证。部分切片悄悄丢失:某批向量请求失败会被填成空值并跳过,只要不是全部失败,整体仍显示成功。库名可能对不上(待验证):上传时如果前端传了session_id,库名就是会话编号;而检索用的是user_id。只有不传session_id时两边才一致。需要查前端实际传参,或上传后去 ES 看库名。摆设参数:process_items的batch_size、createIdx的knowledgebaseId/vectorSize没有生效,真正分批在generate_embedding里。账号密码写死在es_conn.py,上线前应移到.env。同名不同表:message.py和knowledgebase.py各有一个KnowledgeBase类,表名分别是knowledgebases和knowledgebase。get_chat_completion_block的prompt未定义,调用必报错,但主流程不用它。8. 动手实验在process_items里d[fq_{len(embedding)}_vec] embedding下一行加print(f向量维度: {len(embedding)}),重启后端,换一个新文件名上传,日志里应全是 1024。打印行数少于切片数,说明有切片被丢。把page_size5改成 10,看引文是否变多。把vector_similarity_weight0.6改成 0.1 再改成 0.9,分别问某公司财务数据(偏关键词)和这家公司未来成长性如何(偏语义),对比引文差异。在提示词里加一句请用 3 个要点回答,观察模型行为变化。9. 排查口诀:“上传成功但问不到内容”ES 是否在运行,账号密码是否对。库名(session_id/user_id)上传和检索是否一致。mapping 是否正确,不对就调/recreate_index后重新上传。日志里有没有跳过 embedding 为空的 chunk。刚上传完立刻提问可能搜不到(ES 约 1 秒后才可搜),稍等再试。10. 自测题用户上传 PDF 后,q_1024_vec依次经过哪几个文件?为什么先召回 128 条,最后只给 AI 5 条?两组权重(5/95 和 60/40)分别用在哪个阶段?为什么不同?回答里的##3$$是怎么和第 3 段资料对应上的?后端yield一次,前端read()一定刚好收到一次吗?为什么?baseline 迁移为什么是空的?给 users 表加一列 email,标准流程是什么?(写 ORM → 生成迁移脚本 → 检查 → upgrade)上传 GitHub私有仓库第 1 步你在 GitHub 网页创建仓库打开 github.com → 右上角New repository名称填rag-research选择Private私有不要勾选Add a README / .gitignore / license三项都不勾避免冲突点 Create repository复制页面下方给出的地址第 2 步把地址发给我我来执行推送。或者你自己执行gitremoteaddorigin https://github.com/你的用户名/rag-research.gitgitpush-uorigin master推送时会弹出 GitHub 登录窗口Git Credential Manager用浏览器授权即可。之后想公开时在仓库 Settings → Danger Zone → Change visibility 改为 Public。简历写法完整版约 6 行适合项目经历重点位研报智答 —— 基于混合检索的研报问答系统RAG独立开发技术栈Python / FastAPI / Elasticsearch / Docker / React / 阿里百炼构建 PDF 研报知识库问答系统基于 ONNX 视觉模型的版面分析 OCR 解析文档切片向量化后按用户隔离存入 Elasticsearch设计kNN 向量 BM25 关键词混合检索0.6/0.4 加权融合解决纯向量检索对股票代码、财务数字等专有 token 不敏感的问题实现两阶段排序rerank 模型精排并设计第三方服务不可用时自动降级为本地余弦相似度融合排序保证链路可用性自建10 条标注用例的评测集量化 hit1/3/5 与 MRR 指标完成检索权重三组对比实验验证权重调整主要影响排序而非召回hit1 从 50% 提升至 70%打通引文页码溯源全链路deepdoc 版面坐标 → 切片级页码入 ES → 答案引文点击定位到原文页码与段落另实现会话 Markdown 报告导出工程化Docker Compose 编排四服务、SSE 流式输出、JWT 认证、Alembic 迁移独立排查修复 6 个部署阻断问题mapping 缺失、卷挂载错误、XGBoost 版本兼容等精简版3 行简历空间紧张时用独立开发研报 RAG 问答系统deepdoc 解析 Elasticsearch kNN/BM25 混合检索 rerank 精排含降级容错SSE 流式输出Docker Compose 一键部署自建评测集量化检索质量hitk / MRR通过权重对比实验将 hit1 从 50% 优化至 70%实现引文页码溯源与 Markdown 报告导出答案可回溯至 PDF 原文位置面试考点提示这段经历大概率会被追问追问你的答案素材为什么用 ES 而不是 Faiss/MilvusREADME「技术选型与取舍」表格一套引擎同时提供 kNN BM25 元数据过滤 持久化混合检索权重怎么定的三组对比实验数据0.1/0.6/0.9能讲出“权重影响排序不影响召回”的观察rerank 挂了怎么办三级降级异常捕获 → 本地余弦词项融合排序 → 链路不中断遇到最难的问题挑一个讲ES 索引没建 dense_vector mapping 导致检索为空 / Docker 卷挂载路径错导致改码不生效README 部署调试记录表如何衡量系统效果评测脚本 负例验证 MRR 排序敏感性建好仓库后把地址发我我来推送。