Files
smartmate/backend/model/task-class.go
Losita 66c06eed0a Version: 0.9.45.dev.260427
后端:
1. execute 主链路重构为“上下文工具域 + 主动优化候选闭环”——移除 order_guard,粗排后默认进入主动微调,先诊断再从后端候选中选择 move/swap,避免 LLM 自由全局乱搜
2. 工具体系升级为动态注入协议——新增 context_tools_add / remove、工具域与二级包映射、主动优化白名单;schedule / taskclass / web 工具按域按包暴露,msg0 规则包与 execute 上下文同步重写
3. analyze_health 升级为主动优化唯一裁判入口——补齐 rhythm / tightness / profile / feasibility 指标、候选扫描与复诊打分、停滞信号、forced imperfection 判定,并把连续优化状态写回运行态
4. 任务类能力并入新 Agent 执行链——新增 upsert_task_class 写工具与启动注入事务写入;任务类模型补充学科画像与整天屏蔽配置,粗排支持 excluded_days_of_week,steady 策略改为基于目标位置/单日负载/分散度/缓冲的候选打分
5. 运行态与路由补齐优化模式语义——新增 active tool domain/packs、pending context hook、active optimize only、taskclass 写入回盘快照;区分 first_full / global_reopt / local_adjust,并完善首次粗排后默认 refine 的判定

前端:
6. 助手时间线渲染细化——推理内容改为独立 reasoning block,支持与工具/状态/正文按时序交错展示,自动收口折叠,修正 confirm reject 恢复动作

仓库:
7. newAgent 文档整体迁入 docs/backend,补充主动优化执行规划与顺序约束拆解文档,删除旧调试日志文件

PS:这次科研了2天,总算是有些进展了——LLM永远只适合做选择题、判断题,不适合做开放创新题。
2026-04-27 01:09:37 +08:00

