mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-04 00:03:59 +00:00
feat(scheduler): M4 临界区显式化 + 清掉被 pendingInterrupts 取代的 interceptCh
设计依据 docs/zh/input-scheduler-design.md §4.3、§11.1(P5/P6)。 - 临界区语义显式化:让位检查**只在 step 之间**做,执行中的 step (工具 RPC / ONNX / CAS 落盘)天然不可抢占;_consolidation_ 整任务 经 inCriticalSection() 判为不可抢占(它直接改图库) - 删除 interceptCh 与 drainInterrupts:M3b 起中断一律走 pendingInterrupts, 旧的「同行注入 + 三处 drain + 批次放弃」已无写入者,属死代码 - 新增 scheduler_critical_test.go 3 项: P5/P6 工具执行中 arm 了让位信号也不得挂起、必须等工具返回后的安全点; _consolidation_ 判为临界区;无抢占时同批工具必须全部执行(新语义回归) - 验收:agent 全量 + -race;全仓 build/vet 通过
This commit is contained in:
@ -152,54 +152,6 @@ func TestTaskFrame_X3_TerminatesWithinBudget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 中断(interceptCh)在工具批中途到达时:本批**剩余工具被放弃**,直接进入下一轮。
|
||||
//
|
||||
// 这是原实现的 `break` 语义(process.go 旧版工具循环尾部),必须保持。
|
||||
func TestTaskFrame_InterruptAbandonsRemainingBatch(t *testing.T) {
|
||||
sh := NewStageHost()
|
||||
var a *Agent
|
||||
sh.RegisterTool("t_first", sdk.ToolDef{Name: "t_first", Plugin: "t"}, func(args map[string]interface{}) (interface{}, error) {
|
||||
// 工具执行期间产生一次中断(模拟插件在工具里注入打断)。
|
||||
a.interceptCh <- &agentIO.InputEvent{Source: "t", Payload: map[string]interface{}{"content": "新的用户输入"}}
|
||||
return "first-out", nil
|
||||
})
|
||||
sh.RegisterTool("t_second", sdk.ToolDef{Name: "t_second", Plugin: "t"}, func(args map[string]interface{}) (interface{}, error) {
|
||||
t.Fatal("批内第二个工具不应被执行:中断必须放弃剩余批次")
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
sp := &scriptProvider{script: []*agentAPI.CompletionResponse{
|
||||
{Content: "", ToolCalls: []agentAPI.ToolCall{tc("c1", "t_first"), tc("c2", "t_second")}},
|
||||
{Content: "处理完中断后的答复"},
|
||||
}}
|
||||
a = newTaskTestAgent(t, sp, sh)
|
||||
|
||||
resp, toolsUsed, toolResults, err := a.process("开始", a.stageCtxFromInput("开始", "", ""))
|
||||
if err != nil {
|
||||
t.Fatalf("process 返回错误: %v", err)
|
||||
}
|
||||
if resp != "处理完中断后的答复" {
|
||||
t.Fatalf("响应=%q", resp)
|
||||
}
|
||||
if len(toolsUsed) != 1 || toolsUsed[0] != "t_first" {
|
||||
t.Fatalf("toolsUsed=%v,期望只有 t_first", toolsUsed)
|
||||
}
|
||||
if len(toolResults) != 1 || toolResults[0].Output != "first-out" {
|
||||
t.Fatalf("toolResults=%+v,期望只有 t_first 的结果", toolResults)
|
||||
}
|
||||
|
||||
// 第二轮请求里必须出现被打断内容(system 角色、[中断消息] 前缀)。
|
||||
found := false
|
||||
for _, m := range sp.reqs[1].Messages {
|
||||
if m.Role == "system" && len(m.Content) > 0 && m.Content[0:len("[中断消息]")] == "[中断消息]" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("第二轮请求缺少 [中断消息] system 消息")
|
||||
}
|
||||
}
|
||||
|
||||
// 状态机对未知 step 必须失败退出而不是空转。
|
||||
func TestTaskFrame_UnknownStepFails(t *testing.T) {
|
||||
sp := &scriptProvider{}
|
||||
|
||||
Reference in New Issue
Block a user