Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_working_context.go
ddCat 6892732b06 feat(agent): Agent 工作台 - 计划插话记忆、统一模型选择器并修复摘要与视频入库
将计划清单、运行中插话和个人记忆接入云端 Agent;全站模型选择器统一为左右二级菜单。画布摘要按预算截断不再整轮拒绝,首页视频未知宽高可入库。README 补充贡献者 morrow。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:21:25 +08:00

90 lines
2.2 KiB
Go

package app
import (
"encoding/json"
"strings"
)
const (
cloudAgentHistoryKeepRounds = 10
cloudAgentHistoryMaxBytes = 64000
)
func isCloudAgentContinuationMessage(message providerTextMessage) bool {
if message.Role != "user" {
return false
}
content := strings.TrimSpace(message.Content)
return strings.HasPrefix(content, "上一轮真实执行记录") || strings.HasPrefix(content, "上一轮已结束")
}
func cloudAgentHistoryUserInstructionCount(history []providerTextMessage) int {
count := 0
for _, message := range history {
if message.Role == "user" && !isCloudAgentContinuationMessage(message) {
count++
}
}
return count
}
func cloudAgentHistoryJSONSize(history []providerTextMessage) int {
raw, err := json.Marshal(history)
if err != nil {
return 0
}
return len(raw)
}
func dropOldestCloudAgentHistoryRound(history []providerTextMessage) []providerTextMessage {
if len(history) == 0 {
return history
}
if isCloudAgentContinuationMessage(history[0]) || history[0].Role != "user" {
return history[1:]
}
index := 1
for index < len(history) {
if history[index].Role == "user" && !isCloudAgentContinuationMessage(history[index]) {
break
}
index++
}
return history[index:]
}
// trimCloudAgentTextHistory 只把最近若干轮用户对话留给模型。
// 个人记忆、更早轮次和上一轮工具流水不进工作上下文;超字节上限时从最旧一轮往下丢。
func trimCloudAgentTextHistory(history []providerTextMessage, keepRounds, maxBytes int) []providerTextMessage {
if keepRounds <= 0 {
keepRounds = cloudAgentHistoryKeepRounds
}
if maxBytes <= 0 {
maxBytes = cloudAgentHistoryMaxBytes
}
if len(history) == 0 {
return history
}
start := 0
seen := 0
for index := len(history) - 1; index >= 0; index-- {
if history[index].Role != "user" || isCloudAgentContinuationMessage(history[index]) {
continue
}
seen++
if seen == keepRounds {
start = index
break
}
}
trimmed := append([]providerTextMessage{}, history[start:]...)
for cloudAgentHistoryJSONSize(trimmed) > maxBytes && cloudAgentHistoryUserInstructionCount(trimmed) > 1 {
next := dropOldestCloudAgentHistoryRound(trimmed)
if len(next) >= len(trimmed) {
break
}
trimmed = next
}
return trimmed
}