feat(scheduler)!: 中断/排队两类别模型 + 插件声明 L1-L3、L4 内核独占

用户澄清推翻了早期设计的三处前提,本提交按新模型重做调度核心(行为有意变化):

1) 类别由注入 API 决定,与通道名无关
   - InjectInterrupt*                      -> TaskInterrupt(带级别,可被严格更高级中断打断)
   - InjectText*/InjectInputSync*/内核自循环 -> TaskQueued(无级别,可被任何中断打断)
   - 删除按通道名推断的 taskLevel():qq 走 InjectInterruptTextOpts,本就是中断

2) 级别只属于中断
   - 插件在 InjectOptions.Priority 声明 L1-L3(空/非法降级 L1,声明 L4 夹到 L3)
   - L4 内核独占:新增 raiseKernelInterrupt(panic / selfip);requestKernelPreempt 不夹取
   - panic 现在产生一条带 kernel 标记的 L4 中断;L4 自身 panic 不再产生新 L4(防自我放大)

3) 选择结构:四容器固定次序,删除统一比较器
   - immediate(抢占者立即运行)-> 中断队列 L4..L1 -> 栈顶(与队头比级别) -> 排队 FIFO
   - 删除 pickTaskIndex/taskBefore 与“同级 pending 优先”补丁(根因是抢占者进了队列)
   - 中断栈上界改为结构推论 = 4(= 中断级数);删除“超限转 pendingInterrupts”降级

公开 SDK(feature 分支有意新增,纯追加):InjectOptions.Priority + PriorityL1/2/3;
内核 io / proc 桥 / 插件模板同步透传。

设计稿 §2/§3/§4.1/§6.3/§9/§11/§12/§13/§15 按新模型重写。

验收:go build/vet 干净;go test ./... 37 包 ok 0 FAIL;-race 全绿;
e2e(抢占-挂起-恢复)+ 压力(200 排队 + 50 中断,L1/L2/L3 轮转)通过。
This commit is contained in:
JianFeeeee
2026-09-13 06:57:23 +08:00
parent 86a702b3b0
commit cb32032f76
16 changed files with 1021 additions and 443 deletions

View File

@ -1435,7 +1435,12 @@ func (p *Plugin) handleWebhook(w http.ResponseWriter, r *http.Request) {
if p.sdk != nil {
// NoMemoryHTTP 侧来的中断提示,不是对话内容。
p.sdk.InjectInterruptTextOpts(p.name, p.name, interrupt, sdk.InjectOptions{NoMemory: true})
// PriorityQQ 消息是**低级别中断**——既不是时钟那样的实时工作,
// 也不是紧急工作,所以声明 L1完全可等
p.sdk.InjectInterruptTextOpts(p.name, p.name, interrupt, sdk.InjectOptions{
NoMemory: true,
Priority: sdk.PriorityL1,
})
}
w.WriteHeader(http.StatusOK)
}
@ -2531,9 +2536,10 @@ func (p *Plugin) handleDownloadFile(args map[string]interface{}) (interface{}, e
log.Printf("[qq] 文件下载完成: %s", savePath)
if p.sdk != nil {
// NoMemory下载完成的状态通知不是记忆内容。
// Priority同上QQ 侧一律低级别中断L1
p.sdk.InjectInterruptTextOpts(p.name, p.name,
fmt.Sprintf("文件下载完成: %s保存在 %s", filepath.Base(savePath), savePath),
sdk.InjectOptions{NoMemory: true})
sdk.InjectOptions{NoMemory: true, Priority: sdk.PriorityL1})
}
} else {
errMsg = "下载失败,文件可能已过期"

View File

@ -78,8 +78,24 @@ type InjectOptions struct {
NoMemory bool
ContextPolicy string
CleanerName string
// Priority 声明**中断注入**的优先级(仅 InjectInterrupt* 有意义)。
//
// 取值 "L1"/"L2"/"L3";空等同 L1。L4 由内核独占panic / 内核事件 selfip
// 插件声明 L4 会被内核夹到 L3——内核的调度内部属性不接受外部越权。
//
// 排队注入InjectText*/InjectInputSync没有级别它们本就是“不需及时处理”
// 的那一类,可被任何中断打断。
Priority string
}
// 中断优先级取值插件可用范围。L4 不在其中:它由内核保留。
const (
PriorityL1 = "L1"
PriorityL2 = "L2"
PriorityL3 = "L3"
)
// ChannelDef 描述通道在记忆计算层的行为,与 ToolDef.NoMemory/Cleaner 语义一致。
// NoMemory: 此通道输入/输出不参与记忆计算(向量化/关键词提取/蒸馏),但原文保留在上下文中
// Cleaner: 计算层过滤函数,不改原文;仅在向量化/jieba/蒸馏/存档提取关键词时调用