修复: 命名空间上限与网络域解析兜底

- 命名空间超容单条拒绝,上限不变式恢复
- usage 部分字段缺失致整帧内容蒸发,补缺省兜底
- tool_call 缺 id 致整批丢弃,对齐流式 fallback 口径
- 视觉模型判定改词素尾,deepseek-v3 不再误判
- 并行分支 panic 任务对账补齐为失败结果
- plan 执行器死代码清理,实况注释校正
This commit is contained in:
lxy
2026-10-03 17:57:33 +08:00
parent 3e3c6f4d0f
commit 8e09112d59
8 changed files with 259 additions and 19 deletions
+70 -1
View File
@@ -508,16 +508,52 @@ impl Coordinator {
});
}
// 记录最近一次 JoinError 详情:JoinError 不携带 subtask 身份,
// 缺失任务靠循环结束后按 id 对账补齐,错误文本由此带出
let mut join_err_msg: Option<String> = None;
while let Some(join_result) = join_set.join_next().await {
match join_result {
Ok(result) => {
results_map.insert(result.subtask_id.clone(), result);
}
Err(e) => {
tracing::error!("[COORDINATOR] 子任务 panic: {}", e);
// panic 与取消分开留痕:panic 是执行体自身崩溃需排查,取消是外部干预
if e.is_panic() {
tracing::error!("[COORDINATOR] 子任务 panic: {}", e);
} else {
tracing::warn!("[COORDINATOR] 子任务被取消: {}", e);
}
join_err_msg = Some(e.to_string());
}
}
}
// panic/取消的子任务不能凭空消失:返回契约是与 plan.tasks 一一对应,
// 缺失会被下游误读为"无此任务";此处按该任务失败语义补失败结果收敛,
// merge 按 success 过滤自然跳过,不扩散中断整层
for task in layer {
if !results_map.contains_key(&task.id) {
tracing::error!(
subtask = %task.id,
"[COORDINATOR] 子任务执行体崩溃,按失败结果收敛(不扩散中断)"
);
results_map.insert(
task.id.clone(),
ExecutionResult {
subtask_id: task.id.clone(),
persona_id: task_persona
.get(&task.id)
.cloned()
.unwrap_or_default(),
output: join_err_msg
.clone()
.unwrap_or_else(|| "子任务 panic/被取消".to_string()),
success: false,
},
);
}
}
} else {
tracing::info!(
layer = layer_idx,
@@ -1415,6 +1451,39 @@ mod tests {
assert_eq!(results[1].subtask_id, "b");
}
#[tokio::test]
async fn par_05_panicked_task_converges_to_failed_result() {
// 同层并行中某子任务 panic:JoinSet 返回 JoinError(panic 变体),
// 应收敛为该任务的失败结果,而不是让它从结果列表凭空消失
let coord = make_coord();
let plan = Plan::from_tasks(vec![
SubTask::new("t1", "task1"),
SubTask::new("t2", "task2"),
SubTask::new("t3", "task3"),
]);
let results = coord
.dispatch_with_budget(&plan, |task, persona_id: String| async move {
if task.id == "t2" {
panic!("模拟子任务执行体崩溃");
}
ExecutionResult {
subtask_id: task.id,
persona_id,
output: format!("done {}", task.intent),
success: true,
}
}, None)
.await;
assert_eq!(results.len(), 3, "panic 子任务也应占一个结果位");
let t2 = results.iter().find(|r| r.subtask_id == "t2").expect("t2 应有失败结果");
assert!(!t2.success, "panic 子任务应收敛为失败而非成功");
assert!(!t2.output.is_empty(), "失败结果应带 JoinError 详情");
assert!(results.iter().filter(|r| r.subtask_id != "t2").all(|r| r.success),
"其余子任务不受 panic 扩散影响");
}
// -- Plan-driven LLM 规划开关 --
#[test]