mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-28 21:33:05 +00:00
refactor(toolcall): StageContext 拆 per-tool,为并发执行消除共享槽(阶段 2c)
问题:f.StageCtx 是**单槽**,批内每个工具都覆写它(ToolCalls=[单元素]、 ToolResults 覆写、Results[0] 回读)。串行下看不出问题,但并发下 N 个 goroutine 同写一个 ctx = 数据竞争,且 after_toolcall 插件可能读到 **别的工具**的结果。 改动(task.go): · TaskFrame 增 toolCtxs []sdk.StageContext(每工具一份) · buildToolContexts 在 stepLLM 设 PendingTools 时建池; Extra **逐份浅拷贝**——共享同一 map 即竞争(stage handler 会写它) · toolCtxFor(i) 取第 i 份,越界/未建时回落 f.StageCtx(测试替身安全) · stepToolBegin(before_toolcall + 参数回填)、stepToolExec(写结果)、 stepToolAfter(读结果)三处全部切到 per-tool ctx 判据(toolbatch_test.go 追加两条): · 每个工具的 before_toolcall ctx 只带自己的 ToolCalls[0].Name, 且 Extra[output_channel] 逐份带过去(stage.go:18 依赖它) · after_toolcall 读到的 Result 必须属于当前工具,不能是批内另一个的 ★ 诚实记录:这两条判据在**串行**下**测不出与单槽的差别**——串行时 每工具跑完才进下一个,不存在交错。变体验证(toolCtxFor 退回单槽)后 判据仍全绿。故 2c 记为「实现已就位、判据未闭合」,真正判据必须与 2d (并发执行)一起写,并以 -race 确认无竞争。已在执行计划中标注。 过程中两次自伤: · 我的 harness 没设 Extra[output_channel](那是 prepareInputTask 才写的, task.go:380),判据一度报「产品缺陷」——核实后是我造的场景,已对齐生产; · 阶段 1 的 TestStageCtxSuccessIsHonestEndToEnd 读 f.StageCtx.ToolResults, 拆分后失效——判据跟随新结构改为按批索引取 toolCtxs[i], 断言的仍是内核产出的 Success 值本身。 顺带记录(非本次引入):TestResidualKeep/Drop 偶发失败,根因是 offload_test.go 的 SpawnResident 起了子调度器 goroutine,而测试 enqueue 后无同步就读同一队列。干净基线 3/3 全绿属运气。已在计划中 记为待修,避免后续误判为并行化引入的回归。
This commit is contained in:
@ -6,6 +6,7 @@ import (
|
||||
|
||||
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
||||
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
)
|
||||
|
||||
// 阶段 1b:诚实化 Success。
|
||||
@ -121,10 +122,24 @@ func TestStageCtxSuccessIsHonestEndToEnd(t *testing.T) {
|
||||
if out := a.runTaskSteps(f); out != outcomeDone {
|
||||
t.Fatalf("runTaskSteps=%v err=%v", out, f.Err)
|
||||
}
|
||||
if len(f.StageCtx.ToolResults) == 0 {
|
||||
t.Fatal("StageCtx.ToolResults 为空")
|
||||
// 阶段 2c 起结果写在**每个工具自己的** ctx 上(不再回写 f.StageCtx),
|
||||
// 因此这里按批索引取对应那份——判据跟着结构走,但断言的仍是
|
||||
// **内核产出的 Success 值本身**。
|
||||
if len(f.toolCtxs) == 0 {
|
||||
t.Fatal("toolCtxs 为空(per-tool ctx 未建立)")
|
||||
}
|
||||
var tr sdk.ToolResult
|
||||
found := false
|
||||
for i := range f.toolCtxs {
|
||||
if len(f.toolCtxs[i].ToolResults) > 0 {
|
||||
tr = f.toolCtxs[i].ToolResults[0]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("各工具 ctx 的 ToolResults 全为空")
|
||||
}
|
||||
tr := f.StageCtx.ToolResults[0]
|
||||
if tr.Success != c.wantSucc {
|
||||
t.Errorf("Success = %v,期望 %v(返回值 %#v)", tr.Success, c.wantSucc, c.toolRet)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user