feat(memory): P2 读路径打分 + 桌面端记忆面板(看/改/删)
读路径(Generative Agents 公式的 Recency+Importance 两项,Relevance 待 P3): - memory.Get 改为 rankProfiles:Score=0.4·Recency+0.6·Importance,按分降序、截断 top-30。 Recency=0.98^天 指数衰减;未评分行用兜底 importance=5(不被不公平遗忘)。纯函数 + 单测。 - 新增 Store.List(结构化、不截断)+ memory_list 工具。 桌面端记忆面板: - gateway GET /memory(列表) + DELETE /memory?key=(软删),受保护组。 - api listMemory/deleteMemory;MemoryView 右侧从占位 → 真列表:按分排序展示 key/value/重要度/最近时间,可内联编辑(PUT)与删除(软删);左侧登记后自动刷新。 实测:PUT 两条 → GET 返回带 importance/last_seen 的有序列表(较新者靠前)→ DELETE 一条 (软删)→ 再 GET 已消失。rank/recency/兜底 importance 单测过;前端 tsc+构建通过。 至此记忆:召回(打分) + 历史 + 自动对账(P1) + 用户可管控(面板) 闭环。Relevance(Milvus) 留 P3。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -200,6 +200,39 @@ func (h *Handler) SetMemory(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok", "message": res.Content})
|
||||
}
|
||||
|
||||
// ListMemory: GET /api/v1/memory —— 列出当前用户的全部偏好(结构化,供记忆面板)。
|
||||
func (h *Handler) ListMemory(c *gin.Context) {
|
||||
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("memory_list"),
|
||||
&contract.ToolCall{Tool: "memory_list", Args: map[string]any{"user_id": userID(c)}})
|
||||
if err != nil || res == nil || !res.OK {
|
||||
msg := "记忆列举失败"
|
||||
if res != nil && res.Error != "" {
|
||||
msg = res.Error
|
||||
}
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": msg})
|
||||
return
|
||||
}
|
||||
var items []map[string]any
|
||||
_ = json.Unmarshal([]byte(res.Content), &items)
|
||||
c.JSON(http.StatusOK, gin.H{"memories": items})
|
||||
}
|
||||
|
||||
// DeleteMemory: DELETE /api/v1/memory?key= —— 软删当前用户的一条偏好。
|
||||
func (h *Handler) DeleteMemory(c *gin.Context) {
|
||||
key := c.Query("key")
|
||||
if key == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "key required"})
|
||||
return
|
||||
}
|
||||
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("memory_delete"),
|
||||
&contract.ToolCall{Tool: "memory_delete", Args: map[string]any{"user_id": userID(c), "key": key}})
|
||||
if err != nil || res == nil || !res.OK {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "删除失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
// userID 取当前用户标识 —— 仅信任 JWT 鉴权中间件注入的已验证 uid(不再认 header)。
|
||||
// 受保护路由有 RequireAuth 兜底,此处理论上不会返回 anonymous。
|
||||
func userID(c *gin.Context) string {
|
||||
|
||||
Reference in New Issue
Block a user