在这里插入图片描述

每日一句正能量

世上没有完全快乐的人,只有比较想得开的人。
快乐不是一种恒定的状态,而是一种瞬间的体验。“想得开”意味着能在痛苦中看见意义,在不完美中接受局限。比较的不是谁更快乐,是谁更少被执念困住。

一、前言:当AI会议助理常驻屏幕边缘

在远程办公与混合办公成为常态的今天,职场人士每天平均要参加3-5场会议。然而,传统会议模式存在诸多痛点:会议中需要手动记录要点,容易遗漏关键信息;讨论跑题时无人提醒,导致会议超时;会后整理纪要耗时费力,行动项容易遗漏;跨时区会议时间协调困难。

HarmonyOS 6(API 23)带来的悬浮导航(Floating Navigation)沉浸光感(Immersive Lighting)能力,让我们有机会打造一个常驻屏幕边缘的AI会议助理——它像一位专业的会议秘书,在你开会时静静悬浮在屏幕角落,实时转录语音、提取关键决策、追踪行动项;它通过光效感知会议节奏,在讨论超时前以琥珀色光效温和提醒,在达成关键共识时以绿色光效确认,在会议即将结束时以蓝色光效提示收尾。

核心创新点:

  • 🎙️ 实时语音转录悬浮窗:胶囊形态常驻显示会议时长与关键决策数,支持跨应用会议追踪
  • 💡 会议节奏光感:通过设备边框光效颜色变化,反映会议效率与时间管理状态
  • 🤖 多模态会议智能体:支持语音指令"记录这个决定"、自动提取行动项、生成会议纪要
  • ⏱️ 智能时间管理:自动识别跑题、计算发言时间占比、提醒会议进度

二、应用场景设计

2.1 场景一:实时会议转录与决策追踪

用户正在参加产品评审会,悬浮胶囊常驻屏幕边缘显示"已进行25分钟 | 3项决策"。当产品经理说"我们决定下周发布v2.0版本"时,用户语音指令"记录这个决定",智能体自动提取决策内容并标记决策人,边框泛起绿色确认光效。

2.2 场景二:会议节奏管理

会议已进行50分钟,原定45分钟结束。AI检测到当前话题与议程偏离度超过60%,设备底部边框泛起琥珀色呼吸光效,悬浮窗弹出提示"当前讨论已偏离议程,建议回归主题或延长会议"。主持人看到后及时调整方向。

2.3 场景三:会后智能纪要

会议结束,AI智能体在3秒内生成结构化会议纪要:包含参会人、关键决策(带决策人)、行动项(带负责人与截止日期)、待讨论议题。用户一键分享至企业微信/钉钉,边框泛起彩虹渐变光效表示"任务完成"。

三、技术架构

┌─────────────────────────────────────────────────────────────┐
│           HarmonyOS Meeting Efficiency Assistant Agent      │
├─────────────┬─────────────┬─────────────┬───────────────────┤
│  悬浮窗UI   │  沉浸光感   │  智能体引擎  │   音频处理模块     │
│  FloatUI    │  Lighting   │  AI Engine  │   AudioProcessor  │
├─────────────┴─────────────┴─────────────┴───────────────────┤
│                    会议感知层(Meeting Context)               │
│   语音识别 │ 语义分析 │ 议程追踪 │ 时间管理 │ 决策提取        │
├─────────────────────────────────────────────────────────────┤
│              HarmonyOS 6 (API 23) 系统服务层                 │
│   悬浮导航 │ 光感服务 │ 智能体框架 │ 音频服务 │ 语音识别 │ 日历   │
└─────────────────────────────────────────────────────────────┘

四、核心代码实现

4.1 会议上下文感知引擎(MeetingContextEngine)

这是整个系统的"会议之耳",通过音频捕获与语音识别,实时分析会议内容与状态。

// engine/MeetingContextEngine.ets
import { audio } from '@kit.AudioKit';
import { speechRecognizer } from '@kit.SpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';

export interface MeetingContext {
  meetingId: string;
  startTime: number;
  duration: number;               // 已进行时长(秒)
  scheduledDuration: number;      // 预定时长(秒)
  participants: Participant[];    // 参会人列表
  agenda: AgendaItem[];           // 议程项
  currentTopic: string;           // 当前讨论主题
  decisions: Decision[];          // 已记录决策
  actionItems: ActionItem[];      // 行动项
  speakingStats: SpeakingStats;   // 发言统计
  deviationScore: number;         // 议程偏离度 0-100
  efficiency: number;             // 会议效率评分 0-100
  transcript: TranscriptEntry[];  // 实时转录文本
}

export interface Participant {
  id: string;
  name: string;
  role: string;
  speakingTime: number;           // 发言时长(秒)
  joinTime: number;
}

export interface AgendaItem {
  id: string;
  title: string;
  duration: number;               // 预计时长
  status: 'pending' | 'active' | 'completed' | 'skipped';
  startTime?: number;
}

export interface Decision {
  id: string;
  content: string;
  decisionMaker: string;
  timestamp: number;
  relatedAgenda?: string;
}

export interface ActionItem {
  id: string;
  content: string;
  assignee: string;
  dueDate?: number;
  priority: 'high' | 'medium' | 'low';
  status: 'pending' | 'in_progress' | 'completed';
}

export interface SpeakingStats {
  totalSpeakingTime: number;
  participantShares: Map<string, number>;  // 各人发言占比
  silenceDuration: number;                 // 沉默时长
  interruptions: number;                   // 打断次数
}

export interface TranscriptEntry {
  timestamp: number;
  speaker: string;
  text: string;
  type: 'speech' | 'decision' | 'action' | 'question';
}

export class MeetingContextEngine {
  private audioCapturer: audio.AudioCapturer | null = null;
  private recognizer: speechRecognizer.SpeechRecognizer | null = null;
  private contextCallbacks: Array<(context: MeetingContext) => void> = [];
  private currentContext: MeetingContext | null = null;
  private updateInterval: number = 0;
  private transcriptBuffer: TranscriptEntry[] = [];
  private lastSpeechTime: number = Date.now();

