创新项目实训博客(十一):大模型智能标题生成与多级降维兜底策略

文章目录

      • [一、 本周工作概述](#一、 本周工作概述)
      • [二、 数据装配修复:解决"上下文饥饿"](#二、 数据装配修复:解决“上下文饥饿”)
      • [三、 Prompt 工程:引入负向约束控制幻觉](#三、 Prompt 工程:引入负向约束控制幻觉)
      • [四、 文本清洗与多级降级兜底](#四、 文本清洗与多级降级兜底)
      • [五、 冷数据与缺失标签情况处理](#五、 冷数据与缺失标签情况处理)
      • [六、 双轨兜底与降级策略](#六、 双轨兜底与降级策略)
      • [七、 总结](#七、 总结)

项目名称: 智能影记 - Memoria

团队名称: Mnemosyne

时间节点: 2026年6月中旬

一、 本周工作概述

在视频生成的配置阶段,为用户提供一组契合画面氛围的"故事标题",是降低用户创作门槛的关键环节。本周,我主要负责重构和完善了"智能标题生成"模块。

在早期的测试中,我们遇到了一个典型的数据流转问题:当用户从对话搜索或跨相册随意勾选照片进入生成页(ConfigPage)时,由于缺乏完整的事件上下文,照片的标签(Tags)往往无法被正确传递。这导致大模型因为遭遇"上下文饥饿",只能返回干瘪的默认标题。

为了解决这一问题,我从前端数据装配大模型 Prompt 约束后端文本清洗与降级兜底,构建了一套完整的高可用双轨标题生成系统。

二、 数据装配修复:解决"上下文饥饿"

要让 LLM 写出好标题,首先要保证喂给它的 Prompt 拥有充足的养分。

针对之前发现的标签传递丢失问题,我在 story_config_page.dart_generateThemeFromPhotos 方法中重构了数据提取逻辑。核心思路是:优先提取 AI Caption 或 OCR 摘要;如果照片是刚拍摄入库、完全没有经过 AI 标签分析的"白板数据",则强制提取其底层的物理元数据(时间和高德逆地址解析位置)作为降维补偿。

dart 复制代码
// 截取自 story_config_page.dart:核心上下文提取修复[cite: 25]
List<String> topTags = widget.selectedPhotos.map((p) {
  // 1. 优先尝试获取 AI 摘要和标签[cite: 25]
  String desc = p.caption ?? p.ocrSummary ?? p.tags.join(' ');

  // 2. 降维补偿:如果照片完全没被 AI 分析过,提取物理信息[cite: 25]
  if (desc.trim().isEmpty) {
    String loc = p.location != null && p.location != '未知地点' 
        ? p.location! 
        : '某地';
    String date = '${p.dateTaken.year}年${p.dateTaken.month}月';
    desc = '$date 拍摄于 $loc';
  }
  return desc;
}).where((s) => s.trim().isNotEmpty).take(15).toList(); // 限制最多传递15个核心线索防止 Prompt 超长[cite: 25]

通过这一层补救,即使是未打标的离散照片,也能拼凑出类似 ["2026年5月 拍摄于 济南万象城", "2026年5月 拍摄于 某地"] 的有效上下文,确保大模型始终有据可依。

三、 Prompt 工程:引入负向约束控制幻觉

在获取了可靠的 topTags 后,接下来是在 llm_service.dart 中组装 Prompt。

大模型在生成简短文案时,极易产生"过度脑补"的幻觉。例如,仅仅因为照片中有一张带有"购房合同"的 OCR 截图,模型就可能脑补出"未婚妻"、"房主"等剧情词汇。为了控制这种不可预测性,我在 _buildPrompt 中引入了严格的负向指令:

dart 复制代码
String _buildPrompt(EventEntity event, List<String> topTags) {
  // ... 提取时间、地点、季节、joyScore ...
  return '''
你是一个专业的摄影相册文案策划师。请为以下照片事件生成 3 到 5 个简短、富有创意、博客风格的中文标题。

事件信息:
- 时间: $dateStr
- 地点: $location
- 季节: $season
- 主要标签: $tagsStr
- 平均欢乐值: $joyScore 

要求:
1. 标题简洁有力(8-15 个字)
// ... 省略基础格式要求 ...
8. 如果标签明显偏向截图、文档、课件、聊天、春联、屏幕文字,请把事件理解为"文字/资料/屏幕记录"类画面,禁止凭 OCR 或零散词语推断人物职业、身份、关系和剧情
9. 禁止生成"采购员、房主、未婚妻、套路"这类身份或剧情脑补词
''';
}

joyScore(情绪欢乐值)与地点、季节组合输入,同时切断模型对 OCR 文本的过度发散,使得生成的标题既具备情感共鸣,又不会偏离真实的生活记录。

四、 文本清洗与多级降级兜底

工程环境与理想的算法环境不同,大模型的返回结果往往充斥着各种无法控制的冗余字符(如换行符、Markdown 编号、多余的引号等)。更重要的是,必须考虑无网环境或 API 宕机的极端情况。

1. 正则清洗 (_parseResponse)

在接收到 LLM 返回的文本后,我利用正则表达式对每一行进行了机械式的去噪:

dart 复制代码
// 移除常见的编号(如 "1. ", "二、" 等)
cleaned = cleaned.replaceFirst(RegExp(r'^[\d]+\.?\s+'), '');
cleaned = cleaned.replaceFirst(RegExp(r'^[一二三四五六七八九十]+[、.\s]+'), '');

// 移除模型经常擅自加上的前后引号
if (cleaned.startsWith('"') || cleaned.startsWith("'")) {
  cleaned = cleaned.substring(1);
}

2. 本地规则降级 (_getFallbackTitles)

generateCreativeTitles 的外层,我包裹了完整的 try-catch。一旦检测到网络异常、API 返回为空,或是解析后的有效标题数量为 0,系统会立即阻断等待,无缝切换至本地组装逻辑:

dart 复制代码
List<String> _getFallbackTitles(EventEntity event) {
  final location = event.locationName ?? event.district ?? event.city ?? '未知地点';
  final dateRange = event.dateRangeText;
  
  // 提供一套绝对安全、格式对齐的静态模板
  return [
    '$location · $dateRange', 
    '$location 的记忆', 
    '时光印记 · $location'
  ];
}

在前端的 ConfigPage 中,如果上述流程依然抛出异常,还会有一层基于内存状态的终极兜底逻辑:结合地点统计推断出如 xx纪影${firstDate.year}年${firstDate.month}月精选

五、 冷数据与缺失标签情况处理

在开发智能标题生成功能时,我遇到了一个典型的数据缺失场景:

当用户从"故事队列"(跨越多个不同时间和地点的自定义照片集)进入配置页时,很多照片可能是刚刚拍摄的,还未来得及被后台 AI 打上视觉标签。

如果直接将这些毫无标签的照片送给大模型,模型会因为缺乏 Prompt 素材而产生幻觉或生成空泛的废话。

解决方案:物理信息补偿机制

_generateThemeFromPhotos 方法中,我引入了物理信息补偿机制。当检测到照片缺乏 caption 或 tags 时,系统会自动提取该照片的 EXIF 物理信息(时间和地点),将其转换为自然语言描述以"喂饱"大模型的 Prompt。

dart 复制代码
// 核心修复:即使照片没有 AI 标签,也要用时间和地点把 Prompt 喂饱!
List<String> topTags = widget.selectedPhotos.map((p) {
  String desc = p.caption ?? p.ocrSummary ?? p.tags.join(' ');

  // 如果照片完全没被 AI 分析过,那就提取它的物理信息
  if (desc.trim().isEmpty) {
    String loc = p.location != null && p.location != '未知地点' ? p.location! : '某地';
    String date = '${p.dateTaken.year}年${p.dateTaken.month}月';
    desc = '$date 拍摄于 $loc';
  }
  return desc;
}).where((s) => s.trim().isNotEmpty).take(15).toList();

通过这层数据组装,大模型即使在未获取视觉标签的情况下,也能精准提炼出诸如"济南万象城的初夏食光"这类具备时空特征的优质标题。

六、 双轨兜底与降级策略

在移动端 AI 落地时,我们必须考虑到弱网、断网或 API 接口限流等极端情况。因此,我为标题生成模块设计了严格的降级策略。

1. 云端 LLM 提炼(主轨)

当网络通畅时,系统调用 LLMService().generateCreativeTitles,传入事件的上下文和提取出的 topTags,生成一组(1个主标题 + 3个副标题候选)充满文艺气息的文案。

2. 本地启发式推断(备轨)

当 LLM 接口抛出异常或返回为空时,系统会立即 catch 异常,并在几十毫秒内无缝降级到本地的 _deriveSmartTheme 和 _deriveSmartSubtitle 算法。

在本地兜底算法中,我设计了基于地点词频统计的推断逻辑:

dart 复制代码
// 智能推断 1:核心主题 (Title) 兜底策略
String _deriveSmartTheme() {
  // 如果是跨相册拼凑的照片队列
  if (widget.preservePhotoOrder && widget.selectedPhotos.isNotEmpty) {
    final locationCounts = <String, int>{};
    for (var photo in widget.selectedPhotos) {
      if (photo.location != null && photo.location != '未知地点') {
        locationCounts[photo.location!] = (locationCounts[photo.location!] ?? 0) + 1;
      }
    }

    // 统计出现频率最高的地点作为标题核心
    if (locationCounts.isNotEmpty) {
      final topLocation = locationCounts.entries
          .reduce((a, b) => a.value > b.value ? a : b).key;
      return '$topLocation纪影';
    }
    // 如果连地点都没有,兜底时间
    final firstDate = widget.selectedPhotos.first.dateTaken;
    return '${firstDate.year}年${firstDate.month}月精选';
  }
  // ... 单一相册的常规逻辑 ...
}

结合时间跨度的推断(如跨度超过一个月则副标题为"跨越时光的相遇"),这套纯本地的规则引擎保证了用户在任何网络环境下,都能获得一个具有基本语义的默认标题。

七、 总结

本周的工作重点并不在于探索前沿的算法,而在于系统鲁棒性的建设。

从"前端物理字段兜底" -> "LLM 负向词约束" -> "正则正则清洗" -> "本地规则静态降级",这四道防线构成了一个高可用的标题生成管线。它确保了用户在任何网络状态下、选取任何离散照片时,系统都能稳定地在 2 秒内提供一组规整的视频标题,保障了核心创作链路的不中断。

相关推荐
zhuhai_xigedian1 小时前
物联网技术在源网荷储系统中的创新应用
大数据·运维·人工智能·区块链·能源
闵孚龙1 小时前
《PyTorch 深度修炼》优化器:参数到底是怎么被更新的
人工智能·pytorch·python
GEO索引未来1 小时前
AIIA可信GEO专题研讨会召开/AI全面加入618“大战”/谷歌重拳治理“AI投毒”
大数据·人工智能·gpt·chatgpt
朱大喜1 小时前
可视化图表选型:如何选对图,不让数据“撒谎”
人工智能
意图共鸣1 小时前
意图共鸣科技《历史的韵脚》:从第一次能力下放到第三次,AI浪潮背后的技术普及逻辑
人工智能·科技
大数据魔法师1 小时前
AI Agent(六)- Dify 自定义工具实战 - 基于百度天气 API 搭建天气查询 Agent(天气智查助手)
人工智能
lijgvnns1 小时前
使用AI工具作为量化盯盘助手的信息处理与研究辅助方法
大数据·人工智能
杨先生哦1 小时前
【2026热端攻防系列 3/12】反射型&存储型XSS全解:AI批量免杀、WAF绕过与企业级防御
前端·人工智能·笔记·web安全·xss
不良使1 小时前
鸿蒙PC迁移_LocalSend 迁移到鸿蒙 PC:一次 Flutter + Rust + 三方库适配的完整记录
flutter·rust·harmonyos