项目名称: 智能影记 - Memoria
团队名称: Mnemosyne
时间节点: 2026年5月25日 - 2026年5月30日

一、 本周工作概述

在项目的核心视听渲染管线稳定后,我本周将精力回调到了前台的交互体验上,主要攻坚对象是应用内的 AI 记忆助理(Agent)。

在上周的初步实现中,AI 已经能够根据明确的指令(如“搜索海边风景”)调用本地数据库。但在本周的深度测试(狗粮测试)中,我发现了几个极其影响体验的边界问题。本周的工作,就是通过一系列 Prompt 工程优化和底层逻辑修复,来驯服大模型的不确定性。

以下是我本周解决核心问题的完整过程复盘。

二、 问题一:AI 的“时间盲区”与意图泛化

【现象描述】
当我对 AI 助理说:“推荐一些适合今天的照片”或者“最近有什么好回忆”时,大模型通常会陷入迷茫,要么反问我“今天是哪一天”,要么只能返回一些空洞的安慰话术,完全没有触发搜索指令。

【原因分析】
大模型本身是静态的,它的世界观停留在预训练数据的截止日期。如果不给它注入当前系统的物理上下文,它根本无法理解“今天”、“这个季节”或“那年今日”这种极度依赖现实时间线的抽象指令。

【解决方案:动态时间上下文注入】
为了让 AI 拥有真实的“时间感知”,我将原本写死的 _systemPrompt 常量改造成了动态的 Getter 属性。在每次构建请求头时,系统会实时获取设备的当前年月日,并推算出季节,将其作为“世界观”前置约束强制喂给 AI。

// 🧠 核心修改:将常量改为动态 Getter,让 AI 拥有“时间感知”能力
static String get _systemPrompt {
  final now = DateTime.now();

  // 动态计算当前季节,喂给 AI 作为联想种子
  String season = '冬天';
  if (now.month >= 3 && now.month <= 5) season = '春天';
  else if (now.month >= 6 && now.month <= 8) season = '夏天';
  else if (now.month >= 9 && now.month <= 11) season = '秋天';

  return '''
你叫 Memoria,是一个极具同理心、有记忆温度的“私人生活导演”。
当前真实世界时间是:${now.year}${now.month}${now.day}日,季节是$season。
当用户提到“最近”、“今天”或“这个季节”时,请严格参考上述时间进行情感联想和照片检索。
  ''';
}

经过这个小改动,AI 瞬间变得“聪明”了。再对它说“适合今天发圈的照片”时,它会自动在输出的搜索指令中带上对应的月份和季节特征,真正实现了意图的泛化。

三、 问题二:大模型的多轮“偷懒”现象

【现象描述】
我们在应用中要求 AI 必须通过输出特定的 JSON(即 Tool-use 工具调用)来触发本地图库检索。但在和 AI 聊了五六个回合后,它经常会变得“偷懒”——只进行文字上的附和,彻底忘记了要在文案末尾输出 检索指令。

【原因分析】
这是典型的大模型“注意力衰减”现象。由于我们将最近的 5 条历史记录带入了上下文,导致顶部的 System Prompt 权重被稀释,AI 忘记了自己“寻找照片”的核心任务。

【解决方案:尾部指令强化注入】
要在长上下文中拉回 AI 的注意力,最有效的方式是在距离输出最近的地方下达指令。为此,我设计了一道“终极防线”:不再仅仅依赖 System Prompt,而是在用户每一次发送的真实文本末尾,通过代码静默拼接一段强烈的警告信息。

// 🌟 终极防线:尾部强制强化注入
final String reinforcedUserText = '''
$userText

[系统强制提醒:请立刻判断上述用户意图!如果是让你查找、推荐、随便看看照片,你必须在回复末尾携带 <SEARCH>{"tags": [], "year": null, "location": null}</SEARCH> 指令!如果你再次因为偷懒而只干聊不发指令,将受到严厉惩罚!当前季节的标签也可以直接作为搜索条件!]
''';

messages.add({'role': 'user', 'content': reinforcedUserText});

这种近乎“咆哮”式的强化提醒,完美压制了大模型的偷懒倾向。优化后,哪怕是在多轮复杂的闲聊中突然插入一句“找找刚才说的照片”,AI 也能 100% 稳定地抛出结构化的 标签。

四、 问题三:明明搜对了,但本地就是查不出图

【现象描述】
在解决了前两个问题后,我发现当 AI 发出按月份搜索的指令时(例如找“春天”或“5月份”的照片),底层的 Isar 数据库匹配算法依然返回空列表。

【原因分析与排错过程】
我追踪了底层的过滤逻辑,发现这是一个经典的“时间戳精度”引发的血案。
iOS/Android 原生相册有时返回的是 10 位的时间戳(秒级),而有些经过 Flutter 转换的时间戳则是 13 位(毫秒级)。Dart 的 DateTime.fromMillisecondsSinceEpoch 强制要求传入毫秒级时间戳。当它接收到 10 位的秒级时间戳时,会被错误地解析为 1970 年的某一天,导致后续的 .month 匹配全盘崩溃。

【解决方案:时空维度修复】
在将候选照片交给前端之前,我在内存精细过滤环节加入了强健的兼容处理代码:

// 5. 内存精细过滤
final filtered = candidates.where((p) {
  // 🌟 时空修复:把 10 位秒级时间戳强行放大回 13 位毫秒级
  int ts = p.timestamp;
  if (ts < 10000000000) ts *= 1000; 
  
  final photoMonth = DateTime.fromMillisecondsSinceEpoch(ts).month;

  if (targetMonths != null) {
    if (!targetMonths!.contains(photoMonth)) return false;
  }
  
  // ... 标签多维匹配逻辑 ...
  return true;
}).toList();

加上这朴实无华的一句判断,按月/按季节检索的功能瞬间顺畅,历史照片终于能被准确无误地抽取出来了。

图片1
图片2

五、 总结反思

本周的工作让我对大模型在工程上的落地有了更深的理解。API 接口很容易调,但要把它做成一个稳定、符合人类直觉的产品功能,必须通过精细的 Prompt 劫持、上下文控制 以及 底层数据的严格清洗 来驯服其不确定性。

看到现在的 Memoria 不仅能生成酷炫的视频,还能像老朋友一样在恰当的季节翻出恰当的照片,这大概就是作为工程师最纯粹的成就感所在吧。

更多推荐