版本: v0.1 (设计阶段) 日期: 2026-02-16 状态: 方向已定,待实施
传统思维工具(Notion、Obsidian、思维导图)的逻辑是:用户主动组织 → 形成结构。
Kept 应该反过来:用户随口说 → AI 发现结构 → 用户感到惊喜。
用户不应该"管理"想法。某天展开一条想法,发现下面安静地躺着几条相关的旧想法——"原来我一直在想这件事"。这就是 aha moment。
设计原则:懂你但不说破。 关联引擎在后台默默工作,用户感受到的是 "Kept 越来越懂我",但说不清为什么。
三层关联,任一层命中即建立连接:
在现有 prompt 中扩展 THOUGHT 输出,新增 themes
数组:
{
"type": "THOUGHT",
"display_text": "远程办公更高效",
"content": "...",
"themes": ["工作方式", "效率"]
}规则:
为什么这是主关联:
从已有的 people 字段 + 从 content
中提取的地点/事物实体:
实现方式: 复用 LLM 已输出的 people
数组,未来可扩展提取 entities。
使用 Apple 原生 NaturalLanguage 框架的
NLEmbedding:
为什么是兜底:
新 THOUGHT 录入 →
1. 提取 themes,查询已有 THOUGHT 中 themes 有交集的 → 强关联
2. 查询 people/entities 有重叠的 → 弱关联
3. 计算 embedding 余弦相似度 ≥ 0.82 的 → 弱关联
4. 强关联 + 任意弱关联 → 建立连接
5. 仅弱关联(2条以上弱信号) → 建立连接
MemoryEntity 新增字段:
├── themes: String? ← JSON: ["工作方式", "效率"](仅 THOUGHT)
├── embedding: Binary? ← [Float] 序列化,NLEmbedding 128d
├── embeddingModel: String? ← "NLEmbedding-zh-v1"(标记版本以备升级)
├── linkedMemoryIDs: String? ← JSON: ["uuid1", "uuid2"](双向关联)
└── threadID: String? ← 线索标识(同主题 3+ 条时生成)
ThoughtThread:
├── id: String ← 唯一标识
├── theme: String ← 主题名(人类可读)
├── thoughtCount: Int ← 关联 THOUGHT 数量
├── createdAt: Date
└── lastUpdatedAt: Date
不需要存储 thoughtIDs 列表——通过查询
MemoryEntity.threadID == thread.id 获取。
在 prompts.js 的 THOUGHT 输出格式中新增:
当类型为 THOUGHT 时,额外输出 themes 字段:
- themes: 1-3 个抽象主题标签(如"职业方向"、"健康生活"、"创意灵感")
- 标签应是概念层面的,不是具体关键词
- 优先使用以下已有标签(如适用): [动态注入]
- 如果没有合适的已有标签,可以创建新的
memory-types-theory.md 的原则:
"理解体现在行为精准度上,不体现在表达上。懂你但不说破。" 永远不做:"你的本周思考总结"、"基于你的记忆模式,我们发现..."
但完全隐形会浪费关联的价值。 调和方式:
用户主动发现 = 可以展示。AI 主动告知 = 要极度克制。
- 用户展开一条想法,看到相关想法 → 这是用户在翻自己的记忆,不是 AI 分析
- 主页有个安静的区域显示"反复出现的想法" → 这是自然呈现,不是 AI 炫技
- 推送通知说"你经常想到 X" → 这是越界,需要信任门槛
触发: 用户展开一条 THOUGHT 卡片
展示: 在 content 下方,出现一个安静的关联区域:
┌─────────────────────────────────────┐
│ 💭 远程办公其实更适合我 │
│ │
│ [展开后的 content 整理文字...] │
│ │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ 💭 周二想到的 │
│ "在家工作效率确实高很多" │
│ │
│ 💭 上周四想到的 │
│ "要不要跟老板提每周两天远程" │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
└─────────────────────────────────────┘
设计细节:
颜色: 关联区域用
KeptTheme.text.opacity(0.3) —— 比已经很淡的 THOUGHT
色更淡,像纸上的铅笔素描
动画: 涟漪区域跟随 content 一起展开,使用相同的 spring 动画,但 delay 0.1s —— 像水波一样稍后到达
触发条件: 某个主题的 THOUGHT 数量 ≥ 3
位置: 主页 surfaced 区域和 earlier 区域之间,或者 surfaced 上方
┌─────────────────────────────────────┐
│ 你在想的事 │
│ │
│ ┌───────────┐ ┌───────────┐ │
│ │ 工作方式 │ │ 饮食探索 │ │
│ │ 4 条想法 │ │ 3 条想法 │ │
│ │ 最近: 今天 │ │ 最近: 昨天 │ │
│ └───────────┘ └───────────┘ │
│ │
│ ── 记忆 ── │
│ [正常的记忆卡片列表...] │
└─────────────────────────────────────┘
设计细节:
线索详情视图(新页面):
┌─────────────────────────────────────┐
│ ← 返回 工作方式 │
│ │
│ ○ 今天 │
│ │ "远程办公其实更适合我" │
│ │ 整理后的内容... │
│ │ │
│ ○ 3 天前 │
│ │ "在家工作效率确实高很多" │
│ │ 整理后的内容... │
│ │ │
│ ○ 上周四 │
│ │ "要不要跟老板提每周两天远程" │
│ │ 整理后的内容... │
│ │ │
│ ● 12 天前(最早) │
│ "公司新的考勤制度太死板了" │
│ 整理后的内容... │
└─────────────────────────────────────┘
场景: 用户刚说完一条新 THOUGHT,AI 处理后发现有关联
当前 Toast: "✓ 记住了"
增强 Toast(有关联时): "✓ 记住了,这是你第 3 次想到这件事"
设计细节:
实现: 在 processMemory()
后执行关联匹配,如果命中,修改 Toast 文案
场景: 某个主题累积到 3 条 THOUGHT → 推一条本地通知
通知文案: "你最近经常想到「{主题名}」的事"
设计细节:
KEPT_THREAD,action button: "看看" /
"忽略"| 触发方式 | 不做的原因 | 未来条件 |
|---|---|---|
| 周回顾通知 | 像"AI 在定期分析你" | 用户主动开启 |
| 跨类型关联 | ACTION+THOUGHT 关联太主动 | 种子用户验证后 |
| 想法→行动建议 | "要不要变成行动"太指导性 | 人格系统 v1 之后 |
| 想法成熟度分析 | 明显的 AI 分析感 | 深度信任建立后 |
原则:触发的边界由信任决定。 目前只做用户主动发现(涟漪)和最克制的被动提示(录入时 + 临界点通知各一次)。随着用户对 Kept 的信任加深,逐步开放更多触发。
端侧:
- Core Data 新增 themes, embedding, linkedMemoryIDs, threadID 字段
- NLEmbedding 封装:生成 + 余弦相似度计算
- 关联匹配服务:新 THOUGHT 保存后触发,匹配已有 THOUGHT
- MemoryCard 展开区域新增涟漪关联展示
- Toast 增强:"记住了,这是你第 N 次想到这件事"
云端:
- prompts.js 新增 themes 输出要求
- 版本号 → v2.6
端侧:
- ThoughtThread Core Data 实体
- 线索自动生成逻辑(3+ 同主题)
- 主页"你在想的事"横向滚动区域
- 线索详情时间线视图(新页面)
- 线索形成时推送本地通知
- 跨类型关联(THOUGHT ↔ ACTION/INTENT)
- 线索合并(用户手动或 AI 建议)
- 周回顾(用户主动开启)
- 想法 → 行动转化建议
每个实现细节都要过这些检验: