|
|
4fd18a2e83
|
docs(plan): 遗留项收敛——D4 与端到端已完成,提权无需决策
|
2026-09-27 13:18:28 +08:00 |
|
|
|
532500e3c9
|
docs(plan): 内核主线与插件线全部完成,记录两个设计决策与三处遗留
|
2026-09-27 13:06:10 +08:00 |
|
|
|
ae3cdaa1a0
|
docs: 同步两份文档的实现进度(内核主线 0~2.5 全部完成)
|
2026-09-27 11:56:01 +08:00 |
|
|
|
446645d0a0
|
docs(plan): 阶段 2 标记完成,记录并发规则与 2c 判据闭合
|
2026-09-27 11:27:53 +08:00 |
|
|
|
3d12e82f65
|
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 全绿属运气。已在计划中
记为待修,避免后续误判为并行化引入的回归。
|
2026-09-27 11:17:06 +08:00 |
|
|
|
4ba72977d2
|
test(toolcall): 补批内路径的三条缺失判据(阶段 0.5)
同一批多个 tool_call 的循环(StepToolBegin→Exec→After)此前只被
scheduler_critical_test.go:125 一条用例覆盖「按序执行」,缺的三条正是
阶段 2(并行执行层)要改的地方:
· tool_call_id 配对完整性 —— 阶段 2 改消息落法(一个 assistant 带全部
tool_calls + N 条 tool)时,配对断裂上游会直接报错
· ContentOnce 批内语义 —— 同一段 assistant 文本在批内重复 N 次,撑爆上下文
· denied 后继续批内 —— 改成 abort 会丢掉本可执行的后续调用
判据 toolbatch_test.go(4 条),全部确定性断言:阶段 0 已消除 map
迭代随机性,同批工具的落序与配对可稳定断言。
变异验证:令 stepToolBegin 跳过批内最后一个工具后,4 条判据同时 FAIL
(既有那条也 FAIL),报错直指 ToolsUsed=[tool_alpha]、
tool_call_id "c2" 被声明 0 次。
更正一处此前的不准确表述:我曾说「无任何测试直接驱动批内路径」——
不准确。scheduler_critical_test.go:125 已驱动「同批两工具按序执行」;
漏查是因为只 grep 了 PendingTools/ToolIdx 字段名,没查断言内容。
真正缺的是上表三条。
过程中三次自伤(均由「判据先写」暴露):臆造不存在的 helper;
stageHost 置 nil 后又使用;给 newTaskFrame 传 nil 导致 stepPrepare 于
task.go:519 nil 解引用 panic(改用仓内既有 a.stageCtxFromInput)。
顺带记录:生产两处 newTaskFrame 调用都传真实 ctx,但 stepPrepare 对
f.StageCtx 无 nil 兜底——本次不修(无生产触发路径),记为潜在缺口。
回归:internal/agent/... 与 internal/plugins/... 全绿。
|
2026-09-27 08:59:30 +08:00 |
|
|
|
7a566d50b7
|
fix(toolcall): 工具「不存在」类型化 + 修父 io 兜底吞错误 + 修并行 tool_call 落序随机
主线:工具调用并行化改造(阶段 0 与 0.2)。
① flush 顺序随机(process.go)
flushToolCall 由 `for idx := range accs` 驱动,Go map 迭代顺序随机化
⇒ 同一批并行 tool_call 进入 resp.ToolCalls 的顺序每次运行都可能不同。
对 output_send__ 这类用户可见通道,分段消息到达顺序不可复现。
改为收集 index 后 sort.Ints 再 flush(两个调用点统一走 flushAll)。
判据 stream_flush_order_test.go(8 工具 × 200 轮),已变异验证可检测。
② 工具「不存在」类型化(io/channel.go、core/stages.go、core/toolcall.go)
工具是动态注册的,「不存在」是运行期常态而非异常。原先内核用
strings.Contains(err, "not found in any plugin") 判别——约定而非契约,
插件文案含该子串即被误判。改用哨兵 ErrToolNotFound + errors.Is
(沿用仓内 ErrInputChannelUnknown 的先例)。
⚠️ 顺带修一个静默 bug:IOManager 向父兜底时吞掉父的执行失败,
误报为「工具不存在」。后果是设备离线这类本该 retry 的失败被判为
「工具没了」⇒ 整组被跳过,与「插件真没加载」无法区分。改为只传递
「确实不存在」,其余如实上抛。
「不存在」的文案改为可执行指引(get_plugin_tools / output_list_channels),
而非含糊的「执行失败」——后者会让模型反复重试同一个不存在的名字。
判据:toolcall_error_test.go(类型化 vs 诱饵子串、%w 穿透、执行期文案)、
channel_error_test.go(父失败不吞、真的不存在仍可判别)。
两者均经变异验证。回归:internal/agent/... 与 internal/plugins/... 全绿(14 包)。
设计文档:docs/zh/toolcall-contract-and-sequence-design.md
执行计划:docs/zh/toolcall-parallel-execution-plan.md
|
2026-09-27 08:55:25 +08:00 |
|