Files
DevFlow/docs/02-架构设计/专项设计/AiNode自审实施方案-2026-06-16.md
绝尘 998a2f243d 文档: 架构方案文档(意图识别论证+多主题愿景/论证+文档物理分类+边界清晰化)
squash合并:
- 意图识别层论证(8维度+10业界佐证)
- 多主题上下文管理愿景+并存论证+补充论证(多轮agentic)
- 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化)
- 前端架构技术债清单归档
2026-06-19 15:04:04 +08:00

28 KiB
Raw Blame History

AiNode 自审实施方案(②-⑥)

决策基线:决策 a 已定 —— TaskRecordoutput_json 字段task 中心,产出跟 task 走)。① df-storage 迁移struct 字段 + migrations CREATE/ALTER + crud 白名单 + INSERT/UPDATE/SELECT 列)由同批另一 agent 落地,本方案为前置依赖

范围:仅设计文档read-only不改任何 .rs/.ts/.vue 代码。file:line 证据均独立 grep/Read 核验,未采信文档/会话描述声明。

日期2026-06-16 作者AiNode 自审调研 agent


实施状态(2026-06-18 核对)

②-⑤ 全部已落地(原设计 1.2 表中标 ⚠️ 的 crud/白名单缺口已补全,②③④⑤ 已实施)。

① df-storage 迁移 — 已完成(原 ⚠️ 项已补)

  • TaskRecord.output_json: Option<String>crates/df-storage/src/models.rs:72
  • 白名单含 output_jsoncrates/df-storage/src/crud.rs:344(allowed_columns_for("tasks") 已含,注释"ai_execute 写产出 / ai_self_review 读产出自审 / human_review 展示对象")。
  • migrationscrates/df-storage/src/migrations.rs:303(注释同上)。

② AiNode 持 db + 写产出 — 已落地

  • AiNode structcrates/df-nodes/src/ai_node.rs:256(持 db 字段,AiNode::new(db) 构造)。
  • state.rs 工厂闭包注入 dbsrc-tauri/src/state.rs:351-352(registry.register("ai", ... AiNode::new(ai_db.clone())))。
  • execute 写产出:crates/df-nodes/src/ai_node.rs:319-325(若 config 含 task_id → repo.update_field(task_id, "output_json", &json_str))。

③ AiSelfReviewNode 独立节点 — 已落地(原设计 2.3 推荐独立节点路径已采纳,非按 node_id 分支)

  • AiSelfReviewNode structcrates/df-nodes/src/ai_node.rs:438
  • REVIEW_SYSTEM_PROMPT(四维度 prompt 模板)crates/df-nodes/src/ai_node.rs:387
  • JSON 兜底解析 parse_review_json(fn 定义,非 JSON/缺 verdict → verdict=unknown)crates/df-nodes/src/ai_node.rs:396,单测 :1011-1058(valid/invalid/fence 三场景)。
  • build_review_prompt(fn 定义)crates/df-nodes/src/ai_node.rs:449,单测 :1060
  • state.rs 注册:src-tauri/src/state.rs:359-360(registry.register("ai_self_review", ... AiSelfReviewNode::new(review_db.clone())))。

④ human_review 展示(经 DAG inputs 透传HumanNode 零改动)— 已落地

  • review 摘要塞 NodeOutput.datacrates/df-nodes/src/ai_node.rs:586(注释「NodeOutput.data 塞 review 摘要,供下游 human_review 经 inputs["ai_self_review"] 读」)。
  • testing 模板 edge ai_self_review → human_reviewcrates/df-nodes/src/task_workflow_templates.rs:80,单测 :149 验证边方向(assert_eq!(edge.source, "ai_self_review"))。
  • HumanNode 本身零改动(git 历史核验 human_node.rs 在 AiNode 自审批 commit 中无变动)。

⑤ 前端展示 — 已落地TaskDetail.vue 加 output_json 区块(本轮未逐行核验前端 file:line但后端 output_json schema 已定型、前端按 schema 渲染)。

超出原设计、后追加的能力 — gate 闸门

  • testing 模板 ai_self_review 启用 gate:truecrates/df-nodes/src/task_workflow_templates.rs:54-67(阶段3 起 verdict=fail → AiSelfReviewNode 返 Err → 工作流 failed不经 human_review)。原设计 2.3 标"首版保守verdict 仅作展示信号",实际已升级为 DAG 节点闸门(激进方案落地)crates/df-nodes/src/ai_node.rs:598-626(gate==true 时 verdict=fail 返 Err)。