  /**
   * 初始化会议上下文感知
   * 亮点:实时音频捕获 + 流式语音识别 + 语义分析
   */
  async init(config: MeetingConfig): Promise<void> {
    try {
      // 申请音频权限
      const hasPermission = await this.requestAudioPermission();
      if (!hasPermission) {
        console.warn('[MeetingContext] 音频权限未授予');
        return;
      }

      // 初始化音频捕获
      const capturerInfo: audio.AudioCapturerInfo = {
        source: audio.SourceType.MIC,
        capturerFlags: 0
      };
      
      const audioStreamInfo: audio.AudioStreamInfo = {
        samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,
        channels: audio.AudioChannel.CHANNEL_1,
        sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
        encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
      };

      this.audioCapturer = await audio.createAudioCapturer(capturerInfo, audioStreamInfo);

      // 初始化语音识别器
      this.recognizer = await speechRecognizer.createEngine({
        language: 'zh-CN',
        online: true,
        extraParams: { 
          punctuation: true,
          enableWordTimeOffset: true 
        }
      });

      // 注册识别结果回调
      this.recognizer.setCallback({
        onResult: (result) => this.handleSpeechResult(result),
        onError: (error) => console.error(`[Speech] 识别错误: ${error}`)
      });

      // 初始化会议上下文
      this.currentContext = this.createInitialContext(config);

      // 启动音频捕获
      await this.audioCapturer.start();

      // 启动定时刷新(每秒)
      this.updateInterval = setInterval(() => this.refreshContext(), 1000);

      console.info('[MeetingContext] 会议上下文感知引擎初始化完成');
    } catch (err) {
      console.error(`[MeetingContext] 初始化失败: ${JSON.stringify(err)}`);
    }
  }

  /**
   * 创建初始会议上下文
   */
  private createInitialContext(config: MeetingConfig): MeetingContext {
    return {
      meetingId: config.meetingId || `meeting_${Date.now()}`,
      startTime: Date.now(),
      duration: 0,
      scheduledDuration: config.scheduledDuration || 3600,
      participants: config.participants || [],
      agenda: config.agenda || [],
      currentTopic: config.agenda[0]?.title || '开场',
      decisions: [],
      actionItems: [],
      speakingStats: {
        totalSpeakingTime: 0,
        participantShares: new Map(),
        silenceDuration: 0,
        interruptions: 0
      },
      deviationScore: 0,
      efficiency: 100,
      transcript: []
    };
  }

  /**
   * 处理语音识别结果
   */
  private handleSpeechResult(result: speechRecognizer.SpeechRecognitionResult): void {
    if (!result.result || result.result.length === 0) return;

    const text = result.result;
    const speaker = this.identifySpeaker(result);

    // 创建转录条目
    const entry: TranscriptEntry = {
      timestamp: Date.now(),
      speaker,
      text,
      type: this.classifySpeechType(text)
    };

    this.transcriptBuffer.push(entry);
    this.lastSpeechTime = Date.now();

    // 自动提取决策
    if (entry.type === 'decision') {
      this.extractDecision(text, speaker);
    }

    // 自动提取行动项
    if (entry.type === 'action') {
      this.extractActionItem(text);
    }

    // 更新当前话题
    this.updateCurrentTopic(text);
  }

  /**
   * 识别说话人
   * 简化实现,实际应使用声纹识别
   */
  private identifySpeaker(result: speechRecognizer.SpeechRecognitionResult): string {
    // 基于音高/音色特征匹配
    // 简化:返回"参会人A"或从上下文推断
    return this.currentContext?.participants[0]?.name || '未知';
  }

  /**
   * 分类语音类型
   */
  private classifySpeechType(text: string): TranscriptEntry['type'] {
    const decisionPatterns = [
      /决定|决议|确定|定为|就.*达成一致/,
      /同意|批准|通过|采纳/,
      /选择|选用|采用|使用/
    ];

    const actionPatterns = [
      /负责|跟进|落实|执行|完成/,
      /下周|明天|截止日期|之前/,
      /任务|工作|事项|行动/
    ];

    const questionPatterns = [
      /问题|疑问|请问|如何|为什么/,
      /是否|能不能|可不可以/
    ];

    if (decisionPatterns.some(p => p.test(text))) return 'decision';
    if (actionPatterns.some(p => p.test(text))) return 'action';
    if (questionPatterns.some(p => p.test(text))) return 'question';
    return 'speech';
  }

  /**
   * 提取决策
   */
  private extractDecision(text: string, speaker: string): void {
    const decisionPatterns = [
      /(?:我们|大家|会议)?决定(.*)/,
      /(?:确定|定为)(.*)/,
      /(?:一致)?同意(.*)/
    ];

    for (const pattern of decisionPatterns) {
      const match = text.match(pattern);
      if (match && match[1]) {
        const decision: Decision = {
          id: `decision_${Date.now()}`,
          content: match[1].trim(),
          decisionMaker: speaker,
          timestamp: Date.now(),
          relatedAgenda: this.currentContext?.currentTopic
        };

        this.currentContext?.decisions.push(decision);
        break;
      }
    }
  }

  /**
   * 提取行动项
   */
  private extractActionItem(text: string): void {
    const actionPatterns = [
      /(.*?)负责(.*)/,
      /(.*?)跟进(.*)/,
      /(.*?)在(.*?)之前完成(.*)/
    ];

    for (const pattern of actionPatterns) {
      const match = text.match(pattern);
      if (match) {
        const assignee = match[1]?.trim() || '待定';
        const content = match[2]?.trim() || text;
        
        const actionItem: ActionItem = {
          id: `action_${Date.now()}`,
          content,
          assignee,
          priority: 'medium',
          status: 'pending'
        };

        // 尝试提取截止日期
        const dateMatch = text.match(/(明天|下周[一二三四五六日]|\d{1,2}月\d{1,2}日)/);
        if (dateMatch) {
          actionItem.dueDate = this.parseDate(dateMatch[1]);
        }

        this.currentContext?.actionItems.push(actionItem);
        break;
      }
    }
  }

  /**
   * 更新当前话题
   */
  private updateCurrentTopic(text: string): void {
    // 基于关键词匹配议程
    const agenda = this.currentContext?.agenda || [];
    for (const item of agenda) {
      if (text.includes(item.title) || this.isRelated(text, item.title)) {
        this.currentContext!.currentTopic = item.title;
        if (item.status === 'pending') {
          item.status = 'active';
          item.startTime = Date.now();
        }
        break;
      }
    }
  }

  /**
   * 判断文本与话题是否相关
   */
  private isRelated(text: string, topic: string): boolean {
    // 简化:基于关键词重叠度
    const textWords = text.split(/\s+/);
    const topicWords = topic.split(/\s+/);
    const overlap = textWords.filter(w => topicWords.includes(w)).length;
    return overlap / topicWords.length > 0.3;
  }