204 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
import (
"database/sql/driver"
"encoding/json"
"fmt"
"time"
)
// TaskClass 用于和数据库中的 task_classes 表进行映射
type TaskClass struct {
//section 1
ID int `gorm:"column:id;primaryKey;autoIncrement"`
UserID *int `gorm:"column:user_id;index:idx_task_classes_user_id"`
//section 2
Name *string `gorm:"column:name;size:255"`
Mode *string `gorm:"column:mode;type:enum('auto','manual')"`
StartDate *time.Time `gorm:"column:start_date"`
EndDate *time.Time `gorm:"column:end_date"`
SubjectType *string `gorm:"column:subject_type;size:32;comment:学科类型 quantitative|memory|reading|mixed"`
DifficultyLevel *string `gorm:"column:difficulty_level;size:16;comment:难度等级 low|medium|high"`
CognitiveIntensity *string `gorm:"column:cognitive_intensity;size:16;comment:认知强度 low|medium|high"`
//section 3
TotalSlots *int `gorm:"column:total_slots;comment:分配的总节数"`
AllowFillerCourse *bool `gorm:"column:allow_filler_course;default:true"`
Strategy *string `gorm:"column:strategy;type:enum('steady','rapid')"`
ExcludedSlots IntSlice `gorm:"column:excluded_slots;type:json;comment:不想要的时段切片"`
ExcludedDaysOfWeek IntSlice `gorm:"column:excluded_days_of_week;type:json;comment:不想要的星期几切片(1-7)"`
Items []TaskClassItem `gorm:"foreignKey:CategoryID;references:ID"` // 一对多关联:一个 TaskClass 有多个 TaskClassItem
}
// IntSlice 用于把 []int 以 JSON 形式存入/读出数据库 json 字段
type IntSlice []int
func (s IntSlice) Value() (driver.Value, error) {
// nil -> NULL空切片 -> "[]"
if s == nil {
return nil, nil
}
return json.Marshal([]int(s))
}
func (s *IntSlice) Scan(value any) error {
if value == nil {
*s = nil
return nil
}
var data []byte
switch v := value.(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return fmt.Errorf("IntSlice: 不支持的扫描类型: %T", value)
}
var out []int
if err := json.Unmarshal(data, &out); err != nil {
return err
}
*s = IntSlice(out)
return nil
}
// TaskClassItem 用于和数据库中的 task_items 表进行映射
type TaskClassItem struct {
//section 1
ID int `gorm:"column:id;primaryKey;autoIncrement"`
CategoryID *int `gorm:"column:category_id"` //对应 TaskClass 的 ID
//section 2
Order *int `gorm:"column:order"`
Content *string `gorm:"column:content;type:text"`
EmbeddedTime *TargetTime `gorm:"column:embedded_time;type:json;comment:目标时间{date,section_from,section_to}"`
Status *int `gorm:"column:status;comment:1:未安排, 2:已应用"`
}
// UserAddTaskClassRequest 用于处理用户添加任务类别的请求
type UserAddTaskClassRequest struct {
Name string `json:"name" binding:"required"`
StartDate string `json:"start_date" binding:"required"` // YYYY-MM-DD
EndDate string `json:"end_date" binding:"required"` // YYYY-MM-DD
Mode string `json:"mode" binding:"required,oneof=auto manual"`
SubjectType string `json:"subject_type,omitempty"`
DifficultyLevel string `json:"difficulty_level,omitempty"`
CognitiveIntensity string `json:"cognitive_intensity,omitempty"`
Config UserAddTaskClassConfig `json:"config" binding:"required"`
Items []UserAddTaskClassItemRequest `json:"items" binding:"required"`
}
// UserAddTaskClassConfig 用于处理用户添加任务类别时的配置部分
type UserAddTaskClassConfig struct {
TotalSlots int `json:"total_slots" binding:"required,min=1"`
AllowFillerCourse bool `json:"allow_filler_course"`
Strategy string `json:"strategy" binding:"required,oneof=steady rapid"`
ExcludedSlots []int `json:"excluded_slots"`
ExcludedDaysOfWeek []int `json:"excluded_days_of_week"`
}
// UserAddTaskClassItemRequest 用于处理用户添加任务类别时的任务块部分
type UserAddTaskClassItemRequest struct {
ID int `json:"id,omitempty"` // 任务块的数据库主键 ID查询时返回创建时可省略
Order int `json:"order" binding:"required,min=1"`
Content string `json:"content" binding:"required"`
EmbeddedTime *TargetTime `json:"embedded_time"` // 例: 2025-12-22 1-2节; nil 表示未安排
}
// TargetTime 表示任务块的目标时间
type TargetTime struct {
Week int `json:"week"` // 周次
DayOfWeek int `json:"day_of_week"` // 星期几
SectionFrom int `json:"section_from"` // 起始节次
SectionTo int `json:"section_to"` // 结束节次
}
// UserGetTaskClassesResponse 用于返回用户的任务类列表,展示简要信息
type UserGetTaskClassesResponse struct {
TaskClasses []TaskClassSummary `json:"task_classes"`
}
// TaskClassSummary 提供任务类别的简要信息
type TaskClassSummary struct {
ID int `json:"id"`
Name string `json:"name"`
Mode string `json:"mode"`
Strategy string `json:"strategy"`
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
TotalSlots int `json:"total_slots"`
SubjectType string `json:"subject_type,omitempty"`
DifficultyLevel string `json:"difficulty_level,omitempty"`
CognitiveIntensity string `json:"cognitive_intensity,omitempty"`
}
type UserInsertTaskClassItemToScheduleRequest struct {
Week int `json:"week" binding:"required,min=1"`
DayOfWeek int `json:"day_of_week" binding:"required,min=1,max=7"`
StartSection int `json:"start_section" binding:"required,min=1"`
EndSection int `json:"end_section" binding:"required,min=1,gtefield=StartSection"`
EmbedCourseEventID int `json:"embed_course_event_id"` // 可选,嵌入的课程日程事件 ID
}
type UserInsertTaskClassItemToScheduleRequestBatch struct {
TaskClassID int `json:"task_class_id" binding:"required"`
Items []SingleTaskClassItem `json:"items" binding:"required,dive,required"`
}
type SingleTaskClassItem struct {
TaskItemID int `json:"task_item_id" binding:"required"`
Week int `json:"week" binding:"required,min=1"`
DayOfWeek int `json:"day_of_week" binding:"required,min=1,max=7"`
StartSection int `json:"start_section" binding:"required,min=1"`
EndSection int `json:"end_section" binding:"required,min=1,gtefield=StartSection"`
EmbedCourseEventID int `json:"embed_course_event_id"` // 可选,嵌入的课程日程事件 ID
}
// Value 实现 driver.Valuer 接口,负责将 TargetTime 转换为数据库存储的格式
func (t *TargetTime) Value() (driver.Value, error) {
if t == nil {
return nil, nil
}
// 💡 关键:调用 json.Marshal 将结构体转为 []byte
// 这样 GORM 就能把这一串 JSON 存进数据库的 text/json 字段了
return json.Marshal(t)
}
// Scan 实现 sql.Scanner 接口,负责将数据库中的值转换为 TargetTime 结构体
func (t *TargetTime) Scan(value any) error {
if value == nil {
// 如果数据库是 NULL保持指针对应的对象为零值即可
// 或者在业务层判断 nil
return nil
}
var data []byte
switch v := value.(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return fmt.Errorf("TargetTime: 不支持的扫描类型: %T", value)
}
return json.Unmarshal(data, t)
}
// TableName 指定 TaskClass 对应的数据库表名
func (TaskClass) TableName() string {
return "task_classes"
}
// TableName 指定 TaskClassItem 对应的数据库表名
func (TaskClassItem) TableName() string {
return "task_items"
}
// 任务块状态常量
const (
TaskItemStatusUnscheduled = 1 // 未安排
TaskItemStatusApplied = 2 // 已应用
)