鸿蒙 + Flutter 下如何管理 AI 会话——AgentService 设计解析

适合谁看

  • 正在给鸿蒙 Flutter 应用接 AI 会话的人

  • 想把模型调用从页面层抽出来的人

  • 想理解移动端(尤其是鸿蒙端)AI 会话管理该收在哪一层的人

  • 想了解 AgentService 如何和协调器、工具层、鸿蒙原生能力协作的人

问题背景

很多 AI 页面初版都会直接这样写:

  • 页面按钮点一下

  • 直接发请求

  • 把结果塞回状态

这种方式在 Demo 阶段很快,但一旦开始加入:

  • 工具调用

  • 流式输出

  • 会话记忆

  • 页面退出释放

  • 鸿蒙端的语音输入输出联动

  • Token 消耗统计和成本追踪

页面层就会很快变得过重。

真正的难点不是"怎么发一次聊天请求",而是:

怎么让移动端里的 AI 会话成为一个可管理对象------在鸿蒙设备上尤其如此,因为会话生命周期还要和鸿蒙原生引擎的生命周期对齐。

项目中的真实场景

食界探味当前专门抽了:

  • app/lib/core/ai/agent_service.dart --- AI 会话服务层

它上面连接:

  • app/lib/core/ai/agent_providers.dart --- 模型配置和 Provider

下面被:

  • app/lib/core/ai/ai_explore_coordinator.dart --- 协调器消费

同时它间接触发鸿蒙侧的能力:

  • SpeechRecognitionPlugin.ets --- 语音识别

  • TextToSpeechPlugin.ets --- TTS 播报

这说明当前项目并没有让页面直接依赖 AI Provider,而是先放了一层专门的会话服务。这个服务层不感知底层是鸿蒙还是 Android,但它的生命周期管理直接影响鸿蒙端的体验。

核心实现

先说结论:

AgentService 的价值不在"帮你少写几行调用代码",而在"把 AI 会话的创建、使用、续跑和释放都变成可管理的结构"。

一、它先把"当前会话对象"收成了显式状态

AgentService 里,最关键的一个字段是:

复制代码
class AgentService {
  final OpenAIProvider _provider;
  AIAgent? _currentAgent;

这说明项目当前对 AI 会话的理解不是"随手发一次模型请求",而是"当前有一个会话中的 agent"。这一步很重要,因为它让创建 agent、使用 agent、清掉 agent 这些动作都有了明确落点。

二、Provider 层:模型配置和成本追踪

在深入 AgentService 之前,先看它依赖的 Provider 层:

复制代码
// app/lib/core/ai/agent_providers.dart

final zhipuAIProvider = Provider<OpenAIProvider>((ref) {
  return OpenAIProvider(
    config: OpenAIConfig(
      baseUrl: '${EnvConfig.apiBaseUrl}/zhipu/paas/v4',
      apiKey: 'proxy-managed',
      model: 'glm-4.7',
    ),
  );
});

这里有几个值得注意的设计:

  1. API Key 由后端代理管理 --- 客户端持有的是 proxy-managed 占位符,真实的 API Key 在后端注入。这对鸿蒙端的安全性尤其重要,避免密钥暴露在客户端代码中

  2. 通过后端代理转发 --- 请求走 ${EnvConfig.apiBaseUrl}/zhipu/paas/v4,而不是直连智谱 API。这意味着鸿蒙设备即使在受限网络环境下也能正常访问

  3. 模型可切换 --- 项目维护了一个允许使用的模型列表:

    const zhipuAllowedModels = [
    'glm-4v-flash',
    'glm-4-flash',
    'glm-4v',
    'glm-4v-plus',
    'glm-4.5v',
    'glm-4.6',
    'glm-4.6v',
    'glm-4.7',
    ];

  4. Token 成本追踪 --- 每次对话完成后,AgentService 会记录消耗的 token 数量和费用:

