首页 / 博客 / 技术手记

技术手记

「每日故事」是怎么写出来的

拾缕 的「故事」功能要在你点一下之后,把「照片里有什么、在哪儿拍的、那天天气如何」揉成一小段中文文字。这篇手记拆解它的工作方式,以及每个环节上画的隐私边界。

第一步:收集三类事实

故事的原料是结构化事实,不是照片本身。生成任何一天的故事前,引擎先收集三样东西:

DayFactsCollector
  ├─ Vision 标签      (本机,无网络)
  ├─ CLGeocoder 地名  (按 ~1km 缓存)
  └─ Open-Meteo 天气  (仅取整坐标 + 日期,按天缓存)

第二步:三种引擎,一次契约

事实到位后,由选定的「故事引擎」把它们写成文字。三种引擎遵守同一份输出契约,所以可以随时热切换:

云端引擎的默认边界

云端引擎是唯一「数据出设备」的路径,所以它的默认值被设计得非常保守:只发送结构化文字事实(日期、天气、地名、内容标签),不发送任何照片。附带压缩照片是一个独立的开关,藏在设置里,默认关闭,并且开启前有明确的确认说明。

一句话总结隐私模型:识别在本机,天气只送坐标和日期,云端默认只送文字,照片永远 opt-in。

缓存与离线

再次打开已保存的故事会读取本机副本。重新生成可能重新收集事实,并在选择云端时发起新的请求。保存失败会保留本次生成的文字,重试保存不会重新生成;前一次有效保存也会保留供恢复。小组件使用共享快照,互动保留另行校验 StoreKit 权益。

为什么「默认智能」是设备端

把「开箱即用的最佳体验」绑在设备端模型上,意味着没有 API Key 的用户也能拿到自然、有文采的中文叙事,而隐私默认值就是最强档。模板引擎兜底保证功能永远可用,云端引擎留给愿意自己配置的进阶用户——三层按需降级,谁都不被排除。

工程细节会随版本继续演化,行为契约以仓库内 spec/001-day-story.md 为准。下一篇设计手记:小组件设计笔记:一天五个时刻