后端: 1.粗排链路收口(按 task_class_ids 精确加载 ScheduleState + 规划窗口抗脏数据) - 更新conv/schedule_provider.go:新增 LoadScheduleStateForTaskClasses;优先按本轮任务类加载窗口;buildWindowFromTaskClasses 改为逐条过滤坏日期,避免 DayMapping 被全量任务类污染 - 更新model/state_store.go:新增 ScopedScheduleStateProvider 可选接口 - 更新model/graph_run_state.go:EnsureScheduleState 首次加载时优先走 scoped provider,再做 scope 裁剪 2.粗排建议态语义统一(pending/existing → pending/suggested/existing) - 新建tools/status.go:统一 IsPendingTask / IsSuggestedTask / IsExistingTask / scope 过滤逻辑 - 更新node/rough_build.go:粗排回写后任务显式转 suggested;pending 统计仅看“真实 pending” - 更新tools/state.go:ScheduleTask.Status/Slots/Duration 注释补齐 suggested 语义 - 更新tools/read_helpers.go + read_tools.go:overview/list_tasks/task_info 支持 suggested 展示;占用计算按“已落位任务”统一处理 - 更新tools/write_helpers.go + write_tools.go:place/move/swap/unplace 全量切到 suggested/existing/pending 新语义 - 更新tools/registry.go + SCHEDULE_TOOLS.md:工具描述、参数枚举、文档口径同步到 suggested 语义 - 更新conv/schedule_preview.go:预览层统一通过 IsSuggestedTask 输出 suggested,兼容旧快照 - 更新service/agentsvc/agent_newagent.go:预览 debug 摘要改为 pending/suggested/existing 三态统计 3.粗排调试增强 - 更新node/rough_build.go:新增 applied/day_mapping_miss/task_item_match_miss 统计及样本日志,便于排查 placement 未落回 state 的根因 前端:无 仓库:无
122 lines
3.2 KiB
Go
122 lines
3.2 KiB
Go
package conv
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/LoveLosita/smartflow/backend/model"
|
||
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
||
)
|
||
|
||
// ScheduleStateToPreview 将 newAgent 的 ScheduleState 转换为前端预览缓存格式。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只做数据格式转换,不做业务逻辑;
|
||
// 2. 将每个 ScheduleTask 的每个 TaskSlot 转为一条 HybridScheduleEntry;
|
||
// 3. Day → (Week, DayOfWeek) 通过 ScheduleState.DayToWeekDay 转换;
|
||
// 4. 转换失败的 slot(day_index 无效)静默跳过。
|
||
func ScheduleStateToPreview(
|
||
state *newagenttools.ScheduleState,
|
||
userID int,
|
||
conversationID string,
|
||
taskClassIDs []int,
|
||
summary string,
|
||
) *model.SchedulePlanPreviewCache {
|
||
if state == nil {
|
||
return nil
|
||
}
|
||
|
||
entries := make([]model.HybridScheduleEntry, 0, len(state.Tasks))
|
||
for i := range state.Tasks {
|
||
t := &state.Tasks[i]
|
||
// 待安排且无位置的任务不生成 entry。
|
||
if newagenttools.IsPendingTask(*t) {
|
||
continue
|
||
}
|
||
|
||
for _, slot := range t.Slots {
|
||
week, dayOfWeek, ok := state.DayToWeekDay(slot.Day)
|
||
if !ok {
|
||
continue
|
||
}
|
||
|
||
entry := model.HybridScheduleEntry{
|
||
Week: week,
|
||
DayOfWeek: dayOfWeek,
|
||
SectionFrom: slot.SlotStart,
|
||
SectionTo: slot.SlotEnd,
|
||
Name: t.Name,
|
||
}
|
||
|
||
// Type 映射。
|
||
if t.Source == "event" {
|
||
if t.EventType != "" {
|
||
entry.Type = t.EventType
|
||
} else {
|
||
entry.Type = "course"
|
||
}
|
||
} else {
|
||
entry.Type = "task"
|
||
}
|
||
|
||
// Status 映射:existing 不变,suggested / 兼容建议态统一输出为 suggested。
|
||
if shouldMarkSuggestedInPreview(*t) {
|
||
entry.Status = "suggested"
|
||
} else {
|
||
entry.Status = "existing"
|
||
}
|
||
|
||
// ID 映射。
|
||
if t.Source == "event" {
|
||
entry.EventID = t.SourceID
|
||
} else {
|
||
entry.TaskItemID = t.SourceID
|
||
}
|
||
|
||
// 嵌入与阻塞语义。
|
||
entry.CanBeEmbedded = t.CanEmbed
|
||
if t.Source == "event" && t.CanEmbed && t.EmbeddedBy == nil {
|
||
// 可嵌入且当前无嵌入任务 → 不阻塞 suggested 占位。
|
||
entry.BlockForSuggested = false
|
||
} else {
|
||
entry.BlockForSuggested = true
|
||
}
|
||
|
||
entries = append(entries, entry)
|
||
}
|
||
}
|
||
|
||
// 生成摘要(若调用方未提供)。
|
||
if summary == "" {
|
||
existingCount := 0
|
||
suggestedCount := 0
|
||
for _, e := range entries {
|
||
if e.Status == "existing" {
|
||
existingCount++
|
||
} else {
|
||
suggestedCount++
|
||
}
|
||
}
|
||
summary = fmt.Sprintf("共 %d 个日程条目,其中已确定 %d 个,新安排 %d 个。", len(entries), existingCount, suggestedCount)
|
||
}
|
||
|
||
return &model.SchedulePlanPreviewCache{
|
||
UserID: userID,
|
||
ConversationID: conversationID,
|
||
Summary: summary,
|
||
HybridEntries: entries,
|
||
TaskClassIDs: taskClassIDs,
|
||
GeneratedAt: time.Now(),
|
||
}
|
||
}
|
||
|
||
// shouldMarkSuggestedInPreview 判断某条 ScheduleTask 在预览层是否应标记为 suggested。
|
||
//
|
||
// 规则说明:
|
||
// 1. 新语义下,显式 suggested 直接输出为建议态;
|
||
// 2. 兼容旧快照:pending+Slots、existing+Duration>0 的 task_item 也继续按 suggested 输出;
|
||
// 3. 这样前端预览口径可以在迁移期保持稳定,不会因为状态枚举切换而抖动。
|
||
func shouldMarkSuggestedInPreview(t newagenttools.ScheduleTask) bool {
|
||
return newagenttools.IsSuggestedTask(t)
|
||
}
|