HarmonyOS 6(API 23)智能体数据可视化分析工作台:构建「数智视界」四层智能体协同BI平台

文章目录

      • 每日一句正能量
      • 一、前言:当BI遇见AI智能体
      • 二、技术架构设计
      • 三、四层智能体协同机制
        • [3.1 清洗智能体(Cleaner Agent)](#3.1 清洗智能体(Cleaner Agent))
        • [3.2 分析智能体(Analyzer Agent)](#3.2 分析智能体(Analyzer Agent))
        • [3.3 可视化智能体(Visualizer Agent)](#3.3 可视化智能体(Visualizer Agent))
        • [3.4 洞察智能体(Insight Agent)](#3.4 洞察智能体(Insight Agent))
      • 四、环境配置与项目初始化
        • [4.1 工程配置(build-profile.json5)](#4.1 工程配置(build-profile.json5))
        • [4.2 权限配置(module.json5)](#4.2 权限配置(module.json5))
      • 五、核心代码实战
        • [5.1 四层智能体调度器(AgentScheduler.ets)](#5.1 四层智能体调度器(AgentScheduler.ets))
        • [5.2 清洗智能体(CleanerAgent.ets)](#5.2 清洗智能体(CleanerAgent.ets))
        • [5.3 可视化智能体(VisualizerAgent.ets)](#5.3 可视化智能体(VisualizerAgent.ets))
        • [5.4 沉浸光效数据密度同步(DataDensityLightSync.ets)](#5.4 沉浸光效数据密度同步(DataDensityLightSync.ets))
        • [5.5 悬浮导航数据工作台(DataFloatNav.ets)](#5.5 悬浮导航数据工作台(DataFloatNav.ets))
      • 六、关键技术总结
        • [6.1 四层智能体协同调度](#6.1 四层智能体协同调度)
        • [6.2 图表自动推荐策略](#6.2 图表自动推荐策略)
        • [6.3 数据密度光效映射](#6.3 数据密度光效映射)
      • 七、效果展示
      • 八、总结与展望

每日一句正能量

"当我们不再执着于对抗,时间反而能听见生命拔节的声音。"

人常把生活当成战场,习惯与困难、过去、甚至自己对抗。但"对抗"会制造噪音,让人听不见内在的变化。一旦放下执念,时间便从敌人变成土壤,而成长(拔节)原本静默有声,只是我们之前太吵。柔软比刚强更有力量。

行动缓解焦虑,深度获得沉淀,心定产生清明,善物而不役物,坦诚是真城府,把握分寸,留有余地,适可而止,过犹不及,盛极必衰。

一、前言:当BI遇见AI智能体

2026年,商业智能(BI)已从"静态报表展示"进化为"AI驱动的动态洞察平台"。HarmonyOS 6(API 23)发布的HMAF智能体框架ArkUI可视化引擎,为开发者提供了构建"会说话的数据分析工作台"的完整能力。传统BI工具需要用户手动选择图表类型、配置数据源、调整样式,而智能体BI平台通过四层智能体协同(清洗→分析→可视化→洞察),实现"数据自动理解、图表智能生成、洞察主动推荐"。

本文将实战开发一款面向HarmonyOS PC的**「数智视界」**数据可视化分析工作台,核心创新在于:

  • 🧹 清洗智能体:自动识别数据质量问题(缺失值、异常值、重复值),执行去重/补全/标准化
  • 📊 分析智能体:自动选择统计方法(描述统计、趋势分析、关联分析),生成分析报告
  • 🎨 可视化智能体:根据数据特征自动推荐图表类型(柱状图/折线图/饼图/热力图),支持交互式渲染
  • 💡 洞察智能体:基于分析结果自动发现异常、预测趋势、推荐行动方案
  • 💡 沉浸光效数据密度:根据数据密度(稀疏/正常/密集/过载)触发不同系统光效,防止数据过载疲劳

本文代码亮点:完整实现从数据接入、四层智能体协同处理、可视化渲染到洞察推荐的完整链路,所有代码可直接在DevEco Studio 6.0.2 + HarmonyOS SDK 6.1.0(API 23)环境中运行。


二、技术架构设计

架构分层说明

层级 核心组件 职责
应用层 数据画布、悬浮导航、洞察面板、浮动图表窗口、光效同步 用户交互与可视化展示
HMAF智能体层 清洗智能体、分析智能体、可视化智能体、洞察智能体、调度器 四层智能体协同处理
AI系统底座层 MindSpore Lite端侧推理、鸿蒙大模型4.0云端推理、意图引擎、NLU Kit、分布式软总线 AI能力支撑
ArkUI表现层 悬浮导航HdsTabs、沉浸光感ImmersiveLight、多窗口管理、安全区扩展、动画系统 UI渲染与交互

三、四层智能体协同机制

3.1 清洗智能体(Cleaner Agent)

负责数据质量评估与清洗,核心能力:

  • 缺失值检测:自动识别缺失比例,选择删除/填充/插值策略
  • 异常值识别:基于IQR/Z-Score/孤立森林算法检测异常
  • 重复值去重:智能识别业务主键,执行去重操作
  • 数据标准化:自动识别数据类型,执行编码/归一化/离散化
3.2 分析智能体(Analyzer Agent)

负责统计分析与模式发现,核心能力:

  • 描述统计:均值、中位数、标准差、分布特征
  • 趋势分析:时间序列分解、季节性检测、趋势预测
  • 关联分析:相关系数、卡方检验、Apriori关联规则
  • 聚类分析:K-Means、DBSCAN、层次聚类
3.3 可视化智能体(Visualizer Agent)

负责图表生成与交互渲染,核心能力:

  • 图表推荐:基于数据特征自动推荐最优图表类型
  • 交互渲染:支持缩放、筛选、钻取、联动
  • 响应式布局:自适应PC大屏、平板、手机多尺寸
  • 动画过渡:数据更新时平滑过渡动画
3.4 洞察智能体(Insight Agent)

负责异常发现与行动推荐,核心能力:

  • 异常检测:基于统计阈值/机器学习/规则引擎发现异常
  • 趋势预测:ARIMA/LSTM/Prophet时序预测
  • 根因分析:5Why分析、鱼骨图、决策树
  • 行动推荐:基于业务规则推荐优化方案

四、环境配置与项目初始化

4.1 工程配置(build-profile.json5)
json 复制代码
{
  "app": {
    "bundleName": "com.example.datavision",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "minSdkVersion": "6.0.0(23)",
    "targetSdkVersion": "6.1.0(23)"
  },
  "modules": [
    {
      "name": "entry",
      "type": "entry",
      "dependencies": [
        {
          "name": "@ohos/hmaf",
          "version": "6.1.0.100"
        },
        {
          "name": "@ohos/mindspore",
          "version": "6.1.0.100"
        },
        {
          "name": "@ohos/arkui",
          "version": "6.1.0.100"
        }
      ]
    }
  ]
}
4.2 权限配置(module.json5)
json 复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.ACCESS_AI_ENGINE",
        "reason": "用于智能体数据分析与洞察生成",
        "usedScene": { "when": "always" }
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "用于云端大模型推理",
        "usedScene": { "when": "always" }
      },
      {
        "name": "ohos.permission.READ_USER_STORAGE",
        "reason": "用于读取本地数据文件",
        "usedScene": { "when": "user_grant" }
      }
    ]
  }
}

五、核心代码实战

5.1 四层智能体调度器(AgentScheduler.ets)

代码亮点 :实现中央调度器,负责任务分发、状态监控、结果聚合。支持串行执行(默认)、并行执行(独立任务)、条件执行(基于前置结果)三种调度模式。通过AppStorage实现跨组件状态同步。

typescript 复制代码
// entry/src/main/ets/scheduler/AgentScheduler.ets
import { hmaf } from '@kit.HMAFramework';

export enum AgentType {
  CLEANER = 'cleaner',
  ANALYZER = 'analyzer',
  VISUALIZER = 'visualizer',
  INSIGHT = 'insight'
}

export interface AgentTask {
  id: string;
  type: AgentType;
  input: any;
  priority: number;
  dependencies: string[]; // 依赖任务ID
  status: 'pending' | 'running' | 'completed' | 'failed';
  result?: any;
  error?: string;
  startTime?: number;
  endTime?: number;
}

export interface ScheduleConfig {
  mode: 'sequential' | 'parallel' | 'conditional';
  timeout: number;
  retryCount: number;
}

export class AgentScheduler {
  private agents: Map<AgentType, hmaf.Agent> = new Map();
  private taskQueue: AgentTask[] = [];
  private runningTasks: Map<string, AgentTask> = new Map();
  private completedTasks: Map<string, AgentTask> = new Map();
  
  // 回调
  public onTaskStart: ((task: AgentTask) => void) | null = null;
  public onTaskComplete: ((task: AgentTask) => void) | null = null;
  public onTaskFailed: ((task: AgentTask) => void) | null = null;
  public onAllComplete: ((results: Map<string, AgentTask>) => void) | null = null;

  async init(): Promise<void> {
    // 初始化四层智能体
    for (const agentType of Object.values(AgentType)) {
      const agent = await hmaf.createAgent({
        agentName: `${agentType}_agent`,
        agentDescription: this.getAgentDescription(agentType),
        interactionMode: [hmaf.InteractionMode.SYSTEM]
      });
      
      this.agents.set(agentType, agent);
      console.info(`[AgentScheduler] ${agentType}智能体初始化完成`);
    }
  }

  private getAgentDescription(type: AgentType): string {
    const descriptions: Record<AgentType, string> = {
      [AgentType.CLEANER]: '数据清洗智能体:去重、补全、标准化',
      [AgentType.ANALYZER]: '数据分析智能体:统计、趋势、关联',
      [AgentType.VISUALIZER]: '可视化智能体:图表、交互、渲染',
      [AgentType.INSIGHT]: '洞察智能体:异常、预测、建议'
    };
    return descriptions[type];
  }

  // 提交任务
  async submitTask(task: AgentTask): Promise<void> {
    this.taskQueue.push(task);
    console.info(`[AgentScheduler] 任务已提交: ${task.id}`);
    
    // 触发调度
    await this.scheduleTasks();
  }

  // 批量提交任务
  async submitTasks(tasks: AgentTask[]): Promise<void> {
    this.taskQueue.push(...tasks);
    await this.scheduleTasks();
  }

  // 任务调度核心逻辑
  private async scheduleTasks(): Promise<void> {
    // 获取可执行任务(依赖已满足)
    const executableTasks = this.taskQueue.filter(task => 
      task.status === 'pending' && 
      task.dependencies.every(depId => this.completedTasks.has(depId))
    );
    
    // 按优先级排序
    executableTasks.sort((a, b) => b.priority - a.priority);
    
    for (const task of executableTasks) {
      // 从队列移除
      this.taskQueue = this.taskQueue.filter(t => t.id !== task.id);
      
      // 执行任务
      this.executeTask(task);
    }
  }

  // 执行单个任务
  private async executeTask(task: AgentTask): Promise<void> {
    task.status = 'running';
    task.startTime = Date.now();
    this.runningTasks.set(task.id, task);
    
    if (this.onTaskStart) {
      this.onTaskStart(task);
    }
    
    try {
      const agent = this.agents.get(task.type);
      if (!agent) {
        throw new Error(`智能体未找到: ${task.type}`);
      }
      
      // 获取依赖任务的输出作为输入补充
      const dependencyResults = task.dependencies.map(depId => 
        this.completedTasks.get(depId)?.result
      );
      
      const enrichedInput = {
        ...task.input,
        dependencyResults
      };
      
      // 调用智能体
      const result = await agent.sendInput({
        type: hmaf.InputType.SYSTEM,
        content: JSON.stringify(enrichedInput)
      });
      
      task.status = 'completed';
      task.endTime = Date.now();
      task.result = result;
      
      this.runningTasks.delete(task.id);
      this.completedTasks.set(task.id, task);
      
      if (this.onTaskComplete) {
        this.onTaskComplete(task);
      }
      
      // 触发后续任务调度
      await this.scheduleTasks();
      
      // 检查是否全部完成
      if (this.taskQueue.length === 0 && this.runningTasks.size === 0) {
        if (this.onAllComplete) {
          this.onAllComplete(this.completedTasks);
        }
      }
      
    } catch (error) {
      task.status = 'failed';
      task.endTime = Date.now();
      task.error = (error as Error).message;
      
      this.runningTasks.delete(task.id);
      
      if (this.onTaskFailed) {
        this.onTaskFailed(task);
      }
    }
  }

  // 创建完整的数据分析工作流
  async createAnalysisWorkflow(rawData: any): Promise<string> {
    const workflowId = `workflow_${Date.now()}`;
    
    const tasks: AgentTask[] = [
      {
        id: `${workflowId}_clean`,
        type: AgentType.CLEANER,
        input: { data: rawData },
        priority: 4,
        dependencies: [],
        status: 'pending'
      },
      {
        id: `${workflowId}_analyze`,
        type: AgentType.ANALYZER,
        input: { data: rawData },
        priority: 3,
        dependencies: [`${workflowId}_clean`],
        status: 'pending'
      },
      {
        id: `${workflowId}_visualize`,
        type: AgentType.VISUALIZER,
        input: { data: rawData },
        priority: 2,
        dependencies: [`${workflowId}_analyze`],
        status: 'pending'
      },
      {
        id: `${workflowId}_insight`,
        type: AgentType.INSIGHT,
        input: { data: rawData },
        priority: 1,
        dependencies: [`${workflowId}_analyze`, `${workflowId}_visualize`],
        status: 'pending'
      }
    ];
    
    await this.submitTasks(tasks);
    return workflowId;
  }

  // 获取任务状态
  getTaskStatus(taskId: string): AgentTask | undefined {
    return this.runningTasks.get(taskId) || this.completedTasks.get(taskId);
  }

  // 获取工作流结果
  getWorkflowResult(workflowId: string): Record<string, any> {
    const result: Record<string, any> = {};
    
    for (const [taskId, task] of this.completedTasks) {
      if (taskId.startsWith(workflowId)) {
        const stage = taskId.split('_').pop() || '';
        result[stage] = task.result;
      }
    }
    
    return result;
  }

  destroy(): void {
    for (const agent of this.agents.values()) {
      agent.destroy();
    }
    this.agents.clear();
    this.taskQueue = [];
    this.runningTasks.clear();
    this.completedTasks.clear();
  }
}
5.2 清洗智能体(CleanerAgent.ets)

代码亮点:封装数据清洗算法,自动识别并处理数据质量问题。支持缺失值检测(基于比例阈值)、异常值识别(基于IQR和Z-Score)、重复值去重(基于业务主键)、数据标准化(自动类型识别)。

typescript 复制代码
// entry/src/main/ets/agents/CleanerAgent.ets
import { hmaf } from '@kit.HMAFramework';

export interface DataQualityReport {
  totalRows: number;
  missingValueCount: number;
  missingValueRatio: number;
  outlierCount: number;
  outlierRatio: number;
  duplicateCount: number;
  duplicateRatio: number;
  columnTypes: Record<string, string>;
  recommendations: string[];
}

export interface CleanedData {
  data: any[];
  qualityReport: DataQualityReport;
  transformations: string[];
}

export class CleanerAgent {
  private agent: hmaf.Agent | null = null;

  async init(): Promise<void> {
    this.agent = await hmaf.createAgent({
      agentName: '数据清洗智能体',
      agentDescription: '自动识别数据质量问题并执行清洗操作',
      interactionMode: [hmaf.InteractionMode.SYSTEM]
    });
    
    // 注册清洗服务
    this.agent.registerService({
      serviceId: 'clean_data',
      execute: async (params: any) => {
        return this.cleanData(params.data);
      }
    });
    
    console.info('[CleanerAgent] 清洗智能体初始化完成');
  }

  async cleanData(rawData: any[]): Promise<CleanedData> {
    const transformations: string[] = [];
    let cleanedData = [...rawData];
    
    // 1. 缺失值处理
    const missingResult = this.handleMissingValues(cleanedData);
    cleanedData = missingResult.data;
    transformations.push(...missingResult.transformations);
    
    // 2. 异常值处理
    const outlierResult = this.handleOutliers(cleanedData);
    cleanedData = outlierResult.data;
    transformations.push(...outlierResult.transformations);
    
    // 3. 重复值处理
    const duplicateResult = this.handleDuplicates(cleanedData);
    cleanedData = duplicateResult.data;
    transformations.push(...duplicateResult.transformations);
    
    // 4. 数据标准化
    const standardizeResult = this.standardizeData(cleanedData);
    cleanedData = standardizeResult.data;
    transformations.push(...standardizeResult.transformations);
    
    // 生成质量报告
    const qualityReport = this.generateQualityReport(rawData, cleanedData);
    
    return {
      data: cleanedData,
      qualityReport,
      transformations
    };
  }

  private handleMissingValues(data: any[]): { data: any[]; transformations: string[] } {
    const transformations: string[] = [];
    if (data.length === 0) return { data, transformations };
    
    const columns = Object.keys(data[0]);
    let modifiedData = [...data];
    
    for (const column of columns) {
      const missingCount = modifiedData.filter(row => 
        row[column] === null || row[column] === undefined || row[column] === ''
      ).length;
      
      const missingRatio = missingCount / modifiedData.length;
      
      if (missingRatio > 0.5) {
        // 缺失率超过50%,删除该列
        modifiedData = modifiedData.map(row => {
          const { [column]: _, ...rest } = row;
          return rest;
        });
        transformations.push(`删除列 ${column}(缺失率 ${(missingRatio * 100).toFixed(1)}%)`);
      } else if (missingRatio > 0) {
        // 填充缺失值
        const values = modifiedData.map(row => row[column]).filter(v => v !== null && v !== undefined && v !== '');
        const fillValue = this.inferFillValue(values);
        
        modifiedData = modifiedData.map(row => ({
          ...row,
          [column]: row[column] ?? fillValue
        }));
        
        transformations.push(`填充列 ${column} 缺失值(${missingCount}个)为 ${fillValue}`);
      }
    }
    
    return { data: modifiedData, transformations };
  }

  private inferFillValue(values: any[]): any {
    if (values.length === 0) return null;
    
    const type = typeof values[0];
    
    if (type === 'number') {
      // 数值型:使用中位数
      const sorted = [...values].sort((a, b) => a - b);
      return sorted[Math.floor(sorted.length / 2)];
    } else if (type === 'string') {
      // 字符串型:使用众数
      const frequency: Record<string, number> = {};
      values.forEach(v => {
        frequency[v] = (frequency[v] || 0) + 1;
      });
      return Object.entries(frequency).sort((a, b) => b[1] - a[1])[0][0];
    }
    
    return values[0];
  }

  private handleOutliers(data: any[]): { data: any[]; transformations: string[] } {
    const transformations: string[] = [];
    if (data.length === 0) return { data, transformations };
    
    const columns = Object.keys(data[0]);
    let modifiedData = [...data];
    
    for (const column of columns) {
      const values = data.map(row => row[column]).filter(v => typeof v === 'number');
      if (values.length < 10) continue; // 数据量太小,不检测异常
      
      // IQR方法
      const sorted = [...values].sort((a, b) => a - b);
      const q1 = sorted[Math.floor(sorted.length * 0.25)];
      const q3 = sorted[Math.floor(sorted.length * 0.75)];
      const iqr = q3 - q1;
      const lowerBound = q1 - 1.5 * iqr;
      const upperBound = q3 + 1.5 * iqr;
      
      const outlierCount = values.filter(v => v < lowerBound || v > upperBound).length;
      
      if (outlierCount > 0) {
        // 截断异常值
        modifiedData = modifiedData.map(row => ({
          ...row,
          [column]: typeof row[column] === 'number' 
            ? Math.max(lowerBound, Math.min(upperBound, row[column]))
            : row[column]
        }));
        
        transformations.push(`截断列 ${column} 异常值(${outlierCount}个)到 [${lowerBound.toFixed(2)}, ${upperBound.toFixed(2)}]`);
      }
    }
    
    return { data: modifiedData, transformations };
  }

  private handleDuplicates(data: any[]): { data: any[]; transformations: string[] } {
    const transformations: string[] = [];
    if (data.length === 0) return { data, transformations };
    
    // 智能识别主键列
    const columns = Object.keys(data[0]);
    const candidateKeys = columns.filter(col => {
      const values = data.map(row => row[col]);
      return new Set(values).size === values.length;
    });
    
    const keyColumn = candidateKeys[0] || columns[0];
    
    // 去重
    const seen = new Set();
    const deduplicated = data.filter(row => {
      const key = row[keyColumn];
      if (seen.has(key)) return false;
      seen.add(key);
      return true;
    });
    
    const duplicateCount = data.length - deduplicated.length;
    if (duplicateCount > 0) {
      transformations.push(`基于列 ${keyColumn} 去重(删除${duplicateCount}条重复记录)`);
    }
    
    return { data: deduplicated, transformations };
  }

  private standardizeData(data: any[]): { data: any[]; transformations: string[] } {
    const transformations: string[] = [];
    if (data.length === 0) return { data, transformations };
    
    const columns = Object.keys(data[0]);
    let modifiedData = [...data];
    
    for (const column of columns) {
      const values = data.map(row => row[column]);
      const sample = values.find(v => v !== null && v !== undefined);
      
      if (typeof sample === 'string') {
        // 字符串标准化:trim + 统一大小写
        modifiedData = modifiedData.map(row => ({
          ...row,
          [column]: typeof row[column] === 'string' ? row[column].trim().toLowerCase() : row[column]
        }));
        transformations.push(`标准化列 ${column}(trim + 小写)`);
      } else if (typeof sample === 'number') {
        // 数值标准化:Z-Score归一化
        const mean = values.reduce((a, b) => a + (b || 0), 0) / values.length;
        const std = Math.sqrt(values.reduce((a, b) => a + Math.pow((b || 0) - mean, 2), 0) / values.length);
        
        if (std > 0) {
          modifiedData = modifiedData.map(row => ({
            ...row,
            [column]: typeof row[column] === 'number' ? (row[column] - mean) / std : row[column]
          }));
          transformations.push(`标准化列 ${column}(Z-Score)`);
        }
      }
    }
    
    return { data: modifiedData, transformations };
  }

  private generateQualityReport(raw: any[], cleaned: any[]): DataQualityReport {
    const totalRows = raw.length;
    const cleanedRows = cleaned.length;
    
    return {
      totalRows: cleanedRows,
      missingValueCount: 0,
      missingValueRatio: 0,
      outlierCount: 0,
      outlierRatio: 0,
      duplicateCount: totalRows - cleanedRows,
      duplicateRatio: (totalRows - cleanedRows) / totalRows,
      columnTypes: this.inferColumnTypes(cleaned),
      recommendations: this.generateRecommendations(cleaned)
    };
  }

  private inferColumnTypes(data: any[]): Record<string, string> {
    if (data.length === 0) return {};
    
    const types: Record<string, string> = {};
    for (const column of Object.keys(data[0])) {
      const sample = data.find(row => row[column] !== null)?.[column];
      types[column] = typeof sample;
    }
    return types;
  }

  private generateRecommendations(data: any[]): string[] {
    return [
      '建议定期执行数据质量检查',
      '对于关键业务指标,建议设置数据监控告警',
      '考虑建立数据质量评分卡'
    ];
  }

  destroy(): void {
    this.agent?.destroy();
    this.agent = null;
  }
}
5.3 可视化智能体(VisualizerAgent.ets)

代码亮点:基于数据特征自动推荐最优图表类型。支持柱状图(分类比较)、折线图(趋势展示)、饼图(占比分析)、散点图(关联分析)、热力图(矩阵分析)。通过Canvas 2D实现高性能渲染,支持交互式操作。

typescript 复制代码
// entry/src/main/ets/agents/VisualizerAgent.ets
import { hmaf } from '@kit.HMAFramework';
import { CanvasRenderingContext2D } from '@kit.ArkUI';

export enum ChartType {
  BAR = 'bar',
  LINE = 'line',
  PIE = 'pie',
  SCATTER = 'scatter',
  HEATMAP = 'heatmap'
}

export interface ChartConfig {
  type: ChartType;
  title: string;
  xAxis: string;
  yAxis: string;
  data: any[];
  colors: string[];
  interactive: boolean;
}

export class VisualizerAgent {
  private agent: hmaf.Agent | null = null;

  async init(): Promise<void> {
    this.agent = await hmaf.createAgent({
      agentName: '可视化智能体',
      agentDescription: '根据数据特征自动推荐并生成图表',
      interactionMode: [hmaf.InteractionMode.SYSTEM]
    });
    
    this.agent.registerService({
      serviceId: 'generate_chart',
      execute: async (params: any) => {
        return this.generateChart(params.data, params.preferences);
      }
    });
    
    console.info('[VisualizerAgent] 可视化智能体初始化完成');
  }

  async generateChart(data: any[], preferences?: any): Promise<ChartConfig> {
    // 1. 分析数据特征
    const features = this.analyzeDataFeatures(data);
    
    // 2. 推荐图表类型
    const chartType = preferences?.chartType || this.recommendChartType(features);
    
    // 3. 生成图表配置
    const config = this.createChartConfig(chartType, data, features);
    
    return config;
  }

  private analyzeDataFeatures(data: any[]): any {
    if (data.length === 0) return {};
    
    const columns = Object.keys(data[0]);
    const features: any = {
      rowCount: data.length,
      columnCount: columns.length,
      hasTimeSeries: false,
      hasCategories: false,
      hasNumerics: false,
      hasCorrelations: false
    };
    
    for (const column of columns) {
      const values = data.map(row => row[column]);
      const sample = values.find(v => v !== null);
      
      if (this.isDateTime(sample)) {
        features.hasTimeSeries = true;
      } else if (typeof sample === 'string') {
        features.hasCategories = true;
      } else if (typeof sample === 'number') {
        features.hasNumerics = true;
      }
    }
    
    // 检测相关性
    if (features.hasNumerics) {
      const numericColumns = columns.filter(col => {
        const sample = data.find(row => row[col] !== null)?.[col];
        return typeof sample === 'number';
      });
      features.hasCorrelations = numericColumns.length >= 2;
    }
    
    return features;
  }

  private isDateTime(value: any): boolean {
    if (typeof value !== 'string') return false;
    return !isNaN(Date.parse(value));
  }

  private recommendChartType(features: any): ChartType {
    if (features.hasTimeSeries && features.hasNumerics) {
      return ChartType.LINE;
    } else if (features.hasCategories && features.hasNumerics) {
      return ChartType.BAR;
    } else if (features.hasCorrelations) {
      return ChartType.SCATTER;
    } else if (features.hasCategories && !features.hasNumerics) {
      return ChartType.PIE;
    }
    return ChartType.BAR;
  }

  private createChartConfig(type: ChartType, data: any[], features: any): ChartConfig {
    const columns = Object.keys(data[0] || {});
    
    const config: ChartConfig = {
      type,
      title: this.generateTitle(type, features),
      xAxis: columns[0] || '',
      yAxis: columns[1] || '',
      data,
      colors: this.generateColors(type),
      interactive: true
    };
    
    return config;
  }

  private generateTitle(type: ChartType, features: any): string {
    const titles: Record<ChartType, string> = {
      [ChartType.BAR]: '分类对比分析',
      [ChartType.LINE]: '趋势变化分析',
      [ChartType.PIE]: '占比分布分析',
      [ChartType.SCATTER]: '关联关系分析',
      [ChartType.HEATMAP]: '密度分布分析'
    };
    return titles[type] || '数据分析图表';
  }

  private generateColors(type: ChartType): string[] {
    const palettes: Record<ChartType, string[]> = {
      [ChartType.BAR]: ['#06B6D4', '#8B5CF6', '#F59E0B', '#10B981', '#EF4444'],
      [ChartType.LINE]: ['#10B981', '#06B6D4', '#8B5CF6'],
      [ChartType.PIE]: ['#06B6D4', '#8B5CF6', '#F59E0B', '#10B981', '#EF4444', '#EC4899'],
      [ChartType.SCATTER]: ['#06B6D4'],
      [ChartType.HEATMAP]: ['#1E3A8A', '#10B981', '#F59E0B', '#EF4444']
    };
    return palettes[type] || ['#06B6D4'];
  }

  // 渲染图表到Canvas
  renderChart(ctx: CanvasRenderingContext2D, config: ChartConfig, 
              width: number, height: number): void {
    switch (config.type) {
      case ChartType.BAR:
        this.renderBarChart(ctx, config, width, height);
n        break;
      case ChartType.LINE:
        this.renderLineChart(ctx, config, width, height);
        break;
      case ChartType.PIE:
        this.renderPieChart(ctx, config, width, height);
        break;
      case ChartType.SCATTER:
        this.renderScatterChart(ctx, config, width, height);
        break;
    }
  }

  private renderBarChart(ctx: CanvasRenderingContext2D, config: ChartConfig, 
                         width: number, height: number): void {
    const padding = 60;
    const chartWidth = width - padding * 2;
    const chartHeight = height - padding * 2;
    
    const data = config.data;
    const maxValue = Math.max(...data.map(d => d[config.yAxis] || 0));
    const barWidth = chartWidth / data.length * 0.6;
    const gap = chartWidth / data.length * 0.4;
    
    // 绘制坐标轴
    ctx.strokeStyle = '#333333';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(padding, padding);
    ctx.lineTo(padding, height - padding);
    ctx.lineTo(width - padding, height - padding);
    ctx.stroke();
    
    // 绘制柱状图
    data.forEach((item, index) => {
      const x = padding + gap / 2 + index * (barWidth + gap);
      const value = item[config.yAxis] || 0;
      const barHeight = (value / maxValue) * chartHeight;
      const y = height - padding - barHeight;
      
      // 绘制柱子
      ctx.fillStyle = config.colors[index % config.colors.length];
      ctx.fillRect(x, y, barWidth, barHeight);
      
      // 绘制标签
      ctx.fillStyle = '#FFFFFF';
      ctx.font = '12px sans-serif';
      ctx.textAlign = 'center';
      ctx.fillText(String(item[config.xAxis] || ''), x + barWidth / 2, height - padding + 20);
      ctx.fillText(String(value), x + barWidth / 2, y - 5);
    });
    
    // 绘制标题
    ctx.fillStyle = '#FFFFFF';
    ctx.font = 'bold 16px sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(config.title, width / 2, 30);
  }

  private renderLineChart(ctx: CanvasRenderingContext2D, config: ChartConfig, 
                          width: number, height: number): void {
    const padding = 60;
    const chartWidth = width - padding * 2;
    const chartHeight = height - padding * 2;
    
    const data = config.data;
    const maxValue = Math.max(...data.map(d => d[config.yAxis] || 0));
    const minValue = Math.min(...data.map(d => d[config.yAxis] || 0));
    const range = maxValue - minValue || 1;
    
    // 绘制坐标轴
    ctx.strokeStyle = '#333333';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(padding, padding);
    ctx.lineTo(padding, height - padding);
    ctx.lineTo(width - padding, height - padding);
    ctx.stroke();
    
    // 绘制折线
    ctx.strokeStyle = config.colors[0];
    ctx.lineWidth = 3;
    ctx.beginPath();
    
    data.forEach((item, index) => {
      const x = padding + (index / (data.length - 1)) * chartWidth;
      const value = item[config.yAxis] || 0;
      const y = height - padding - ((value - minValue) / range) * chartHeight;
      
      if (index === 0) {
        ctx.moveTo(x, y);
      } else {
        ctx.lineTo(x, y);
      }
      
      // 绘制数据点
      ctx.fillStyle = config.colors[0];
      ctx.beginPath();
      ctx.arc(x, y, 4, 0, Math.PI * 2);
      ctx.fill();
    });
    
    ctx.stroke();
    
    // 绘制标题
    ctx.fillStyle = '#FFFFFF';
    ctx.font = 'bold 16px sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(config.title, width / 2, 30);
  }

  private renderPieChart(ctx: CanvasRenderingContext2D, config: ChartConfig, 
                         width: number, height: number): void {
    const centerX = width / 2;
    const centerY = height / 2;
    const radius = Math.min(width, height) / 3;
    
    const data = config.data;
    const total = data.reduce((sum, item) => sum + (item[config.yAxis] || 0), 0);
    
    let currentAngle = -Math.PI / 2;
    
    data.forEach((item, index) => {
      const value = item[config.yAxis] || 0;
      const angle = (value / total) * Math.PI * 2;
      
      // 绘制扇形
      ctx.fillStyle = config.colors[index % config.colors.length];
      ctx.beginPath();
      ctx.moveTo(centerX, centerY);
      ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + angle);
      ctx.closePath();
      ctx.fill();
      
      // 绘制标签
      const labelAngle = currentAngle + angle / 2;
      const labelX = centerX + Math.cos(labelAngle) * (radius * 0.7);
      const labelY = centerY + Math.sin(labelAngle) * (radius * 0.7);
      
      ctx.fillStyle = '#FFFFFF';
      ctx.font = '12px sans-serif';
      ctx.textAlign = 'center';
      ctx.fillText(`${item[config.xAxis] || ''}`, labelX, labelY);
      ctx.fillText(`${((value / total) * 100).toFixed(1)}%`, labelX, labelY + 15);
      
      currentAngle += angle;
    });
    
    // 绘制标题
    ctx.fillStyle = '#FFFFFF';
    ctx.font = 'bold 16px sans-serif';
n    ctx.textAlign = 'center';
    ctx.fillText(config.title, width / 2, 30);
  }

  private renderScatterChart(ctx: CanvasRenderingContext2D, config: ChartConfig, 
                             width: number, height: number): void {
    const padding = 60;
    const chartWidth = width - padding * 2;
    const chartHeight = height - padding * 2;
    
    const data = config.data;
    const xValues = data.map(d => d[config.xAxis] || 0);
    const yValues = data.map(d => d[config.yAxis] || 0);
    const xMax = Math.max(...xValues);
    const xMin = Math.min(...xValues);
    const yMax = Math.max(...yValues);
    const yMin = Math.min(...yValues);
    
    // 绘制坐标轴
    ctx.strokeStyle = '#333333';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(padding, padding);
    ctx.lineTo(padding, height - padding);
    ctx.lineTo(width - padding, height - padding);
    ctx.stroke();
    
    // 绘制散点
    data.forEach((item) => {
      const x = padding + ((item[config.xAxis] - xMin) / (xMax - xMin || 1)) * chartWidth;
      const y = height - padding - ((item[config.yAxis] - yMin) / (yMax - yMin || 1)) * chartHeight;
      
      ctx.fillStyle = config.colors[0];
      ctx.globalAlpha = 0.6;
      ctx.beginPath();
      ctx.arc(x, y, 6, 0, Math.PI * 2);
      ctx.fill();
      ctx.globalAlpha = 1.0;
    });
    
    // 绘制标题
    ctx.fillStyle = '#FFFFFF';
    ctx.font = 'bold 16px sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(config.title, width / 2, 30);
  }

  destroy(): void {
    this.agent?.destroy();
    this.agent = null;
  }
}
5.4 沉浸光效数据密度同步(DataDensityLightSync.ets)

代码亮点:根据数据密度(稀疏/正常/密集/过载)动态调整系统光效。数据密度越高,光效越强烈,提醒用户注意数据过载风险。支持自动降档机制,当数据密度超过阈值时自动降低光效强度。

typescript 复制代码
// entry/src/main/ets/controllers/DataDensityLightSync.ets
import { lighting } from '@kit.ArkUI';

export type DataDensity = 'sparse' | 'normal' | 'dense' | 'overload';

export class DataDensityLightSync {
  private isSupported: boolean = false;
  private currentDensity: DataDensity = 'normal';

  async init(): Promise<void> {
    this.isSupported = lighting.isImmersiveLightSupported();
    if (!this.isSupported) {
      console.warn('[DataDensityLightSync] 设备不支持沉浸光感');
      return;
    }
    
    await this.setDensity('normal');
    console.info('[DataDensityLightSync] 数据密度光效同步器初始化完成');
  }

  async syncDensity(rowCount: number, columnCount: number): Promise<void> {
    const density = this.calculateDensity(rowCount, columnCount);
    if (density !== this.currentDensity) {
      await this.setDensity(density);
      this.currentDensity = density;
    }
  }

  private calculateDensity(rows: number, cols: number): DataDensity {
    const cellCount = rows * cols;
    
    if (cellCount < 1000) return 'sparse';
    if (cellCount < 10000) return 'normal';
    if (cellCount < 100000) return 'dense';
    return 'overload';
  }

  private async setDensity(density: DataDensity): Promise<void> {
    if (!this.isSupported) return;
    
    const effects: Record<DataDensity, any> = {
      'sparse': {
        type: 'solid',
        position: 'bottom_edge',
        color: '#1E3A8A', // 深蓝:数据稀疏
        brightness: 20,
        duration: 0
      },
      'normal': {
        type: 'breathing',
        position: 'bottom_edge',
        color: '#10B981', // 绿色:数据正常
        brightness: 40,
        duration: 0,
        frequency: 3000
      },
      'dense': {
        type: 'breathing',
        position: 'all_edges',
        color: '#F59E0B', // 橙色:数据密集
        brightness: 60,
        duration: 0,
        frequency: 2000
      },
      'overload': {
        type: 'flashing',
        position: 'all_edges',
        color: '#EF4444', // 红色:数据过载
        brightness: 80,
        duration: 0,
        flashCount: 3,
        frequency: 1000
      }
    };

    try {
      await lighting.setImmersiveLight(effects[density]);
      console.info(`[DataDensityLightSync] 数据密度光效已同步: ${density}`);
    } catch (error) {
      console.error('[DataDensityLightSync] 光效设置失败:', error);
    }
  }

  async reset(): Promise<void> {
    if (this.isSupported) {
      await lighting.resetImmersiveLight();
    }
  }
}
5.5 悬浮导航数据工作台(DataFloatNav.ets)

代码亮点 :将HarmonyOS 6的HdsTabs悬浮导航改造为数据分析工作台控制器。支持数据/分析/图表/洞察/设置五种模式切换,每种模式对应不同智能体。长按展开详细控制面板,双击快速执行分析工作流。

typescript 复制代码
// entry/src/main/ets/components/DataFloatNav.ets
import { HdsTabs, HdsTabsController, hdsMaterial } from '@kit.UIDesignKit';
import { SymbolGlyphModifier } from '@kit.ArkUI';

export interface DataNavAction {
  type: 'data' | 'analyze' | 'chart' | 'insight' | 'settings';
  payload?: any;
}

@Component
export struct DataFloatNav {
  // 状态
  @State private activeIndex: number = 0;
  @State private isExpanded: boolean = false;
  @State private showDetailPanel: boolean = false;
  @State private dataDensity: string = 'normal';
  @State private agentStatus: string = 'idle';
  
  // 回调
  onAction: ((action: DataNavAction) => void) | null = null;
  
  private tabController: HdsTabsController = new HdsTabsController();

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Column() {
        HdsTabs({
          controller: this.tabController,
          barPosition: BarPosition.End,
          tabs: [
n            {
              title: '数据',
              icon: new SymbolGlyphModifier($r('app.media.ic_data')).fontSize(24),
              content: () => { this.DataTabContent() }
            },
            {
              title: '分析',
              icon: new SymbolGlyphModifier($r('app.media.ic_analyze')).fontSize(24),
              content: () => { this.AnalyzeTabContent() }
            },
            {
              title: '图表',
              icon: new SymbolGlyphModifier($r('app.media.ic_chart')).fontSize(24),
              content: () => { this.ChartTabContent() }
            },
            {
              title: '洞察',
              icon: new SymbolGlyphModifier($r('app.media.ic_insight')).fontSize(24),
              content: () => { this.InsightTabContent() }
            },
            {
              title: '设置',
              icon: new SymbolGlyphModifier($r('app.media.ic_settings')).fontSize(24),
              content: () => { this.SettingsTabContent() }
            }
          ],
          floatingStyle: {
            enabled: true,
            backgroundBlurStyle: BlurStyle.Thin,
            backgroundOpacity: 0.85,
            systemMaterialEffect: hdsMaterial.SystemMaterialEffect.IMMERSIVE,
            shadow: {
              radius: 20,
              color: 'rgba(0,0,0,0.3)',
              offsetX: 0,
              offsetY: -5
            }
          }
        })
          .onChange((index: number) => {
            this.activeIndex = index;
            this.notifyAction(index);
          })
      }
      .width('90%')
      .height(this.isExpanded ? 220 : 80)
      .margin({ bottom: 20 })
      .animation({
        duration: 300,
        curve: Curve.EaseInOut
      })
      
      // 数据密度指示器
      this.DensityIndicator()
    }
    .width('100%')
    .height('100%')
    .gesture(
      GestureGroup(GestureMode.Sequence,
        LongPressGesture({ duration: 500 })
          .onAction(() => {
            this.showDetailPanel = !this.showDetailPanel;
          }),
        TapGesture({ count: 2 })
          .onAction(() => {
            if (this.onAction) {
              this.onAction({ type: 'analyze', payload: { quick: true } });
            }
          })
      )
    )
  }

  @Builder
  DataTabContent(): void {
    Column({ space: 10 }) {
      Text('数据源管理')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
      
      Row({ space: 10 }) {
        Button('导入CSV')
          .height(36)
          .backgroundColor('rgba(6, 182, 212, 0.2)')
          .fontColor('#06B6D4')
          .borderRadius(18)
          .onClick(() => {
            if (this.onAction) {
              this.onAction({ type: 'data', payload: { action: 'import_csv' } });
            }
          })
        
        Button('连接数据库')
          .height(36)
          .backgroundColor('rgba(139, 92, 246, 0.2)')
          .fontColor('#8B5CF6')
          .borderRadius(18)
          .onClick(() => {
            if (this.onAction) {
              this.onAction({ type: 'data', payload: { action: 'connect_db' } });
            }
          })
      }
    }
    .padding(16)
  }

  @Builder
  AnalyzeTabContent(): void {
    Column({ space: 10 }) {
      Text('分析任务')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
      
      Row({ space: 10 }) {
        Button('执行清洗')
          .height(36)
          .backgroundColor('rgba(6, 182, 212, 0.2)')
          .fontColor('#06B6D4')
          .borderRadius(18)
        
        Button('执行分析')
          .height(36)
          .backgroundColor('rgba(139, 92, 246, 0.2)')
          .fontColor('#8B5CF6')
          .borderRadius(18)
      }
      
      Row({ space: 10 }) {
        Button('生成洞察')
          .height(36)
          .backgroundColor('rgba(16, 185, 129, 0.2)')
          .fontColor('#10B981')
          .borderRadius(18)
      }
    }
    .padding(16)
  }

  @Builder
  ChartTabContent(): void {
    Column({ space: 10 }) {
      Text('图表类型')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
      
      Row({ space: 8 }) {
        ForEach(['柱状图', '折线图', '饼图', '散点图'], (chart: string) => {
          Text(chart)
            .fontSize(12)
            .fontColor('#FFFFFF')
            .padding({ left: 12, right: 12, top: 6, bottom: 6 })
            .backgroundColor('rgba(255,255,255,0.1)')
            .borderRadius(12)
        })
      }
    }
    .padding(16)
  }

  @Builder
  InsightTabContent(): void {
    Column({ space: 10 }) {
      Text('智能洞察')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
      
      Text('Q3销售额增长23%')
        .fontSize(13)
        .fontColor('#10B981')
      
      Text('用户留存率下降5%')
        .fontSize(13)
        .fontColor('#F59E0B')
    }
    .padding(16)
  }

  @Builder
  SettingsTabContent(): void {
    Column({ space: 12 }) {
      Text('分析设置')
        .fontSize(14)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
      
      Row({ space: 10 }) {
        Text('自动清洗')
          .fontSize(13)
          .fontColor('#888888')
          .layoutWeight(1)
        
        Toggle({ type: ToggleType.Switch, isOn: true })
          .selectedColor('#06B6D4')
      }
      
      Row({ space: 10 }) {
        Text('实时洞察')
          .fontSize(13)
          .fontColor('#888888')
          .layoutWeight(1)
        
        Toggle({ type: ToggleType.Switch, isOn: true })
          .selectedColor('#10B981')
      }
    }
    .padding(16)
  }

  @Builder
  DensityIndicator(): void {
    Column() {
      Row({ space: 6 }) {
n        Circle()
          .width(10)
          .height(10)
          .fill(this.getDensityColor())
        
        Text(`数据密度: ${this.dataDensity}`)
          .fontSize(11)
          .fontColor(this.getDensityColor())
      }
      .backgroundColor('rgba(20,20,40,0.9)')
      .padding({ left: 12, right: 12, top: 6, bottom: 6 })
      .borderRadius(12)
    }
    .position({ x: '75%', y: '85%' })
  }

  private getDensityColor(): string {
    const colors: Record<string, string> = {
      'sparse': '#1E3A8A',
      'normal': '#10B981',
      'dense': '#F59E0B',
      'overload': '#EF4444'
    };
    return colors[this.dataDensity] || '#888888';
  }

  private notifyAction(index: number): void {
    if (!this.onAction) return;
    
    const actions: DataNavAction['type'][] = ['data', 'analyze', 'chart', 'insight', 'settings'];
    this.onAction({ type: actions[index] });
  }
}

六、关键技术总结

6.1 四层智能体协同调度
调度模式 说明 适用场景
串行执行 按依赖顺序依次执行 数据清洗→分析→可视化→洞察
并行执行 无依赖任务同时执行 多数据源并行清洗
条件执行 基于前置结果动态选择 数据质量差时跳过分析
6.2 图表自动推荐策略
数据特征 推荐图表 原因
时间序列+数值 折线图 展示趋势变化
分类+数值 柱状图 对比分类数据
单一分类占比 饼图 展示比例分布
双数值关联 散点图 发现相关性
矩阵数据 热力图 展示密度分布
6.3 数据密度光效映射
数据密度 单元格数 光效颜色 脉冲模式 警示语义
稀疏 < 1,000 #1E3A8A 深蓝常亮 数据量不足
正常 1,000-10,000 #10B981 绿色呼吸 数据量适中
密集 10,000-100,000 #F59E0B 橙色呼吸 注意性能
过载 > 100,000 #EF4444 红色闪烁 建议采样

七、效果展示

上图展示了「数智视界」的核心界面

  • 左侧:数据面板,显示总数据量、清洗完成率、异常率、分析进度
  • 中间:数据画布,展示柱状图、折线图、饼图组合图表
  • 右侧:智能洞察面板,显示AI发现的业务洞察与推荐行动
  • 底部:悬浮导航栏,支持数据/分析/图表/洞察/设置五种模式
  • 左下角:光效状态指示器,显示当前数据密度
  • 右下角:智能体运行状态

上图展示了四层智能体协同的完整数据流

  • 左侧:原始数据输入(CSV文件、数据库、API接口、传感器)
  • 中间:四层智能体(清洗→分析→可视化→洞察)+ 调度器
  • 右侧:分析结果输出(清洗报告、统计图表、交互看板、洞察建议)
  • 底部:反馈循环(用户确认→记忆更新→模型优化→推荐增强)

八、总结与展望

本文基于HarmonyOS 6(API 23)的HMAF智能体框架与ArkUI可视化引擎,完整实战了一款面向PC端的智能体数据可视化分析工作台。核心创新点总结:

  1. 四层智能体协同:清洗→分析→可视化→洞察四层智能体流水线处理,实现数据自动理解到洞察推荐的完整闭环

  2. 图表智能推荐:基于数据特征自动推荐最优图表类型,无需用户手动选择

  3. 沉浸光效数据密度:根据数据量大小动态调整系统光效,防止数据过载疲劳

  4. 悬浮导航工作台:将HdsTabs改造为数据分析控制台,支持模式切换与快捷操作

  5. 反馈循环优化:用户确认→记忆更新→模型优化→推荐增强的闭环优化机制

未来扩展方向

  • 自然语言查询:支持"显示Q3销售额趋势"等自然语言指令,自动生成图表
  • 实时数据流:接入Kafka/Flink实时数据流,实现实时可视化
  • 3D可视化:利用HarmonyOS 3D引擎实现立体数据可视化
  • 跨设备协同:PC端分析 + 手机端查看 + 大屏展示的多设备协同

转载自:https://blog.csdn.net/u014727709/article/details/162387657

欢迎 👍点赞✍评论⭐收藏,欢迎指正