后端: 1. 排程工具从 tools/ 根目录拆分为 tools/schedule 独立子包 - 12 个排程工具文件等价迁入 tools/schedule/,tools/ 根目录仅保留 registry.go 作为统一注册入口 - 所有依赖方(conv / model / node / prompt / service)import 统一切到 schedule 子包 2. Web 搜索工具链落地(tools/web 子包) - 新增 web_search(结构化检索)与 web_fetch(正文抓取)两个读工具,支持博查 API / mock 降级 - 启动流程按配置选择 provider,未识别类型自动降级为 mock,不阻断主流程 - 执行提示补齐 web 工具使用约束与返回值示例 - config.example.yaml 补齐 websearch 配置段 前端:无 仓库:无
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. 这样 list_tasks / 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
|
||
}
|