Repository navigation
Timeflow Product Report
- 产品定位
- 目标用户
- 核心价值
- 竞品对比
- 核心场景
- 产品功能总览
- 核心交互
- 日程基本概念
- 提醒基本概念
- 语音交互
- 在线与离线边界
- 本地与云端边界
- 技术实现概述
- 与早期设计的关键差异
- 已知限制与待办
- 测试与质量状况
- 本期明确不包含
- 附录:字段与枚举速查
TimeFlow 是一款以语音为核心操作方式、以本地可靠提醒为核心价值的个人日程应用。
用户用一句自然语言(例如"明天下午三点和张三开会,提前十五分钟提醒我")完成日程与提醒的创建、查询、修改、删除;提醒在设备本地判定与送达,断网仍可触发。
传统日历页面只承担查看和信息展示,不承担日程或提醒的创建、修改、删除与配置——这些操作只通过语音完成。
首期目标用户是有日程任务管理需求、同时容易遗漏时间事项和地点事项的 Android 用户(通勤、开车、做家务等双手被占用场景)。
他们的共同需求:
- 不希望经过多层 GUI 表单才能记录日程。
- 希望用一句自然语言完成日程和提醒设置。
- 不仅需要"到时间提醒",也需要"到达某处时提醒"。
- 希望重要提醒比普通系统通知更难被忽略。
- 网络不可用时,已设置的提醒仍然能够触发。
- 语音完成核心操作:日程与提醒通过语音创建、查询、修改、删除。
- 提醒本地可达:时间判定、地点判定与提醒下发均由设备本地执行,不依赖云端轮询。
- 提醒强度分级:低 / 中 / 高三级送达方式,重要提醒更难被忽略。
- 断网保持提醒:离线时不能使用云端语音能力,但已注册的本地提醒继续工作。
TimeFlow 的对比对象分为三类:手机自带语音助手(小爱同学 / Siri / 小艺等)、手机系统自带日历(Apple 日历及各安卓厂商自带日历)与传统日历/待办软件(Google Calendar、Outlook、Todoist、TickTick/滴答清单等)。其中手机语音助手是最贴近的竞品——它也能语音、也能一句话创建,但只能做"单轮命令",做不到多轮追问、消歧、确认。苹果日历仅作为产品能力与交互习惯的参考,TimeFlow 不接入苹果日历或其他任何第三方日历。
| 维度 | TimeFlow | 手机语音助手(小爱同学/Siri) | 手机系统日历 | 传统日历/待办软件 |
|---|---|---|---|---|
| 核心操作方式 | 语音(自然语言多轮) | 语音(单轮命令) | GUI 表单/点选 | GUI 表单/点选(少数支持自然语言速记) |
| 时间提醒 | ✅ 指定时刻 / 提前 | ✅ 一句话创建 | ✅ | ✅ |
| 地点提醒 | ✅ 到达地点 | ❌ 无 | 基本没有 | 少数(需手动设围栏) |
| 提醒强度分级 | ✅ 低 / 中 / 高 | 部分 单一通知/添加闹钟 | ❌ 单一通知 | 部分(简单铃声/震动) |
| 本地判定与断网送达 | ✅ 本地判定,断网仍触发 | 部分 | ✅ 系统闹钟本地 | 多数依赖云端/同步 |
| 提醒完成 / 延期 | ✅ 内建处置 | 有限 | 有限(稍后提醒) | 部分 |
| 语音多轮追问 / 消歧 / 确认 | ✅ | ❌ | ❌ | 极少 |
| 日历只读展示 | ✅ 只读月历 + 语音浮层 | 落到系统日历(无语音浮层) | 有 | 有 |
| 第三方日历接入 | ❌ 明确不做 | 接入系统日历 | 本身即系统日历 | ✅ 通常是卖点 |
| 离线创建 / 编辑 | ❌ 仅离线查看 | ❌ | ✅ | 部分 |
- 语音为核心操作方式:一句自然语言完成增删改查,并支持多轮追问、消歧与删除/重复范围确认;手机日历与传统软件仍以 GUI 表单为主。
- 地点提醒:支持"到达某地提醒",是绝大多数日历软件不具备的能力。
- 提醒强度分级:低/中/高三级送达(通知 → 震动+弹窗 → 声音+震动+弹窗),比单一日历通知更难被忽略。
- 本地可靠送达:时间判定、地点判定与提醒下发都在设备本地完成,断网仍触发,不依赖云端轮询。
- 提醒处置:内建"提醒完成/延期",并区分"已处理本轮"与"日程完成"。
- 不做 GUI 编辑:日程/提醒的增删改只走语音,日历页与详情页只读——与手机日历、传统软件的 GUI 编辑路径相反。
- 不做第三方日历接入:不聚合外部日历,定位为独立的语音日程助手,而非"日历聚合器"。
- 不做多维上下文动态调整(路程耗时/天气/导航等),对应 Proposal #164,明确不实现。
三类核心场景:单次时间型、重复时间型、地点型。
用户点击麦克风按钮说:"明天下午三点和张三开会,提前十五分钟提醒我。"
TimeFlow 创建一次性时间日程与提醒,并在本地注册提醒。到达触发时间后,设备按提醒强度通知。
用户说:"每周一早上九点开晨会。"
TimeFlow 创建重复时间日程(按重复规则展开),每周一早上九点自动提醒。
用户说:"提醒我到学林路地铁站拿快递。"
TimeFlow 创建地点提醒,设备进入地铁站位置范围后,在本地触发提醒。
以下功能为当前代码已实现并(大部分)已真机验证的能力:
- 注册与登录合一:用户名未注册则创建账号,已注册则校验密码。
- 用户名 3–64 字符;密码至少 8 字符(服务端上限 128)。
- 认证返回 JWT 访问令牌;密码只存哈希(Argon2)。
- 语音创建 / 查询 / 修改 / 删除日程。
- 多轮对话:信息缺失时主动追问,目标歧义时列出候选确认,删除与重复范围等高风险操作二次确认。
- 每次变更经鉴权 → 业务规则 → 幂等 → 云端事务,事务提交成功后返回结果。
- 每个日程最多配置 1 个提醒(提醒是日程上的字段,不是独立表)。
- 支持四类提醒触发类型(见第 9 节)与三级强度。
- 时间与地点提醒在设备本地判定与送达。
- 三级强度:低(系统通知)/ 中(震动 + 弹窗)/ 高(提示音 + 震动 + 弹窗)。
- 处置操作:提醒完成 与 提醒延期(默认延期 10 分钟)。
- 后台与锁屏均可触发(时间型由原生 AlarmManager 独占触发,进程被杀也能拉起;地点型依赖前台服务轮询,见 9.1 节)。
- 离线可查看本地缓存日程、使用已注册提醒、执行完成/延期。
- 网络恢复后与云端同步(日程快照作为本地 SQLite 权威状态)。
- 月历首页 + 日程详情(时间型与地点型)。
- 支持重复日程展开与"仅本次/本次及未来"例外展示。
- 无任何 GUI 增删改入口。
- 地点检索(腾讯地图),支持地点歧义追问。
- 自动日程分类(创建日程时由 LLM 归类为 8 类之一,用户无需语音指定)。
- 可观测性:Prometheus 指标 + OpenTelemetry/Tempo 链路追踪。
打开 App 后用同一表单登录或注册:新用户名创建账号,已存在用户名校验密码。登录成功后进入日历首页。
- 月历展示日程,点击查看详情(时间型详情页 / 地点型详情卡片 / 重复日程单次例外详情)。
- 首页提供两个语音入口:按住说话 与 免提连续对话。
- 按住说话(push-to-talk):按住麦克风按钮说话,松开发送,一次一轮。
- 免提连续对话(continuous):进入通话式界面持续监听,多轮对话,支持自然结束(语音指令挂断 / 空闲超时 / 手动静音 / 切后台自动静音)。
- 语音回复同时通过 TTS 播报 和 屏幕文字 呈现。
提醒触发后,用户可即时执行:
- 提醒完成:标记本轮已处理(不等于日程完成)。
- 提醒延期:默认延后 10 分钟,重新注册提醒。
麦克风、通知、位置等权限不可用时,展示原因与开启方式,并停止依赖该权限的功能。首次启动提供权限引导页。
一条日程包含以下字段:
| 字段 | 说明 |
|---|---|
| 标题 title | 字符串,默认「新建日程」 |
| 类型 schedule_type | time(时间型)/ location(地点型) |
| 是否重复 schedule_kind | once(单次)/ recurring(重复) |
| 分类 category | 8 类之一或空:work / study / exercise / entertainment / social / rest / personal / other |
| 是否全天 is_all_day | 布尔 |
| 开始 / 结束时间 | 时区感知时间戳 |
| 时区 timezone | IANA 时区标识 |
| 重复规则 recurrence_rule | RFC 5545 RRULE(仅重复日程) |
| 地点 | 名称 + 经纬度(NUMERIC(9,6)) |
| 提醒配置 | 见第 9 节,每日程最多 1 个 |
| 状态 status | active / deleted(软删除) |
| 版本 revision | 乐观并发版本号,用于同步 |
- 无标题 → 「新建日程」。
- 无开始时间 → 当前时间之后的下一个整点。
- 非全天无结束时间 → 开始时间 + 1 小时。
- 全天仅说一天 → 覆盖整个自然日。
- 未说重复 → 不重复。
- 未提地点 → 按时间型处理(不主动搜地点、不编坐标)。
- 未说提醒 → 默认 medium 强度:非全天用 before_start 提前 15 分钟;全天用 at_time 当天 10:00(带时区偏移)。
- 重复规则用 RRULE 表达,如 FREQ=WEEKLY;BYDAY=MO。
- 支持单次例外(occurrence override):cancel(取消某一次)/ replace(替换某一次)。
- 修改 / 删除重复日程时提供三种范围:
- this_occurrence 仅本次
- this_and_future 本次及未来
- entire_series 整个系列
提醒配置是日程上的字段,云端与客户端都没有独立 reminder 表;每个日程最多 1 个提醒(reminder_type 为空 = 未配置提醒)。
| 类型 | 含义 | 触发依据 |
|---|---|---|
| at_time | 指定时刻提醒 | 绝对触发时刻 |
| before_start | 开始前提醒 | 开始时间 + 提前偏移分钟数 |
| arrive_location | 到达指定地点提醒 | 进入目标地点范围 |
地点类提醒(arrive_location)在客户端通过前台服务 + 距离轮询(Haversine)判定进出围栏,默认围栏半径 400 米;不依赖云端、不依赖系统原生 Geofencing。
| 强度 | 送达方式 | 是否依赖 TTS |
|---|---|---|
| low | 系统通知 | 否 |
| medium | 震动 + 应用内弹窗 | 否 |
| high | 本地提示音 + 震动 + 弹窗(TTS 可用时由 TTS 充当提示音) | 是(可降级) |
TTS 不可用时,高强度提醒改用本地提示音,并继续震动、显示弹窗,不丢失提醒。
| 状态 | 含义 | 是否同步云端 |
|---|---|---|
| pending | 待处理 | 否(本地) |
| confirmed | 用户已处理本轮(不等于日程完成) | 是(唯一同步云端的状态) |
| snoozed | 已延期(默认 10 分钟) | 否(本地) |
- 按住说话(push-to-talk):默认模式,一次一轮。
- 免提连续对话(continuous):多轮连续,服务端智能断句,支持自然结束。
- 意图理解:把自然语言转成结构化日程操作(创建 / 查询 / 修改 / 删除)。
- 追问:缺字段时按 missing_field 追问(一次问一件,能算的不问)。
- 消歧:目标指代不明时,先查询再以 ambiguous_target 列出候选让用户选。
- 确认:删除、重复范围等高风险操作以 confirmation 二次确认。
- 地点解析:带精确地名时调用地点检索,精确匹配直接采用,多条候选对不上时才让用户选;「家/公司」等模糊指代先追问具体地址;「到 X 提醒我」按到达地点提醒处理(不追问时间);搜不到如实说明,不编造坐标。
服务端由 TIMEFLOW_VOICE_AGENT_MODE 切换两条语音后端,对客户端暴露同一套 WS 协议、同一套日程工具,客户端对模式无感知:
| 模式 | 编排 | 核心模型 | 音频链路 |
|---|---|---|---|
| 模式 1(默认,实时端到端) | RealtimeAgent | 阿里云 Qwen-Audio 实时(qwen-audio-3.0-realtime-plus) | 16kHz 音频进 → 24kHz PCM 语音出(单模型完成转写+理解+语音) |
| 模式 2(组合式/级联) | ComposedVoiceAgent | Qwen3-ASR → OpenAI 兼容 LLM → Qwen-Audio-TTS | 16kHz ASR 进 → LLM 文本 → 24kHz TTS 出 |
能控制的能力
| 维度 | 模式 1(实时端到端) | 模式 2(组合式) |
|---|---|---|
| 可独立调优的环节 | 无(转写/理解/语音融合在一个模型里) | ASR、LLM、TTS 三段各自独立可调 |
| LLM 可否更换 | 否(模型固定) | 是(任意 OpenAI 兼容模型,可换更便宜/更强) |
| 系统提示词 / 工具 schema | 固定(build_instructions) | 完全可控,可随时改 |
| 声线(voice) | 单一 vendor voice 参数 | TTS 段独立 voice 参数 |
| VAD / 断句 | vendor turn detection + VAD 阈值/静音 | ASR 段 VAD 阈值/静音独立控制 |
| 对话历史窗口 | max_history_turns(连续 10 / 按住说话 5) | 由 LLM 上下文自行管理 |
| 长回复截断 | 模型自行控制 | SpeechPipeline 强制截断(约 100 字 +「后面省略」) |
成本
| 维度 | 模式 1 | 模式 2 |
|---|---|---|
| 计费方式 | 单一实时模型(音频+文本 token) | 三段分别计费(ASR 音频 + LLM token + TTS 音频) |
| 上下文开销 | 每次响应都重发 system prompt + 工具 schema | LLM 上下文可裁剪(字段裁剪 input token 降约 73%) |
| 降本空间 | 有限(精简 prompt、会话上下文按体量重建) | 大(换 LLM、裁字段、截断回复) |
延时
- 模式 1:单模型端到端,无 ASR→LLM→TTS 三段串行累加,首字延迟更低(#166 论证端到端的延迟优势)。
- 模式 2:三段串行累加,延迟更高;真机实测(组合式管线)首播 P95 约 1.5 秒(查询)~ 17 秒(歧义修改)。缓解手段是每一环都流式 + barge-in 打断。
语音的控制
- 模式 1:语音由端到端模型直接生成,"屏幕文字"与"嘴里说的话"同源,业务层无法单独改写播报文案;声线整会话只能用一个 voice 参数;打断由 vendor turn detection 自动处理。
- 模式 2:TTS 是独立一段,播报文案 = LLM 输出文本,业务层可在 TTS 前截断/改写;"屏幕文字"(voice.dialogue.reply 流式)与"语音播报"(voice.tts.speech_text)两条通路可分开控制(流式负责显示,speech_text 非空时覆盖一次);打断走 ASR SpeechStarted 检测 + 取消当前 turn。
| 能力 | 在线 | 离线 |
|---|---|---|
| 语音管理日程 | ✅ | ❌ |
| 语音提问 / 确认 | ✅ | ❌ |
| TTS 播报 | ✅ | ❌ |
| 云端同步 | ✅ | 恢复后同步 |
| 查看本地缓存日程 | ✅ | ✅ |
| 已注册提醒触发 | ✅ | ✅ |
| 提醒完成 / 延期 | ✅ | ✅ |
离线时不支持 ASR、LLM、TTS,以及日程/提醒的创建、修改、删除。
TimeFlow 采用按数据域分权的双层权威模型:
| 云端保存 / 执行 | 本地负责 |
|---|---|
| 以账号隔离的日程数据(含提醒配置) | 日程缓存(SQLite) |
| 同步所需的暂存记录与同步状态 | 时间 / 地点判断 |
| 提醒的最终确认状态(仅 confirmed) | 提醒注册、触发、延期 |
| — | 通知、弹窗、震动、声音 |
| — | 当前提醒实例的运行状态(pending/snoozed 等) |
关键区分:提醒配置(云端同步字段)与本地提醒实例/运行状态(永不同步)是两个不同概念。同步只同步配置与同步元数据,不同步提醒运行状态。
本节仅概述,详细协议与分层见架构文档 Architecture-interface-design v4.0。
- 分层:business(领域)/ data(仓储与模型)/ gateway(HTTP/WebSocket 入站)/ intelligence(语音编排)/ infrastructure(第三方适配与运行时)。
- 数据模型(3 张表):accounts(账号)、schedules(云端日程,含提醒配置与分类)、schedule_occurrence_overrides(重复日程单次例外)。
-
HTTP 接口(极小):
- POST /api/v1/auth/access — 注册/登录合一,返回 JWT
- GET /api/v1/schedule/snapshot — 账号日程全量快照(含例外)
- PUT /api/v1/schedule/reminder-state — 提醒最终确认状态(仅 confirmed)
- GET /api/v1/health、GET /metrics
- WebSocket:/ws 承载认证后的语音会话(音频二进制帧 + JSON 控制消息)。
- 第三方:通义实时语音 / 通义 ASR / OpenAI 兼容 LLM / 通义 TTS / 腾讯地图检索。
- 功能域:认证、日程(日历只读)、语音助手、提醒送达、同步。
- 本地存储:SQLite(local_schedules 表,提醒字段在日程上,不建独立 reminder 表)。
- 原生模块:timeflow-alarm(AlarmManager 独占触发、响铃页、声音服务、开机恢复、厂商权限适配)。
- 权限:麦克风、通知、位置(后台定位用于地点围栏)。
本文档以代码为准,修正了早期产品设计草案中的以下差异(早期表述已过时,不要采用):
| 主题 | 早期草案 | 当前定稿 |
|---|---|---|
| 日程分类 | 工作 / 家庭 / 生活(3 类) | 8 类:work / study / exercise / entertainment / social / rest / personal / other |
| 默认提醒强度 | 低强度 | medium |
| 全天默认提醒时间 | 当天 10:00 | 当天 10:00(一致) |
| 重复日程删除范围 | 2 种 | 3 种(新增 entire_series) |
| 提醒触发类型 | 3 种(时间/到达/返回) | 3 种(时间拆为 at_time 与 before_start,地点统一为一类) |
| 每个日程提醒数量 | 早期曾写「最多两个」 | 最多 1 个(已定稿) |
| 云端日程表 | 曾讨论新建 schedules_cloud 并存 | 直接改造 schedules 表(不新建并存表) |
以下为 2026-08(MS4 收尾)逐条回到代码排查后的当前状态。
仍存在的问题(按影响程度排列):
-
实时会话闲置后首轮失败(自愈,不修复):通义 realtime session 在 180 秒无响应后被厂商关闭;服务端能检测到(
_next_event捕获异常 /send_audio异常上抛 →finally判定不可复用 →_discard清出缓存),无需重启后端。代价是 180 秒空隙后的第一次开口会失败一次(报错或无反应),下一次才新建会话成功——体感为「隔一会儿第一次没反应,再试一次即恢复」。决定不修复,不影响正常使用。 -
音频消费失败静默(不修复):后台音频消费任务(
_drain_to_sink)抛异常时仅记服务端日志、不回错误给客户端,客户端表现为「一直等待结果」直到超时。修复路径明确——给VoiceStreamHandlers注入main.py已建好的ConnectionManager(WebSocketResultSink同款),并复用已有的ERROR_INTERNAL(INTERNAL_ERROR),无需新增错误码。决定暂不修复,不影响正常使用。 - device_id 无下游消费:仅做自证式核对,无多设备/限流/推送定向用途。
- 语音质量待优化(真机测试定位):ASR 对近音词/专有名称识别偏差、复杂场景间歇超时、自然语言参数提取偶发截断、同义输入数据规范不一致、歧义修改响应偏长。
已解决 / 不再适用:
- 时区硬编码:session.hello 的 timezone 已接线(SessionContext → StreamContext → ToolBox),Asia/Shanghai 仅作兜底默认值。
- 定位监听:LocationMonitorPort 已有真实 ExpoLocationMonitor 实现,地点检索走腾讯地图真实地理编码。
- 地点提醒:只保留「到达地点」一类(「记住当前位置」已移除)。
- JS 兜底:已废弃,提醒送达统一走原生模块。
设计取舍(非缺陷):
- 地点提醒改用前台服务 + 距离轮询(默认 400 米),已移除系统原生 Geofencing(#380);进程被杀后地点提醒在下次启动前不可用,属明确接受的代价。
- 已执行 87 条测试用例,核心功能覆盖 46 条,LLM/语音性能专项 20 项。
- 固定语音测试 150 次,有效样本 114 个。
- 测试环境:Android 模拟器 + Android 16 真机(Xiaomi,SDK 36)。
- 已真机验证:真实麦克风语音输入、ASR、TTS、日程 CRUD、数据同步、后台/锁屏提醒、声音/震动提醒、提醒确认、延期后重新注册。
- 结论:语音日程 CRUD、数据同步、Android 提醒已形成完整业务闭环。
- GUI 创建 / 修改 / 删除 / 配置日程和提醒(日历页与详情页只读)。
- 第三方日历接入。
- 离线 ASR、离线 TTS。
- 多设备使用与多设备提醒协调。
- 基于多维上下文(路程耗时 / 天气 / 导航 / 关联日程 / 交互状态)动态调整提醒(对应 Proposal #164,Proposal-NoPlan)。
- 开放式插件架构。
- 面向不同人群的可定制产品形态。
| 字段 | 取值 |
|---|---|
| schedule_type | time 或 location |
| schedule_kind | once 或 recurring |
| category | work / study / exercise / entertainment / social / rest / personal / other(可空) |
| status | active 或 deleted |
| reminder_type | at_time / before_start / arrive_location(地点提醒,可空) |
| reminder_strength | low / medium / high(有提醒时必填) |
| reminder_disposition_state | confirmed(云端仅存此值) |
| recurrence_rule | RFC 5545 RRULE 正文 |
| 删除范围 | this_occurrence / this_and_future / entire_series |
| 例外动作 | cancel 或 replace |
| 状态 | 同步云端 |
|---|---|
| pending | 否 |
| confirmed | 是 |
| snoozed(默认 +10 分钟) | 否 |
missing_field(缺字段)· ambiguous_target(目标歧义)· recurrence_scope(重复范围)· confirmation(确认)
| 强度 | 弹窗 | 震动 | 声音 / TTS |
|---|---|---|---|
| low | ✅ | — | — |
| medium | ✅ | ✅ | — |
| high | ✅ | ✅ | ✅(TTS 可用时) |