Version: 0.9.46.dev.260427
后端: 1. taskclass 执行闭环继续收紧——Plan / Execute 全面切到“最小工具闭环”视角,明确学习目标/总节数/禁排时段/排除星期默认停留 taskclass 域;未给日期范围时禁止擅自补 start_date/end_date,upsert_task_class 重试前先做写前检查并区分“内部表示修正”与“必须追问用户”的关键时间事实 2. QuickTask / TaskQuery 轻量链路继续收敛——新增 model/taskquery_contract.go 统一查询协议,QuickTaskDeps / start.go 改用 model 层参数;删除 query_tasks / quick_note_create 旧工具实现,避免任务查询与随口记再回流 execute 工具链 3. schedule 微调工具继续瘦身——下线 spread_even / min_context_switch 及其复合规划逻辑,清理 analyze_load / analyze_subjects / analyze_context / analyze_tolerance 等历史能力;execute 顺序策略收敛为局部 move / swap,提示词与工具目录仅暴露当前真实可用工具 4. 执行与时间线体验补齐——execute 为流式 speak 补发归一化尾部,避免 deliver 文案黏连;前端时间线新增 interrupt / status 协议识别、工具事件归并与状态过滤,减少 ToolTrace 重复和会话重建误判 前端: 5. AssistantPanel 适配新版 timeline extra 事件——schedule_agent.ts 补齐 interrupt / status kind,工具调用与结果按摘要/参数/工具名合并,恢复历史时不再把协议事件误判成用户消息
This commit is contained in:
@@ -165,26 +165,6 @@ type analyzeHealthMetrics struct {
|
||||
CanClose bool `json:"can_close"`
|
||||
}
|
||||
|
||||
// AnalyzeLoad 已退出主动优化主链路。
|
||||
func AnalyzeLoad(state *ScheduleState, args map[string]any) string {
|
||||
return encodeAnalyzeFailure("analyze_load", "deprecated", "analyze_load 已退出主动优化链路")
|
||||
}
|
||||
|
||||
// AnalyzeSubjects 已被 analyze_rhythm 吸收。
|
||||
func AnalyzeSubjects(state *ScheduleState, args map[string]any) string {
|
||||
return encodeAnalyzeFailure("analyze_subjects", "deprecated", "analyze_subjects 已被 analyze_rhythm 吸收")
|
||||
}
|
||||
|
||||
// AnalyzeContext 已被 analyze_rhythm 吸收。
|
||||
func AnalyzeContext(state *ScheduleState, args map[string]any) string {
|
||||
return encodeAnalyzeFailure("analyze_context", "deprecated", "analyze_context 已被 analyze_rhythm 吸收")
|
||||
}
|
||||
|
||||
// AnalyzeTolerance 已退出主动优化主链路。
|
||||
func AnalyzeTolerance(state *ScheduleState, args map[string]any) string {
|
||||
return encodeAnalyzeFailure("analyze_tolerance", "deprecated", "analyze_tolerance 已退出主动优化链路")
|
||||
}
|
||||
|
||||
// AnalyzeRhythm 输出认知节奏层面的结构化观察。
|
||||
func AnalyzeRhythm(state *ScheduleState, args map[string]any) string {
|
||||
if state == nil {
|
||||
@@ -958,164 +938,6 @@ func buildSemanticProfileIssues(metrics analyzeSemanticProfileMetrics) []analyze
|
||||
}}
|
||||
}
|
||||
|
||||
func buildAnalyzeHealthDecision(
|
||||
state *ScheduleState,
|
||||
snapshot analyzeHealthSnapshot,
|
||||
) analyzeHealthDecision {
|
||||
base := buildAnalyzeHealthDecisionBase(state, snapshot)
|
||||
decision := analyzeHealthDecision{
|
||||
ShouldContinueOptimize: base.ShouldContinueOptimize,
|
||||
PrimaryProblem: base.PrimaryProblem,
|
||||
ProblemScope: base.ProblemScope,
|
||||
IsForcedImperfection: base.IsForcedImperfection,
|
||||
RecommendedOperation: base.RecommendedOperation,
|
||||
ImprovementSignal: buildHealthImprovementSignal(
|
||||
snapshot.Rhythm,
|
||||
snapshot.Tightness,
|
||||
base.ProblemScope,
|
||||
base.RecommendedOperation,
|
||||
snapshot.Profile,
|
||||
snapshot.Feasibility,
|
||||
),
|
||||
}
|
||||
|
||||
// 1. 只有“高认知相邻”这类当前 P1 真正能靠确定性候选修复的问题,才进入候选枚举。
|
||||
// 2. 若所有合法候选都只是平移/无增益/恶化,则直接回到 close,避免把 LLM 逼成苦力工。
|
||||
// 3. close 永远保留为兜底选项,让 LLM 可以自然收口,而不是为了完成任务感继续乱挪。
|
||||
problem, ok := pickPrimaryHealthProblem(state, snapshot)
|
||||
if !ok || problem.Kind != healthProblemHeavyAdjacent || problem.Pair == nil {
|
||||
decision.Candidates = []analyzeHealthCandidate{
|
||||
buildHealthCloseCandidate("保持当前安排并收口:当前没有可继续处理的候选认知问题。", snapshot, base),
|
||||
}
|
||||
decision.ShouldContinueOptimize = false
|
||||
decision.RecommendedOperation = "close"
|
||||
decision.ImprovementSignal = buildHealthImprovementSignal(
|
||||
snapshot.Rhythm,
|
||||
snapshot.Tightness,
|
||||
decision.ProblemScope,
|
||||
decision.RecommendedOperation,
|
||||
snapshot.Profile,
|
||||
snapshot.Feasibility,
|
||||
)
|
||||
return decision
|
||||
}
|
||||
|
||||
beneficial := buildHealthCandidatesForProblem(state, snapshot, problem)
|
||||
if len(beneficial) == 0 {
|
||||
decision.Candidates = []analyzeHealthCandidate{
|
||||
buildHealthCloseCandidate("保持当前安排并收口:当前所有合法 move / swap 都只会平移、无增益或恶化问题。", snapshot, base),
|
||||
}
|
||||
decision.ShouldContinueOptimize = false
|
||||
decision.RecommendedOperation = "close"
|
||||
if snapshot.Tightness.TightnessLevel == "locked" || snapshot.Tightness.TightnessLevel == "tight" {
|
||||
decision.IsForcedImperfection = true
|
||||
}
|
||||
decision.ImprovementSignal = buildHealthImprovementSignal(
|
||||
snapshot.Rhythm,
|
||||
snapshot.Tightness,
|
||||
decision.ProblemScope,
|
||||
decision.RecommendedOperation,
|
||||
snapshot.Profile,
|
||||
snapshot.Feasibility,
|
||||
)
|
||||
return decision
|
||||
}
|
||||
|
||||
decision.Candidates = append(decision.Candidates, beneficial...)
|
||||
decision.Candidates = append(decision.Candidates,
|
||||
buildHealthCloseCandidate("如果不想继续挪动,也可以保持当前安排并直接收口。", snapshot, base),
|
||||
)
|
||||
decision.ShouldContinueOptimize = true
|
||||
decision.RecommendedOperation = strings.TrimSpace(beneficial[0].Tool)
|
||||
decision.ImprovementSignal = buildHealthImprovementSignal(
|
||||
snapshot.Rhythm,
|
||||
snapshot.Tightness,
|
||||
decision.ProblemScope,
|
||||
decision.RecommendedOperation,
|
||||
snapshot.Profile,
|
||||
snapshot.Feasibility,
|
||||
)
|
||||
return decision
|
||||
}
|
||||
|
||||
func pickPrimaryRhythmProblem(
|
||||
rhythm analyzeRhythmMetrics,
|
||||
tightness analyzeTightnessMetrics,
|
||||
) (summary string, scope *analyzeProblemScope, operation string, ok bool) {
|
||||
type rhythmCandidate struct {
|
||||
score int
|
||||
summary string
|
||||
scope *analyzeProblemScope
|
||||
preferSwap bool
|
||||
}
|
||||
|
||||
candidates := make([]rhythmCandidate, 0, len(rhythm.Days)*2)
|
||||
for _, day := range rhythm.Days {
|
||||
if day.HeavyAdjacent && !shouldTreatHeavyAdjacencyAsAcceptable(rhythm, day) {
|
||||
score := 300 + day.SwitchCount*8 + int(day.Fragmentation*20)
|
||||
candidates = append(candidates, rhythmCandidate{
|
||||
score: score,
|
||||
summary: fmt.Sprintf("第 %d 天存在高认知强度任务相邻,学起来会发紧", day.DayIndex),
|
||||
scope: &analyzeProblemScope{DayRange: []int{day.DayIndex}},
|
||||
preferSwap: true,
|
||||
})
|
||||
}
|
||||
if day.SwitchCount >= 5 && day.Fragmentation >= 0.75 {
|
||||
score := 220 + day.SwitchCount*10 + int(day.Fragmentation*100)
|
||||
candidates = append(candidates, rhythmCandidate{
|
||||
score: score,
|
||||
summary: fmt.Sprintf("第 %d 天切换次数偏多,学习节奏明显发碎", day.DayIndex),
|
||||
scope: &analyzeProblemScope{DayRange: []int{day.DayIndex}},
|
||||
preferSwap: false,
|
||||
})
|
||||
}
|
||||
if day.MaxBlock >= 5 {
|
||||
score := 140 + day.MaxBlock*10
|
||||
candidates = append(candidates, rhythmCandidate{
|
||||
score: score,
|
||||
summary: fmt.Sprintf("第 %d 天连续同科目学习块过长,节奏略显单一", day.DayIndex),
|
||||
scope: &analyzeProblemScope{DayRange: []int{day.DayIndex}},
|
||||
preferSwap: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return "", nil, "close", false
|
||||
}
|
||||
sort.SliceStable(candidates, func(i, j int) bool {
|
||||
if candidates[i].score != candidates[j].score {
|
||||
return candidates[i].score > candidates[j].score
|
||||
}
|
||||
leftDay := 1 << 30
|
||||
rightDay := 1 << 30
|
||||
if candidates[i].scope != nil && len(candidates[i].scope.DayRange) > 0 {
|
||||
leftDay = candidates[i].scope.DayRange[0]
|
||||
}
|
||||
if candidates[j].scope != nil && len(candidates[j].scope.DayRange) > 0 {
|
||||
rightDay = candidates[j].scope.DayRange[0]
|
||||
}
|
||||
return leftDay < rightDay
|
||||
})
|
||||
best := candidates[0]
|
||||
operation = chooseHealthOperation(tightness, best.preferSwap)
|
||||
return best.summary, best.scope, operation, true
|
||||
}
|
||||
|
||||
func chooseHealthOperation(tightness analyzeTightnessMetrics, preferSwap bool) string {
|
||||
switch {
|
||||
case tightness.TightnessLevel == "locked":
|
||||
return "close"
|
||||
case preferSwap && tightness.CrossClassSwapOptions > 0:
|
||||
return "swap"
|
||||
case tightness.LocallyMovableTaskCount > 0:
|
||||
return "move"
|
||||
case tightness.CrossClassSwapOptions > 0:
|
||||
return "swap"
|
||||
default:
|
||||
return "close"
|
||||
}
|
||||
}
|
||||
|
||||
func shouldTreatHeavyAdjacencyAsAcceptable(rhythm analyzeRhythmMetrics, day analyzeContextDay) bool {
|
||||
// 1. 若整体切换本来就少、同类型切换占比很高,说明当前节奏更像“同类硬课顺着学”,
|
||||
// 这类情况不该因为“高认知相邻”四个字就被反复优化。
|
||||
|
||||
Reference in New Issue
Block a user