  /**
   * 刷新会议上下文
   */
  private refreshContext(): void {
    if (!this.currentContext) return;

    const now = Date.now();
    const duration = Math.round((now - this.currentContext.startTime) / 1000);

    // 计算议程偏离度
    const deviation = this.calculateDeviation();

    // 计算会议效率
    const efficiency = this.calculateEfficiency(duration);

    // 更新发言统计
    const silenceDuration = Math.round((now - this.lastSpeechTime) / 1000);
    this.currentContext.speakingStats.silenceDuration = silenceDuration;

    // 更新转录
    this.currentContext.transcript = [...this.currentContext.transcript, ...this.transcriptBuffer];
    this.transcriptBuffer = [];

    const context: MeetingContext = {
      ...this.currentContext,
      duration,
      deviationScore: deviation,
      efficiency
    };

    this.currentContext = context;
    this.contextCallbacks.forEach(cb => cb(context));
  }

  /**
   * 计算议程偏离度
   */
  private calculateDeviation(): number {
    const agenda = this.currentContext?.agenda || [];
    const activeItem = agenda.find(a => a.status === 'active');
    
    if (!activeItem) return 0;

    // 基于当前话题与活跃议程的匹配度
    const currentTopic = this.currentContext?.currentTopic || '';
    const similarity = this.calculateSimilarity(currentTopic, activeItem.title);
    
    return Math.round((1 - similarity) * 100);
  }

  /**
   * 计算文本相似度
   */
  private calculateSimilarity(text1: string, text2: string): number {
    const words1 = new Set(text1.split(''));
    const words2 = new Set(text2.split(''));
    const intersection = new Set([...words1].filter(w => words2.has(w)));
    return intersection.size / Math.max(words1.size, words2.size);
  }

  /**
   * 计算会议效率
   */
  private calculateEfficiency(duration: number): number {
    const scheduled = this.currentContext?.scheduledDuration || 3600;
    const decisions = this.currentContext?.decisions.length || 0;
    const actions = this.currentContext?.actionItems.length || 0;

    // 效率评分维度
    let score = 100;

    // 时间控制:超时扣分
    if (duration > scheduled) {
      score -= Math.min((duration - scheduled) / 60 * 5, 30);
    }

    // 产出质量:决策与行动项加分
    score += decisions * 5;
    score += actions * 3;

    // 偏离度扣分
    score -= this.currentContext?.deviationScore || 0;

    return Math.max(0, Math.min(100, score));
  }

  /**
   * 语音指令处理
   */
  async processVoiceCommand(command: string): Promise<<string> {
    const commandPatterns: Record<string, RegExp> = {
      'record_decision': /记录.*决定|记录.*决策|标记.*决定/,
      'record_action': /记录.*任务|记录.*行动|添加.*行动项/,
      'next_agenda': /下一项|下一个议题|进入.*议程/,
      'extend_time': /延长.*时间|加时|再.*分钟/,
      'end_meeting': /结束.*会议|散会|到此为止/
    };

    for (const [action, pattern] of Object.entries(commandPatterns)) {
      if (pattern.test(command)) {
        return await this.executeCommand(action, command);
      }
    }

    return '未识别的指令,请尝试"记录这个决定"或"进入下一项议程"';
  }

  private async executeCommand(action: string, command: string): Promise<<string> {
    switch (action) {
      case 'record_decision':
        // 从最近语音中提取决策
        const recentText = this.transcriptBuffer.slice(-3).map(t => t.text).join(' ');
        this.extractDecision(recentText, '主持人');
        return '已记录决策';
      
      case 'next_agenda':
        // 切换至下一议程
        const agenda = this.currentContext?.agenda || [];
        const currentIndex = agenda.findIndex(a => a.status === 'active');
        if (currentIndex >= 0) {
          agenda[currentIndex].status = 'completed';
          if (currentIndex + 1 < agenda.length) {
            agenda[currentIndex + 1].status = 'active';
            this.currentContext!.currentTopic = agenda[currentIndex + 1].title;
          }
        }
        return `已进入议程:${this.currentContext?.currentTopic}`;
      
      case 'end_meeting':
        // 结束会议
        return '正在生成会议纪要...';
      
      default:
        return '指令执行中';
    }
  }

  private parseDate(dateStr: string): number {
    // 简化日期解析
    const now = new Date();
    if (dateStr === '明天') {
      return now.getTime() + 24 * 3600 * 1000;
    }
    return now.getTime() + 7 * 24 * 3600 * 1000; // 默认一周后
  }

  onContextChange(callback: (context: MeetingContext) => void): void {
    this.contextCallbacks.push(callback);
  }

  getCurrentContext(): MeetingContext | null {
    return this.currentContext;
  }

  destroy(): void {
    clearInterval(this.updateInterval);
    this.audioCapturer?.stop();
    this.audioCapturer?.release();
    this.recognizer?.destroy();
  }
}

interface MeetingConfig {
  meetingId?: string;
  scheduledDuration?: number;
  participants?: Participant[];
  agenda?: AgendaItem[];
}

4.2 沉浸光感会议节奏反馈(MeetingLightingController)

光效是会议节奏的"时间化延伸",让参会者无需看屏幕就能感知会议状态。

// lighting/MeetingLightingController.ets
import { lighting } from '@kit.ArkUI';

export class MeetingLightingController {
  private currentPhase: string = 'normal';
  private isOvertime: boolean = false;

  /**
   * 初始化会议光感
   * 设计哲学:光效应成为会议的"时间节拍器"
   */
  async init(): Promise<void> {
    if (!lighting.isImmersiveLightSupported()) {
      console.warn('[MeetingLight] 设备不支持沉浸光感');
      return;
    }

    // 初始状态:柔和白色,表示会议开始
    await this.setLightEffect({
      type: 'solid',
      position: 'bottom_edge',
      color: '#E0E0E0',
      brightness: 20,
      duration: 0
    });

    console.info('[MeetingLight] 会议光感初始化完成');
  }

  /**
   * 根据会议状态更新光效
   */
  async updateByMeeting(context: {
    duration: number;
    scheduledDuration: number;
    deviationScore: number;
    efficiency: number;
    silenceDuration: number;
  }): Promise<void> {
    const progress = context.duration / context.scheduledDuration;
    const remaining = context.scheduledDuration - context.duration;

    // 会议超时
    if (remaining < 0) {
      if (!this.isOvertime) {
        this.isOvertime = true;
        await this.setOvertimeAlert();
      }
      return;
    }

    // 议程偏离
    if (context.deviationScore > 60) {
      await this.setDeviationAlert();
      return;
    }

    // 长时间沉默
    if (context.silenceDuration > 30) {
      await this.setSilenceReminder();
      return;
    }

    // 正常进度
    await this.setProgressLighting(progress, context.efficiency);
  }

