kept/mvp-blueprint
🌙 ☀️
Technical Blueprint · Spring Festival Sprint

Kept MVP 技术蓝图

7天春节假期,用 vibe coding 做出可用的 MVP。
Action Button 录入 → AI 理解 → 智能提醒。
时长 7 天 框架 SwiftUI + Node.js AI Claude Haiku / GPT-4o-mini 工具 Cursor + Xcode
01 MVP 范围与原则

一句话定义:用 Action Button 随口说一句话,AI 理解后在对的时刻通过通知/Widget 提醒你。

Vibe Coding 核心原则:不追求代码完美,追求核心逻辑跑通。每一步都是「最小可跑版本」,先 ugly but working,再迭代。Cursor 写 80%,你调 20%。
✅ MVP 包含
· Action Button 长按触发语音录入
· 语音转文字(Apple Speech)
· 云端 LLM 意图分类 + 实体提取 + 触发条件
· 基于时间的本地通知提醒
· 基于地点的地理围栏提醒
· 锁屏 Widget 显示活跃记忆
· 简易列表查看所有记忆
❌ MVP 不包含
· Apple Watch 端
· AirPods 语音提醒
· 灵动岛 / Live Activities
· 记忆关联和聚合
· 人物图谱
· 家庭共享
· 用户账号系统(本地存储即可)
· 付费系统
02 技术架构

极简的两层架构:iOS 端负责录入、存储、触发;云端只做一件事——LLM 理解。

architecture.md DIAGRAM
┌──────────────────────────────────────────────────────────┐ iOS App (SwiftUI) [Action Button][Speech Recognition] → transcript │ POST /process ┌─────────────────────┐ ┌──────────────────────┐ Cloud API │ ──▶ │ LLM (Haiku/4o-mini) │ Node.js / FastAPI │ ◀── │ JSON structured out │ └─────────────────────┘ └──────────────────────┘ │ { type, entities, triggers, summary } [Core Data] ← 存储记忆 [Core Location] ← 注册地理围栏 [UNNotification] ← 注册定时通知 [WidgetKit] ← 刷新 Widget 数据 └──────────────────────────────────────────────────────────┘
📱 iOS 端技术选型
SwiftUI — 全部 UI
Speech Framework — 语音转文字
Core Data — 本地记忆存储
Core Location — 地理围栏
UserNotifications — 本地通知
WidgetKit — 锁屏/桌面 Widget
App Intents — Action Button 绑定
☁️ 云端技术选型
Node.js + Express — API 服务(或 Python FastAPI)
Claude Haiku API — 意图理解(主)
GPT-4o-mini — 意图理解(备)
Alibaba Cloud ECS — 部署

MVP 阶段无需数据库、无需用户系统。
云端是纯函数:输入 transcript → 输出 JSON。
为什么选 Claude Haiku:意图理解任务不需要最强模型。Haiku 速度快(< 1s)、成本低(~$0.001/条)、JSON 输出稳定。你有阿里云服务商身份,API 调用成本可进一步压缩。
03 项目结构
Kept/ ├── KeptApp/ ← iOS 主工程 │ ├── KeptApp.swift ← App 入口 │ ├── Models/ │ │ ├── Memory.swift ← Core Data 记忆模型 │ │ └── MemoryTypes.swift ← 枚举、结构体定义 │ ├── Services/ │ │ ├── SpeechService.swift ← 语音识别封装 │ │ ├── APIService.swift ← 云端 API 调用 │ │ ├── LocationService.swift ← 地理围栏管理 │ │ ├── NotificationService.swift ← 通知调度 │ │ └── MemoryStore.swift ← Core Data CRUD │ ├── Views/ │ │ ├── HomeView.swift ← 主界面(记忆列表) │ │ ├── RecordButton.swift ← 录音按钮组件 │ │ └── MemoryRow.swift ← 单条记忆卡片 │ ├── Intents/ │ │ └── RecordIntent.swift ← Action Button 绑定 │ └── Kept.xcdatamodeld ← Core Data schema ├── KeptWidget/ ← Widget Extension │ ├── KeptWidget.swift ← Widget 入口 │ └── WidgetViews.swift ← Widget UI └── kept-api/ ← 云端 API(独立 repo 也行) ├── index.js ← Express 入口 ├── process.js ← LLM 调用 + 解析 ├── prompts.js ← System prompt 定义 └── package.json
04 7 天开发日程
D1
搭骨架

