fix(rag): 检索三路不再静默吞错 —— 逐路诊断 + 一路挂不拖垮全部

排查"向量路为什么是空的"花了半小时,因为空就是空,没有任何线索。这次把
整条检索链上的静默降级一次清掉。

真 bug(不只是可观测性):
  - kb_search 与 Search() 都拿 rag.Ready() 当总闸,而 Ready() 只代表"向量路
    可用"(embedding + Milvus)。全文(bleve)与图谱(Neo4j)根本不依赖它们,却
    被一并毙掉 → "模型配置没下发"表现为"整个知识库什么都搜不到",还不报错。
    改为逐路判定,任一路可用就仍有召回。

不再吞错:
  - milvus.search 原先把 error 转成 nil,nil —— 检索失败与无召回彻底无法区分;
  - bleve.search / graph.search 出错直接回 nil,连日志都没有;
  - searchPaths 丢掉 embedding 的 error。
    三处改为如实返回,错误统一打日志。

逐路诊断(RouteDiag):每路上报 ok/empty/disabled/error + 耗时 + 原因,经
kb_search 的 diag 参数(仅试验台传,生产调用返回值不变)→ gateway → 检索
试验台。界面上现在能直接看出"这一路没配置/报错了/确实没匹配",不必翻日志。
内存兜底索引也会在 note 里点明"重启即清零"。

测试:3 组,覆盖"无 embedding 时全文仍可召回"、三种空的区分、内存索引提示。
把总闸加回去验证过第一条确实会红——测试能抓到这个回归,不是摆设。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-20 15:10:03 +08:00
parent 00ad6c8ac0
commit b26fe21408
10 changed files with 341 additions and 42 deletions
+16 -3
View File
@@ -347,11 +347,24 @@ func (g *Gateway) kbSearch(ctx context.Context, call *contract.ToolCall) *contra
if v, ok := call.Args["topK"].(float64); ok && v > 0 {
topK = int(v)
}
if !g.rag.Ready() {
return &contract.ToolResult{OK: true, Content: "[]"}
}
// 这里**不能**用 g.rag.Ready() 当总闸。Ready() 要求 embedding 已配置 + Milvus 已连,
// 但全文(bleve)与图谱(Neo4j)两路根本不依赖它们;一刀切返回 "[]" 会让"embedding 没配好"
// 表现为"整个知识库搜不到东西",且没有任何错误信息。改为让 searchPaths 逐路判定,
// 各路自己上报 disabled/error(见 rag.RouteDiag)。
// mode 空=生产混合检索(含 rerank);显式 vector/fulltext/graph/hybrid=评测用单路/纯融合(不 rerank)。
mode, _ := call.Args["mode"].(string)
// diag=true(仅检索试验台会传):返回 {hits, routes} 对象,带每一路的
// ok/empty/disabled/error 诊断。不传时保持裸数组,生产调用方不受影响。
if d, _ := call.Args["diag"].(bool); d {
hits, diags := g.rag.SearchByModeDiag(ctx, kb, q, topK, mode)
if hits == nil {
hits = []rag.Hit{}
}
data, _ := json.Marshal(map[string]any{"hits": hits, "routes": diags})
return &contract.ToolResult{OK: true, Content: string(data)}
}
var hits []rag.Hit
var err error
if mode == "" {