整个项目的核心大致按:加载文档→文本切割→向量化→存向量→关键词索引→混合检索→重排序→多轮改查询再检索→拼上下文并生成回答。 加载文档:根据文件后缀选用不同方式把内容抽成纯文本。支持常见类型如 PDF、纯文本与Markdown、Word(docx)、Excel(xlsx/xls)、PPT(pptx) 等。 文本切割:采用RecursiveCharacterTextSplitter的切割方式,设置最大chunk_size数和chunk_overlap的比例,按:段落、换行、中文句号逗号分号冒号空格的顺序切割,减少把一句话从中间硬撕开的情况。 Embedding(向量化):用Hugging Face下载到本地的Sentence Transformers模型,把每一个文本块当作一整段话编码成一条固定长度的向量。这个阶段会把传入的文本和用户提问query都进行向量化。 存向量(向量库):用 FAISS 存这些向量,并根据向量数量自动选索引方式:量小时用精确 L2 距离(简单可靠);中等时用 IVF 类(先聚类再查,省时间);很大时用带压缩的 IVF-PQ(更省内存、更快,精度略让一点)。FAISS存储向量的时候只存上传文档文本块的索引和向量的映射,所以要自己再建一个字典通过索引把文本内容、元数据、向量做一个映射,将用户query向量化,然后拿到FAISS在同一套模型、同一纬度下做近邻搜索,拿到索引,再去字典通过索引取回文本和元数据。 关键词检索(BM25):对同一批文本块另建一套 BM25 索引;中文会先分词,再按词频做统计打分。它和向量检索做的是两件事:一个看意思像不像,一个看关键词对没对上。 混合检索:用户提问时,一边用query向量在FAISS里找最相近的块,一边用 BM25 按关键词找分高的块。两路结果会按权重合成一个分数(语义一路、关键词一路),再排序得到一批候选;同一条内容若在两边都出现了,分数会叠加,更不容易漏重要片段。 重排序:混合检索是粗召回,候选还是略多、略粗,要再对每条「问题 + 片段」做一次精召回,用本地Ollama的大模型逐条打分或用交叉编码器模型,最终留下少数几条最相关的文本。 递归查询优化:系统会用大模型看当前搜到的内容够不够答;不够就让大模型改写或收窄查询再搜下一轮,直到判定够用或达到轮数上限,减轻「问法不对就搜不到」的问题。 生成回答:把最终选中的片段 拼成带文件名等来源标记的上下文,必要时做简单冲突检测(不同片段说法不一致时在提示里让模型交代清楚),再用固定模板约束模型:主要依据参考内容、不够就说不知道、用中文有条理写、要让人看出信息从哪来,最后调本地 Ollama 生成答案。 项目前端用的是 Gradio,它不单独写前后端分离的页面,而是用 Gradio 的组件搭出:文档上传与处理进度、问答对话区、分块可视化表格、简单系统监控等。用户上传后,页面背后的处理函数会清空旧索引、走「抽取→分块→向量化→建FAISS与BM25」整套建库流程;提问则直接调用生成模块里的问答函数,把检索和生成串起来。 后端是 FastAPI 提供的 HTTP 接口。它给同一套能力加了「程序可调」的入口:上传文件时把文件落到临时路径,再复用和 Gradio 相同的文档处理函数,保证建库逻辑一致;问答接口把问题交给同样的问答函数(通常放在线程里执行,避免卡住异步服务)。
欢迎各位大佬们批评与指教!