⑥ 端到端联调 — 代码层完成,实测类待用户:联调依赖 secret 下沉+provider 注入链(docs/02-架构设计/专项设计/secret下沉与provider注入方案-2026-06-16.mdAiNode/AiSelfReviewNode 改经 provider_id + df_storage::secret 解析FR-S1 mask 对齐)。实测类(tauri dev 跑 testing 模板 ai_self_review→human_review 闭环)待用户执行。

DRY 优化(SW-260618-09)AiNode/AiSelfReviewNode execute provider 三件套逐字重复已抽 resolve_and_parse + provider_from_params helper(crates/df-nodes/src/ai_node.rs:70/80 注释)。

原文以下设计正文保持不变,作为历史设计记录;落地形态以上方"实施状态"为准。


一、现状盘点file:line 证据)

1.1 已就绪(自审闭环地基)

能力 位置 状态
NodeContext 结构 crates/df-workflow/src/node.rs:12-26 字段:node_id / inputs / config / execution_id / event_bus / node_status无 task 数据、无 db 句柄
AiNode 通用执行 crates/df-nodes/src/ai_node.rs:116-164 AiNode.execute 调 LLM → 返 NodeOutput{text, model, usage}(仅内存,不落 task
AiNode 参数解析 crates/df-nodes/src/ai_node.rs:35-109 parse_params(config, inputs)prompt 优先上游 inputs["prompt"] > config.promptprovider 配置从 config 取
HumanNode 审批 crates/df-nodes/src/human_node.rs:39-176 阻塞节点,发 HumanApprovalRequest 等响应reject 关键字L19-22→ Err → 工作流 failed
testing 模板(拓扑) crates/df-nodes/src/task_workflow_templates.rs:57-72 ai_self_reviewhuman_review 串行;节点级 config 仅含 human title/optionsai_self_review config 为空 {}
模板选择 crates/df-nodes/src/task_workflow_templates.rs:28-35 template_for(target_status) 按 in_progress/testing/done 选模板
DagDef 选模板 src-tauri/src/commands/workflow.rs:84-92 空 dag + target_status → template_for 自动选
deep_merge④-1 crates/df-workflow/src/executor.rs:102-108 + crates/df-workflow/src/dag.rs:169-188 executor 构建 NodeContext 时 deep_merge(initial_config, node_config),节点级覆盖全局级;模板节点 config 为空 → 全局 config 原样穿透
工作流联动任务 src-tauri/src/commands/workflow.rs:235-308 task_id+target_status 都 Some 时,完成推进/失败退回(regression_target L46-54
TaskAdvanceNode db 注入先例 crates/df-nodes/src/task_advance_node.rs:98-110 + src-tauri/src/state.rs:265-266 节点 struct 持 db: Arc<Database>NodeRegistry::register 工厂闭包 move 捕获 db.clone() 注入 —— AiNode 可复用同一模式
run_workflow 全局 config src-tauri/src/commands/workflow.rs:67-76 签名含 config: serde_json::Value,闭包内 executor.run(&runtime_dag, config) 传为 initial_config

1.2 待 ①迁移落地(前置依赖)

缺口 位置 说明
struct 字段 crates/df-storage/src/models.rs:69-76 已有 output_json: Option<String>(决策 a 已落地到模型层)
migrations CREATE crates/df-storage/src/migrations.rs:371 CREATE TABLE tasks 含 output_json TEXT(新库)
migrations ALTER老库 crates/df-storage/src/migrations.rs:394-395 附近 ⚠️ 未见 ALTER TABLE tasks ADD COLUMN output_json老库升级缺列,需 ①迁移补)
crud SELECT crates/df-storage/src/crud.rs:779 ⚠️ list_active SELECT 列未含 output_json
crud INSERT/UPDATE crates/df-storage/src/crud.rs:748-762 ⚠️ INSERT/UPDATE_FULL 语句未含 output_json 列
crud 行映射 crates/df-storage/src/crud.rs:544 ⚠️ output_json: row.get("output_json") 存在,但 SELECT 缺列 → 运行时 row.get 会报错(待 ①补 SELECT
crud 白名单 crates/df-storage/src/crud.rs:342-343 ⚠️ allowed_columns_for("tasks") 未含 output_jsonAiNode 写产出走通用 update_field 必需)

结论①迁移尚未完成struct 已加字段crud 链路与白名单未跟上)。本方案所有 ②-⑥ 步骤均强依赖 ①完成,尤其白名单与 SELECT 列。


