RAG项目学习笔记:用pgvector检索chunk的逻辑详解
记录学习RAG项目的过程项目中配置了 Milvus 和 PGVector 两种向量检索的实现。本次来看一下使用pgvector检索chunk时具体的逻辑是怎样的。0. 先看一下项目中代码是怎么写的Override public ListRetrievedChunk retrieveByVector(float[] vector, RetrieveRequest request) { // 设置ef_search提升召回率 // noinspection SqlDialectInspection,SqlNoDataSourceInspection jdbcTemplate.execute(SET hnsw.ef_search 200); String vectorLiteral toVectorLiteral(vector); // noinspection SqlDialectInspection,SqlNoDataSourceInspection return jdbcTemplate.query(SELECT id, content, 1 - (embedding ?::vector) AS score FROM t_knowledge_vector WHERE metadata-collection_name ? ORDER BY embedding ?::vector LIMIT ?, (rs, rowNum) - RetrievedChunk.builder() .id(rs.getString(id)) .text(rs.getString(content)) .score(rs.getFloat(score)) .build(), vectorLiteral, request.getCollectionName(), vectorLiteral, request.getTopK() ); }1. 设置索引参数首先执行了jdbcTemplate.execute(SET hnsw.ef_search 200);解析HNSW 在最底层搜索时不是找到第一个看起来最近的就停而是维护一个候选队列不断尝试邻居直到确认没有更近的为止。问题来了队列太小 → 走两步就停了可能错过真正的最近邻快但不准队列太大 → 要考察的节点多搜得仔细但慢准但慢ef_search就是控制这个队列大小的旋钮。1.1 什么是HNSW索引HNSW 全称是分层可导航小世界图是最主流的索引算法。如果你学习过数据结构图那么就很好理解了。1、分层最底层包含所有向量每上一层包含的向量就越少节点稀疏从而能实现长距离跳跃。类似的思想跳表2、可导航假设向量数据库里有 8 个向量A~H建了 3 层图查询和 Q 最相似的向量。最顶层入口节点 A、B → 计算 A、B 与 Q 的距离B 更近移动到 B下沉到中间层。中间层从 B 出发看 B 的邻居C、D、EE 最近且比 B 更近 → 移动到 E再看 E 的邻居没有比 E 更近的 → 下沉到最底层。最底层含全部 A~H从 E 出发看 E 的邻居H 最近且比 E 更近 → 移动到 H再看 H 的邻居没有比 H 更近的 → H 就是结果。2. 来看执行的SQLSELECT id, content, 1 - (embedding ?::vector) AS score FROM t_knowledge_vector WHERE metadata-collection_name ? ORDER BY embedding ?::vector LIMIT ?逐个解析2.1 : :是什么两个冒号代表强制类型转换也就是把传入的字符串类型的向量数组转换为pgvector的向量类型vector。Q为什么要传入字符串类型A因为JDBC驱动不认识vector类型无法直接将float[]当做参数绑定所以在数据库端做一个类型转换。2.2 是什么在 PostgreSQL 的pgvector扩展中定义了三个常用的距离操作符余弦距离-欧几里得距离#负内积所以embedding ?::vector表示用db中存储的embedding向量和参数传入的向量做余弦距离计算。余弦距离 1 - 余弦相似度取值范围[0,2]0表示方向完全相同距离02表示方向完全相反距离最远。Q为什么要用1-余弦距离A目的是把距离转换成分数1 - 0 1 距离最近分数最高1 - 2 -1 距离最远分数最低这样做语义上更直观score“得分高匹配好”符合直觉。下游排序也方便。2.3 - 是什么where条件中的-写法 PostgreSQL 的 JSON/JSONB 路径操作符用于从 JSON 中按键提取文本值对应的-提取的是JSON 对象顺带一提MySQL 也有同名操作符-但语法不同需要加$符insert into test VALUES(1,{name: 张三, age: 25, hobbies: [读书, 跑步]}) SELECT * from test WHERE info-$.name 张三;

相关新闻