检索链路与模块协作
公开教程 · 无需登录

检索链路与模块协作

浏览器仍然只访问本站的 Go 接口。第 3 课的认证模块与材料模块继续生效;本课在其后追加切分、向量嵌入、向量库与对话模块。嵌入网关与 Qdrant 均位于服务端,班级编号仅从登录会话读取。

1新增模块的位置

第 3 课已包含浏览器、Nginx、认证模块、材料模块与 MySQL。本课沿用既有链路,仅在材料上传成功之后,由检索模块将正文转换为可检索的切片。

浏览器 只访问本站 Nginx /api 反向代理至 Go Go 认证模块(请求先经此处) 材料模块(文件与正文) 检索模块 切分 · 检索 · 回答编排 MySQL 会话、正文与切片文本 Qdrant 仅存向量与编号,不含正文 两类模型网关 嵌入:文本 → 向量 对话:有切片时才调用
客户端无法直连 Qdrant,也无法直连两类模型网关。它们仅接受 Go 中检索模块发起的调用,密钥不会下发到页面。

两类网关不是同一个模块

嵌入网关将一段文本转换为固定维度的浮点向量,在入库与向量检索时使用。对话网关读取切片正文并生成简短回答,仅在问答接口已取得依据之后才被调用。关键字检索不触及二者;没有命中切片时,对话网关也不会被触发。

2上传时的入库流程

1 保存原文材料文件与 knowledge_entries.body_text
2 切分按本次策略写入 knowledge_chunks
3 嵌入每个切片转换为一条向量
4 写入 Qdrant向量主键与切片主键一致

向量库的 payload 仅包含 class_id、material_id、knowledge_entry_id、chunk_id、chunk_index,不含正文。嵌入失败时材料记录仍然保留,对应切片标记为 failed,不写入不完整的向量数据。教师可按新策略重建索引:先删除旧切片与旧向量记录,再重新写入。

材料模块 文件 + 正文 事务已提交 切分 读取 body_text 不改写原文 MySQL 切片 先删除旧切片 再写入新记录 嵌入 每段一条 向量 Qdrant 向量主键 = 切片主键
材料模块先保留原文。检索模块失败时原文仍然存在,仅这批切片无法用于向量检索。

3检索时的三条路径

模式 访问的组件 排序方式
keyword 仅查询 MySQL 全文索引;不调用嵌入服务,也不访问 Qdrant 按全文相关度由高到低
vector 问句先做向量嵌入,再在 Qdrant 中按 class_id 过滤;余弦相似度低于 0.35 的候选丢弃;随后以向量主键回 MySQL 取回正文 按余弦相似度由高到低
hybrid 同时执行上述两条路径 各路先独立过滤,再按名次执行 RRF(k = 60);缺席的一路不贡献分数
摘录始终取自 MySQL 的 chunk_text。Qdrant 不可用时,关键字检索仍可返回结果;向量与混合模式返回 503,不编造相似度分数。空查询返回 400。
关键字 只查 MySQL 全文索引 按全文相关度由高到低 向量 问句向量嵌入 Qdrant 按班级过滤 余弦低于 0.35 的候选丢弃 回 MySQL 取回正文 向量主键 = 切片主键 混合 两条路径均执行,各路先过滤,再按名次执行 RRF k = 60;缺席的一路不贡献分数
三条路径最终都回到 MySQL 取回正文,Qdrant 中不保存正文。混合检索融合的是两路的名次,而非将分数相加。

问答接口再引入一个模块

POST /api/ask 自身不实现第三种检索。它先由检索模块以混合模式取回本班前 4 条切片;若一条都没有,则在检索模块处直接返回固定文案,对话网关不会被调用。存在命中切片时,对话模块收到的是材料标题、切片序号与切片正文,以及用户本轮提问;它不会获得向量分量,也不会获得其他班级的材料。