8ff68078b7
桌面端「知识库」模块从占位变为可用:入库(切块/embedding/Milvus 监控) +
检索调试台(向量召回,带分数与来源)。
- mcp-go: 新工具 kb_search(返回结构化 JSON [{text,score}]);rag.Hit 加 json 标签
- gateway: POST /api/v1/kb/search → kb_search(结构化命中给检索台)
- desktop: lib/api ingestKb/searchKb;新 KbView(左 入库+监控日志 / 右 检索台命中列表
带 Milvus 来源徽标+分数);App 接 kb 视图;LeftNav 知识库 ready
- 验证: gateway/mcp-go build✓ + e2e PASS + 前端 build✓;真实浏览器——入库3条→监控
'已入库3块';语义查询'存储和搜索向量的组件'→Milvus(0.612)>Neo4j>NATS 排序正确,
全走真实百炼 embedding(控制面下发)+Milvus
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
1.9 KiB
Go
65 lines
1.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/sundynix/sundynix-shared/contract"
|
|
)
|
|
|
|
// KbIngest: POST /api/v1/kb/ingest —— 把文本入库到知识库(→ mcp-go kb_ingest → 切块/embedding/Milvus)。
|
|
// 供知识库管理页/脚本调用。
|
|
func (h *Handler) KbIngest(c *gin.Context) {
|
|
var body struct {
|
|
KB string `json:"kb"`
|
|
Text string `json:"text"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil || body.Text == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "text required"})
|
|
return
|
|
}
|
|
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("kb_ingest"),
|
|
&contract.ToolCall{Tool: "kb_ingest", Args: map[string]any{"kb": body.KB, "text": body.Text}})
|
|
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
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"status": "ok", "message": res.Content})
|
|
}
|
|
|
|
// KbSearch: POST /api/v1/kb/search —— 检索台:查某知识库,返回带分数的命中(→ mcp-go kb_search)。
|
|
func (h *Handler) KbSearch(c *gin.Context) {
|
|
var body struct {
|
|
KB string `json:"kb"`
|
|
Q string `json:"q"`
|
|
TopK int `json:"topK"`
|
|
}
|
|
if err := c.ShouldBindJSON(&body); err != nil || body.Q == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "q required"})
|
|
return
|
|
}
|
|
args := map[string]any{"kb": body.KB, "q": body.Q}
|
|
if body.TopK > 0 {
|
|
args["topK"] = body.TopK
|
|
}
|
|
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})
|
|
}
|