二、五项实施方案

2.1 方案①NodeContext 注入 task 机制

问题ai_execute/ai_self_review 需拿到 task_id + task 数据description / output_json+ provider 配置;ai_execute 还需 db 句柄写产出。当前 NodeContextnode.rs:12-26无此二者。

三选项对比

选项 机制 优点 缺点
A. config 注入 task_id + 节点持 db 句柄(推荐) run_workflow 全局 config 注入 task_id+provider{base_url,api_key,model,protocol}AiNode struct 加 db: Arc<Database>state.rs 工厂闭包 move 注入(复用 TaskAdvanceNode 先例 零改 NodeContext不动 node.rsdb 注入已有先例state.rs:265config 经 deep_merge 穿透AiNode 与 TaskAdvanceNode 注入方式一致 AiNode 从无状态单例变有状态state.rs:257-259 工厂闭包需改 move db.clone
B. NodeContext 加 task 字段 改 node.rs 加 task: Option<TaskRecord> + db 句柄字段 节点直接读,省一次 DB 查 改 NodeContext 影响所有 NodeHumanNode/SleepNode/test 构造全改human_node.rs:236 make_ctx 等测试全改executor.rs:99-113 构造点要读 DBdf-workflow 反向依赖 df-storage/df-task循环依赖风险
C. IPC handler AiNode 通过 event_bus 发事件,前端/IPC 侧写 task 解耦 AiNode 在 DAG 后台线程,无 AppHandle/State引入跨线程往返HumanNode 那套 broadcast 模式不适合写 DB过度复杂

推荐:选项 A。理由:

  1. 零侵入 NodeContext —— 不动 node.rs所有现有 Node 与测试human_node.rs:236 make_ctx、executor.rs:99-113零改动。
  2. 先例已验证 —— TaskAdvanceNodetask_advance_node.rs:98-110就是这套struct 持 db: Arc<Database>state.rs:265-266 工厂闭包 move |_config| { Box::new(...::new(db.clone())) } 注入。AiNode 照搬。
  3. config 穿透已就绪 —— run_workflow 全局 configworkflow.rs:72→ executor deep_mergeexecutor.rs:102-108→ 模板节点 config 空 {} → 全局 config 原样进 NodeContext.config。前端调 workflowApi.run(name, {}, config, taskId, target)workflow.ts:12-26即可把 task_id/provider 推进去。
  4. DB 句柄天然可得 —— state.rs:205 已有 db: Arc<Database>build_registry 入参就是它state.rs:252

注入路径

前端 TaskDetail.handleWorkflowAdvance
  → workflowApi.run(name, {}, {task_id, provider:{base_url,api_key,model,protocol}}, taskId, "testing")  (workflow.ts:12-26)
  → run_workflow(task_id, target_status, config)  (workflow.rs:67)
  → executor.run(dag, config)  // config 作 initial_config  (workflow.rs:245)
  → NodeContext.config = deep_merge(initial_config, node_config={})  (executor.rs:102-108)
  → AiNode.execute(ctx): ctx.config["task_id"] / ctx.config["provider"] 可读
  → AiNode 持 self.db → TaskRepo::new(&self.db).get_by_id(task_id) 读 task

provider 配置来源:前端从 ai_list_providersapi/ai.ts:76取默认 providertypes.ts:172-178 AiProviderRecord拼成 {base_url, api_key, model, protocol} 注入 config。api_key 安全:复用 src-tauri/src/commands/ai/secret.rs ensure_resolved_keyai_node.rs:50 注释已对齐此行为),前端不直接拿明文,由后端在注入 config 前解析(或前端传 provider_id后端 AiNode 内部解析——更安全,见风险)。

2.2 方案②ai_execute 写产出

前置方案①AAiNode 持 db + config 有 task_id

写产出路径ai_node.rs:155-163 当前返 NodeOutput 后追加落库):

AiNode.execute(ctx):
  1. parse_params(&ctx.config, &ctx.inputs)  → p  (现有,ai_node.rs:119)
  2. provider.complete(request) → response  (现有,ai_node.rs:146)
  3. 【新增】若 ctx.config 有 task_id:
       let task_id = ctx.config["task_id"].as_str()
       let repo = TaskRepo::new(&self.db)
       // 产出 schema: {text, model, usage} 直接序列化(与 NodeOutput.data 一致)
       let output_json = serde_json::to_string(&json!({text, model, usage}))?
       repo.update_field(task_id, "output_json", &output_json).await?  // 需 ①白名单含 output_json
  4. return NodeOutput::from_value(...)  (现有,ai_node.rs:155)

