《60天AI学习计划启动 | Day 44: 工作流可视化配置(简单 DAG / 步骤 UI)》

Day 44:工作流可视化配置(简单 DAG / 步骤 UI)

学习目标

  • 理解 “AI 工作流 = 多步骤 + Agent/工具 + 线性/分支关系” 的前端抽象
  • 掌握 用简单步骤列表模拟「线性工作流」的配置方式
  • 会写 一个最小可用的“工作流编辑器”数据结构 + React 组件雏形

核心知识点

  • 1. 工作流模型(简化版,先线性后再考虑 DAG)

    • Workflow:id / name / steps[]
    • Step:id / name / type(collect/analyze/report/notify等) / agentId / config
    • config 里放参数,如时间范围、要分析的指标等
    • 将 Day 43 的任务(collect→analyze→report)抽象成“可配置模板”
  • 2. 可视化编辑的基本形态

    • 左侧:步骤列表(可添加/删除/上下移动)
    • 中间:选中步骤的配置表单(type/agent/参数)
    • 右侧(可选):预览 JSON(方便对接后端)

实战作业(附代码)

作业 1:定义工作流 & 步骤 TS 类型

// workflowTypes.ts
export type WorkflowStepType = 'collect' | 'analyze' | 'report' | 'notify'

export interface WorkflowStep {
  id: string
  name: string
  type: WorkflowStepType
  agentId: string
  config: Record<string, any>
}

export interface Workflow {
  id: string
  name: string
  steps: WorkflowStep[]
}

作业 2:最小“工作流编辑器”组件雏形(线性步骤)

// WorkflowEditor.tsx
import React, { useState } from 'react'
import type { Workflow, WorkflowStep, WorkflowStepType } from './workflowTypes'

const STEP_TYPES: WorkflowStepType[] = ['collect', 'analyze', 'report', 'notify']

const AGENT_OPTIONS = [
  { id: 'frontend', label: '前端专家 Agent' },
  { id: 'analytics', label: '报表分析 Agent' },
  { id: 'doc', label: '文档助手 Agent' }
]

function createEmptyStep(): WorkflowStep {
  return {
    id: crypto.randomUUID(),
    name: '新步骤',
    type: 'collect',
    agentId: 'analytics',
    config: {}
  }
}

export const WorkflowEditor: React.FC = () => {
  const [wf, setWf] = useState<Workflow>({
    id: 'wf_demo',
    name: '每日巡检工作流模板',
    steps: [createEmptyStep()]
  })
  const [selectedStepId, setSelectedStepId] = useState<string | null>(
    wf.steps[0]?.id ?? null
  )

  const updateStep = (id: string, patch: Partial<WorkflowStep>) => {
    setWf(prev => ({
      ...prev,
      steps: prev.steps.map(s => (s.id === id ? { ...s, ...patch } : s))
    }))
  }

  const addStep = () => {
    const step = createEmptyStep()
    setWf(prev => ({ ...prev, steps: [...prev.steps, step] }))
    setSelectedStepId(step.id)
  }

  const removeStep = (id: string) => {
    setWf(prev => ({ ...prev, steps: prev.steps.filter(s => s.id !== id) }))
    if (selectedStepId === id) setSelectedStepId(null)
  }

  const moveStep = (id: string, dir: -1 | 1) => {
    setWf(prev => {
      const idx = prev.steps.findIndex(s => s.id === id)
      if (idx < 0) return prev
      const target = idx + dir
      if (target < 0 || target >= prev.steps.length) return prev
      const arr = [...prev.steps]
      const [step] = arr.splice(idx, 1)
      arr.splice(target, 0, step)
      return { ...prev, steps: arr }
    })
  }

  const selected = wf.steps.find(s => s.id === selectedStepId) || null

  return (
    <div style={{ display:'flex', gap:16 }}>
      {/* 左侧:步骤列表 */}
      <div style={{ width:220 }}>
        <h4>步骤列表</h4>
        <button onClick={addStep}>+ 添加步骤</button>
        <ul>
          {wf.steps.map((s, idx) => (
            <li
              key={s.id}
              style={{
                cursor:'pointer',
                padding:4,
                background: s.id === selectedStepId ? '#eef' : 'transparent'
              }}
              onClick={() => setSelectedStepId(s.id)}
            >
              {idx + 1}. {s.name} ({s.type})
              <button onClick={e => { e.stopPropagation(); moveStep(s.id, -1) }}>↑</button>
              <button onClick={e => { e.stopPropagation(); moveStep(s.id, 1) }}>↓</button>
              <button onClick={e => { e.stopPropagation(); removeStep(s.id) }}>删</button>
            </li>
          ))}
        </ul>
      </div>

      {/* 中间:选中步骤配置 */}
      <div style={{ flex:1 }}>
        <h4>步骤配置</h4>
        {selected ? (
          <>
            <div>
              名称:
              <input
                value={selected.name}
                onChange={e => updateStep(selected.id, { name: e.target.value })}
              />
            </div>
            <div>
              类型:
              <select
                value={selected.type}
                onChange={e => updateStep(selected.id, { type: e.target.value as WorkflowStepType })}
              >
                {STEP_TYPES.map(t => (
                  <option key={t} value={t}>{t}</option>
                ))}
              </select>
            </div>
            <div>
              Agent:
              <select
                value={selected.agentId}
                onChange={e => updateStep(selected.id, { agentId: e.target.value })}
              >
                {AGENT_OPTIONS.map(a => (
                  <option key={a.id} value={a.id}>{a.label}</option>
                ))}
              </select>
            </div>
            {/* 简单 config 示例:collect 步骤配置时间范围 */}
            {selected.type === 'collect' && (
              <div>
                时间范围:
                <input
                  placeholder="如 last_7_days"
                  value={selected.config.range || ''}
                  onChange={e =>
                    updateStep(selected.id, {
                      config: { ...selected.config, range: e.target.value }
                    })
                  }
                />
              </div>
            )}
          </>
        ) : (
          <div>请选择一个步骤</div>
        )}
      </div>

      {/* 右侧:JSON 预览 */}
      <div style={{ width:280 }}>
        <h4>JSON 预览</h4>
        <pre style={{ fontSize:12, whiteSpace:'pre-wrap' }}>
          {JSON.stringify(wf, null, 2)}
        </pre>
      </div>
    </div>
  )
}

明日学习计划预告(Day 45)

  • 主题:将工作流能力嵌入前端产品 + 运行监控页面
  • 方向
    • 把今天配置好的 Workflow 作为“模板”,真正触发执行并查看运行记录
    • 在前端做一个简单的「任务实例列表 + 状态 + 查看报告」页
posted @ 2025-12-17 11:16  XiaoZhengTou  阅读(1)  评论(0)    收藏  举报