    if (chunk.isDone && chunk.usage != null) {
    AppLogger.info(
    '[AgentService] 消耗: ${ZhipuPricing.formatCostLog(chunk.usage!)}',
    );
    }

成本计算逻辑:

复制代码
class ZhipuPricing {
  static const double inputPricePerMillion = 3.0;   // 输入 ¥3/百万 token
  static const double outputPricePerMillion = 14.0;  // 输出 ¥14/百万 token

  static String formatCostLog(TokenUsage usage) {
    final inputCost = usage.promptTokens * inputPricePerMillion / 1000000;
    final outputCost = usage.completionTokens * outputPricePerMillion / 1000000;
    final totalCost = inputCost + outputCost;
    return '输入: ${usage.promptTokens} tokens (¥${inputCost.toStringAsFixed(6)}), '
        '输出: ${usage.completionTokens} tokens (¥${outputCost.toStringAsFixed(6)}), '
        '总计: ${usage.totalTokens} tokens (¥${totalCost.toStringAsFixed(6)})';
  }
}

在鸿蒙端调试时,这些日志能帮你快速判断:模型调用是否正常、token 消耗是否合理、成本是否可控。

三、createAgent() 让会话初始化集中发生

createAgent() 当前做的事情并不只是"new 一个对象",而是把一组会话初始条件都收在了一起:

复制代码
AIAgent createAgent({
  required String systemPrompt,
  List<Tool>? tools,
  String? model,
  int maxMessages = 50,
  bool enableAutoToolExecution = false,
}) {
  final agent = AIAgent(
    provider: _provider,
    config: AIAgentConfig(
      systemPrompt: systemPrompt,
      model: model,
      enableAutoToolExecution: enableAutoToolExecution,
      additionalParams: {'thinking': {'type': 'disabled'}},
    ),
    memoryManager: ConversationMemory(maxMessages: maxMessages),
  );

  if (tools != null) {
    for (final tool in tools) {
      agent.addTool(tool);
    }
  }

  _currentAgent = agent;
  return agent;
}

各参数的职责:

参数 作用 为什么放在服务层
systemPrompt 定义 AI 的角色和行为规范 避免每个页面重复写 prompt
tools 注册业务工具(搜索菜品、详情等) 工具注册逻辑统一收口
model 指定模型名称,默认用 Provider 配置 支持按场景切换模型
maxMessages 记忆窗口大小,控制历史消息保留数量 移动端内存有限,必须主动控制
enableAutoToolExecution 是否自动执行工具调用 避免协调器手动管理工具执行

注意 additionalParams: {'thinking': {'type': 'disabled'}} --- 这禁用了模型的思考链输出。在移动端,思考链会增加延迟和 token 消耗,禁用后响应更快。

这说明 AgentService 当前其实已经在承担 agent 工厂 + 会话策略入口 的职责。页面层和协调器层不再需要自己拼这些初始条件。

四、为什么记忆窗口要留在这层

createAgent() 里,ConversationMemory(maxMessages: maxMessages) 是一个很值得注意的点。

它说明项目已经意识到:移动端 AI 会话不是无限上下文,而应该主动控制会话窗口大小和历史消息保留数量。

食界探味在协调器中实际使用的配置:

复制代码
// ai_explore_coordinator.dart

_agentService.createAgent(
  systemPrompt: _systemPrompt,
  tools: [...],
  enableAutoToolExecution: true,
  maxMessages: 30,  // 最多保留 30 条历史消息
);

为什么是 30?因为:

  1. 鸿蒙端设备内存有限,过多历史消息会占用大量内存

  2. 菜品推荐场景不需要太长的对话历史

  3. 30 条足够覆盖 10-15 轮对话,满足"探索 → 细化 → 推荐"的典型流程

这类决策如果直接塞进页面层,后面会很难统一。放在 AgentService 里就更合理,因为它更接近"会话策略",而不是"页面渲染策略"。

五、为什么流式对话也应该收在这层

chatWithToolsStream() 是这份代码里最关键的方法之一:

复制代码
Future<void> chatWithToolsStream({
  required String message,
  AIAgent? agent,
  void Function(String)? onThinking,
  void Function(String)? onContent,
  void Function(ToolCall)? onToolCall,
  void Function(String)? onComplete,
}) async {
  final targetAgent = agent ?? _currentAgent;
  if (targetAgent == null) {
    throw StateError('没有可用的 Agent,请先调用 createAgent()');
  }

  await for (final chunk in targetAgent.chatStreamRaw(message)) {
    // 1. 思考内容(已禁用,但保留接口)
    if (chunk.reasoningContent != null && onThinking != null) {
      onThinking(chunk.reasoningContent!);
    }
    // 2. 增量文本(流式输出给页面展示)
    if (chunk.content != null && onContent != null) {
      onContent(chunk.content!);
    }
    // 3. 工具调用(模型决定调用业务工具)
    if (chunk.toolCalls != null &&
        chunk.toolCalls!.isNotEmpty &&
        onToolCall != null) {
      for (final toolCall in chunk.toolCalls!) {
        onToolCall(toolCall);
      }
    }
    // 4. 完成时记录 token 消耗
    if (chunk.isDone && chunk.usage != null) {
      AppLogger.info(
        '[AgentService] 消耗: ${ZhipuPricing.formatCostLog(chunk.usage!)}',
      );
    }
  }

这段代码把流式的四个阶段统一收口了:

复制代码
用户消息 → [reasoningContent] → [content] → [toolCalls] → [isDone + usage]
             ↑ 思考(已禁用)    ↑ 文本输出    ↑ 工具调用     ↑ 完成统计

协调器只需要关注回调,不需要理解底层 chunk 结构:

复制代码
// ai_explore_coordinator.dart

await _agentService.chatWithToolsStream(
  message: text,
  onContent: (chunk) {
    // 只关心:文本增量来了,更新页面
    buffer.write(chunk);
    state = state.copyWith(
      status: AiSessionStatus.responding,
      streamingText: buffer.toString(),
    );
  },
  onToolCall: (toolCall) {
    // 只关心:模型在调用工具,更新状态为"搜索中"
    state = state.copyWith(status: AiSessionStatus.searching);
  },
  onComplete: (full) {
    // 只关心:回复完成
    state = state.copyWith(status: AiSessionStatus.idle, streamingText: full);
  },
);

这样做的好处是:协调器层不需要直接理解底层流对象细节,页面层更不需要直接碰这些底层 chunk 结构。

六、工具调用为什么也放在这里处理

chatWithToolsStream() 在流式对话结束后,还会自动检测并执行待执行的工具调用:

复制代码
// 如果有待执行的工具调用,自动执行并继续对话
if (targetAgent.pendingToolCalls != null &&
    targetAgent.pendingToolCalls!.isNotEmpty) {
  await for (final chunk in targetAgent.executeToolsAndContinue()) {
    if (chunk.content != null && onContent != null) {
      onContent(chunk.content!);
    }
    if (chunk.isDone && chunk.fullContent != null && onComplete != null) {
      onComplete(chunk.fullContent!);
    }
    if (chunk.isDone && chunk.usage != null) {
      AppLogger.info(
        '[AgentService] 工具回调消耗: ${ZhipuPricing.formatCostLog(chunk.usage!)}',
      );
    }
  }
}

这意味着一次完整的 AI 交互可能是这样的:

复制代码
用户: "推荐牛肉吃法"
  ↓
AgentService: 调用 chatWithToolsStream
  ↓
模型: 返回 tool_call: search_dishes({ingredients: ["牛肉"]})
  ↓
AgentService: 检测到 pendingToolCalls,自动执行 executeToolsAndContinue
  ↓
工具: 查询数据库,返回 5 道牛肉菜品
  ↓
模型: 基于工具结果生成推荐文案
  ↓
AgentService: 通过 onContent 回调把文案流式输出
  ↓
协调器: 收到文案 + matchedDishes,更新页面状态

关键在于:工具执行和继续对话的逻辑完全在 AgentService 内部完成,协调器不需要手动管理这个循环。如果这部分留在页面层或者每个协调器里各自拼,后面很容易变成每个页面有自己的一套工具续跑逻辑。

七、为什么 clearCurrentAgent() 很关键

很多移动端 AI 功能一开始都会忽略这一点:页面退出了,会话要不要清。

食界探味当前已经明确给了:

复制代码
void clearCurrentAgent() {
  _currentAgent?.clearHistory();  // 清除对话历史
  _currentAgent = null;           // 释放引用
}

协调器在两个场景下会调用它:

  1. 重置会话 --- 用户点击"新对话"按钮:

    // ai_explore_coordinator.dart

    void reset() {
    _agentService.clearCurrentAgent();
    _agentInitialized = false;
    if (mounted) {
    state = const AiSessionState();
    }
    }

  2. 重新初始化 --- 每个 coordinator 实例首次调用时,先清掉旧 agent 再创建新的:

    void _ensureAgent() {
    if (_agentInitialized) return;
    _agentInitialized = true;

    _agentService.clearCurrentAgent(); // 先清旧的
    _agentService.createAgent( // 再建新的
    systemPrompt: _systemPrompt,
    tools: [...],
    enableAutoToolExecution: true,
    maxMessages: 30,
    );
    }

这说明当前项目并没有把 AI 会话默认当成永久全局状态,而是允许某个页面会话结束后重置。这对鸿蒙端来说尤其重要,因为:

  • 鸿蒙设备内存管理更严格,长期悬挂的会话对象会占用不可回收的内存

  • 鸿蒙的页面生命周期和 Android 不完全一致,需要更主动地管理资源

  • 用户在鸿蒙设备上切换应用再回来时,会话状态应该被正确恢复或清理

八、为什么 dispose() 必须放在服务层

在当前代码里,dispose() 最后还会调用 _provider.dispose()

复制代码
void dispose() {
  _currentAgent = null;
  _provider.dispose();  // 释放底层模型 Provider
}

同时 Riverpod Provider 也在 onDispose 时触发了这件事:

复制代码
final agentServiceProvider = Provider<AgentService>((ref) {
  final provider = ref.watch(zhipuAIProvider);
  final service = AgentService(provider);
  ref.onDispose(() => service.dispose());  // Provider 销毁时自动释放
  return service;
});

这说明当前结构非常明确地把 AI 资源释放责任收到了服务层,而不是页面层自己猜什么时候释放。这一步很重要,因为 AI Provider、会话对象和流式任务都可能比普通页面状态更重。

在鸿蒙端,资源释放尤其关键:

  1. 鸿蒙的内存回收机制和 Android 不同 --- 不能依赖 GC 自动回收大对象

  2. 流式任务必须显式取消 --- 如果页面退出时还有 pending 的流式请求,必须中止

  3. Provider 的 dispose 时机和页面对齐 --- Riverpod 的 autoDispose 会自动处理,但前提是服务层的 dispose() 实现正确

九、协调器如何消费 AgentService

理解了 AgentService 的设计后,再看协调器是怎么用它的:

复制代码
class AiExploreCoordinator extends StateNotifier<AiSessionState> {
  final AgentService _agentService;
  final FoodRepository _foodRepository;

  // ... 初始化 ...

  void _ensureAgent() {
    if (_agentInitialized) return;
    _agentInitialized = true;

    _agentService.clearCurrentAgent();
    _agentService.createAgent(
      systemPrompt: _systemPrompt,
      tools: [
        SearchDishesTool(_foodRepository, onDishesFound: _onDishesFound),
        GetDishDetailTool(_foodRepository),
        GetRandomDishTool(_foodRepository, onDishesFound: _onDishesFound),
        GetDishesByIngredientTool(_foodRepository, onDishesFound: _onDishesFound),
      ],
      enableAutoToolExecution: true,
      maxMessages: 30,
    );
  }

协调器向 AgentService 注册了 4 个业务工具:

工具 功能 回调
SearchDishesTool 根据食材/口味/地域搜索菜品 onDishesFound → 更新 matchedDishes
GetDishDetailTool 获取某道菜的详细信息 无(直接返回文本)
GetRandomDishTool 随机推荐一道菜 onDishesFound → 更新 matchedDishes
GetDishesByIngredientTool 按食材查同食材的其他吃法 onDishesFound → 更新 matchedDishes

注意工具的 onDishesFound 回调------它直接把查询到的菜品数据推给协调器的状态,协调器再通过 Riverpod 通知页面渲染菜品卡片。这就是"AI 推荐 + 业务卡片"联动的关键链路。

十、AgentService 在整体架构中的位置

从整体架构看,AgentService 的位置非常清晰:

复制代码
┌─────────────────────────────────────────────────┐
│                   页面层                          │
│  AiAssistantScreen                               │
│       │                                          │
│       ▼                                          │
│               协调器层                            │
│  AiExploreCoordinator                            │
│       │                                          │
│       ▼                                          │
│               服务层                              │
│  AgentService ← agentServiceProvider             │
│       │                                          │
│       ▼                                          │
│               Provider 层                        │
│  zhipuAIProvider → OpenAIProvider                 │
│       │                                          │
│       ▼                                          │
│               工具层                              │
│  SearchDishesTool / GetDishDetailTool / ...       │
│                                                  │
├─────────────────────────────────────────────────┤
│               鸿蒙原生层(间接)                   │
│  SpeechRecognitionPlugin  TextToSpeechPlugin     │
│  ← 通过 Channel 被协调器调用,AgentService 不直接 │
│     感知,但会话生命周期影响语音体验                │
└─────────────────────────────────────────────────┘

核心要点:

  1. AgentService 不感知鸿蒙 --- 它是纯 Flutter/AI 服务层,不 import 任何鸿蒙相关代码

  2. 协调器是鸿蒙和 AI 的桥梁 --- 它同时调用 AgentService(AI 能力)和 Channel(鸿蒙能力)

  3. 页面层最轻 --- 只负责 UI 渲染和用户交互,所有 AI/语音逻辑都委托给下层

  4. Provider 层管理依赖 --- Riverpod 自动处理创建、缓存、销毁的生命周期

关键代码位置

文件 作用
app/lib/core/ai/agent_service.dart AI 会话服务层
app/lib/core/ai/agent_providers.dart 模型配置、Provider、成本追踪
app/lib/core/ai/ai_explore_coordinator.dart 协调器,消费 AgentService
app/lib/core/ai/models/ai_session_state.dart 会话状态模型
app/lib/core/ai/tools/ 4 个业务工具
app/lib/core/platform/speech_recognition_channel.dart 语音识别通道(协调器调用)
app/lib/core/platform/text_to_speech_channel.dart TTS 通道(协调器调用)

鸿蒙侧与 AgentService 的协作关系

虽然 AgentService 本身是纯 Flutter 层,但它在鸿蒙端的行为有特殊意义:

生命周期对齐

复制代码
鸿蒙页面创建
  → AiAssistantScreen initState
    → coordinator 创建(Riverpod autoDispose)
      → AgentService 创建(Provider)
        → zhipuAIProvider 创建
          → OpenAIProvider 初始化

鸿蒙页面销毁
  → AiAssistantScreen dispose
    → coordinator dispose(自动)
      → AgentService dispose
        → _currentAgent = null
        → _provider.dispose()

语音联动时序

复制代码
用户按住语音按钮
  → coordinator.startVoiceInput()
    → SpeechRecognitionChannel.startListening()  [鸿蒙原生]
      → 用户说话...
      → 鸿蒙识别完成,返回文本
    → coordinator.submitQuery(text)
      → AgentService.chatWithToolsStream()
        → 模型推理 + 工具调用
        → 流式输出
  → coordinator.speakText(response)
    → TextToSpeechChannel.speak()  [鸿蒙原生]
      → 鸿蒙 TTS 引擎播报
  → 用户退出页面
    → coordinator dispose
      → TextToSpeechChannel.stop()  [鸿蒙原生]
        → 鸿蒙 TTS 引擎停止

在这个流程中,AgentService 不直接调用任何鸿蒙 API,但它的 chatWithToolsStream 是整个链路的核心------所有 AI 推理和工具调用都在这里完成。协调器负责把鸿蒙的语音输入喂给 AgentService,再把 AgentService 的输出喂给鸿蒙的 TTS。

资源管理对比

场景 Android 行为 鸿蒙行为 AgentService 的应对
页面退出 Activity 销毁,GC 回收 Ability 组件销毁,内存管理更严格 dispose() 显式释放
流式中断 网络断开,chunk 停止 网络切换(WiFi→移动数据),chunk 可能中断 mounted 检查 + 异常捕获
后台切回 onResume,状态恢复 前后台切换,Ability 状态恢复 Riverpod autoDispose 自动管理
内存不足 系统杀进程 系统回收 Ability clearCurrentAgent() 主动释放

常见坑

  • 页面层直接 new agent,导致会话生命周期四处散落 → 一定要通过 AgentService 集中管理

  • 工具调用逻辑分散在多个页面或协调器里 → 统一注册到 AgentService,通过 enableAutoToolExecution 自动执行

  • 流式输出直接在页面层消费底层 chunk → 用 AgentService 的回调封装,页面只关心文本增量

  • 没有显式清理当前会话 ,导致状态长期悬挂 → 调用 clearCurrentAgent(),尤其是页面退出时

  • 鸿蒙端不主动释放 Provider ,导致内存泄漏 → 确保 ref.onDispose 正确注册

  • API Key 硬编码在客户端 → 走后端代理,客户端只持占位符

  • 记忆窗口设得太大,鸿蒙设备内存吃紧 → 移动端建议 30-50 条,按场景调整

  • 流式任务没有 mounted 检查 ,页面退出后继续更新状态 → 协调器每个回调开头加 if (!mounted) return

可复用模板

如果你要在自己的鸿蒙 + Flutter 项目里做类似的 AI 会话管理,可以参考这个结构:

AgentService 模板

复制代码
class AgentService {
  final OpenAIProvider _provider;
  AIAgent? _currentAgent;

  AgentService(this._provider);

  AIAgent? get currentAgent => _currentAgent;

  AIAgent createAgent({
    required String systemPrompt,
    List<Tool>? tools,
    String? model,
    int maxMessages = 50,
    bool enableAutoToolExecution = false,
  }) {
    final agent = AIAgent(
      provider: _provider,
      config: AIAgentConfig(
        systemPrompt: systemPrompt,
        model: model,
        enableAutoToolExecution: enableAutoToolExecution,
      ),
      memoryManager: ConversationMemory(maxMessages: maxMessages),
    );

    if (tools != null) {
      for (final tool in tools) {
        agent.addTool(tool);
      }
    }

    _currentAgent = agent;
    return agent;
  }

  Future<void> chatWithToolsStream({
    required String message,
    AIAgent? agent,
    void Function(String)? onContent,
    void Function(ToolCall)? onToolCall,
    void Function(String)? onComplete,
  }) async {
    final targetAgent = agent ?? _currentAgent;
    if (targetAgent == null) {
      throw StateError('没有可用的 Agent,请先调用 createAgent()');
    }

    await for (final chunk in targetAgent.chatStreamRaw(message)) {
      if (chunk.content != null && onContent != null) {
        onContent(chunk.content!);
      }
      if (chunk.toolCalls != null && chunk.toolCalls!.isNotEmpty) {
        for (final toolCall in chunk.toolCalls!) {
          onToolCall?.call(toolCall);
        }
      }
    }

    // 自动执行工具调用并继续对话
    if (targetAgent.pendingToolCalls != null &&
        targetAgent.pendingToolCalls!.isNotEmpty) {
      await for (final chunk in targetAgent.executeToolsAndContinue()) {
        if (chunk.content != null && onContent != null) {
          onContent(chunk.content!);
        }
        if (chunk.isDone && chunk.fullContent != null) {
          onComplete?.call(chunk.fullContent!);
        }
      }
    }
  }

  void clearCurrentAgent() {
    _currentAgent?.clearHistory();
    _currentAgent = null;
  }

  void dispose() {
    _currentAgent = null;
    _provider.dispose();
  }
}

Riverpod Provider 模板

复制代码
final agentServiceProvider = Provider<AgentService>((ref) {
  final provider = ref.watch(zhipuAIProvider);
  final service = AgentService(provider);
  ref.onDispose(() => service.dispose());
  return service;
});

协调器消费模板

复制代码
class AiCoordinator extends StateNotifier<AiSessionState> {
  final AgentService _agentService;
  bool _agentInitialized = false;

  void _ensureAgent() {
    if (_agentInitialized) return;
    _agentInitialized = true;

    _agentService.clearCurrentAgent();
    _agentService.createAgent(
      systemPrompt: '你的系统提示词...',
      tools: [/* 你的工具列表 */],
      enableAutoToolExecution: true,
      maxMessages: 30,
    );
  }

  Future<void> submitQuery(String text) async {
    _ensureAgent();

    final buffer = StringBuffer();
    await _agentService.chatWithToolsStream(
      message: text,
      onContent: (chunk) {
        buffer.write(chunk);
        state = state.copyWith(streamingText: buffer.toString());
      },
      onToolCall: (toolCall) {
        state = state.copyWith(status: AiSessionStatus.searching);
      },
      onComplete: (full) {
        state = state.copyWith(status: AiSessionStatus.idle);
      },
    );
  }

  void reset() {
    _agentService.clearCurrentAgent();
    _agentInitialized = false;
    state = const AiSessionState();
  }
}

本篇总结

AgentService 在食界探味里的价值,是把 AI 从"页面里发一次请求"提升成了"移动端里一个可管理的会话对象"。

这层一旦单独成立:

  • 页面层只关心 UI 渲染,不碰模型细节

  • 协调器层专注于产品流程编排,通过 AgentService 的回调驱动状态

  • 工具层统一注册到 AgentService,不分散在各处

  • 鸿蒙原生能力由协调器直接调用,AgentService 保持平台无关

  • 资源生命周期由 Riverpod Provider 统一管理,确保鸿蒙端不泄漏

在鸿蒙设备上,这套分层让 AI 助手既能享受 Flutter 的跨平台 UI 能力,又能无缝接入鸿蒙的语音识别和 TTS 能力,同时保持了清晰的职责边界和资源管理。

相关推荐
Xiaofeng36931 小时前
ChatGPT 5.5 多模态能力拆解,技术原理通俗讲解
人工智能·chatgpt
逻辑君1 小时前
认知神经科学研究报告【20260072】
人工智能·深度学习·数学建模
跨境猫小妹1 小时前
多国海关字段持续细化后跨境卖家如何搭建商品信息映射表
大数据·数据库·人工智能·跨境电商·跨境·营销策略
川石课堂软件测试1 小时前
UI自动化测试|下拉选择框&弹出框&滚动条操作实践
开发语言·python·jmeter·ui·docker·单元测试·harmonyos
再玩一会儿看代码1 小时前
2026 年 ChatGPT 套餐怎么选?Free、Go、Plus、Pro、Business、Enterprise 一次讲清楚
人工智能·gpt·chatgpt·golang·openai·codex
枫叶丹41 小时前
【HarmonyOS 6.0】MDM Kit 新增支持通过设备管理设置桌面壁纸能力详解
华为·harmonyos
网安情报局1 小时前
GPT-5.5+GPT-Image-2国内使用指南:AI聚合大模型平台实测体验
人工智能·gpt·ai
AI科技星1 小时前
第三卷:质数王朝志 第四章:RSA护国玄阵,质数锁天地,一数镇万法
android·人工智能·架构·概率论·学习方法
菜鸟分享录1 小时前
AI 学习路线 03:线性代数、概率统计、梯度下降到底有什么用?
人工智能·线性代数·ai