# run_15(上下文:编排超时兜底修复回归) - 类型:编排型(HTTP/2 vs HTTP/3 对比,「对比」命中编排信号词) - 目的:验证「编排 150s 硬截止兜底」在 ctx 贯穿 + agent 轮次 ctx 检查 + executeSubTasks wall-clock 截止后真正生效 - 结果:✅ **HTTP 200,150.0s 收敛**(此前同用例 run_14 为 210s 无限挂起,curl 200s 0 字节) - 时间:2026-09-25 21:30:22 → 21:32:52 ## 修复内容(本回归所验证) 1. `llm.go`:所有 LLM 调用(Generate/GenerateFull/GenerateStream/xxxFallback/Embed)加 `context.Context` 参数,`post` 用 `http.NewRequestWithContext`,使上层 ctx(编排 150s 兜底)真正能取消进行中的 HTTP 请求。 2. `agent.go RunWithUsage`:每轮 tool_calls 前检查 `ctx.Err()`,ctx 取消立即退出,避免慢 worker 靠单次已发起成功的 HTTP 响应续命拖死 `wg.Wait()`。 3. `orchestrate.go executeSubTasks`:分批 worker 的 `wg.Wait()` 改为与 `ctx.Done()` 竞争(select),到点不等最慢 worker,**150s 硬截止返回当前已完成产物**。 ## 效果对照 | 维度 | 修复前(run_14) | 修复后(run_15) | |---|---|---| | 请求收敛 | 210s 无限挂起,curl 200s 0 字节 | ✅ 150s HTTP 200 返回 | | 后续请求 | 被阻塞(run_14 曾连带超时) | ✅ 不阻塞 | | ctx 传导 | 失效 | ✅ ai_call_log 出现 `context deadline exceeded` 的快速失败(0ms)| | 产物 | - | 超时兜底返回「已按子任务完成,但结果为空」+ 部分子任务标题 | ## 多策略修复后 → 成功(2026-09-25 22:02) 对「worker 空回复 / tool_calls 死循环」叠加三重策略后重跑同用例(request 见 `request.json`,结果存 `response_success.json`): | 指标 | 结果 | |---|---| | 收敛时长 | ✅ **94s**(无需 150s 兜底,正常完成) | | 产物体量 | ✅ 约 19KB 深度对比正文(含核心判断/共同点/各自问题/RTT/传输效率/选型/来源引用) | | 编排完整性 | ✅ plan(5子任务)→worker 执行→merge→审核 worker 全部跑通 | | ai_call_log | 4 个 worker 1.4-2.1s 极快产出(无工具收敛路径)+ 2 个 worker 21-23s + 主回复 94s | ### 三重策略明细(`internal/ai/agent.go` + `orchestrate.go`) 1. **roundWithEmptyRecovery**:单轮「空响应」→ 带工具重试一次 → 仍空降级「无工具纯文本」重试,兼容「带工具即空、纯文本正常」的路由。 2. **tool 死循环收敛**:`RunWithUsage` 统计连续「只发工具调用、无结论文本」轮数,≥3 轮即中断,用无工具纯文本基于已回填的搜索结果重生成结论——直接掐断模型反复搜索不下结论的死循环。 3. **worker 级兜底**:`executeSubTasks.runOne` 在 agent 失败/空时,用无工具 `GenerateWithFallback` 再生成一次,保证每个 worker 至少有可拼接内容。 ## 遗留问题(非本次修复范围) - 部分子任务产物仍含模型原样输出的 `|tool|` 转义残留(个别 worker 未完全收敛时把工具调用文本化),但最终 merge 正文干净,不影响主交付。 ## 回归确认(修改未误伤其它路径) | 用例 | 路径 | 结果 | |---|---|---| | run_03(周末闲聊) | direct_answer | ✅ HTTP 200,4.2s,内容完整(纯文本直答,无产物) | | run_12(2026 诺贝尔奖联网查) | single_search / orchestrated | ✅ HTTP 收敛返回(ai_call_log 记录 123s success;curl 90s 断开属本端上限,服务端实际 123s 完成,不挂死) | ## 下一步建议 - 单考 worker 空回复:在 `RunWithUsage` 或 worker 侧对「模型最终回复为空」做一次退避重试;或对不兼容 tool_calls 的路由降级为无工具生成。 - 已确认修改未误伤 direct_answer / single_search 路径;剩 worker 空回复与单问调用偏慢,根因是当前路由 `deepseek-v4-flash` 对带 web_search 工具的多轮 tool_calls 支持不佳,属独立问题待商定。