修复: 命名空间上限与网络域解析兜底
- 命名空间超容单条拒绝,上限不变式恢复 - usage 部分字段缺失致整帧内容蒸发,补缺省兜底 - tool_call 缺 id 致整批丢弃,对齐流式 fallback 口径 - 视觉模型判定改词素尾,deepseek-v3 不再误判 - 并行分支 panic 任务对账补齐为失败结果 - plan 执行器死代码清理,实况注释校正
This commit is contained in:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user