feat(admin): 数据源页转 RAG 运维台 + 支付/模型菜单重组 + 概览升级为仪表盘
后端: - 新增 POST /admin/kb/search:管理端跨租户检索,支持 mode 指定单路 (vector/fulltext/graph/hybrid),不走 scopedKB(否则会被强制锁到调用者 自己的 space,跨租户排障就没法做了) - KB 清单补 space_id(检索键是 <space_id>/<name>,缺它前端拼不出 key) 前端: - 数据源&RAG 页补「检索试验台」:同一 query 并排跑生产链路 + 四路诊断, 召回不准时能直接定位是向量/分词/图谱哪一环挂了 - 支付拆成「配置 / 订单与对账」两个子页,挂到运维 > 支付 下; 导航支持二级菜单(NavParent 命中子路由自动展开) - SettingsPage → ModelConfigPage「模型配置」,模型参数与计费规则合一 - 概览 → 仪表盘:并入计费与用量(UsagePage → UsageSection), 去掉系统健康拓扑(与服务状态页重复,同一份 /admin/status 数据) - 全局隐藏滚动条(保留滚动) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -553,6 +553,48 @@ func (h *Handler) KbSearch(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"hits": hits})
|
||||
}
|
||||
|
||||
// AdminKbSearch: POST /api/v1/admin/kb/search —— 管理端「检索试验台」:按**完整作用域键**
|
||||
// 检索任意租户的知识库(kb 形如 "<space_id>/<name>",由 /admin/datasources 清单给出)。
|
||||
//
|
||||
// 与用户侧 KbSearch 的关键差别:这里**不做 scopedKB 改写** —— 用户侧会把库名套上调用者
|
||||
// 自己的 space_id,那样 admin 只能搜到自己空间的库,试验台就废了。跨租户是本端点的目的
|
||||
// (运维排障:线上召回不准时,定位是 embedding、切块还是融合的问题)。
|
||||
//
|
||||
// mode: 空=生产混合(含 rerank);vector/fulltext/graph/hybrid=单路/纯融合(不 rerank),
|
||||
// 用于逐路对比看是哪一路没召回。
|
||||
func (h *Handler) AdminKbSearch(c *gin.Context) {
|
||||
var body struct {
|
||||
KB string `json:"kb"` // 完整作用域键 "<space_id>/<name>"
|
||||
Q string `json:"q"`
|
||||
TopK int `json:"topK"`
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Q == "" || body.KB == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "kb 与 q 必填"})
|
||||
return
|
||||
}
|
||||
args := map[string]any{"kb": body.KB, "q": body.Q}
|
||||
if body.TopK > 0 {
|
||||
args["topK"] = body.TopK
|
||||
}
|
||||
if body.Mode != "" {
|
||||
args["mode"] = body.Mode
|
||||
}
|
||||
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("kb_search"),
|
||||
&contract.ToolCall{Tool: "kb_search", Args: args})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if !res.OK {
|
||||
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})
|
||||
}
|
||||
|
||||
// KbGraph: GET /api/v1/kb/graph?kb= —— 某知识库的图谱三元组(→ mcp-go kb_graph,Neo4j)。
|
||||
func (h *Handler) KbGraph(c *gin.Context) {
|
||||
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("kb_graph"),
|
||||
|
||||
Reference in New Issue
Block a user