fix: 6 Go code quality fixes + 4 plugin bug fixes

This commit is contained in:
小唯 2026-06-20 16:51:13 +08:00
parent 12c4c58c1c
commit dc8d074cf5
4 changed files with 76 additions and 58 deletions

View File

@ -60,7 +60,7 @@ func (ga *GraphAPI) Query(w http.ResponseWriter, r *http.Request) {
return
}
// 默认跨 namespace 搜索(空 = 匹配所有§2.5.6
results := ga.Graph.Query(normalizeEntity(req.Entity), req.Relation, req.Namespace)
results := ga.Graph.Query(NormalizeEntity(req.Entity), req.Relation, req.Namespace)
respond(w, 200, map[string]interface{}{"results": results, "count": len(results)})
}
@ -89,8 +89,8 @@ func (ga *GraphAPI) Navigate(w http.ResponseWriter, r *http.Request) {
// 模式 2: 双向 BFSsource + target
if req.Source != "" && req.Target != "" {
source := normalizeEntity(req.Source)
target := normalizeEntity(req.Target)
source := NormalizeEntity(req.Source)
target := NormalizeEntity(req.Target)
paths, err := ga.Graph.NavigateBiDir(source, target, req.MaxHops, req.Namespace, req.RelationFilter)
if err != nil {
respondError(w, 500, "navigate failed: "+err.Error())
@ -109,7 +109,7 @@ func (ga *GraphAPI) Navigate(w http.ResponseWriter, r *http.Request) {
respondError(w, 400, "entity (or source+target) required")
return
}
entity := normalizeEntity(req.Entity)
entity := NormalizeEntity(req.Entity)
paths, err := ga.Graph.Navigate(entity, req.MaxHops, req.Namespace, req.RelationFilter)
if err != nil {
respondError(w, 500, "navigate failed: "+err.Error())
@ -162,8 +162,8 @@ func stripPrefix(id string) string {
return strings.TrimPrefix(id, "n_")
}
// normalizeEntity 规整实体名:去特殊字符 + n_前缀保留中文和 Unicode 字符
func normalizeEntity(entity string) string {
// NormalizeEntity 规整实体名:去特殊字符 + n_前缀保留中文和 Unicode 字符
func NormalizeEntity(entity string) string {
if strings.HasPrefix(entity, "n_") {
entity = entity[2:]
}

View File

@ -149,10 +149,26 @@ func applyForgettingLinkage(skill *BetaSkill, success bool) {
// applyDegreePenalty 降低记忆 degreeretired skill → degree -2
func applyDegreePenalty(memID string, penalty int) error {
// degree 保护通过 BayesianSkills.GetSkillForMemory() 在 Forgetter.ShouldForget 中生效
_ = memID
_ = penalty
return nil
ldb := getLDB()
if ldb == nil {
return fmt.Errorf("ldb not available")
}
candidates, err := ldb.GetSkillCandidates(1, 1000)
if err != nil {
return err
}
for _, mem := range candidates {
if mem.ID == memID {
newImportance := mem.Importance - float64(penalty)*0.05
if newImportance < 0.01 {
newImportance = 0.01
}
return ldb.Update("memories", memID, map[string]any{
"importance": newImportance,
})
}
}
return fmt.Errorf("memory not found: %s", memID)
}
// applyDecayAcceleration 加速记忆衰减(通过更新 importance

View File

@ -29,26 +29,6 @@ import (
// cachedGraphStoreRef 全局图谱缓存引用(用于 graph write 操作时主动失效)
var cachedGraphStoreRef *storage.GraphCacheRef
// normalizeEntity 规整实体名:去特殊字符 + n_前缀
func normalizeEntity(entity string) string {
if strings.HasPrefix(entity, "n_") {
entity = entity[2:]
}
// 保留中文、字母、数字、下划线、连字符,过滤其他特殊字符
clean := strings.Map(func(r rune) rune {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' ||
(r >= 0x4e00 && r <= 0x9fa5) { // 中文 Unicode 范围
return r
}
return '_'
}, strings.ToLower(strings.TrimSpace(entity)))
// 合并连续下划线
for strings.Contains(clean, "__") {
clean = strings.ReplaceAll(clean, "__", "_")
}
return strings.Trim(clean, "_")
}
// skillByNameHandler GET/DELETE /api/v1/skills/{name} 的 method 分支分发器
func skillByNameHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@ -260,19 +240,6 @@ func NewServer() http.Handler {
// ─── 跨 Agent 缓存失效回调 ───────────────────
// 收到其他 Agent 的广播 → 失效本地缓存
governance.GlobalEventBus.Subscribe("cache.invalidate", "self")
go func() {
// 本地处理 cache.invalidate 事件(通过 HTTP self-call
http.HandleFunc("/_internal/cache/invalidate", func(w http.ResponseWriter, r *http.Request) {
var evt struct {
Payload map[string]string `json:"payload"`
}
json.NewDecoder(r.Body).Decode(&evt)
ns := evt.Payload["namespace"]
storage.SearchCacheInstance.Invalidate(ns)
log.Printf("[cache] 跨 Agent 失效: %s", ns)
w.WriteHeader(200)
})
}()
// ─── 路由注册 ──────────────────────────────
@ -442,10 +409,10 @@ func NewServer() http.Handler {
var noteResults []map[string]interface{}
var paths []map[string]interface{}
if entity != "" {
p, err := graphStore.Navigate(normalizeEntity(entity), maxHops, "", nil)
p, err := graphStore.Navigate(routes.NormalizeEntity(entity), maxHops, "", nil)
if err == nil {
paths = p
entities := []string{normalizeEntity(entity)}
entities := []string{routes.NormalizeEntity(entity)}
for _, path := range p {
if s, ok := path["source"].(string); ok {
entities = append(entities, s)
@ -499,7 +466,7 @@ func NewServer() http.Handler {
var entityA, entityB string
// 模式1: "A和B是什么关系" / "A和B有什么关系" / "A和B的关系"
hasAnd := strings.Contains(cleanQuery, "和") || strings.Contains(cleanQuery, "与")
if hasAnd && (strings.Contains(cleanQuery, "关系") || strings.Contains(cleanQuery, "关")) {
if hasAnd && (strings.Contains(cleanQuery, "关系") || strings.Contains(cleanQuery, "关")) {
delim := "和"
if strings.Contains(cleanQuery, "与") {
delim = "与"
@ -524,13 +491,13 @@ func NewServer() http.Handler {
var result map[string]interface{}
if entityA != "" && entityB != "" {
// 双向查询A → B 的关系
paths, _ := graphStore.Navigate(normalizeEntity(entityA), 2, "", nil)
relB, _ := graphStore.Navigate(normalizeEntity(entityB), 2, "", nil)
paths, _ := graphStore.Navigate(routes.NormalizeEntity(entityA), 2, "", nil)
relB, _ := graphStore.Navigate(routes.NormalizeEntity(entityB), 2, "", nil)
// 找 A → B 的直接边
var directPath map[string]interface{}
for _, p := range paths {
toNorm := normalizeEntity(p["to"].(string))
targetNorm := normalizeEntity(entityB)
toNorm := routes.NormalizeEntity(p["to"].(string))
targetNorm := routes.NormalizeEntity(entityB)
if toNorm == targetNorm {
directPath = p
break
@ -548,7 +515,7 @@ func NewServer() http.Handler {
} else {
// 单实体查询:找 entity 的关系网
entity := cleanQuery
paths, _ := graphStore.Navigate(normalizeEntity(entity), 2, "", nil)
paths, _ := graphStore.Navigate(routes.NormalizeEntity(entity), 2, "", nil)
// 按 relation 分组
relCounts := make(map[string]int)
for _, p := range paths {
@ -702,7 +669,10 @@ func NewServer() http.Handler {
EventType string `json:"event_type"`
Callback string `json:"callback_url"`
}
json.NewDecoder(r.Body).Decode(&req)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
governance.GlobalEventBus.Subscribe(req.EventType, req.Callback)
respondJSON(w, 200, map[string]string{"status": "subscribed"})
})
@ -711,7 +681,10 @@ func NewServer() http.Handler {
EventType string `json:"event_type"`
Callback string `json:"callback_url"`
}
json.NewDecoder(r.Body).Decode(&req)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
governance.GlobalEventBus.Unsubscribe(req.EventType, req.Callback)
respondJSON(w, 200, map[string]string{"status": "unsubscribed"})
})
@ -726,7 +699,10 @@ func NewServer() http.Handler {
Type string `json:"type"`
Payload map[string]string `json:"payload"`
}
json.NewDecoder(r.Body).Decode(&evt)
if err := json.NewDecoder(r.Body).Decode(&evt); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
ns := evt.Payload["namespace"]
storage.SearchCacheInstance.Invalidate(ns)
log.Printf("[cache] 跨 Agent 失效接收: %s", ns)
@ -777,7 +753,10 @@ func NewServer() http.Handler {
AgentType string `json:"agent_type"`
}
if r.Method == "POST" {
json.NewDecoder(r.Body).Decode(&req)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
forgetter.SetAgentType(req.AgentType)
}
respondJSON(w, 200, map[string]interface{}{
@ -796,7 +775,10 @@ func NewServer() http.Handler {
Namespace string `json:"namespace"`
AgentID string `json:"agent_id"`
}
json.NewDecoder(r.Body).Decode(&req)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
if req.Content == "" {
respondJSON(w, 400, map[string]string{"error": "content required"})
return
@ -964,7 +946,24 @@ func NewServer() http.Handler {
// G6: ephemeral namespace 管理
mux.HandleFunc("/api/v1/admin/ephemeral/clean", func(w http.ResponseWriter, r *http.Request) {
cleaned := []string{}
now := time.Now().UnixMilli()
agents := agentRegistry.ListAgents()
var cleaned []string
for _, agent := range agents {
if now-agent.LastSeen > 5*60*1000 {
ns := agent.AgentID + "-ephemeral"
zeroVec := make([]float32, 1024)
memories, _ := ldb.Search("memories", zeroVec, 1000, ns)
for _, mem := range memories {
ldb.SoftDelete(mem.ID, "ephemeral_expired")
}
if len(memories) > 0 {
cleaned = append(cleaned, ns)
log.Printf("[ephemeral] cleaned %d memories from %s (inactive for %ds)",
len(memories), ns, (now-agent.LastSeen)/1000)
}
}
}
respondJSON(w, 200, map[string]interface{}{
"status": "cleaned",
"cleaned_namespaces": cleaned,
@ -981,7 +980,10 @@ func NewServer() http.Handler {
var evt struct {
Payload map[string]string `json:"payload"`
}
json.NewDecoder(r.Body).Decode(&evt)
if err := json.NewDecoder(r.Body).Decode(&evt); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
ns := evt.Payload["namespace"]
storage.SearchCacheInstance.Invalidate(ns)
log.Printf("[cache] 内部跨 Agent 失效: %s", ns)

Binary file not shown.