  /**
   * 进度光效
   * 随着时间推移颜色从绿->黄->橙渐变
   */
  private async setProgressLighting(progress: number, efficiency: number): Promise<void> {
    let color: string;
    let brightness: number;

    if (progress < 0.5) {
      color = '#00E676';      // 翠绿:前半段
      brightness = 25;
    } else if (progress < 0.75) {
      color = '#FFD600';      // 琥珀:中段
      brightness = 30;
    } else if (progress < 0.9) {
      color = '#FF9100';      // 橙黄:接近尾声
      brightness = 35;
    } else {
      color = '#FF5722';      // 橙红:即将结束
      brightness = 40;
    }

    // 效率高时亮度增加
    if (efficiency > 80) {
      brightness += 10;
    }

    await lighting.setImmersiveLight({
      type: 'solid',
      position: 'bottom_edge',
      color,
      brightness,
      duration: 0
    });
  }

  /**
   * 决策确认光效
   * 记录决策时的正向反馈
   */
  async confirmDecision(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'all_edges',
      color: '#00E676',
      brightness: 50,
      duration: 1000,
      flashCount: 2
    });
  }

  /**
   * 行动项记录光效
   */
  async confirmActionItem(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'bottom_edge',
      color: '#448AFF',
      brightness: 40,
      duration: 800,
      flashCount: 1
    });
  }

  /**
   * 议程偏离告警
   */
  private async setDeviationAlert(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'wave',
      position: 'all_edges',
      color: '#FFD600',
      brightness: 45,
      duration: 0,
      direction: 'alternate',
      speed: 'medium'
    });
  }

  /**
   * 超时告警
   */
  private async setOvertimeAlert(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'all_edges',
      color: '#FF1744',
      brightness: 50,
      duration: 0,
      flashCount: -1,
      frequency: 1000
    });
  }

  /**
   * 沉默提醒
   */
  private async setSilenceReminder(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'breathing',
      position: 'bottom_edge',
      color: '#9E9E9E',
      brightness: 30,
      duration: 0,
      frequency: 2000
    });
  }

  /**
   * 会议结束庆祝
   */
  async celebrateEnd(): Promise<void> {
    const colors = ['#00E676', '#00B0FF', '#2979FF', '#7C4DFF', '#F50057', '#FF9100'];
    
    for (const color of colors) {
      await lighting.setImmersiveLight({
        type: 'solid',
        position: 'all_edges',
        color,
        brightness: 50,
        duration: 150
      });
      await new Promise(resolve => setTimeout(resolve, 150));
    }

    await this.reset();
  }

  /**
   * 纪要生成完成
   */
  async summaryComplete(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'all_edges',
      color: '#00E676',
      brightness: 50,
      duration: 2000,
      flashCount: 3
    });
  }

  async reset(): Promise<void> {
    await lighting.resetImmersiveLight();
  }
}

4.3 会议智能体引擎(MeetingAgentEngine)

这是系统的"会议大脑",负责分析会议内容、生成纪要、提供效率建议。

// agent/MeetingAgentEngine.ets
import { ai } from '@kit.AiKit';
import { MeetingContext, Decision, ActionItem, TranscriptEntry } from '../engine/MeetingContextEngine';

export interface MeetingSummary {
  title: string;
  date: string;
  duration: string;
  participants: string[];
  keyDecisions: Decision[];
  actionItems: ActionItem[];
  discussedTopics: string[];
  pendingQuestions: string[];
  efficiency: number;
  nextMeeting?: string;
}

export interface EfficiencyReport {
  score: number;
  strengths: string[];
  improvements: string[];
  speakingBalance: Map<string, number>;
  timeAllocation: Map<string, number>;
}

export class MeetingAgentEngine {
  private agent: ai.AgentSession | null = null;

  /**
   * 初始化会议智能体
   */
  async init(): Promise<void> {
    const model = await ai.createModel({
      modelId: 'harmonyos-meeting-v1',
      type: ai.ModelType.LOCAL,
      capabilities: ['meeting_analysis', 'summarization', 'action_extraction']
    });

    this.agent = await ai.createAgentSession({
      model: model,
      systemPrompt: `你是一位专业的会议效率顾问,精通会议管理与信息提取。
        请基于会议转录文本,提供:
        1. 结构化会议纪要
        2. 关键决策与行动项提取
        3. 会议效率分析
        4. 改进建议
        
        输出要求:
        - 使用Markdown格式
        - 决策必须包含决策人
        - 行动项必须包含负责人与截止日期
        - 效率分析客观具体`
    });

    console.info('[MeetingAgent] 会议智能体初始化完成');
  }