关键约束

  • update_fieldcrud.rs:165走白名单校验 —— 必须 ①把 output_json 加进 tasks 白名单crud.rs:342-343
  • 产出 schema 与 NodeOutput.data 一致ai_node.rs:155-163下游 ai_self_review 既可从 inputs["ai_execute"].dataDAG 层),也可从 task.output_json 读(跨 DAG/重启)。推荐读 inputsDAG 内闭环task.output_json 仅作持久化与 human_review 展示源

in_progress 模板适用in_progress_templatetask_workflow_templates.rs:40-50单 ai_execute 节点正是写产出处。testing 模板的 ai_self_review 读上游/读 task.output_json。

2.3 方案③ai_self_review prompt 模板与自审结果

输入ai_self_review 节点读上游 inputs["ai_execute"]DAG 层 testing 模板无 ai_execute故实际读 task.output_json+ task.description需求

prompt 模板草案

【系统提示 system_prompt】
你是严格的代码/产出审查员。审查任务产出是否符合需求,按四维度给出结构化结论。
只输出 JSON不要任何额外文字。

【用户提示 prompt】
## 任务需求
{task.description}

## 待审产出
{task.output_json 解析后的 text 字段}

## 审查维度
1. 需求符合度:产出是否覆盖需求描述的所有要点
2. 产出完整性:是否有遗漏、未完成的部分
3. 正确性:逻辑/事实/语法是否正确
4. 边界处理:异常输入、空值、错误路径是否考虑

## 输出格式(严格 JSON
{
  "verdict": "pass" | "fail",
  "dimensions": {
    "requirement_fit": {"score": 0-10, "issues": ["..."]},
    "completeness":   {"score": 0-10, "issues": ["..."]},
    "correctness":    {"score": 0-10, "issues": ["..."]},
    "boundary":       {"score": 0-10, "issues": ["..."]}
  },
  "summary": "一句话总结",
  "suggestions": ["改进建议1", "改进建议2"]
}
verdict=fail 当且仅当任一维度 score < 6 或有阻断性 issue。

自审结果写回 output_json不覆盖,加 review 子字段(保留 ai_execute 原始产出供 human_review 对照):

{
  "text": "<ai_execute 原始产出>",
  "model": "...",
  "usage": {...},
  "review": {
    "verdict": "pass" | "fail",
    "dimensions": {...},
    "summary": "...",
    "suggestions": [...],
    "reviewed_at": "<now_millis>",
    "reviewer_model": "<自审用的 model>"
  }
}

verdict=fail 的处理:当前 testing 模板task_workflow_templates.rs:57-72ai_self_review → human_review 串行ai_self_review 返 Ok不 Err设计选择ai_self_review 不据 verdict 主动 Err自审是辅助最终决策权在人。verdict=fail 时:

  • 写回 output_json带 review.fail 信号)
  • 节点返 Ok让 human_review 节点继续,人看到 fail 结论再定)
  • human_review 可在审批卡片高亮显示 verdict=fail前端红标人选拒绝 → Err → 工作流 failed → regression_targetworkflow.rs:46-54 testing→in_review退回

替代方案(更激进,不推荐首版)ai_self_review verdict=fail 直接返 Err → 工作流 failed → 自动退回 in_review跳过人工。风险LLM 自审误判直接退回人无干预机会。首版保守verdict 仅作展示信号。

实现要点

  • prompt 模板放节点 config 还是硬编码?—— 放 configrun_workflow 全局 config 注入 review_prompt_template),模板可热替换。但首版可硬编码在 AiNode 逻辑里(按 node_id=="ai_self_review" 分支),简化首版。
  • LLM 输出解析:serde_json::from_str(&response.text) 解 JSON失败兜底 verdict=unknown, summary=原文(防 LLM 不按要求输出)。

2.4 方案④human_review 展示

human_review 节点human_node.rs:39当前从 ctx.config 读 title/description/optionshuman_node.rs:42-56HumanApprovalRequesthuman_node.rs:74-83问题HumanApprovalRequest 事件载荷df-core events只有 title/description/options不含 task 产出/自审结果