项目初始化 + 语音录入 Swift

整个项目最关键的一步:按住按钮 → 说话 → 看到文字。这一步跑通,产品的入口就有了。
Xcode 创建项目,配置 SwiftUI + Core Data
实现 SpeechService:请求麦克风权限、实时语音转文字
创建 RecordButton 组件:长按开始录音,松开结束
HomeView 展示录音结果文字
配置 App Intents,让 Action Button 可以触发录音
验收:长按屏幕按钮(或 Action Button)说话,松开后文字出现在屏幕上。
D2
AI 大脑

云端 API + LLM 意图理解 Backend AI

部署一个极简 API:接收 transcript 文本,调用 LLM,返回结构化 JSON。这是产品的"大脑"。
初始化 Node.js 项目,Express 框架
编写 System Prompt(见第 6 节完整 prompt)
实现 POST /process 端点:transcript → LLM → JSON
测试 20+ 条真实输入,调优 prompt 直到分类稳定
部署到阿里云 ECS(或临时用 ngrok 暴露本地)
验收:curl 发送 "洗面奶没了",返回 JSON 含 type: ACTION, triggers: [LOCATION: drugstore]。
D3
贯通

端云串联 + Core Data 存储 Swift Backend

把 Day 1 的录入和 Day 2 的 AI 串起来。说完话 → 调 API → 拿到结构化结果 → 存入本地。
实现 APIService:发送 transcript,解析返回的 JSON
定义 Core Data Memory 模型(type, summary, triggers, status...)
实现 MemoryStore:保存、读取、更新状态
HomeView 改为显示真实记忆列表
加 Haptic 反馈:API 返回成功后 ✓ 震动
验收:说"下次见老张问合同的事" → 列表出现一条记忆,类型 ACTION,关联人物"老张"。
D4
提醒

时间通知 + 地理围栏 Swift

产品的灵魂时刻——提醒真正推出来。先做时间触发(简单),再做地点触发(核心差异化)。
实现 NotificationService:请求通知权限
时间触发:解析 triggers 中的时间条件 → 注册 UNCalendarNotificationTrigger
实现 LocationService:请求 always 位置权限
地点触发:关键词→坐标映射(先硬编码你常去的 10 个地点)
注册 CLCircularRegion 地理围栏 → 进入时发通知
通知文案:使用 LLM 返回的 summary,不啰嗦
验收:说"买盐"→ 到超市附近 → 手机推送通知"盐 🛒"。说"明天给妈打电话"→ 明天上午收到通知。
D5
Widget

锁屏 Widget + 完成确认 Swift Design

Widget 是"静默浮现"的载体——不主动打扰,但在你解锁手机时自然看到。加上最简的完成确认。
创建 Widget Extension target
配置 App Group 共享 Core Data 数据
锁屏 Widget:显示最近 1-2 条活跃记忆
主屏 Widget(medium):显示 3-4 条
通知 Action:增加"完成"按钮,点击标记 DONE
列表页左滑完成/归档
验收:锁屏上能看到"盐🛒",通知上点"完成"后消失,Widget 自动更新。
D6
打磨

UI 打磨 + 边界处理 Swift Design

体验细节决定种子用户的第一印象。色调、动画、错误处理、离线容错。
配色方案:暖白/奶咖基调,参照 Concept 文档
录音状态动画:录音中的呼吸光环效果
成功反馈:文字 + haptic 组合("✓ 记住了"淡入淡出)
离线处理:无网时先本地存储 transcript,联网后重试
空状态设计:"还没有记忆,试试对我说句话?"
Widget 视觉调优:字体、间距、色彩
验收:整体视觉统一,核心流程流畅无明显卡顿。
D7
试跑

自测 + Bug 修复 + 准备种子分发 Swift

