修复: 工作流推进链路与执行入口优化
This commit is contained in:
@@ -13,7 +13,7 @@ use df_ai::provider::LlmProvider;
|
||||
// AiNode 路由 — 节点 config.model_id 优先;否则按 TaskRequirements 路由
|
||||
// (默认 Standard + needs_tool_use=true)。池空/无匹配兜底 record.default_model。
|
||||
use df_ai::router::{select_model_id, TaskRequirements};
|
||||
use df_storage::crud::AiProviderRepo;
|
||||
use df_storage::crud::{AiProviderRepo, TaskRepo};
|
||||
use df_storage::db::Database;
|
||||
use df_storage::models::AiProviderRecord;
|
||||
use df_storage::secret::{ensure_resolved_key, resolve_provider_secret};
|
||||
@@ -72,7 +72,66 @@ pub(crate) async fn resolve_and_parse(
|
||||
inputs: &HashMap<String, NodeOutput>,
|
||||
) -> anyhow::Result<AiNodeParams> {
|
||||
let provider_cfg = resolve_provider(db, config).await?;
|
||||
parse_params(config, inputs, provider_cfg)
|
||||
// P0-B: parse_params 缺 prompt 报错时兜底 —— 工作流推进链模板 AiNode config 无 prompt
|
||||
// (in_progress/testing/done 三链首节点 AiNode 仅带 provider_id, prompt 留空),
|
||||
// 原行为直接 Err 致整链必失败。兜底:从 config.task_id 读 TaskRecord 生成基于
|
||||
// title/description 的执行 prompt;无 task_id 用通用默认。不改 LLM 调用结构,
|
||||
// 仅 parse_params 失败时补 prompt 重解析(provider_cfg 已 resolve 不重复)。
|
||||
// 实现:parse_params 按值吃 provider_cfg,失败需重建 → 先 clone 一份供兜底重解析用,
|
||||
// 避免 ResolvedProvider(api_key 等)无法从 Err 取回(原签名为 by-value 消费)。
|
||||
match parse_params(config, inputs, provider_cfg.clone()) {
|
||||
Ok(p) => Ok(p),
|
||||
Err(e) => {
|
||||
let prompt = resolve_fallback_prompt(db, config)
|
||||
.await
|
||||
.unwrap_or_else(|| default_fallback_prompt());
|
||||
// 把兜底 prompt 注入 config 后重解析(prompt 字段补齐后 parse_params 不再缺 prompt)。
|
||||
// 用 mutable config copy 避免改原 config 借用(config: &serde_json::Value 不可变借)。
|
||||
let mut cfg = config.clone();
|
||||
if let Some(obj) = cfg.as_object_mut() {
|
||||
obj.insert("prompt".to_string(), serde_json::Value::String(prompt));
|
||||
} else {
|
||||
// config 非 object(理论不会,防御):构造 {prompt: ...} 替代。
|
||||
cfg = serde_json::json!({ "prompt": prompt });
|
||||
}
|
||||
// tracing 兜底可见性(用户/排查:看到 AiNode 用了默认 prompt 而非报错)。
|
||||
tracing::warn!(
|
||||
error = %e,
|
||||
"AiNode 缺 prompt 兜底: 从 task_id/通用默认生成执行 prompt"
|
||||
);
|
||||
parse_params(&cfg, inputs, provider_cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// P0-B 兜底 prompt 构建:从 config.task_id 读 TaskRecord.title/description 生成执行 prompt。
|
||||
///
|
||||
/// 无 task_id / 查询失败 / task 不存在 → None,调用方降级到 `default_fallback_prompt`。
|
||||
/// description 空只取 title;非空拼「标题(描述)」。truncate 防 token 滥用。
|
||||
async fn resolve_fallback_prompt(db: &Arc<Database>, config: &serde_json::Value) -> Option<String> {
|
||||
let task_id = config.get("task_id").and_then(|v| v.as_str())?;
|
||||
if task_id.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let repo = TaskRepo::new(db);
|
||||
let task = repo.get_by_id(task_id).await.ok()??;
|
||||
let title = task.title.trim();
|
||||
let desc = task.description.trim();
|
||||
let body = if desc.is_empty() {
|
||||
title.to_string()
|
||||
} else {
|
||||
format!("{}({})", title, truncate_for_summary(desc))
|
||||
};
|
||||
Some(format!(
|
||||
"根据任务「{}」完成开发,产出可执行代码/方案并自检。要求:逐步推理、\
|
||||
关键决策给出依据、产出可直接落地。",
|
||||
body
|
||||
))
|
||||
}
|
||||
|
||||
/// P0-B 通用兜底 prompt:无 task_id 时用(独立 AiNode 调用 / config 无 task 关联)。
|
||||
fn default_fallback_prompt() -> String {
|
||||
"请根据上下文完成当前任务,产出可执行结果并自检。若需更多信息请说明。".to_string()
|
||||
}
|
||||
|
||||
/// SW-260618-09: build_provider 5 行封装 DRY(AiNode/AiSelfReviewNode execute 逐字重复)。
|
||||
|
||||
Reference in New Issue
Block a user