基础理解类
Q1、描述项目中文档向量化完整的处理流程,并说明每个环节的作用:
考察点:对RAG整体流程的把握,能否清晰表达从文档上传到可检索的完整链路。
Step1:上传文件(文件状态是pending)
Step2:利用Tika对文档进行处理,这样可以支持不同格式的文件,例如:pdf、doc等。输出是:Srting content(文档全文)
Step3:RecursiveCharacterTextSplitter(LangChain框架的核心组建)分块处理,将处理后的文档分块处理以适合embedding。输出是:List<String> chunks
Step4:利用智谱的embedding API对分块后的文档批量向量化,每次处理最多处理10个文本块,维度1024。输出是:List<List<float>> embeddings
Step5:将向量化的结果存储到postgreSQl+pgvector 存储。持久化向量和元数据,支持高效相似度搜索。存储document_vectoes表(向量) + document_chunks表(内容)。输出:向量库索引完成。
文档状态更改为completed
Q2:为什么文本分块时要设置chunkOverlap(重叠字符)?如果设置为 0 会有什么问题?
/** * 文本分块重叠 */ private int chunkOverlap = 50;考察点:对分块策略的理解,语义边界切割的影响。
1、保持语义完成性
- 关键信息可能恰好被边界切段:例如:ROI为25%。这里25%可能刚好会被切割掉
2、提高检索的召回率
- 查询可能与多个重叠块都匹配
-增加了相关信息被命中的概率
3、提供上下文窗口
-LLM生成答案时,重叠区域提供了连续的上下文
-减少因分块导致的语义割裂
如果设置为0,没有重叠的话,关键信息被切断——>检索不到完成语义
有重叠的话,边界的信息两个chunk都包含
技术细节类
Q3:代码中使用了递归字符分割器,它的分隔符优先级是 ["\n\n", "\n", " ", ""],请解释这个设计背后的考虑?
考察点:对文本语义结构的理解,为什么段落 > 行 > 单词 > 字符。
段落边界>行边界>词边界>字符级别
1、优先在自然边界分割
"\n\n" → 段落之间通常表达不同的思想或主题
"\n" → 句子或列表项之间有明确的停顿
" " → 单词之间是语言的自然分割点
这样切割不会破坏段落结构和语义
2、递归降级策略
1. 尝试用 "\n\n" 分割 → 如果每个段都 < 100字,直接返回
2. 段落过长 → 用 "\n" 进一步分割
3. 某行过长 → 用空格按词分割
4. 某词过长 → 按字符强制分割
3、keepSeparator=true 作用,保留“\n”,保留格式信息,可以使得在某些场景下语义更完整。
Q4:向量存储时为什么需要两张表(document_chunks 和 document_vectors)?一张表能满足需求吗?
考察点:数据表设计思想,查询性能、存储效率、字段扩展性的权衡。
1、查询场景分离:document_chunks表可以按照按document_id列出所有chunk、全文预览,不需要向量。document_vectors表用于相似度搜索,支持向量表
2、存储效率考虑:
vector(1024) 字段占用:
- 存储空间:约 4KB per chunk(1024 × 4字节)
- 索引开销:pgvector 需要额外的索引结构(IVFFlat/HNSW)
如果只存一张表:
- 所有不涉及向量的查询也会加载/扫描这个大字段
- 查询性能下降,存储成本增加
3、扩展性设计
document_chunks: