项目名称: 智能影记 - Memoria
团队名称: Mnemosyne
时间: 2026.05.20 - 2026.05.30
当前阶段: Phase 5 (核心体验深挖与性能调优)

一、 本周工作概述

随着 Memoria 核心的视听渲染管线在前期基本跑通,本周团队进入了对核心业务逻辑的深度打磨期。在实际测试中我们发现两个亟待优化的痛点:一是 AI 助手在多轮交互中容易遗漏工具调用指令,对时间概念缺乏感知;二是本地图库在展示大量高分辨率图片时,存在偶发的滑动掉帧现象。

为此,本周团队重点完成了以下两项开发任务:

  1. AI Agent 逻辑重构:优化了 LLM 的 Prompt 注入策略,引入了动态时间上下文感知,并改进了 Tool-use(工具调用)的触发机制。

  2. 数字相册与高性能图库上线:实现了基于 JSON 动态解析的数字相册排版引擎,重构了相册标签聚类逻辑,并自研了基于队列控制的图片并发解码调度器,显著优化了长列表滑动性能。

二、 AI Agent 优化:上下文强化与意图约束

在此前的版本中,AI 虽然能处理明确的搜索指令,但在面对如“推荐一些最近适合发朋友圈的照片”这类模糊语义指令时,检索准确率较低。

  1. 动态时间感知注入
    为了让大模型理解“最近”或“当前季节”的概念,我们将原本静态的系统提示词(_systemPrompt)重构为动态生成的 Getter 属性。在每次发起请求前,系统会自动计算当前的真实时间与季节,并将其作为前置约束传入:
// 截取自 chat_service.dart:动态生成时间约束
static String get _systemPrompt {
  final now = DateTime.now();
  String season = '冬天';
  if (now.month >= 3 && now.month <= 5) season = '春天';
  // ...省略其它季节判断...
  
  return '''
你叫 Memoria,是一个极具同理心、文艺且专业的“生活记忆导演”。
当前真实世界时间是:${now.year}${now.month}${now.day}日,季节是$season。
当用户提到“最近”、“今天”或“这个季节”时,请严格参考上述时间进行情感联想和照片检索。
  ''';
}
  1. 结构化 XML 检索与尾部指令强化
    我们规范了 Agent 触发本地搜索的数据格式,要求其必须输出包含 tags, year, location 的结构化 XML 标签。
    同时,为解决大模型在多轮对话中容易产生“注意力偏移”而忘记输出工具指令的问题,我们采用了一种尾部上下文强化注入的策略。在拼接用户的最后一条消息时,静默追加一段系统规则约束:
// 尾部指令强化:强制唤醒大模型的意图判断
final String reinforcedUserText = '''
$userText

[系统强制提醒:请立刻判断上述用户意图!如果是让你查找、推荐照片,你必须在回复末尾携带 <SEARCH>{"tags": [], "year": null, "location": null}</SEARCH> 结构!当前季节的标签也可以直接作为搜索条件!]
''';
messages.add({'role': 'user', 'content': reinforcedUserText});

这种在 User Prompt 尾部追加强约束的做法,有效避免了模型只闲聊不检索的现象,提升了混合检索的触发稳定性。

三、 数字相册引擎:样式令牌化与标签聚类

为了让照片回顾更具层次感,本周我们对本地图库的展示形态进行了全面升级。

  1. 标签层次聚类
    在 album_tag_browser_service.dart 中,我们引入了层级化的标签组织策略。系统会将端侧 AI 跑出的离散细粒度标签,向上聚类映射为粗粒度的主题(Coarse Tags,如“户外”、“美食”、“聚会”)。通过 buildCoarseClusters 方法,自动为每个主题计算包含的照片总数,并提取最具代表性的封面照片,从而在 UI 层构建结构化的相册流。

  2. 样式令牌化映射
    对于云端 LLM 生成的数字相册排版,我们构建了一套映射模型。在 album_book_models.dart 中,定义了如 AlbumFontPreset 和 colorTokens 等枚举变量。系统接收到云端返回的轻量级 JSON 后,即可将其解析为对应的 Flutter 渲染参数:

// 从 JSON 动态解析排版样式
factory AlbumElementStyle.fromJson(Map<String, dynamic> json) {
  return AlbumElementStyle(
    fontId: json['font_id']?.toString() ?? 'sans_clean',
    fontSize: _asDouble(json['font_size'], fallback: 20),
    colorToken: json['color_token']?.toString() ?? 'ink_soft',
    align: AlbumTextAlignValue.values.firstWhere(
      (value) => value.name == json['align']?.toString(),
      orElse: () => AlbumTextAlignValue.left,
    ),
    // 映射圆角与阴影等 UI 属性...
  );
}

四、 性能调优:图片并发解码调度器

在数字相册及标签瀑布流中,同屏常常需要展示数十张高清图片。如果依赖 Flutter 默认的 Image 组件逻辑,快速滑动时会并发发起海量的 I/O 读取和图片解码请求,瞬间拉高 CPU 占用,造成 UI 线程卡顿。

为解决这一工程痛点,我们在 album_page_deferred_image.dart 中实现了一个自定义的图片并发解码调度器(Deferred Scheduler)。

核心设计思路如下:

  • 全局并发锁:设置 _maxConcurrent = 4,严格限制同一时刻处于解码状态的图片数量。

  • 基于 Hash 的微秒级错峰:即便多张图片同时进入屏幕,我们也为其分配了离散的延迟时间,打散解码请求,防止主线程发生阻塞。

// 截取自 album_page_deferred_image.dart
void _startDeferredLoad() {
  // 利用图片路径的 hash 值生成随机的延迟时间,错峰发起 I/O 与解码请求
  final delayMs = 30 + (widget.path.hashCode.abs() % 11) * 28;
  
  _timer = Timer(Duration(milliseconds: delayMs), () {
    if (!mounted || _ticket.completed) return;
    
    // 延迟结束后,真正挂载 Widget,交由底层引擎解码
    setState(() {
      _ready = true; 
    });
  });
}

结合首帧渲染完毕的回调(_onFirstFrame)来释放全局并发锁,该调度器成功将原本拥挤在一个 16ms 渲染周期内的任务平滑摊派到了几百毫秒中。实测结果表明,在加载上千张本地照片的极端场景下,列表滑动的掉帧率得到了显著控制。

效果图:
图片1
图片2

五、 本周小结

本周,团队将研发精力下沉至具体的工程实现细节。通过 Prompt 优化解决了 LLM 的调度稳定性问题,通过构建状态机与并发队列解决了移动端海量图片渲染的性能瓶颈。项目整体的可用性与流畅度有了质的提升。

更多推荐