fix(subagent): 失败/超时子智能体返回结构化结果,支持前端点击查看详情
spawn()/resume() 在会话创建后的失败(含超时、执行错误、AgentCreationFailed)
现返回 Ok(TaskToolResult { status: "failed"/"timeout", task_id }) 而非 Err(e),
使 tool_result 内容始终为结构化 JSON。前端已有的 taskStatusConfig、StatusIcon、
"查看完整会话"按钮自动生效,同时修复服务重启后点击入口丢失的问题。
- runtime.rs: 提取 handle_task_failure 辅助方法统一处理失败;将 model_resolver/
create_subagent 的 ? 纳入 result 块,确保所有会话后失败都返回结构化结果;
修复 resume() 未区分 timeout 的 bug
- tool.rs: 根据 task_result.status 判断 success,失败时 output 仍为 JSON
- agent_loop.rs: 失败时 result.output 非空则透传(仅影响 task 工具),其他工具回退
到 "Error: {error}" 纯文本
This commit is contained in:
parent
4e74d66a7c
commit
1e9075e1ed
@ -1752,7 +1752,14 @@ impl AgentLoop {
|
||||
output = %result.output,
|
||||
"Tool returned an error result"
|
||||
);
|
||||
ToolExecutionOutcome::failure(format!("Error: {}", error), Some(error))
|
||||
// task 工具在失败时提供结构化 JSON output(含 task_id),直接透传;
|
||||
// 其他工具 output 为空,走纯文本 "Error: {error}" 回退。
|
||||
let failure_output = if !result.output.is_empty() {
|
||||
result.output
|
||||
} else {
|
||||
format!("Error: {}", error)
|
||||
};
|
||||
ToolExecutionOutcome::failure(failure_output, Some(error))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@ -648,6 +648,39 @@ impl DefaultSubAgentRuntime {
|
||||
Err(_) => Err(TaskError::Timeout),
|
||||
}
|
||||
}
|
||||
|
||||
/// 会话创建后的失败处理:标记状态、持久化、发布错误事件、返回结构化失败结果。
|
||||
///
|
||||
/// 返回 `Ok(TaskToolResult)` 而非 `Err`,确保 tool_result 携带 `task_id` 供前端导航。
|
||||
/// `save_task_session` 失败是基础设施故障,仍通过 `?` 返回 `Err`。
|
||||
async fn handle_task_failure(
|
||||
&self,
|
||||
session: TaskSession,
|
||||
error: TaskError,
|
||||
) -> Result<TaskToolResult, TaskError> {
|
||||
let status = error.as_status();
|
||||
tracing::warn!(
|
||||
task_id = %session.id,
|
||||
session_id = %session.session_id,
|
||||
status = %status,
|
||||
error = %error,
|
||||
"Task failed, updating session"
|
||||
);
|
||||
let mut session = session;
|
||||
if status == "timeout" {
|
||||
session.mark_timeout();
|
||||
} else {
|
||||
session.mark_failed(error.to_string());
|
||||
}
|
||||
self.task_repository.save_task_session(&session).await?;
|
||||
publish_subagent_error(&self.bus, &session, &error.to_string()).await;
|
||||
Ok(TaskToolResult {
|
||||
status: status.to_string(),
|
||||
summary: error.to_string(),
|
||||
output: String::new(),
|
||||
task_id: session.id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -763,6 +796,10 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
// 6-8. 构建提示词、创建子代理、执行任务
|
||||
// 统一为单个 Result 表达式:model_resolver / create_subagent / execute_task
|
||||
// 的任何失败都流入下方 match 的 Err 分支,经 handle_task_failure 返回结构化结果。
|
||||
let result: Result<TaskToolResult, TaskError> = {
|
||||
// 6. 构建子代理系统提示词
|
||||
// 实时按 def.capability 过滤技能索引(替代冻结快照,反映运行时技能增删)
|
||||
let skills_index = if def.capability.has_skill_policy() {
|
||||
@ -808,9 +845,9 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
)?;
|
||||
|
||||
// 8. 执行任务
|
||||
let result = self
|
||||
.execute_task(agent, &session, &def, task.prompt.clone())
|
||||
.await;
|
||||
self.execute_task(agent, &session, &def, task.prompt.clone())
|
||||
.await
|
||||
};
|
||||
|
||||
// 9. 更新会话状态并保存
|
||||
match result {
|
||||
@ -828,24 +865,9 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
Ok(tool_result)
|
||||
}
|
||||
Err(e) => {
|
||||
let mut session = session;
|
||||
let status = e.as_status();
|
||||
tracing::warn!(
|
||||
task_id = %session.id,
|
||||
session_id = %session.session_id,
|
||||
status = %status,
|
||||
error = %e,
|
||||
"Task failed, updating session"
|
||||
);
|
||||
if status == "timeout" {
|
||||
session.mark_timeout();
|
||||
} else {
|
||||
session.mark_failed(e.to_string());
|
||||
}
|
||||
self.task_repository.save_task_session(&session).await?;
|
||||
// 发布子智能体 ErrorNotification,metadata 注入 subagent_task_id 供前端路由到对应子智能体层
|
||||
publish_subagent_error(&self.bus, &session, &e.to_string()).await;
|
||||
Err(e)
|
||||
// 会话创建后的任何失败(含 AgentCreationFailed、Timeout、ExecutionFailed)
|
||||
// 统一返回结构化结果,携带 task_id 供前端导航
|
||||
self.handle_task_failure(session, e).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -907,7 +929,8 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
.find_subagent_def(&session.subagent_type)
|
||||
.map_err(TaskError::InvalidArguments)?;
|
||||
|
||||
// 5. 创建子代理
|
||||
// 5-6. 创建子代理 + 执行(统一为 Result,失败走 handle_task_failure)
|
||||
let result: Result<TaskToolResult, TaskError> = {
|
||||
let agent = self.create_subagent(
|
||||
&session,
|
||||
system_prompt,
|
||||
@ -915,11 +938,9 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
parent_context.nesting_depth,
|
||||
parent_context.task_id.clone(),
|
||||
)?;
|
||||
|
||||
// 6. 使用历史继续执行
|
||||
let result = self
|
||||
.execute_task_with_history(agent, &session, additional_prompt)
|
||||
.await;
|
||||
self.execute_task_with_history(agent, &session, additional_prompt)
|
||||
.await
|
||||
};
|
||||
|
||||
// 7. 更新会话状态
|
||||
match result {
|
||||
@ -932,12 +953,8 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
||||
Ok(tool_result)
|
||||
}
|
||||
Err(e) => {
|
||||
let mut session = session;
|
||||
session.mark_failed(e.to_string());
|
||||
self.task_repository.save_task_session(&session).await?;
|
||||
// 发布子智能体 ErrorNotification,metadata 注入 subagent_task_id 供前端路由到对应子智能体层
|
||||
publish_subagent_error(&self.bus, &session, &e.to_string()).await;
|
||||
Err(e)
|
||||
// 修复:原代码一律 mark_failed,未处理 timeout;现统一走 handle_task_failure
|
||||
self.handle_task_failure(session, e).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,12 +162,21 @@ impl Tool for TaskTool {
|
||||
};
|
||||
|
||||
// 5. 构建返回结果
|
||||
// spawn()/resume() 在会话创建后的失败也返回 Ok(TaskToolResult { status: "failed"/"timeout" }),
|
||||
// 携带 task_id 供前端导航。仅会话创建前的失败(参数校验、def 未找到等)返回 Err。
|
||||
match result {
|
||||
Ok(task_result) => Ok(ToolResult {
|
||||
success: true,
|
||||
Ok(task_result) => {
|
||||
let success = task_result.status == "success";
|
||||
Ok(ToolResult {
|
||||
success,
|
||||
output: serde_json::to_string(&task_result)?,
|
||||
error: None,
|
||||
}),
|
||||
error: if success {
|
||||
None
|
||||
} else {
|
||||
Some(task_result.summary.clone())
|
||||
},
|
||||
})
|
||||
}
|
||||
Err(e) => Ok(ToolResult {
|
||||
success: false,
|
||||
output: String::new(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user