This repository has been archived on 2026-08-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Cyrene/backend/ai-core/cmd/trace.go
T
AskaEth fd44b15d81 feat: 全链路优化 — 死锁修复、MD3主题、上下文持久化、群聊自然化、打字状态、知识库
**死锁根因修复**
- periodicThinkLoop:1015 orphaned lock → 删除(字段已原子化)
- RecordUserMessage 隔离为 recordMu
- atomic.Int64 替换 lastUserMessage/lastThinkTime 等

**MD3 / Android 17 主题**
- 毛玻璃卡片 (backdrop-filter)
- MD3 色彩令牌 (pink primary #f472b6)
- icons.js 独立矢量图标库 + 运行时 emoji 替换
- 无边框卡片、圆角按钮、阴影层次

**上下文持久化**
- AddMessage → saveToDB 异步写 PostgreSQL
- LoadFromDB 恢复 (admin-session-main + 懒加载)
- LLMMessage.Timestamp 字段

**群聊与适配器**
- group_ambient 模式: 非@消息让 LLM 自己判断是否插话
- 戳一戳动作消息总是回复
- NapCat 打字状态 (set_input_status, 最小3秒显示)
- HTTP API 配置 (http_url/http_token)

**知识库 & 防编造**
- knowledge.CanHandle 对 chat 意图也触发
- 关键词预筛选避免无关 embedding 调用
- persona + synthesizer 三重诚实规则
- 工具结果持久化到会话历史

**平台桥接器**
- detached:true Go进程独立存活
- ethend 重启自动接管已运行服务
- stop() 接管模式 taskkill/F/ PID
- Windows netstat 替代 fuser 获取 PID
- 重复适配器种子逻辑修复
- 失败转发日志 Direction: error

**崩溃诊断**
- crashlog 包 (Recover + WrapHTTP + LLMCall)
- /api/v1/debug/goroutines 端点
- thinker 操作日志 + 30s stats
- 日志写入 logs/ 目录持久化

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 11:29:29 +08:00

88 lines
2.5 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"sync"
"time"
)
// TraceEvent represents a single step in the message processing pipeline.
type TraceEvent struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
SessionID string `json:"session_id,omitempty"`
UserID string `json:"user_id,omitempty"`
Hop string `json:"hop"` // message_received, intent, subsession, llm_call, tool_call, vision, synthesis, review, response, think
Label string `json:"label"`
Status string `json:"status"` // success, error, running
Detail string `json:"detail,omitempty"`
DurationMs int64 `json:"duration_ms,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
}
var (
traceMu sync.Mutex
traceEvents []TraceEvent
traceMax = 200
)
// AddTraceEvent appends a trace event to the in-memory ring buffer.
func AddTraceEvent(hop, sessionID, userID, label, status, detail string, durationMs int64, data map[string]interface{}) {
traceMu.Lock()
defer traceMu.Unlock()
ev := TraceEvent{
ID: fmt.Sprintf("%s-%d", hop, time.Now().UnixNano()),
Timestamp: time.Now(),
SessionID: sessionID,
UserID: userID,
Hop: hop,
Label: label,
Status: status,
Detail: detail,
DurationMs: durationMs,
Data: data,
}
traceEvents = append(traceEvents, ev)
if len(traceEvents) > traceMax {
traceEvents = traceEvents[len(traceEvents)-traceMax:]
}
}
// GetTraceEvents returns recent trace events, optionally filtered by session.
func GetTraceEvents(sessionID string, limit int) []TraceEvent {
traceMu.Lock()
defer traceMu.Unlock()
if limit <= 0 || limit > len(traceEvents) {
limit = len(traceEvents)
}
result := make([]TraceEvent, 0)
// Return newest first.
for i := len(traceEvents) - 1; i >= 0 && len(result) < limit; i-- {
ev := traceEvents[i]
if sessionID == "" || ev.SessionID == sessionID {
result = append(result, ev)
}
}
return result
}
// registerTraceEndpoint adds the trace events API.
func registerTraceEndpoint(mux *http.ServeMux) {
mux.HandleFunc("/api/v1/trace/events", func(w http.ResponseWriter, r *http.Request) {
sessionID := r.URL.Query().Get("session_id")
limit := 100
if l, err := strconv.Atoi(r.URL.Query().Get("limit")); err == nil && l > 0 && l <= 500 {
limit = l
}
events := GetTraceEvents(sessionID, limit)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"events": events,
"total": len(events),
})
})
}