展示方案human_review 节点在发 HumanApprovalRequest 前,读 task.output_json把"产出摘要 + 自审结论"拼进 description 字段(最小改动,复用现有事件结构):

human_review.execute(ctx):
  1. 读 task.output_json经方案①AiNode 持 db + ctx.config["task_id"]
     → 解析 review.verdict / review.summary / review.suggestions
     → 解析 textai_execute 产出)
  2. 拼接 description:
     description = format!("
       ## AI 自审结论:{verdict}
       {summary}
       建议:{suggestions}
       
       ## 产出
       {text_前 N 字}
     ")
  3. ctx.config["description"] = description  (覆盖模板默认空串,human_node.rs:46-48)
  4. 发 HumanApprovalRequest现有流程,human_node.rs:74-83

约束HumanNode 当前不持 dbhuman_node.rs:36 pub struct HumanNode; 无字段。需同样按方案①A 给 HumanNode 加 db: Arc<Database> + state.rs:254-256 工厂闭包改 move db。human_review 的 description 由 ai_self_review 节点算好,经 DAG outputs 透传inputs["ai_self_review"].data—— 更优HumanNode 零改动

  • ai_self_review 节点把"自审结论 + 产出摘要"塞进 NodeOutput.data
  • human_review 读 inputs["ai_self_review"].data["review_summary"] 拼进 description
  • HumanNode 不需 db零改动

推荐后者HumanNode 零改动db 注入只给 AiNode

审批卡片显示什么(前端):

  • title模板已定「核对 AI 自审结果」task_workflow_templates.rs:66
  • description自审 verdictpass/fail 红绿标)+ summary + suggestions + 产出摘要
  • options同意 / 拒绝task_workflow_templates.rs:67含拒绝触发退回

2.5 方案⑤:前端展示 output_json

当前 TaskDetail.vuesrc/views/TaskDetail.vue:1-140展示 title/status/description/priority/branch 等,无 output_json 展示。已有工作流推进按钮L71-91+ workflow-event 监听L81-90 轻量进度)+ df-data-changed 自动刷新L82 注释)。

UI 方案(在 TaskDetail.vue 信息区加一个区块L124 workflowDef 后):

<!-- F-AiNodeSelfReview: 任务产出 + 自审结果展示 -->
<div v-if="task.output_json" class="info-item info-block">
  <span class="label">{{ $t('taskDetail.output') }}</span>
  <div class="value output-block">
    <!-- 自审结论卡若有 review 子字段 -->
    <div v-if="parsedOutput.review" class="review-card" :class="reviewVerdictClass">
      <span class="review-verdict">{{ reviewVerdictLabel }}</span>
      <span class="review-summary">{{ parsedOutput.review.summary }}</span>
      <ul v-if="parsedOutput.review.suggestions?.length" class="review-suggestions">
        <li v-for="(s, i) in parsedOutput.review.suggestions" :key="i">{{ s }}</li>
      </ul>
    </div>
    <!-- AI 产出Markdown 渲染,复用 useRendered -->
    <div v-if="parsedOutput.text" class="output-text ai-md" v-html="renderedOutput"></div>
  </div>
</div>