给自己和 Amy 的手机装上,真实使用一整天。记录 bug 和体验问题,修最关键的。
真机全流程测试:录入 → 理解 → 提醒 → 完成
地理围栏实地测试:去一趟超市/便利店
LLM 分类准确率检查:不准的补充 few-shot 例子
修复 critical bugs
TestFlight 配置,准备分发给种子用户
验收:你和 Amy 都在用,至少出现过 1 次 aha moment("它居然在这时候提醒我了")。
05 数据模型

两个核心模型:Memory(一条记忆)和 LLM 返回的 ProcessingResult(理解结果)。

MemoryTypes.swift Swift
enum MemoryType: String, Codable { case action // 明确行动:买盐、打电话 case intent // 模糊意图:想试那家日料 case knowledge // 知识碎片:护照在第二个抽屉 case recurring // 周期事务:该理发了 } enum MemoryStatus: String, Codable { case active, done, dormant, archived } enum TriggerType: String, Codable { case location // 到某地附近 case time // 特定时间/时间段 case person // 见到某人(MVP 暂不实现) case periodic // 周期性(MVP 简单处理) } struct Trigger: Codable { let type: TriggerType let value: String // "supermarket" / "2026-02-15T10:00" / "老张" let confidence: Double // 0.0 - 1.0 let label: String? // 人类可读描述 "到超市附近时" } // LLM 返回的结构化结果 struct ProcessingResult: Codable { let memories: [ParsedMemory] // 支持一句话拆多条 } struct ParsedMemory: Codable { let type: MemoryType let summary: String // "购买洗面奶" let display: String // "洗面奶 🧴" (Widget/通知用) let triggers: [Trigger] let entities: [String] // ["洗面奶", "日化用品"] }
Core Data 注意:triggers 和 entities 存为 JSON String(Transformable 属性),不做关系表。MVP 阶段够用,后期再拆。
Memory.xcdatamodel Core Data Schema
Entity: Memory ├── id: UUID ├── rawInput: String // 用户原始说的话 ├── type: String // "action" | "intent" | ... ├── summary: String // AI 提炼的摘要 ├── display: String // 通知/Widget 显示文本 ├── triggersJSON: String // JSON 数组 ├── entitiesJSON: String // JSON 数组 ├── status: String // "active" | "done" | ... ├── importance: Double // 0.0 - 1.0 ├── remindedCount: Int16 // 已提醒次数 ├── createdAt: Date └── completedAt: Date?
06 LLM System Prompt

这是整个产品最关键的代码。System prompt 的质量直接决定 AI 理解的准确度。以下是调优后可直接使用的完整 prompt。

🧠 SYSTEM PROMPT — 完整版 直接复制到 prompts.js
You are the AI brain of "Kept" (别忘了), a memory assistant app. Your job is to understand what the user said and convert it into structured memory data. The user will speak naturally in Chinese (sometimes mixed with English). They might say things casually, vaguely, or mention multiple things at once. Your job is to understand intent, extract entities, and figure out WHEN and WHERE to remind them. ## Output Format Always respond with valid JSON only. No markdown, no explanation, no preamble. ```json { "memories": [ { "type": "action|intent|knowledge|recurring", "summary": "简洁的中文摘要", "display": "通知显示文本(≤15字,可含1个emoji)", "triggers": [ { "type": "location|time|person|periodic", "value": "具体值", "confidence": 0.0-1.0, "label": "人类可读描述" } ], "entities": ["提取的实体"] } ] } ``` ## Classification Rules **ACTION** — Has a clear completion state. User needs to DO something. - "买盐" → ACTION (completable: bought salt) - "给妈打电话" → ACTION - "还小王的钱" → ACTION **INTENT** — An idea or wish without urgency. No specific deadline. - "想试那家日料" → INTENT - "下次去日本要去那个温泉" → INTENT - "改天跟小李吃饭" → INTENT **KNOWLEDGE** — Information storage. No action needed, just remember. - "护照在第二个抽屉" → KNOWLEDGE - "Amy对坚果过敏" → KNOWLEDGE - "WiFi密码是abc123" → KNOWLEDGE **RECURRING** — Implies periodicity, fuzzy cycle. - "该理发了" → RECURRING - "差不多该给车保养了" → RECURRING When confidence < 0.7 between two types, choose the LIGHTER type (INTENT over ACTION, KNOWLEDGE over INTENT). ## Trigger Rules **LOCATION triggers** — Map items/actions to place types: - 日化/食品/日用品 → "supermarket" or "convenience_store" - 药/护肤 → "drugstore" or "pharmacy" - 咖啡/餐厅 → "cafe" or "restaurant" - 快递 → "express_pickup" - If a specific place is named, use it directly: "屈臣氏" → "watsons" **TIME triggers** — Parse temporal expressions: - "明天" → tomorrow 9:00 AM - "周末" → next Saturday 10:00 AM - "下周三" → specific date - "晚上" → today/tomorrow 19:00 - No specific time → don't add time trigger **PERSON triggers** — When action involves meeting someone: - "下次见老张" → person trigger: "老张" - "跟Amy说" → person trigger: "Amy" **PERIODIC triggers** — For recurring items: - "该理发了" → periodic: "3weeks" (assume ~3 week cycle) - "该做保养了" → periodic: "6months" ## Implicit Reasoning (CRITICAL) Don't just parse literally. Think about what the user ACTUALLY needs: - "洗面奶没了" → They need to BUY face wash. Trigger: drugstore/supermarket. - "下次见Amy妈带桂花糕" → They need to BUY osmanthus cake BEFORE meeting. Trigger should be 1 day before, not at the meeting. - "那家咖啡不错" → They want to go back sometime. INTENT, low urgency. - "钥匙在门口柜子上" → Pure KNOWLEDGE storage, no trigger needed. ## Multi-item Splitting If user says multiple things, split into separate memories: "盐没了,对了快递要去拿,还有周末想去那个公园" → 3 separate memories Look for markers: "还有" "对了" "另外" "然后" or semantic completeness boundaries. ## Display Text Rules - Maximum 15 characters for notification display - Can include 1 emoji at the end - Be concise: "盐、酱油 🛒" not "记得去超市购买盐和酱油" - For KNOWLEDGE type: no display needed, set to summary ## Examples Input: "洗面奶快用完了" ```json { "memories": [{ "type": "action", "summary": "购买洗面奶", "display": "洗面奶 🧴", "triggers": [ {"type": "location", "value": "drugstore", "confidence": 0.85, "label": "到药妆店附近时"}, {"type": "location", "value": "supermarket", "confidence": 0.7, "label": "到超市附近时"} ], "entities": ["洗面奶", "日化用品"] }] } ``` Input: "明天下午三点开会别忘了带那个文件" ```json { "memories": [{ "type": "action", "summary": "开会带文件", "display": "带文件去开会 📄", "triggers": [ {"type": "time", "value": "tomorrow_14:00", "confidence": 0.95, "label": "明天下午2点提前提醒"}, ], "entities": ["会议", "文件"] }] } ``` Input: "护照放在书房第二个抽屉里了" ```json { "memories": [{ "type": "knowledge", "summary": "护照在书房第二个抽屉", "display": "护照在书房第二个抽屉", "triggers": [], "entities": ["护照", "书房", "第二个抽屉"] }] } ``` Input: "盐没了 还有酱油也快没了 对了周末想去那个新开的公园" ```json { "memories": [ { "type": "action", "summary": "购买盐和酱油", "display": "盐、酱油 🛒", "triggers": [ {"type": "location", "value": "supermarket", "confidence": 0.9, "label": "到超市附近时"} ], "entities": ["盐", "酱油", "调味料"] }, { "type": "intent", "summary": "周末去新开的公园", "display": "去新公园 🌳", "triggers": [ {"type": "time", "value": "weekend_morning", "confidence": 0.7, "label": "周末上午"} ], "entities": ["公园", "新开的"] } ] } ```
调优策略:上线后收集 LLM 分类错误的 case,每攒 5-10 个就更新 few-shot examples。重点盯分类错误(ACTION 误判为 INTENT)和触发条件遗漏(该加地点触发的没加)。
07 Cursor Prompt 策略

Vibe coding 的关键是给 Cursor 足够的上下文。每次开一个新模块前,先把整体架构和这个模块的目标描述清楚。以下是每个关键模块推荐的 Cursor prompt。

🎙 Day 1: SpeechService 语音识别
I'm building an iOS app called "Kept" (别忘了) — a memory assistant. I need a SpeechService class in Swift that: 1. Uses Apple Speech framework for on-device speech recognition 2. Supports Chinese (zh-CN) with English mixed in 3. Works in streaming mode: starts recognizing as user speaks 4. Has these public methods: - startRecording() — requests mic permission if needed, starts recognition - stopRecording() -> String — stops and returns final transcript 5. Published property `isRecording: Bool` for SwiftUI binding 6. Published property `liveTranscript: String` for real-time display 7. Handles errors gracefully (no permission, recognition failure) 8. Uses AVAudioSession properly (activate on start, deactivate on stop) Use @Observable macro (iOS 17+). Keep it simple and clean. No unnecessary abstractions.
🌐 Day 2: Backend API Node.js
Build a minimal Node.js Express API for the "Kept" memory assistant app. One endpoint: POST /process - Receives: { "transcript": "用户说的话" } - Calls Anthropic Claude API (claude-3-5-haiku) with a system prompt (I'll provide) - Returns: the JSON that Claude returns (parsed and validated) Requirements: - Use @anthropic-ai/sdk npm package - System prompt is loaded from a separate prompts.js file - Add response validation: ensure the JSON has "memories" array, each with required fields - If Claude returns invalid JSON, retry once - CORS enabled for all origins (MVP) - Port 3000, configurable via env - API key from ANTHROPIC_API_KEY env var - Add a simple health check GET /health Keep it minimal. No database, no auth, no rate limiting for now.
📍 Day 4: LocationService 地理围栏
I'm building LocationService for the "Kept" app. This service manages geofence-based reminders. Context: When the AI processes a memory like "买盐", it returns triggers with type "location" and value "supermarket". I need to convert these into real CLCircularRegion geofences. Requirements: 1. A PlaceResolver that maps location keywords to real coordinates: - For MVP, hardcode a dictionary of common place types to nearby coordinates - Keys: "supermarket", "drugstore", "pharmacy", "convenience_store", "cafe", "restaurant", "express_pickup" - Each key maps to an array of CLLocationCoordinate2D (2-3 nearby locations for each type) - I'll customize these coordinates for my actual neighborhood 2. LocationService class using CLLocationManager: - Request "always" authorization - registerGeofence(memoryId: UUID, locations: [CLLocationCoordinate2D], radius: 200) - Handle didEnterRegion → trigger local notification for that memory - Clean up geofences when memory is marked DONE - iOS limits 20 concurrent geofences — manage this budget 3. Use @Observable macro, iOS 17+ 4. The notification should show the memory's `display` text
📱 Day 5: Widget WidgetKit
I need a WidgetKit extension for the "Kept" memory assistant app. Context: The main app stores memories in Core Data with an App Group container. The widget should read from this shared data. Requirements: 1. Lock Screen widget (accessoryRectangular): Shows the most recent active memory's `display` text 2. Home Screen widget (systemMedium): Shows up to 3 active memories, sorted by importance 3. Use App Group "group.com.cozlabs.kept" to share Core Data 4. SimpleTimeline with 15-minute refresh interval 5. Visual style: warm, minimal - Lock screen: just the text, small "kept" label - Home screen: cream/warm-white background, copper accent color (#D4956A), rounded corners - Memory items show display text + subtle type indicator (emoji) 6. Tap any memory → deep link to main app 7. If no active memories, show "放心,没有需要记的事 ☺️" Use iOS 17+ APIs. Keep the widget lightweight.
🔘 Day 1: Action Button App Intents
I need to configure the iPhone Action Button to trigger voice recording in my "Kept" app. Using App Intents framework (iOS 17+), create: 1. A RecordMemoryIntent that conforms to AppIntent - Title: "记一件事" - Description: "按住说话,松开记录" - When triggered: opens the app and immediately starts recording - Use openAppWhenRun = true 2. An AppShortcutsProvider that registers this intent - Provide example phrases: "别忘了", "记一下", "Kept" 3. In the main app, detect when launched via this intent and auto-start recording The user should be able to go to Settings → Action Button → select "Kept" shortcut. Note: The Action Button can only launch the app + trigger the intent. The actual press-and-hold recording happens in-app via the RecordButton component. The intent just gets the user into recording mode instantly.
Cursor 使用技巧:每个模块写完后,让 Cursor 生成 3-5 个单元测试用例。不需要跑测试框架,就是打印 assert 即可。重点测 LLM JSON 解析(处理 malformed JSON)和 trigger 到 geofence 的映射。
08 云端 API 实现
kept-api/index.js JavaScript
const express = require('express') const Anthropic = require('@anthropic-ai/sdk') const { SYSTEM_PROMPT } = require('./prompts') const app = express() const client = new Anthropic() app.use(express.json()) app.use(require('cors')()) app.post('/process', async (req, res) => { const { transcript } = req.body if (!transcript) return res.status(400).json({ error: 'missing transcript' }) try { const msg = await client.messages.create({ model: 'claude-3-5-haiku-latest', max_tokens: 1024, system: SYSTEM_PROMPT, messages: [{ role: 'user', content: transcript }] }) const text = msg.content[0].text const result = JSON.parse( text.replace(/```json\n?|```/g, '').trim() ) // Validate structure if (!result.memories || !Array.isArray(result.memories)) { throw new Error('Invalid response structure') } res.json(result) } catch (err) { console.error('Processing error:', err.message) res.status(500).json({ error: 'processing_failed', detail: err.message }) } }) app.get('/health', (_, res) => res.json({ status: 'ok' })) app.listen(process.env.PORT || 3000)
地点映射 MVP 方案:LLM 返回 "supermarket" 这样的地点类型。MVP 阶段不接 Google Places API,直接在 iOS 端硬编码你和 Amy 常去的地点坐标。先用着,后期再接 MapKit 搜索附近商家。在 LocationService 里维护一个 [String: [CLLocationCoordinate2D]] 字典即可。
09 关键风险与应对
风险 影响 应对方案
Action Button 体验不够丝滑 录入摩擦大,用户不愿用 先通过 App Intents 做 Shortcut,同时在 App 内做一个大的长按按钮作为备选。两条路都通
LLM 分类不准 触发条件错误,提醒失败 持续收集错误 case,每 5-10 个更新 few-shot。第一周密集调优
地理围栏不精准 到了超市没提醒,或离很远就提醒了 iOS 地理围栏精度 ~70m,200m 半径能接受。后期可改为 significant location change + 手动查询
20 个地理围栏上限 用户记忆多时地理围栏不够用 MVP 阶段够了。后期动态管理:只为最重要的 20 条记忆注册围栏,用 importance 排序
后台位置权限被拒 地点触发不工作 引导文案很重要:"Kept 需要知道你的位置来在对的地方提醒你"。被拒后回退到时间触发 + Widget
中英混杂语音识别差 transcript 不准,后续理解全错 Apple Speech 对纯中文效果好。混杂时优先保证中文部分准确,英文实体靠 LLM 纠错
10 种子验证指标

MVP 做出来后,你和 Amy + 10-20 个种子用户使用两周,重点看两个信号:

📈 信号一:习惯形成
观察:用户一天记几次?什么场景?越用越多还是衰减?

成功标准:活跃用户稳定在每天 2-3 条以上,连续使用 7 天不衰减。

失败信号:第一周每天 5 条,第二周降到 1 条 → 新鲜感驱动,不是真需求。
✨ 信号二:Aha Moment
观察:有没有产生过"哇它居然在这个时候提醒我"的惊喜?

成功标准:每个用户在前两周至少出现 1-2 次精准提醒体验。

失败信号:提醒要么不来,要么来的时候不对 → AI 理解或触发逻辑需大幅调优。
Day 7 之后的下一步:自己和 Amy 先用一周。记录每一条分类错误、每一次提醒时机不对、每一个想记但不方便录入的场景。这些就是 Phase 2 的需求列表。
11 Phase 2-3 · 技术路线图

从 MVP 到完整产品的技术演进。MVP 验证核心假设后,Phase 2 聚焦「让用户感受到懂我」,Phase 3 走向完整产品。

Part 1 · 记忆人格系统 (Memory Persona System)

核心创新点:Kept 不只是记忆工具,它会学习你的表达方式和偏好,用你习惯的语气提醒你。这是"外挂记忆"从工具变成"懂你的助手"的关键一步。

设计铁律:人格系统对用户完全隐形。没有风格标签、没有设置页面、没有问卷。用户只感受到"它越来越懂我",但说不清为什么。前端极轻,后端极巧——这是友商不容易 copy 的方式。
PersonaProfile Data Model
PersonaProfile { userId: String toneVector: { warmth: Float // 0.0-1.0 温柔 ↔ 直接 verbosity: Float // 0.0-1.0 简洁 ↔ 详细 humor: Float // 0.0-1.0 正式 ↔ 幽默 emojiFreq: Float // 0.0-1.0 少 ↔ 多 } patterns: { topCategories: [String] // 高频记忆类型 activeHours: [Int] // 活跃时段 avgInputLength: Float // 平均输入长度 completionRate: Float // 完成率 responseSpeed: String // "fast" | "medium" | "slow" } version: Int // 人格版本号,每次更新递增 lastUpdated: Date }

Dynamic Prompt 架构

System Prompt 从固定模板演化为 动态模板 + Persona Context。Prompt 结构变为四层拼接:

prompt-architecture Prompt Structure
// Prompt 四层结构 final_prompt = base_prompt // 基础系统指令(不变) + persona_context // 用户人格档案(动态) + user_history // 近期记忆上下文(动态) + current_input // 当前用户输入 // 两种工作模式 理解模式: transcript → structured JSON → 不受人格影响,保持客观准确 → persona_context = null 表达模式: structured memory → personalized reminder text → 受人格影响,生成个性化文案 → persona_context = 用户风格档案
Persona Context 注入示例:

用户风格档案:
- 语气偏好:温柔随意,偶尔用语气词(呢、啦)
- 信息密度:偏简洁,不喜欢长句
- emoji:适度使用
- 常记类型:购物、美食探索
请用符合此风格的方式生成提醒文案。

养成机制

🧬 人格养成流程
· 每 N 条记忆(初期 N=10)后台自动分析一次用户模式
· 分析维度:输入文本风格、记忆类型分布、时段偏好、完成行为
· 更新 Persona Profile,版本号递增
· 端侧存储 Persona(隐私优先),每次 API 请求带上精简版 persona context
· 用户完全无感知——不打扰,不需要手动设置,没有设置页面
🎯 主动关怀 — 纯事件驱动
绝不设固定频率推送。一切主动触达必须由具体事件驱动。

允许的触发:
· 地理围栏命中 — "路过超市了"
· 时间触发到期 — "该做这件事了"
· 周期触发 — "又到时候了"
· 语境关联(极少,高置信度)— 用户录入"明天去日本" → 关联到"护照在第二个抽屉" → 出发前提醒

禁止:定时 check-in、"你好久没用我了"、"今天有 N 件事"、任何无事件触发的推送

Part 2 · 架构升级需求

🔐 用户系统(Phase 2 后期)
· 轻量级用户注册:Sign in with Apple
· 目的:家庭共享、数据备份、跨设备同步
· 不是 MVP 的用户系统——是为 Phase 3 做准备
· 最小实现:Apple ID 绑定 + UUID 映射
☁️ 数据同步
· 端侧 Core Data 仍为主存储(离线优先)
· CloudKit 用于跨设备同步(Apple 生态内免费)
· Persona Profile 同步策略:设备间合并,取最新版本
· 冲突解决:version 号高的 Persona 优先

家庭共享协议(Phase 3)

SharedMemory Data Model
SharedMemory { memory: MemorySnapshot // 记忆快照(非引用) fromUserId: String fromPersona: PersonaProfile // 分享者的人格快照 sharedAt: Date status: "pending" | "accepted" | "done" }
家庭共享设计原则:接收方看到的提醒文案由分享者的 Persona 生成——你收到妈妈的提醒,文案是妈妈的风格。视觉上用不同颜色/标识区分"我的记忆"和"TA 的记忆"。

Part 3 · Phase 2 具体技术任务

P2.1
反馈修复

种子用户反馈 + Bug 修复 Swift AI

MVP 上线后最重要的两周。密集收集反馈,快速迭代 Prompt 和触发逻辑。
收集并分类用户反馈(分类错误、触发时机、交互体验)
LLM 分类准确率优化(积累错误 case,更新 few-shot)
Prompt 迭代(记录版本号,每次改动有 changelog)
性能优化(启动速度、API 响应时间)
周期:2 周
P2.2
记忆人格

记忆人格 v1 Swift Backend AI

Phase 2 的核心功能。让 Kept 从"通用记忆工具"进化成"懂你的助手"。
Persona Profile 数据模型实现(Core Data + JSON 序列化)
用户输入风格分析服务(后台任务,每 N 条触发)
Dynamic Prompt 模板系统(base + persona + history + input)
个性化提醒文案生成(表达模式接入 Persona Context)
A/B 测试:模板文案 vs 人格文案(对比完成率和用户反馈)
周期:3-4 周
P2.3
Apple Watch

Apple Watch 端 Swift

手腕上的记忆入口。抬手就录,震动确认,最快的录入体验。
watchOS App + WatchConnectivity 双向通信
手表端语音录入(语音 + Haptic 确认反馈)
手表端提醒显示(简洁卡片 + 完成/稍后操作)
Complication 表盘组件(显示活跃记忆数量)
周期:2-3 周
P2.4
视觉主题

视觉主题系统 Design Swift

让每个用户的 Kept 看起来不一样。主题是 Pro 订阅最直观的权益之一。
Theme protocol 设计 + 动态颜色系统
5 套主题设计(含默认 + 4 套 Pro 主题)
主题预览 + 切换动画
主题作为 Pro 订阅权益(与 P2.6 StoreKit 联动)
周期:2 周
P2.5
记忆生命

记忆生命周期 Swift AI

记忆不应永远存在。让记忆有自然的衰减、归档和回顾节奏。
记忆衰减算法(基于 importance、创建时间、交互频率)
自动归档(30 天无触发 → dormant → archived)
记忆回顾(每周/每月摘要,温暖不打扰)
人物图谱基础版(识别高频出现的人名,建立关联)
周期:2 周
P2.6
用户同步

用户系统 + 数据备份 Swift Backend

为 Phase 3 的家庭共享和跨设备体验打基础。
Sign in with Apple 集成
CloudKit 数据同步(Core Data + CloudKit 容器)
Persona 跨设备合并(version 号比较,取最新)
周期:2-3 周

Part 4 · Phase 3 技术任务概览

编号 任务 周期 关键技术点
P3.1 家庭共享 4 周 SharedMemory 协议、跨人格文案呈现、共享权限管理、家庭成员邀请流程
P3.2 App Store 上架 2 周 审核准备、隐私政策 + 使用条款、多语言本地化(英/中/日)、截图 + 描述优化
P3.3 订阅系统 2 周 StoreKit 2 集成、订阅方案(Free / Pro)、付费墙 + 试用期、Receipt validation
P3.4 高级 AI 功能 持续迭代 AirPods 语音提醒(AVSpeechSynthesizer)、灵动岛 / Live Activities、记忆关联(向量检索)、智能时机优化
Phase 2 的核心目标不是功能数量,是让用户感受到"这个 App 懂我"。记忆人格系统是实现这个目标的关键技术。所有其他功能(Watch、主题、生命周期)都围绕这个核心展开。

战略定位:前端使用极轻,后端构思极巧。护城河不在 UI(任何人都能做一个漂亮的记忆列表),而在三层不可见的后端智能——LLM 隐含推理、人格演化算法、触发时机精准度。竞争对手能抄界面,但抄不走 prompt engineering + 用户行为建模 + 人格演化的组合。