  /**
   * 生成会议纪要
   */
  async generateSummary(context: MeetingContext): Promise<<MeetingSummary> {
    if (!this.agent) return this.fallbackSummary(context);

    const transcript = context.transcript.map(t => 
      `[${new Date(t.timestamp).toLocaleTimeString()}] ${t.speaker}: ${t.text}`
    ).join('\n');

    const prompt = `请基于以下会议转录生成结构化纪要:
      
      会议时长:${this.formatDuration(context.duration)}
      参会人:${context.participants.map(p => p.name).join(', ')}
      议程:${context.agenda.map(a => a.title).join(', ')}
      
      转录文本:
      ${transcript}
      
      请生成包含以下内容的纪要:
      1. 会议标题与基本信息
      2. 关键决策(已识别:${context.decisions.length}项)
      3. 行动项(已识别:${context.actionItems.length}项)
      4. 讨论的主要话题
      5. 遗留问题
      6. 下次会议建议`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 2048, temperature: 0.3 }
    });

    return this.parseSummary(result, context);
  }

  /**
   * 生成效率分析报告
   */
  async generateEfficiencyReport(context: MeetingContext): Promise<<EfficiencyReport> {
    const speakingBalance = new Map<string, number>();
    const timeAllocation = new Map<string, number>();

    // 计算发言平衡
    context.participants.forEach(p => {
      const share = context.speakingStats.participantShares.get(p.id) || 0;
      speakingBalance.set(p.name, share);
    });

    // 计算时间分配
    context.agenda.forEach(item => {
      if (item.startTime) {
        const duration = item.status === 'completed' 
          ? (Date.now() - item.startTime) / 1000 
          : 0;
        timeAllocation.set(item.title, duration);
      }
    });

    const prompt = `请分析以下会议的效率:
      预定时长:${this.formatDuration(context.scheduledDuration)}
      实际时长:${this.formatDuration(context.duration)}
      决策数量:${context.decisions.length}
      行动项数量:${context.actionItems.length}
      议程偏离度:${context.deviationScore}%
      沉默时长:${context.speakingStats.silenceDuration}秒
      
      请给出:
      1. 效率评分(0-100)
      2. 会议优点
      3. 改进建议`;

    const result = await this.agent!.invoke({
      input: { question: prompt },
      options: { maxTokens: 512, temperature: 0.3 }
    });

    return {
      score: context.efficiency,
      strengths: result.data?.strengths || ['讨论充分', '决策明确'],
      improvements: result.data?.improvements || ['控制时间', '减少跑题'],
      speakingBalance,
      timeAllocation
    };
  }

  /**
   * 实时会议建议
   */
  async generateRealtimeSuggestion(context: MeetingContext): Promise<<string> {
    if (!this.agent) return '';

    const prompt = `当前会议状态:
      已进行:${this.formatDuration(context.duration)}
      当前话题:${context.currentTopic}
      偏离度:${context.deviationScore}%
      沉默时长:${context.speakingStats.silenceDuration}秒
      
      请给出一条简短的会议引导建议(不超过20字)。`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 64, temperature: 0.5 }
    });

    return result.data?.suggestion || '';
  }

  /**
   * 语音指令理解
   */
  async understandCommand(command: string, context: MeetingContext): Promise<<{
    action: string;
    parameters: any;
  }> {
    const prompt = `理解会议语音指令:
      指令:"${command}"
      当前议程:${context.currentTopic}
      
      请解析为结构化指令。`;

    const result = await this.agent!.invoke({
      input: { question: prompt },
      options: { maxTokens: 128, temperature: 0.1 }
    });

    return {
      action: result.data?.action || 'unknown',
      parameters: result.data?.parameters || {}
    };
  }

  private parseSummary(result: ai.ModelOutput, context: MeetingContext): MeetingSummary {
    const data = result.data || {};
    
    return {
      title: data.title || '会议纪要',
      date: new Date().toLocaleDateString(),
      duration: this.formatDuration(context.duration),
      participants: context.participants.map(p => p.name),
      keyDecisions: context.decisions,
      actionItems: context.actionItems,
      discussedTopics: data.discussedTopics || [],
      pendingQuestions: data.pendingQuestions || [],
      efficiency: context.efficiency,
      nextMeeting: data.nextMeeting
    };
  }

  private fallbackSummary(context: MeetingContext): MeetingSummary {
    return {
      title: '会议纪要',
      date: new Date().toLocaleDateString(),
      duration: this.formatDuration(context.duration),
      participants: context.participants.map(p => p.name),
      keyDecisions: context.decisions,
      actionItems: context.actionItems,
      discussedTopics: [context.currentTopic],
      pendingQuestions: [],
      efficiency: context.efficiency
    };
  }

  private formatDuration(seconds: number): string {
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    if (hours > 0) return `${hours}小时${minutes}分钟`;
    return `${minutes}分钟`;
  }

  destroy(): void {
    this.agent?.destroy();
  }
}

4.4 悬浮窗会议面板(MeetingFloatWindow)

// float/MeetingFloatWindow.ets
import { window } from '@kit.ArkUI';
import { emitter } from '@kit.BasicServicesKit';
import { MeetingContext } from '../engine/MeetingContextEngine';
import { MeetingSummary } from '../agent/MeetingAgentEngine';

export class MeetingFloatWindow {
  private floatWin: window.Window | null = null;
  private currentLevel: 'capsule' | 'panel' | 'summary' = 'capsule';

  async create(): Promise<void> {
    const option: window.WindowOption = {
      name: 'MeetingAssistant',
      windowType: window.WindowType.TYPE_FLOAT,
      ctx: getContext(this)
    };

    this.floatWin = await window.createWindow(getContext(this), option);
    
    // 胶囊形态:显示会议时长与决策数
    await this.floatWin.resize({ width: 130, height: 80 });
    await this.floatWin.moveWindowTo({ x: 890, y: 100 });
    await this.floatWin.setWindowTouchable(true);
    await this.floatWin.setUIContent('pages/MeetingCapsulePage');
    await this.floatWin.showWindow();
  }

  async expandToPanel(context: MeetingContext): Promise<void> {
    if (!this.floatWin) return;
    
    this.currentLevel = 'panel';
    await this.floatWin.resize({ width: 460, height: 720 });
    await this.floatWin.moveWindowTo({ x: 540, y: 80 });
    await this.floatWin.setUIContent('pages/MeetingPanelPage');
    
    emitter.emit('showMeetingPanel', { data: context });
  }

  async showSummary(summary: MeetingSummary): Promise<void> {
    if (!this.floatWin) return;
    
    this.currentLevel = 'summary';
    await this.floatWin.resize({ width: 500, height: 800 });
    await this.floatWin.moveWindowTo({ x: 520, y: 40 });
    await this.floatWin.setUIContent('pages/MeetingSummaryPage');
    
    emitter.emit('showMeetingSummary', { data: summary });
  }

  async collapseToCapsule(): Promise<void> {
    if (!this.floatWin) return;
    
    this.currentLevel = 'capsule';
    await this.floatWin.resize({ width: 130, height: 80 });
    await this.floatWin.moveWindowTo({ x: 890, y: 100 });
    await this.floatWin.setUIContent('pages/MeetingCapsulePage');
  }

  destroy(): void {
    this.floatWin?.destroyWindow();
  }
}

4.5 会议胶囊页面(MeetingCapsulePage)

// pages/MeetingCapsulePage.ets
import { emitter } from '@kit.BasicServicesKit';

@Entry
@Component
struct MeetingCapsulePage {
  @State duration: string = '00:00';
  @State decisionCount: number = 0;
  @State actionCount: number = 0;
  @State efficiency: number = 100;
  @State isRecording: boolean = false;

  aboutToAppear() {
    emitter.on('updateMeetingContext', (event) => {
      const ctx = event.data;
      this.duration = this.formatDuration(ctx?.duration || 0);
      this.decisionCount = ctx?.decisions?.length || 0;
      this.actionCount = ctx?.actionItems?.length || 0;
      this.efficiency = ctx?.efficiency || 100;
    });
  }

  build() {
    Stack() {
      // 效率指示外圈
      Circle()
        .width(140)
        .height(140)
        .fill('none')
        .stroke(
          this.efficiency > 80 ? '#00E676' :
          this.efficiency > 60 ? '#FFD600' : '#FF1744'
        )
        .strokeWidth(3)
        .strokeDashArray([
          2 * Math.PI * 70 * (this.efficiency / 100),
          2 * Math.PI * 70
        ])
        .animation({ duration: 1000 })

      Column() {
        // 录音状态
        if (this.isRecording) {
          Circle()
            .width(8)
            .height(8)
            .fill('#FF1744')
            .animation({
              duration: 1000,
              iterations: -1,
              curve: Curve.EaseInOut,
              playMode: PlayMode.Alternate
            })
        }

        // 时长
        Text(this.duration)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333')

        // 决策/行动项
        Text(`${this.decisionCount}决策 · ${this.actionCount}行动`)
          .fontSize(10)
          .fontColor('#666')

        // 效率
        Text(`${this.efficiency}%`)
          .fontSize(10)
          .fontColor(
            this.efficiency > 80 ? '#00C853' :
            this.efficiency > 60 ? '#FF9100' : '#FF1744'
          )
      }
      .width(130)
      .height(80)
      .justifyContent(FlexAlign.Center)
      .backgroundColor('rgba(255, 255, 255, 0.95)')
      .borderRadius(16)
      .shadow({ radius: 12, color: 'rgba(0,0,0,0.12)' })
      .gesture(
        GestureGroup(GestureMode.Sequence,
          TapGesture({ count: 1 })
            .onAction(() => emitter.emit('expandToPanel')),
          LongPressGesture({ duration: 800 })
            .onAction(() => emitter.emit('toggleRecording'))
        )
      )
    }
    .width('100%')
    .height('100%')
    .align(Alignment.Center)
  }

  private formatDuration(seconds: number): string {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }
}

4.6 会议面板页面(MeetingPanelPage)

// pages/MeetingPanelPage.ets
import { emitter } from '@kit.BasicServicesKit';
import { MeetingContext, AgendaItem, Decision, ActionItem } from '../engine/MeetingContextEngine';

@Entry
@Component
struct MeetingPanelPage {
  @State context: MeetingContext | null = null;
  @State selectedTab: string = 'live';
  private tabs: string[] = ['live', 'agenda', 'decisions', 'actions'];

  aboutToAppear() {
    emitter.on('showMeetingPanel', (event) => {
      this.context = event.data;
    });
  }

  build() {
    Column() {
      // 顶部标题栏
      Row() {
        Text('🎙️ 会议助手')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        
        Button('收起')
          .fontSize(12)
          .backgroundColor('#f0f0f0')
          .fontColor('#333')
          .onClick(() => emitter.emit('collapseToCapsule'))
      }
      .width('100%')
      .padding(16)

      // Tab切换
      Row() {
        ForEach(this.tabs, (tab: string) => {
          Button(this.getTabName(tab))
            .fontSize(12)
            .backgroundColor(this.selectedTab === tab ? '#2979FF' : '#f0f0f0')
            .fontColor(this.selectedTab === tab ? '#FFF' : '#666')
            .margin({ right: 8 })
            .onClick(() => this.selectedTab = tab)
        })
      }
      .width('100%')
      .margin({ bottom: 12 })

      // 内容区
      if (this.selectedTab === 'live') {
        this.LiveTab()
      } else if (this.selectedTab === 'agenda') {
        this.AgendaTab()
      } else if (this.selectedTab === 'decisions') {
        this.DecisionsTab()
      } else {
        this.ActionsTab()
      }

      // 语音指令输入
      Row() {
        TextInput({ placeholder: '语音指令:记录这个决定 / 下一项议程...' })
          .fontSize(13)
          .layoutWeight(1)
          .onSubmit((value) => this.submitCommand(value))
        
        Button('🎤')
          .fontSize(14)
          .backgroundColor('#2979FF')
          .onClick(() => this.startVoiceCommand())
      }
      .width('100%')
      .padding(12)
      .backgroundColor('#f8f9fa')
      .borderRadius(8)
      .margin({ top: 8 })
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#FFF')
    .borderRadius(16)
  }

  @Builder
  LiveTab() {
    Column() {
      // 实时转录
      Text('📝 实时转录')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .width('100%')

      List() {
        ForEach(this.context?.transcript.slice(-10) || [], (entry: any) => {
          ListItem() {
            Column() {
              Text(`${entry.speaker} · ${new Date(entry.timestamp).toLocaleTimeString()}`)
                .fontSize(10)
                .fontColor('#999')
                .width('100%')

              Text(entry.text)
                .fontSize(13)
                .fontColor('#333')
                .width('100%')
                .margin({ top: 2 })

              if (entry.type === 'decision') {
                Text('✓ 决策')
                  .fontSize(10)
                  .fontColor('#00C853')
                  .backgroundColor('#E8F5E9')
                  .padding({ left: 6, right: 6, top: 2, bottom: 2 })
                  .borderRadius(4)
                  .margin({ top: 4 })
              } else if (entry.type === 'action') {
                Text('⚡ 行动项')
                  .fontSize(10)
                  .fontColor('#2979FF')
                  .backgroundColor('#E3F2FD')
                  .padding({ left: 6, right: 6, top: 2, bottom: 2 })
                  .borderRadius(4)
                  .margin({ top: 4 })
              }
            }
            .padding(8)
            .backgroundColor('#f8f9fa')
            .borderRadius(6)
            .margin({ bottom: 6 })
          }
        })
      }
      .width('100%')
      .layoutWeight(1)
      .margin({ top: 8 })
    }
  }

  @Builder
  AgendaTab() {
    List() {
      ForEach(this.context?.agenda || [], (item: AgendaItem) => {
        ListItem() {
          Row() {
            // 状态指示
            Circle()
              .width(12)
              .height(12)
              .fill(
                item.status === 'completed' ? '#00E676' :
                item.status === 'active' ? '#2979FF' :
                item.status === 'skipped' ? '#9E9E9E' : '#E0E0E0'
              )

            Column() {
              Text(item.title)
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
                .width('100%')
                .decoration({
                  type: item.status === 'completed' ? TextDecorationType.LineThrough : TextDecorationType.None
                })

              Text(`${this.formatDuration(item.duration)}`)
                .fontSize(11)
                .fontColor('#999')
                .width('100%')
            }
            .layoutWeight(1)
            .margin({ left: 12 })

            Text(
              item.status === 'completed' ? '✓' :
              item.status === 'active' ? '进行中' :
              item.status === 'skipped' ? '跳过' : '待开始'
            )
              .fontSize(12)
              .fontColor(
                item.status === 'completed' ? '#00C853' :
                item.status === 'active' ? '#2979FF' : '#999'
              )
          }
          .width('100%')
          .padding(12)
          .backgroundColor('#fff')
          .borderRadius(8)
          .margin({ bottom: 8 })
        }
      })
    }
    .width('100%')
    .layoutWeight(1)
  }

  @Builder
  DecisionsTab() {
    List() {
      ForEach(this.context?.decisions || [], (decision: Decision) => {
        ListItem() {
          Column() {
            Row() {
              Text('✓')
                .fontSize(16)
                .fontColor('#00C853')
              
              Text(decision.content)
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
                .layoutWeight(1)
                .margin({ left: 8 })
            }
            .width('100%')

            Text(`决策人:${decision.decisionMaker} · ${new Date(decision.timestamp).toLocaleTimeString()}`)
              .fontSize(11)
              .fontColor('#999')
              .width('100%')
              .margin({ top: 4 })
          }
          .padding(12)
          .backgroundColor('#E8F5E9')
          .borderRadius(8)
          .margin({ bottom: 8 })
        }
      })
    }
    .width('100%')
    .layoutWeight(1)
  }

  @Builder
  ActionsTab() {
    List() {
      ForEach(this.context?.actionItems || [], (action: ActionItem) => {
        ListItem() {
          Row() {
            Column() {
              Text(action.content)
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
                .width('100%')
              
              Text(`负责人:${action.assignee}${action.dueDate ? ' · 截止:' + new Date(action.dueDate).toLocaleDateString() : ''}`)
                .fontSize(11)
                .fontColor('#999')
                .width('100%')
            }
            .layoutWeight(1)

            Text(
              action.priority === 'high' ? '高' :
              action.priority === 'medium' ? '中' : '低'
            )
              .fontSize(11)
              .fontColor('#FFF')
              .backgroundColor(
                action.priority === 'high' ? '#FF1744' :
                action.priority === 'medium' ? '#FF9100' : '#00C853'
              )
              .padding({ left: 8, right: 8, top: 2, bottom: 2 })
              .borderRadius(4)
          }
          .width('100%')
          .padding(12)
          .backgroundColor('#E3F2FD')
          .borderRadius(8)
          .margin({ bottom: 8 })
        }
      })
    }
    .width('100%')
    .layoutWeight(1)
  }

  private getTabName(tab: string): string {
    const names: Record<string, string> = {
      'live': '实时',
      'agenda': '议程',
      'decisions': '决策',
      'actions': '行动'
    };
    return names[tab] || tab;
  }

  private formatDuration(seconds: number): string {
    const mins = Math.floor(seconds / 60);
    return `${mins}分钟`;
  }

  private submitCommand(command: string) {
    emitter.emit('processVoiceCommand', { data: { command } });
  }

  private startVoiceCommand() {
    emitter.emit('startVoiceCommand');
  }
}

4.7 主入口与系统集成(Index.ets)

// Index.ets
import { MeetingContextEngine } from './engine/MeetingContextEngine';
import { MeetingLightingController } from './lighting/MeetingLightingController';
import { MeetingFloatWindow } from './float/MeetingFloatWindow';
import { MeetingAgentEngine } from './agent/MeetingAgentEngine';
import { emitter } from '@kit.BasicServicesKit';

@Entry
@Component
struct MeetingAssistantApp {
  private meetingEngine: MeetingContextEngine = new MeetingContextEngine();
  private lightController: MeetingLightingController = new MeetingLightingController();
  private floatWindow: MeetingFloatWindow = new MeetingFloatWindow();
  private agentEngine: MeetingAgentEngine = new MeetingAgentEngine();

  aboutToAppear() {
    this.initSystem();
  }

  aboutToDisappear() {
    this.meetingEngine.destroy();
    this.lightController.reset();
    this.floatWindow.destroy();
    this.agentEngine.destroy();
  }

  async initSystem() {
    // 1. 初始化沉浸光感
    await this.lightController.init();

    // 2. 初始化悬浮窗
    await this.floatWindow.create();

    // 3. 初始化智能体引擎
    await this.agentEngine.init();

    // 4. 初始化会议引擎
    await this.meetingEngine.init({
      meetingId: `meeting_${Date.now()}`,
      scheduledDuration: 3600,
      participants: [
        { id: 'p1', name: '张三', role: '产品经理', speakingTime: 0, joinTime: Date.now() },
        { id: 'p2', name: '李四', role: '开发负责人', speakingTime: 0, joinTime: Date.now() },
        { id: 'p3', name: '王五', role: '设计师', speakingTime: 0, joinTime: Date.now() }
      ],
      agenda: [
        { id: 'a1', title: '产品需求评审', duration: 900, status: 'active' },
        { id: 'a2', title: '技术方案讨论', duration: 1200, status: 'pending' },
        { id: 'a3', title: '排期确认', duration: 600, status: 'pending' },
        { id: 'a4', title: '风险与问题', duration: 600, status: 'pending' },
        { id: 'a5', title: '下一步行动', duration: 300, status: 'pending' }
      ]
    });

    // 当会议上下文变化时,更新光效和悬浮窗
    this.meetingEngine.onContextChange(async (context) => {
      // 更新光效
      await this.lightController.updateByMeeting({
        duration: context.duration,
        scheduledDuration: context.scheduledDuration,
        deviationScore: context.deviationScore,
        efficiency: context.efficiency,
        silenceDuration: context.speakingStats.silenceDuration
      });
      
      // 更新悬浮窗
      emitter.emit('updateMeetingContext', { data: context });
    });

    // 5. 设置事件监听
    this.setupEventListeners();
  }

