后端: 1. 移除 list_tasks 读工具,消除与 query_target_tasks 的功能重叠 - 删除backend/newAgent/tools/registry.go 中 list_tools 注册 - 删除 backend/newAgent/tools/schedule/read_tools.go 中 ListTasks 函数及 6个独有辅助函数(formatExistingList / formatSuggestedList / formatPendingList / formatListTasksEmptyResult / looksLikeTaskClassIDList / validateListTasksStatus) - 更新 backend/newAgent/prompt/execute.go:清理全部 list_tasks 相关规则约束并重新编号,统一工具引用为query_target_tasks - 更新 backend/newAgent/prompt/execute_context.go:删除 list_tasks 返回值示例 case 分支 - 更新backend/newAgent/tools/schedule/read_helpers.go / status.go:清理注释中的 list_tasks 引用 2. 新增 execute 历史消息注入改造 handoff 文档 -新建 backend/newAgent/HANDOFF_execute_history_reform.md:记录 msg1 从人工摘要改为真实对话流(user + assistant speak)的改造方案,待后续实施 前端:无 仓库:无
119 lines
4.4 KiB
Go
119 lines
4.4 KiB
Go
package schedule
|
||
|
||
import "slices"
|
||
|
||
// 任务状态常量。
|
||
//
|
||
// 说明:
|
||
// 1. existing 表示“数据库里已经存在的已安排事实”,例如课程表事件、已持久化任务块;
|
||
// 2. suggested 表示“当前轮内存态里的建议落位”,来源可能是粗排结果,也可能是用户确认后的工具预排;
|
||
// 3. pending 表示“仍未落位的真实待安排任务”。
|
||
const (
|
||
TaskStatusExisting = "existing"
|
||
TaskStatusSuggested = "suggested"
|
||
TaskStatusPending = "pending"
|
||
)
|
||
|
||
// IsPendingTask 判断任务是否属于“真实待安排”状态。
|
||
//
|
||
// 并行迁移说明:
|
||
// 1. 只有 pending 且没有 Slots,才视为真正未落位;
|
||
// 2. 旧快照里可能存在“pending 但已有 Slots”的粗排遗留形态,这类任务不应继续算作待安排;
|
||
// 3. 这样可以在不强制清洗旧快照的前提下,先把新旧语义统一到“pending=无落位”。
|
||
func IsPendingTask(task ScheduleTask) bool {
|
||
return task.Status == TaskStatusPending && len(task.Slots) == 0
|
||
}
|
||
|
||
// IsSuggestedTask 判断任务是否属于“建议落位 / 可优化”状态。
|
||
//
|
||
// 并行迁移说明:
|
||
// 1. 新语义使用显式 suggested 状态;
|
||
// 2. 兼容旧 rough_build 快照:pending + Slots 视为 suggested;
|
||
// 3. 兼容旧 place 快照:existing + source=task_item + Duration>0 + Slots 视为 suggested。
|
||
func IsSuggestedTask(task ScheduleTask) bool {
|
||
if len(task.Slots) == 0 {
|
||
return false
|
||
}
|
||
if task.Status == TaskStatusSuggested {
|
||
return true
|
||
}
|
||
if task.Status == TaskStatusPending {
|
||
return true
|
||
}
|
||
if task.Status == TaskStatusExisting && task.Source == "task_item" && task.Duration > 0 {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// IsExistingTask 判断任务是否属于“已确定事实层”。
|
||
//
|
||
// 说明:
|
||
// 1. 这里会主动排除 suggested 兼容形态,避免旧快照里的 existing+Duration>0 被误当成已确定任务;
|
||
// 2. 这样 get_overview 等工具才能稳定区分”事实层 existing”和”建议层 suggested”。
|
||
func IsExistingTask(task ScheduleTask) bool {
|
||
return task.Status == TaskStatusExisting && !IsSuggestedTask(task)
|
||
}
|
||
|
||
// IsPlacedTask 判断任务当前是否已经拥有可操作的落位。
|
||
//
|
||
// 说明:
|
||
// 1. existing 和 suggested 都属于“已落位”;
|
||
// 2. pending 只有在并行迁移兼容形态(pending + Slots)下,才会被 IsSuggestedTask 吸收进来。
|
||
func IsPlacedTask(task ScheduleTask) bool {
|
||
return IsExistingTask(task) || IsSuggestedTask(task)
|
||
}
|
||
|
||
// IsTaskInRequestedClassScope 判断 task_item 是否属于“本轮请求涉及的任务类范围”。
|
||
//
|
||
// 说明:
|
||
// 1. task_class_ids 为空时,视为不做范围裁剪,统一返回 true;
|
||
// 2. 仅 source=task_item 才有 task_class_id 语义,event 不参与该判断;
|
||
// 3. 迁移期若 task_item 缺失 TaskClassID,则在有显式 scope 时按“不在范围内”处理,
|
||
// 避免把域外 pending 误混进本轮粗排/微调。
|
||
func IsTaskInRequestedClassScope(task ScheduleTask, taskClassIDs []int) bool {
|
||
if len(taskClassIDs) == 0 {
|
||
return true
|
||
}
|
||
if task.Source != "task_item" {
|
||
return false
|
||
}
|
||
return task.TaskClassID > 0 && slices.Contains(taskClassIDs, task.TaskClassID)
|
||
}
|
||
|
||
// FilterScheduleStateForTaskClassScope 按“本轮请求的任务类范围”裁剪工具态里的域外 pending。
|
||
//
|
||
// 步骤说明:
|
||
// 1. existing / suggested 一律保留,因为它们已经是事实层或建议层落位,会参与冲突判断;
|
||
// 2. 仅移除“域外真实 pending”,避免粗排校验和读工具把别的任务类误算进来;
|
||
// 3. TaskClasses 元数据也同步按 scope 裁剪,避免 prompt/工具读到无关约束;
|
||
// 4. 这里做就地裁剪,调用方无需再维护第二份 scoped state。
|
||
func FilterScheduleStateForTaskClassScope(state *ScheduleState, taskClassIDs []int) {
|
||
if state == nil || len(taskClassIDs) == 0 {
|
||
return
|
||
}
|
||
|
||
filteredTasks := make([]ScheduleTask, 0, len(state.Tasks))
|
||
for _, task := range state.Tasks {
|
||
if !IsPendingTask(task) {
|
||
filteredTasks = append(filteredTasks, task)
|
||
continue
|
||
}
|
||
if IsTaskInRequestedClassScope(task, taskClassIDs) {
|
||
filteredTasks = append(filteredTasks, task)
|
||
}
|
||
}
|
||
state.Tasks = filteredTasks
|
||
|
||
if len(state.TaskClasses) == 0 {
|
||
return
|
||
}
|
||
filteredMetas := make([]TaskClassMeta, 0, len(state.TaskClasses))
|
||
for _, meta := range state.TaskClasses {
|
||
if slices.Contains(taskClassIDs, meta.ID) {
|
||
filteredMetas = append(filteredMetas, meta)
|
||
}
|
||
}
|
||
state.TaskClasses = filteredMetas
|
||
}
|