实现要点

  • parsedOutput = computed(() => JSON.parse(task.output_json))(包 try/catch解析失败降级显示原文
  • reviewVerdictClasspass→绿fail→红
  • renderedOutput:复用 useRenderedTaskDetail.vue:150, 190-192 已用于 description渲染 markdown
  • i18n keytaskDetail.output / taskDetail.review.pass / taskDetail.review.fail
  • workflow 完成后 task 经 df-data-changed 自动刷新现有机制TaskDetail.vue:82 注释output_json 自动出现

约束output_json 是 Option<String>models.rs:76前端 task.output_json 可能为 undefined旧任务无产出—— v-if="task.output_json" 守卫。


三、②-⑥ 实施步骤拆解(标可并发)

依赖链:①迁移(另一 agent→ ②-⑥。②-⑥ 内部:

② AiNode 持 db + 写产出 ──┐
                          ├─→ ⑤ 前端展示(依赖 output_json schema 定型即③review schema
③ ai_self_review prompt ──┤
                          ├─→ ④ human_review 展示(依赖③ review schema + ② 产出 schema
                          ┘
⑥ 端到端联调(依赖②③④⑤)

步骤②ai_execute 写产出(依赖①白名单)

  • crates/df-nodes/src/ai_node.rsAiNode struct 加 db: Arc<Database> 字段 + new(db) 构造
  • crates/df-nodes/src/ai_node.rs:executeLLM 完成后,若 ctx.config["task_id"] 存在 → TaskRepo::new(&self.db).update_field(task_id, "output_json", &json) 落库
  • src-tauri/src/state.rs:257-259ai 工厂闭包改 move |_config| Box::new(AiNode::new(db.clone()))(对齐 state.rs:265 TaskAdvanceNode 写法)
  • 单测mock db 验证 update_field 调用 + output_json 内容

步骤③ai_self_review prompt 模板(依赖② schema

  • crates/df-nodes/src/ai_node.rs:按 node_id 分支(或新增 AiReviewNode 独立节点类型)—— 推荐独立节点 ai_self_review 类型,复用 AiNode 大部分逻辑但 prompt 模板固定、输出解析 JSON
  • prompt 模板(见 2.3)硬编码或 config 注入
  • LLM 输出 JSON 解析 + 兜底
  • 自审结果写回 task.output_json加 review 子字段,不覆盖 text
  • 注册 ai_self_review 节点类型到 state.rs build_registry
  • testing 模板节点类型 "ai" → "ai_self_review"task_workflow_templates.rs:59

步骤④human_review 展示(依赖③ review schema

  • crates/df-nodes/src/ai_node.rsai_self_review 节点):把 review 摘要塞进 NodeOutput.data供下游 human_review 读
  • 不改 HumanNodehuman_node.rs 零改动)—— human_review 节点 config 的 description 在模板层或 ai_self_review 输出层拼接
  • 方案testing 模板给 human_review 节点 config 加 description 占位,或新增轻量"描述拼装"逻辑(读 inputs["ai_self_review"]
  • 若选 HumanNode 持 db 方案human_node.rs 加 db 字段 + state.rs:254 工厂闭包改 move不推荐多改一处

步骤⑤:前端展示(依赖③ schema 定型)—— 可与②③④并发

  • src/views/TaskDetail.vue:加 output_json 展示区块(见 2.5
  • src/api/types.tsTaskRecord 类型加 output_json?: string
  • src/locales/*.json:加 taskDetail.output / review.pass / review.fail i18n key
  • review.verdict 红绿标样式

步骤⑥:端到端联调(依赖②③④⑤全完成)

  • 前端 TaskDetail 点工作流推进testing→ run_workflow → ai_self_review 调 LLM → 写 output_json → human_review 审批卡显示自审结论 → 人同意/拒绝 → 任务态推进/退回
  • 验证 regression_targetworkflow.rs:46-54拒绝 → failed → testing 退回 in_review

可并发标注

  • ②③④ 串行(共享 ai_node.rs / schema
  • ⑤ 可与②③④ 并发(前端独立,仅依赖 schema 文档约定)
  • ⑥ 必须最后(全依赖)

四、风险

风险 等级 说明 缓解
①迁移未完成阻塞 ②-⑥ P0 阻断 crud 白名单crud.rs:342-343/ SELECT 列crud.rs:779/ INSERT-UPDATE 语句crud.rs:748-762/ ALTERmigrations.rs均未含 output_json 本方案明确标注前置依赖①agent 须先完成 crud 链路全量(白名单+SELECT+INSERT+UPDATE+ALTER
api_key 明文经 config 注入 P0 安全 前端拼 provider config 含 api_key经 IPC明文+ config明文传到 AiNode前端可拿到明文 key违背 FR-S1 mask 设计) 改传 provider_id(非 api_keyAiNode 内部调 secret::ensure_resolved_keysrc-tauri/src/commands/ai/secret.rs解析前端 config 只放 provider_id + model + protocolbase_url/api_key 后端解析注入
LLM 自审输出不合规 JSON P1 LLM 可能不按要求输出纯 JSON 兜底解析JSON.parse 失败 → verdict=unknown + summary=原文prompt 强约束 + temperature=0
AiNode 从无状态变有状态 P2 state.rs:257-259 当前 Box::new(AiNode) 无参,改 new(db.clone()) 后工厂闭包要 move db对齐 state.rs:265 已有写法) 低风险TaskAdvanceNode 同模式已验证;注意所有 AiNode 测试构造点同步
HumanNode 是否持 db 的方案分歧 P2 方案④有两个选项HumanNode 持 db vs ai_self_review 输出透传) 推荐后者HumanNode 零改动review 摘要经 DAG inputs 透传;减少改动面
ai_self_review verdict=fail 是否自动退回 P2 激进方案fail→Err→自动退回vs 保守方案fail→展示人定 首版保守verdict 仅作展示信号;后续可加 config 开关 auto_reject_on_fail
testing 模板 ai 节点类型改 ai_self_review P2 task_workflow_templates.rs:59 当前 dag.add_node("ai_self_review", "ai", ...) 类型是 "ai";若新增独立 ai_self_review 节点类型,模板要改 + registry 要注册 若复用 AiNode按 node_id 分支 prompt则模板不改若独立节点则模板 + registry 都改(见步骤③)
output_json schema 演进 P3 未来产出类型多样化(代码/文档/分析),单一 text+review schema 可能不够 schema 设计预留扩展字段output_json 本就是自由 JSON 字符串,可演进

五、依赖

  • ① df-storage 迁移(另一 agent本方案前置TaskRecord struct models.rs:76 已加)→ migrationsCREATE / ALTER ⚠️ 缺)→ crudSELECT ⚠️ / INSERT ⚠️ / UPDATE ⚠️ / 白名单 ⚠️ 全缺)。须全量完成 crud 链路 + 白名单含 output_json
  • provider 配置注入链:前端 ai_list_providersapi/ai.ts:76+ 默认 providertypes.ts:172-178→ run_workflow config → deep_merge。已就绪仅需前端拼装逻辑。
  • secret 解析(若走 provider_id 方案src-tauri/src/commands/ai/secret.rs ensure_resolved_key 已就绪ai_node.rs:50 注释对齐)。

六、附录file:line 证据索引

文件 内容
crates/df-workflow/src/node.rs 12-26 NodeContext 结构(无 task/db
crates/df-nodes/src/ai_node.rs 35-109 parse_params 参数解析
crates/df-nodes/src/ai_node.rs 112 pub struct AiNode;(无状态)
crates/df-nodes/src/ai_node.rs 116-164 AiNode.execute 调 LLM 返 NodeOutput
crates/df-nodes/src/ai_node.rs 155-163 产出 schema {text,model,usage}
crates/df-nodes/src/human_node.rs 36 pub struct HumanNode;(无状态)
crates/df-nodes/src/human_node.rs 39-176 HumanNode.execute 审批流程
crates/df-nodes/src/human_node.rs 19-22 REJECT_KEYWORDS
crates/df-nodes/src/task_workflow_templates.rs 28-35 template_for 选模板
crates/df-nodes/src/task_workflow_templates.rs 57-72 testing 模板ai_self_review→human_review
crates/df-nodes/src/task_advance_node.rs 98-110 TaskAdvanceNode db 注入先例
crates/df-workflow/src/executor.rs 99-113 NodeContext 构造
crates/df-workflow/src/executor.rs 102-108 deep_merge 节点级覆盖全局
crates/df-workflow/src/dag.rs 169-188 deep_merge 实现
src-tauri/src/commands/workflow.rs 67-76 run_workflow 签名config/task_id/target_status
src-tauri/src/commands/workflow.rs 84-92 空 dag + target → template_for
src-tauri/src/commands/workflow.rs 235-308 工作流联动任务推进/退回
src-tauri/src/commands/workflow.rs 46-54 regression_target 失败退回映射
src-tauri/src/state.rs 252-266 build_registry 节点注册AiNode L257 / TaskAdvanceNode L265
crates/df-storage/src/models.rs 53-79 TaskRecord含 output_json L76
crates/df-storage/src/migrations.rs 371 CREATE TABLE output_json TEXT
crates/df-storage/src/crud.rs 165 update_field 通用写
crates/df-storage/src/crud.rs 342-343 tasks 白名单(缺 output_json
crates/df-storage/src/crud.rs 748-762 TaskRepo INSERT/UPDATE缺 output_json
crates/df-storage/src/crud.rs 779 list_active SELECT缺 output_json
src/api/workflow.ts 12-26 workflowApi.run含 config/taskId/targetStatus
src/api/ai.ts 76 ai_list_providers
src/api/types.ts 172-178 AiProviderRecordapi_key/base_url
src/views/TaskDetail.vue 71-91 工作流推进按钮 + 轻量进度
src/views/TaskDetail.vue 124-127 workflowDef 区块output_json 展示插入点)
src-tauri/src/commands/ai/secret.rs - ensure_resolved_keyapi_key 安全解析)