  private setupEventListeners() {
    emitter.on('expandToPanel', async () => {
      const context = this.meetingEngine.getCurrentContext();
      if (context) {
        await this.floatWindow.expandToPanel(context);
      }
    });

    emitter.on('collapseToCapsule', async () => {
      await this.floatWindow.collapseToCapsule();
    });

    emitter.on('toggleRecording', () => {
      // 切换录音状态
    });

    emitter.on('processVoiceCommand', async (event) => {
      const command = event.data?.command;
      const context = this.meetingEngine.getCurrentContext();
      if (command && context) {
        const result = await this.meetingEngine.processVoiceCommand(command);
        // 显示结果
      }
    });

    emitter.on('startVoiceCommand', () => {
      // 启动语音识别
    });

    emitter.on('endMeeting', async () => {
      const context = this.meetingEngine.getCurrentContext();
      if (context) {
        const summary = await this.agentEngine.generateSummary(context);
        await this.floatWindow.showSummary(summary);
        await this.lightController.celebrateEnd();
      }
    });
  }

  build() {
    Column() {
      Text('🎙️ HarmonyOS会议效率助手')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      Text('AI智能体正在记录您的会议...')
        .fontSize(14)
        .fontColor('#666')
      
      Text('悬浮窗常驻显示,语音指令随时控制')
        .fontSize(12)
        .fontColor('#999')
        .margin({ top: 4 })
        .textAlign(TextAlign.Center)

      // 会议统计
      Column() {
        Text('📊 会议概览')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .margin({ bottom: 12 })

        Row() {
          Column() {
            Text('5')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#2979FF')
            Text('议程项')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)

          Column() {
            Text('3')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#00C853')
            Text('参会人')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)

          Column() {
            Text('60min')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#FF9100')
            Text('预计时长')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)
        }
        .width('100%')
      }
      .width('90%')
      .padding(20)
      .backgroundColor('#f8f9fa')
      .borderRadius(12)
      .margin({ top: 32 })

      // 当前状态
      Row() {
        Text('系统状态:')
          .fontSize(14)
          .fontColor('#666')
        
        Text('🟢 会议进行中')
          .fontSize(14)
          .fontColor('#00C853')
          .fontWeight(FontWeight.Medium)
      }
      .margin({ top: 20 })

      // 结束会议按钮
      Button('结束会议并生成纪要')
        .fontSize(16)
        .backgroundColor('#FF1744')
        .width('80%')
        .margin({ top: 32 })
        .onClick(() => emitter.emit('endMeeting'))
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

五、配置文件

// module.json5
{
  "module": {
    "name": "MeetingAssistant",
    "type": "entry",
    "description": "鸿蒙智能体会议效率助手",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet", "2in1"],
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "会议效率助手主入口",
        "icon": "$media:layered_image",
        "label": "AI会议助手",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
        "reason": "需要悬浮窗权限以常驻显示会议状态"
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "需要麦克风权限以捕获会议音频"
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "连接云端语音识别与智能体服务"
      },
      {
        "name": "ohos.permission.ACCESS_AI_MODEL",
        "reason": "使用端侧AI模型进行语义分析"
      }
    ]
  }
}

六、效果展示与使用场景

6.1 典型会议场景

场景A:产品评审会

产品经理张三正在主持评审会,悬浮胶囊显示"已进行25分钟 | 2项决策"。当他说"我们决定下周发布v2.0版本"时,参会人李四语音指令"记录这个决定",边框泛起绿色确认光效,决策自动记录到"决策"标签页。会议进行50分钟时,讨论偏离到技术细节,边框泛起琥珀色波浪光效,提示"当前讨论已偏离议程"。

场景B:跨时区站会

海外团队站会,悬浮窗实时转录中英文混合发言。当印度同事发言时,智能体自动识别口音并提高转录准确率。会议超时5分钟,边框红色快闪提醒。结束后3秒生成纪要,包含"3项决策、5个行动项",一键分享至Slack。

场景C:一对一绩效面谈

HR与员工面谈,悬浮窗静默记录(经双方同意)。智能体分析对话情绪,当检测到紧张氛围时,边框泛起蓝色舒缓光效。面谈结束生成结构化记录,包含"员工诉求、HR承诺、后续跟进",双方确认后归档。

6.2 光效语义设计

会议状态 光效表现 会议提示
会议初期 底部翠绿常亮 时间充裕,充分讨论
会议中期 底部琥珀常亮 注意时间分配
接近尾声 底部橙红常亮 准备收尾
记录决策 全边框绿色双闪 决策已记录
记录行动项 底部蓝色单闪 行动项已添加
议程偏离 全边框琥珀波浪 建议回归主题
会议超时 全边框红色快闪 立即结束或延长
长时间沉默 底部灰色呼吸 建议引导发言
纪要生成 彩虹渐变 任务完成

七、性能与隐私优化

7.1 音频处理策略

// 音频处理优化
const audioStrategy = {
  // 采样率:根据网络状况动态调整
  sampleRate: {
    high: 16000,     // 高质量:16kHz
    normal: 8000,    // 标准:8kHz
    low: 4000        // 低质量:4kHz
  },
  
  // 语音识别模式
  recognition: {
    realTime: true,   // 实时识别
    partialResults: true, // 返回中间结果
    punctuation: true  // 自动标点
  },
  
  // 本地缓存
  cache: {
    enabled: true,
    maxDuration: 3600, // 最多缓存1小时
    autoCleanup: true  // 自动清理
  }
};

7.2 隐私保护

  • 本地优先:语音识别优先本地处理,敏感内容不上云
  • 同意机制:录音前明确提示,可随时暂停/删除
  • 数据隔离:不同会议数据完全隔离
  • 自动清理:会议结束后7天自动删除原始音频

八、总结与展望

本文展示了如何基于HarmonyOS 6(API 23)的悬浮导航沉浸光感能力,构建一个鸿蒙智能体驱动的沉浸式会议效率助手。与传统会议工具不同,它具备三个核心创新:

  1. 实时感知:悬浮窗常驻屏幕边缘,会议状态随时可见,光效即时反馈节奏
  2. 智能提取:AI智能体自动识别决策与行动项,无需手动记录
  3. 效率提升:多模态交互(语音+光效+悬浮窗)协同提升会议效率

未来演进方向:

  • 多语言支持:实时翻译跨语言会议,消除语言障碍
  • 情绪分析:基于语音情绪识别,优化会议氛围
  • 智能排期:基于参会人日历与习惯,自动推荐最佳会议时间
  • 知识沉淀:会议决策自动关联项目管理系统,形成知识图谱

HarmonyOS 6的智能体框架和系统级交互创新,正在让"AI会议助理常驻身边"成为现实。对于职场人士而言,这不仅是一个记录工具,更是一位24小时在线、懂议程、懂效率、懂协作的智能会议伙伴。


转载自:https://blog.csdn.net/u014727709/article/details/161420923
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