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:
@@ -548,9 +548,18 @@ func (h *Handler) KbSearch(c *gin.Context) {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": res.Error})
|
||||
return
|
||||
}
|
||||
var hits []map[string]any
|
||||
_ = json.Unmarshal([]byte(res.Content), &hits)
|
||||
c.JSON(http.StatusOK, gin.H{"hits": hits})
|
||||
var out struct {
|
||||
Hits []map[string]any `json:"hits"`
|
||||
Routes []map[string]any `json:"routes"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(res.Content), &out); err != nil {
|
||||
// 兼容老 mcp-go(未部署新版时仍回裸数组),别因为这个把试验台整个打挂。
|
||||
var hits []map[string]any
|
||||
_ = json.Unmarshal([]byte(res.Content), &hits)
|
||||
c.JSON(http.StatusOK, gin.H{"hits": hits})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"hits": out.Hits, "routes": out.Routes})
|
||||
}
|
||||
|
||||
// AdminKbSearch: POST /api/v1/admin/kb/search —— 管理端「检索试验台」:按**完整作用域键**
|
||||
@@ -573,7 +582,10 @@ func (h *Handler) AdminKbSearch(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "kb 与 q 必填"})
|
||||
return
|
||||
}
|
||||
args := map[string]any{"kb": body.KB, "q": body.Q}
|
||||
// diag=true:让 mcp-go 连每一路的诊断一起回(ok/empty/disabled/error)。
|
||||
// 试验台要回答的是"这一路为什么空"——只有命中数回答不了:没配置、报错、
|
||||
// 确实没匹配,三者在结果上都是空数组。
|
||||
args := map[string]any{"kb": body.KB, "q": body.Q, "diag": true}
|
||||
if body.TopK > 0 {
|
||||
args["topK"] = body.TopK
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user