新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理

后端:
- 工作流推进链(D-03):advance_task/状态机/闸门走 df-nodes Node trait,conditions 条件引擎扩展
- 想法评估闭环:启发式评分+对抗评估,df-ideas/scoring + df-storage/idea_eval_repo + idea 前端打通
- 全局事件数据总线:df-ai/context+context_helpers+augmentation 跨模块解耦
- AI planner/plan_hint/intent:aichat B 路线并行多轮基础
- patch_file 加固(TD-03/04):读改写整体锁防 lost update,expected_hash 合约闭环
- 压缩超时兜底(F-15 卡死根治)
- F-09 多会话并发:LlmConcurrency per-conv + streamingGuard 前端守护 + verify 脚本
- 知识注入 DRY/skills/audit 扩展

清理:
- aichat 技术债(误报 allow/死导入/过时注释 30 项)
- URGENT.md 删除(11 项加急全解决/迁 todo)
- 文档整理(todo/待决策/待审查/ARCHITECTURE/INDEX + 总线/技术债审查新文档)
This commit is contained in:
2026-06-21 20:51:26 +08:00
parent 330bb7f505
commit bd6a41fe6e
111 changed files with 11932 additions and 1034 deletions

View File

@@ -5,6 +5,7 @@ use std::collections::HashMap;
use df_types::events::WorkflowEvent;
use df_types::types::NodeId;
use crate::conditions::ConditionEngine;
use crate::dag::Dag;
use crate::eventbus::EventBus;
use crate::node::{NodeContext, NodeOutput};
@@ -57,14 +58,16 @@ impl DagExecutor {
tracing::info!("DAG 执行开始,共 {} 层", layers.len());
// 预建入边索引target → 直接前驱列表O(E) 一次构建
// 预建入边索引target → 直接前驱列表 + 边条件表达式(O(E) 一次构建)
// 避免在内层按节点循环中调用 dag.predecessors()(每次 O(E) 全表扫描,整体 O(V·E))。
let mut adjacency_in: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
// 含 condition 是为 conditions-eval feature 接入:flag 开时按入边 condition 过滤前驱 input。
// flag 关时 condition 字段被忽略,行为与增强前完全一致(零破坏)。
let mut adjacency_in: HashMap<NodeId, Vec<(NodeId, Option<String>)>> = HashMap::new();
for edge in &dag.edges {
adjacency_in
.entry(edge.target.clone())
.or_default()
.push(edge.source.clone());
.push((edge.source.clone(), edge.condition.clone()));
}
for (layer_idx, layer) in layers.iter().enumerate() {
@@ -77,6 +80,61 @@ impl DagExecutor {
anyhow::anyhow!("节点 {} 不存在于 DAG 中", node_id)
})?;
// 构建节点上下文:通过入边索引 O(入度) 取前驱,而非 O(E) 全表扫描。
//
// conditions-eval feature(默认关):
// 关:所有入边 inputs 全收集,condition 字段完全忽略 = 旧行为(零破坏)。
// 开:对带 condition 的入边以 source output.data 为 context 求值;
// false 则不收集该前驱 input。若节点【存在带 condition 的入边,且无任何
// 入边(条件或无条件)放行】,则跳过该节点执行(不入 outputs、保持 Pending、
// 不 emit),实现条件路由。无条件边 source 有 output 即视为放行。
// 求值失败保守 false(对齐引擎语义)。
//
// 注:跳过判定在 set_running 之前 —— 跳过的节点不应被标记 Running/触发事件,
// 保持 Pending 终态(与"条件未命中"语义一致)。
let eval_conditions = cfg!(feature = "conditions-eval");
let mut inputs: HashMap<NodeId, NodeOutput> = HashMap::new();
let mut has_any_cond_edge = false;
let mut any_edge_passed = false; // 任一入边放行(含无条件边)
if let Some(preds) = adjacency_in.get(node_id) {
for (pred_id, cond) in preds {
if let Some(out) = outputs.get(pred_id) {
if eval_conditions {
if let Some(cond) = cond {
has_any_cond_edge = true;
// 以 source output.data 为 context 求值;失败保守 false
let passed = ConditionEngine::evaluate(cond, &out.data)
.unwrap_or(false);
if passed {
inputs.insert(pred_id.clone(), out.clone());
any_edge_passed = true;
}
} else {
// 无 condition 的入边:无条件放行(必收集)
inputs.insert(pred_id.clone(), out.clone());
any_edge_passed = true;
}
} else {
// flag 关:condition 完全忽略,旧行为全收集
inputs.insert(pred_id.clone(), out.clone());
any_edge_passed = true;
}
}
}
} else {
// 根节点(无入边):不参与条件跳过判定,正常执行
any_edge_passed = true;
}
// 条件路由:flag 开 + 存在条件边 + 没有任何入边放行 → 跳过执行
if eval_conditions && has_any_cond_edge && !any_edge_passed {
tracing::info!(
"节点 {} 所有入边条件均不满足,跳过执行(条件路由)",
node_id
);
continue;
}
// 发送 NodeStarted 事件
self.event_bus
.send(WorkflowEvent::NodeStarted {
@@ -86,16 +144,6 @@ impl DagExecutor {
self.state_machine.set_running(node_id.clone())?;
// 构建节点上下文:通过入边索引 O(入度) 取前驱,而非 O(E) 全表扫描
let mut inputs = HashMap::new();
if let Some(preds) = adjacency_in.get(node_id) {
for pred_id in preds {
if let Some(out) = outputs.get(pred_id) {
inputs.insert(pred_id.clone(), out.clone());
}
}
}
let ctx = NodeContext {
node_id: node_id.clone(),
inputs,