## 独立复核确认(初審 sa-137 + 复核 sa-141 双重验证) ### 问题 OpenAI provider 并行 tool call 的发射顺序由 Go map 随机遍历决定,模型 idx 表达的顺序意图丢失。 ### 事实链 1. **累积容器是 map**:`internal/provider/openai.go:732` `toolCalls := make(map[int]*ToolCallDelta)`(key = 模型原始 idx) 2. **两处发射循环直接遍历 map**(Go map 遍历顺序被 runtime 主动随机化): - `openai.go:904-921`(finish_reason=tool_calls 到达时) - `openai_stream_flush.go:23-47`(残留 flush 路径) 3. **下游按到达顺序赋予语义,无任何重排**: - `internal/agent/agent.go:6055-6063`:按事件到达顺序 append 到 `toolCalls` slice 与 `content []ToolUseBlock`(全库 grep 无 `sort.*Index` 消费点) - `agent.go:3938`:副作用工具严格按 slice 顺序串行执行(只读工具才有 pre-execute 并行) 4. **跨 provider 不一致**:Anthropic(`anthropic_stream_dispatch.go:139` 按块序)与 Gemini(`gemini.go:502` 按流序)均保序,仅 OpenAI 路径随机 ### 触发场景 模型一条 assistant 消息返回 2+ 并行 tool call(`delta.tool_calls[{index:0,...},{index:1,...}]`,args 分片累积,finish_reason="tool_calls")→ 发射循环遍历 map → 同一流每次解析顺序不同(2 call 时约 50% 颠倒)。 ### 危害(medium) - 顺序敏感的耦合批次(mkdir→write_file、git_add→git_commit 等模型用 idx 表达意图的对)以随机顺序执行且无告警;batch-coupling 检测器只警告不重排 - 同输入非确定输出:会话转录/回放顺序每次不同,破坏 deterministic-replay 与评测可重复性 - 协议层安全(tool result 按 tool_call_id 配对),无崩溃/错配——故非 high ### 修复方向 两处发射循环前按 key 排序: ```go for _, idx := range slices.Sorted(maps.Keys(toolCalls)) { ... emit ... } ``` `openai.go:904` 与 `openai_stream_flush.go:23` 同改,下游无需改动。 severity: medium | 来源:本轮 provider 深读初審 + 独立复核 subagent 双确认
Activity
topcheer commentedon Oct 16, 2026
复核坐实+已修:PR #4247(两发射循环 keys 排序,伴测 50 轮重排断言发射序恒定)。跨 provider 一致性恢复(Anthropic/Gemini 均保序)。
Merge pull request #4247 from topcheer/fix/issue-4244