v1.7 Session 会话机制 + 多工作区隔离 + Reporter 抽象

This commit is contained in:
zhuyongxin
2026-05-29 14:09:12 +08:00
parent 3e91c7d0a1
commit e480c5c55e
6 changed files with 424 additions and 120 deletions
+101
View File
@@ -0,0 +1,101 @@
package engine
import (
"sync"
"time"
"go-tiny-claw/internal/schema"
)
// Session 代表了一次持续的人机交互过程。它负责维护该会话的完整历史。
type Session struct {
ID string
WorkDir string // 该会话绑定的物理工作区
CreatedAt time.Time
UpdatedAt time.Time
// 存放此 Session 中所有的用户输入、大模型回复和工具调用结果
history []schema.Message
mu sync.RWMutex // 读写锁,防止并发读写历史时发生 Data Race
}
func NewSession(id string, workDir string) *Session {
return &Session{
ID: id,
WorkDir: workDir,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
history: make([]schema.Message, 0),
}
}
// Append 线程安全地向 Session 中追加消息
func (s *Session) Append(msgs ...schema.Message) {
s.mu.Lock()
defer s.mu.Unlock()
s.history = append(s.history, msgs...)
s.UpdatedAt = time.Now()
// 【持久化预留点】:在真实的工业级实现中(如 Claude Code),
// 我们会在这里将 s.history 以 JSONL 的格式 Append 到 workDir/.claw/sessions/xxx.jsonl 中。
// s.SaveToDisk()
}
// GetWorkingMemory 是驾驭工程的核心!
// 它不返回全量历史,而是从后往前截取最近的 N 条消息,形成 Agent 的“短期工作记忆”。
func (s *Session) GetWorkingMemory(limit int) []schema.Message {
s.mu.RLock()
defer s.mu.RUnlock()
total := len(s.history)
if total <= limit || limit <= 0 {
// 如果历史总量小于限制,或者不设限,全量返回 (需要深拷贝以防外部修改)
res := make([]schema.Message, total)
copy(res, s.history)
return res
}
// 截取最近的 limit 条消息
res := make([]schema.Message, limit)
copy(res, s.history[total-limit:])
// 【驾驭防线】:大模型 API 强制要求历史消息的连续性!
// 如果我们截断的第一条消息恰好是一个 ToolResult (RoleUser 且含有 ToolCallID),
// 但发出这个请求的 ToolCall 被我们截断抛弃了,大模型 API 会直接报 400 Bad Request。
// 因此,如果切片首条属于“孤儿”工具响应,我们必须将其强行舍弃,顺延到下一条正常的 User/Assistant 消息。
for len(res) > 0 {
if res[0].Role == schema.RoleUser && res[0].ToolCallID != "" {
res = res[1:]
} else {
break
}
}
return res
}
// ==========================================
// 全局 Session Manager: 用于多用户/多终端隔离
// ==========================================
type SessionManager struct {
sessions map[string]*Session
mu sync.RWMutex
}
var GlobalSessionMgr = &SessionManager{
sessions: make(map[string]*Session),
}
// GetOrCreate 获取或创建一个会话
func (sm *SessionManager) GetOrCreate(id string, workDir string) *Session {
sm.mu.Lock()
defer sm.mu.Unlock()
if sess, exists := sm.sessions[id]; exists {
return sess
}
sess := NewSession(id, workDir)
sm.sessions[id] = sess
return sess
}