跳转至

向量查询在事务里静默失败,服务长期只返回词法结果

goagent 检索服务内部出过一次独立的向量失效,时间比向量空间事故更晚。

现象

系统已接入混合检索,但页面效果和纯词法阶段几乎没差别。调试面板长期只有 retrieval_method=lexical,vector_score 没有实际贡献;一些本应由向量补位的 query 仍然召回不上来。

单独查库能看到向量近邻:库里有向量,也能算出结果,但服务内的最终结果像完全没走向量链路。

排查

先确认问题出在哪一段。检索链路分成嵌入、查库、服务内执行几段,面板长期只有词法结果,可能对应三个方向:嵌入损坏、库里没向量、服务内执行失败。

第一步单独跑一次 query 嵌入和向量 SQL,数据库正常返回近邻,前两个方向排除,范围收到服务内执行路径。

第二步看服务日志。向量路失败时留有固定关键字:

knowledge vector search failed

错误发生在向量检索执行阶段,向量路没有产出任何结果;服务随后退回词法结果,请求本身不报错,所以面板上只能看到长期只有词法。

第三步顺着执行路径往下找,失败点在检索执行前的参数设置。为提高 ivfflat 索引的召回率,事务内要设置 ivfflat.probes,实现用了参数化写法:

SET LOCAL ivfflat.probes = $1

SET LOCAL 不接受占位符,这条语句在 PostgreSQL 里直接报语法错误。向量检索每次都在事务里设参时失败,服务退回词法结果。

根因

两个机制叠加:

  • ivfflat 是近似索引:建索引时向量被聚类分到若干列表,查询只探查其中 probes 个,默认值很小,不调大召回率不够。所以当时要在检索事务里调这个参数。
  • SET 是 PostgreSQL 的 utility 语句,不走执行器的参数绑定,协议里的 $1 占位符只能出现在普通查询语句中。SET LOCAL ... = $1 在语法层就被拒绝,每条向量检索一开始设参就报错。

向量路失败后服务直接退回词法结果,请求不报错。混合检索下词法路还在正常产出,页面只是效果接近纯词法,没有显式失败信号,问题因此长期没被注意到。

修复与后续

修复改用事务内的 set_config(...) 设置参数:它是普通函数调用,可以正常走参数绑定,把作用域设为事务级后效果和 SET LOCAL 一致。面板的 retrieval_method、vector_score、lexical_score 保留为长期观测字段,向量路失效会直接反映在面板字段上。

修复没有留下独立测试记录,验证就是排查路径本身:单独跑向量 SQL 确认数据库能返回,再对比服务内最终输出。

留下的检查规则:每次检索改动后,单独验证向量路 top 结果和服务最终 top 结果是否一致。这条规则的背景是:接入混合检索不等于向量链路真的在执行,ANN 参数和事务级设置的改动要直接验证数据库执行行为,不能只看最终页面效果。