ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

大脑活动量化与数据建模:Edgi 的 Strava 式活动流设计

2026/8/29 1:42:15 拓冰建站 浏览量
大脑活动量化与数据建模:Edgi 的 Strava 式活动流设计 打开电脑看一眼自己的浏览器历史、笔记软件、Kindle 高亮和稍后读列表混乱得像一间没有整理过的书房。但你手机上有一张很漂亮的 Strava 图表记录着上周跑了多少公里有一份详尽的 Letterboxd 看片清单标注着每部电影的评分和短评。大脑每天都在高强度工作可它产出的数据反而最不体面最难以回顾也最容易被忽视。这就是 Edgi 这个项目有意思的地方。“Show HN: Edgi – Letterboxd or Strava for your brain”一句话就把产品定位说清楚了给大脑装一个运动记录仪。本文不打算停留在“好酷”的层面而是要拆开讲清楚三件事这类产品到底在解决什么真实问题它背后的数据结构和技术难点是什么以及如果你想做一个类似 MVP应该从哪些模块开始落地。先说结论Edgi 这类产品的价值不在于“多了一个记录工具”而在于它第一次把读书、思考、写作、深度学习这些原本非结构化的“大脑活动”用 Strava 式的活动流模型变成可量化、可回溯、可分享的数据。对开发者来说最值得学习的不是 UI而是这种“把不可见行为变成结构化数据”的建模思路。1. 这篇文章真正要解决的问题先想想一个很常见的场景周末晚上你回忆这一周做了什么。能想起来的往往是零散的碎片周一改了一个 bug周二开了两小时的会周三看了几十篇技术文章但看完就忘了。到了写周报或者做季度复盘的时候你根本说不清自己这周到底在“什么”上花了多少时间也没有任何数据可以支撑这个回顾。我们愿意在 Strava 上记录跑步因为那是一种“看得见的努力”愿意在 Letterboxd 上标记电影因为那是品味的展示。但大脑的劳动——阅读、笔记、思考、创作、专注复习——它们更频繁、更琐碎、也更私密传统工具一直没有很好地支持。已有的工具都缺一块任务管理工具Todoist、Things记录的是“未来要做的事”不关注你实际把时间花在了哪里。笔记工具Notion、Obsidian记录的是“思考的产出”不记录你读了多少、想了多久、状态如何。时间追踪工具RescueTime、Toggl记录的是“应用使用时间”但缺少语义它知道你打开了 Chrome 两小时却不知道你是在写文章还是在刷短视频。阅读工具Kindle、Readwise只覆盖阅读这一个子集无法统一呈现“阅读 笔记 写作 思考”的整体图景。Edgi 类产品的切入点是把 Strava 的“一次骑行 一次活动”模型迁移到大脑活动上。“一次深度阅读 一次活动”“一次两小时写作 一次活动”“一次专注冥想 一次活动”。记录之后用户能看到自己这周的“脑力活动”分布而不是单纯地看屏幕时间。这篇文章适合三类读者第一类对量化自我Quantified Self感兴趣的开发者想知道这类产品的功能边界与技术复杂性。 第二类正在做阅读追踪、行为记录、学习管理类产品的开发者需要一套清晰的数据建模参考。 第三类日常被信息过载困扰想用数据重新认识自己大脑使用方式的用户。注意本文会基于项目标题和产品类比做合理推断而不是照搬官方文档。Edgi 目前还处于 Show HN 阶段的早期产品实际功能以官方发布为准但同类产品在数据模型和工程实现上高度相似下面的思路可以直接复用。2. “for your brain”是什么意思拆解两个类比理解 Edgi 的捷径是拆开题目里的两个参照物。2.1 Letterboxd 在记录什么Letterboxd 是一个电影社交平台核心操作是看完一部电影后标记“看过”打分写一句短评然后把它加入自己的年度清单。它解决的是“电影记忆管理”问题。很多人看完电影不写任何记录一个月后就只记得“好看”或“不好看”。Letterboxd 把这种一次性消费变成了一种数据资产你的观影历史、你的口味分布、你的年度总结。它的价值不在于打分本身而在于“低摩擦 可回溯”。2.2 Strava 在记录什么Strava 是一个运动追踪平台核心操作是开启一次跑步或骑行记录结束时保存得到速度、距离、心率、路线等一系列指标并和过去的自己、和社区里的朋友比较。它解决的是“训练过程可视化”问题。跑步这件事如果不记录你只知道“我跑过”但不知道“我这周比上周进步了多少”。Strava 把运动变成了时间序列数据让普通人拥有了专业运动员才有的数据化复盘能力。2.3 Edgi 把两个模型叠加到“大脑”上用 Letterboxd 的模型你可以给一本读过的书打分、写短评、维护一份年度阅读清单形成品味档案。 用 Strava 的模型你可以“记录”一次深度工作会话从几点开始、持续多久、中间有没有打断、结束时产出了什么形成脑力活动的时间序列。两套模型合并在一起“for your brain”的含义就完整了大脑活动既有“品味的沉淀”读过的书、看过的资料、写过的笔记也有“状态的追踪”专注时间、阅读时长、思考强度。整个系统想解决的是同一个问题如何建立一个人的“精神履历”——不是简历那种技能列表而是你大脑每天都在如何运转的行为记录。2.4 和传统效率工具的本质差异这里要强调一个容易被忽视的点。传统效率工具是“以任务为中心”的它们关心“你是不是完成了该做的事”Edgi 这类工具是“以数据为中心”的它们关心“你实际上把脑力用在了哪里状态如何变化”。“任务”是目标导向的而“活动”是行为导向的。这个区别在数据模型上非常关键任务管理系统的主实体是 Todo状态只有未完成和已完成而行为记录系统的主实体是 Activity需要描述持续时长、开始时间、关联对象、质量评分、精力状态等多维信息。这意味着Edgi 类产品的数据库设计会比普通 To-Do 应用复杂得多因为它本质上是一个面向“自我认知”的时间序列系统。3. 从数据角度看这类产品为什么值得做抛开产品概念的光环开发者更关心的是这背后有什么真实的数据价值为什么值得投入精力去记录所谓“大脑活动”3.1 大脑数据是目前最大的数据盲区我们已经在手机上积累了大量的行为数据位置轨迹、社交互动、购物偏好、视频观看历史。但所有平台都没有完整覆盖一件事你读了多少、思考了什么、产出什么。在数据价值链上“大脑活动”是被互联网巨头普遍忽视的高价值盲区。如果你能采集用户每周的阅读时长、写作时长、笔记产出数量、深度专注总时长你得到的数据比社交平台的“点赞行为”更能反映一个人的认知状态。这类数据的商业想象空间很大但在早期阶段它的核心价值首先是“对用户自己有用”。3.2 数据形态足够复杂有技术挑战大脑活动数据有四个明显特征第一非结构化。一篇笔记可以是文字、代码、思维导图、语音备忘内容几乎无法统一建模。第二强主观性。同样是“阅读一小时”有人是在深度精读经典有人只是在刷资讯流二者的认知价值完全不一样。第三多源异构。数据分散在 Kindle、微信读书、浏览器历史、笔记软件、日历、Apple Health 之中合流非常困难。第四隐私高度敏感。阅读和思考记录是比运动数据更私密的个人信息如果同步到云端就涉及用户对平台的核心信任问题。这四个特征决定了Edgi 类产品不是“页面画几个表单”就能做出来的。数据采集、数据清洗、标签体系、隐私保护每一个环节都考验工程能力。这也是为什么做这类产品一个清晰的数据建模方案比前端交互更重要。3.3 产品闭环从记录到自我认知物理世界有“身体指标”的概念体重、体脂、睡眠时长。大脑世界同样需要“认知指标”的概念深度阅读时长、专注会话次数、输出字数、知识主题分布。有了这些指标用户才能在两周之后回答这些问题我最近是不是读得太散我的深度工作时间是不是被会议挤掉了我这个月有没有持续在同一个领域产出这种“看见”本身就是一种改变的动力。产品如果能把闭环跑通让用户产生“我要重新分配精力”的冲动留存是水到渠成的事。对开发者来说这个闭环意味着你构建的不只是一个记录工具而是一套帮助用户建立自我觉察的反馈系统。这类产品的留存来自“数据积累”用户数据越多离开成本越高。这与社交产品的网络效应一样重要也是产品长期价值的根基。4. 大脑活动记录系统的数据建模从工程角度说“for your brain”不是一个抽象的营销概念而是一个非常具体的数据建模问题。核心任务是回答用什么数据结构描述一次“大脑活动”4.1 核心实体设计活动流模型先看核心表结构。下面是一个可运行的设计适合作为 MVP 起点。-- 文件路径schema.sql -- 核心活动表一次阅读、写作、思考都算一条记录 CREATE TABLE activities ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, -- 用户标识 type TEXT NOT NULL, -- reading / writing / thinking / reviewing / learning_side_project started_at TEXT NOT NULL, -- ISO 8601 时间例如 2025-01-12T19:30:0008:00 duration_minutes INTEGER NOT NULL, -- 记录时长单位分钟 source_type TEXT, -- kindle / browser / manual / notion / apple_health / readwise / api source_id TEXT, -- 外部系统的主键用于幂等去重 title TEXT, -- 本次活动的标题或主题 quality_score INTEGER, -- 用户自评 1-5代表这次活动的质量 note TEXT, -- 短评或备注 created_at TEXT NOT NULL DEFAULT (datetime(now)), UNIQUE(user_id, source_type, source_id) -- 防止同一来源记录重复导入 ); -- 标签表活动与标签多对多 CREATE TABLE tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, name TEXT NOT NULL, UNIQUE(user_id, name) ); CREATE TABLE activity_tags ( activity_id INTEGER NOT NULL REFERENCES activities(id), tag_id INTEGER NOT NULL REFERENCES tags(id), PRIMARY KEY (activity_id, tag_id) ); -- 统计快照表用户可能要按月看自己的大脑活动 CREATE TABLE monthly_summary ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, month TEXT NOT NULL, -- 格式 YYYY-MM type TEXT NOT NULL, total_minutes INTEGER NOT NULL, activity_count INTEGER NOT NULL, avg_quality REAL, UNIQUE(user_id, month, type) );几个关键设计决策说明第一用户维度必须从一开始就加上。虽然 MVP 可能只有你自己在用但后面做多用户、做社交、做分享都需要 user_id 隔离。第二使用started_at duration_minutes而不是started_at ended_at。因为用户记录时往往只记得“我读了一个小时”不记得精确的结束时间。读取时统一用started_atduration_minutes计算结束时间。第三source_type source_id的联合唯一约束极其重要。它保证同一条 Kindle 阅读记录不会因为同步逻辑的 bug 被导入两次。真实环境里重复数据很难清理最好在表结构上就杜绝。第四quality_score是这类产品特有的字段。运动数据有配速、心率这些客观指标大脑活动没有统一的客观度量只能依赖用户自评。这张表保留这个字段以后做“什么类型的活动质量更高”这类分析才有素材。4.2 用事件流而不是状态存储新手容易犯的错误是去设计一张“用户信息表”记录用户的累计阅读时长、累计写作时长。这个设计在初期看着方便但很快会出问题。原因是“累计值”是状态状态可以推导不能当唯一数据源。如果用户导入了 100 条历史记录你的累计逻辑就要重算一遍如果用户删除了某条记录累计值也要同步更新。任何状态不同步就会出现“记录显示我读了 100 小时但明细加起来只有 95 小时”的尴尬情况。正确做法是activities 表是唯一事实来源累计值永远通过SELECT SUM(duration_minutes) FROM activities WHERE user_id ?动态计算。如果需要高性能查询再用定时任务把聚合结果刷到 monthly_summary 表而不是在写入时维护累计字段。4.3 为什么不需要“睡眠”和“心率”做一个大脑活动记录产品时很容易被“要不要接入 Apple Health记录心率、睡眠”带偏。这里要区分两类数据一类是本产品自己就能产生的业务数据比如阅读记录、写作时长、笔记数量另一类是第三方设备数据比如心率变异性、睡眠分期。设备数据听起来很硬核但它对“用户是否更了解自己的大脑”这个核心闭环帮助有限。更关键的是接入设备数据会迅速消耗你的开发资源从传感器权限、数据同步到图表展示每一条链路都不简单。MVP 阶段的建议是不做设备数据。把精力集中在用户主动记录、外部阅读数据自动导入和统计反馈这个主线上。等到用户已经养成了记录习惯再考虑引入身体指标做关联分析比如“睡好了之后深度阅读时长是不是更长”。5. 最小可行产品快速实现一个 Edgi 类 MVP下面给一个完整的 MVP 实现路径。技术栈选用 FastAPI SQLite 原生 HTML目的是让过程尽可能短一个人在一个晚上可以跑完。这个实现的完整代码路径如下edgi-mvp/ ├── main.py # FastAPI 应用包含接口和页面 ├── schema.sql # 上面的建表语句 ├── requirements.txt # 依赖 └── data.db # 运行时自动生成5.1 环境准备建议使用 Python 3.10 及以上版本。需要安装的依赖较少核心是 FastAPI 和 Uvicorn。mkdir edgi-mvp cd edgi-mvp python3 -m venv venv source venv/bin/activate pip install fastapi uvicorn[standard]如果你偏好使用 Conda也可以直接创建虚拟环境后安装依赖。版本要求并不苛刻以实际安装到的最新稳定版为准即可。5.2 核心接口实现先创建main.py实现三个核心能力记录活动、查询活动列表、查看每日统计。# 文件路径edgi-mvp/main.py import sqlite3 from datetime import date, datetime from typing import Optional from fastapi import FastAPI, HTTPException, Query, Request from fastapi.responses import HTMLResponse from pydantic import BaseModel DB_PATH data.db app FastAPI(titleEdgi MVP) def get_db(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row return conn def init_db(): with open(schema.sql, r, encodingutf-8) as f: schema f.read() conn get_db() conn.executescript(schema) conn.commit() conn.close() class ActivityIn(BaseModel): type: str started_at: str duration_minutes: int source_type: str manual source_id: Optional[str] None title: Optional[str] None quality_score: Optional[int] None note: Optional[str] None class ActivityOut(ActivityIn): id: int created_at: str app.on_event(startup) def startup(): init_db() app.post(/api/activities, response_modelActivityOut) def create_activity(activity: ActivityIn): if activity.duration_minutes 0: raise HTTPException(status_code400, detailduration_minutes 必须大于 0) if activity.quality_score is not None and not (1 activity.quality_score 5): raise HTTPException(status_code400, detailquality_score 必须在 1-5 之间) conn get_db() try: cur conn.execute( INSERT INTO activities (user_id, type, started_at, duration_minutes, source_type, source_id, title, quality_score, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) , ( default-user, activity.type, activity.started_at, activity.duration_minutes, activity.source_type, activity.source_id, activity.title, activity.quality_score, activity.note, ), ) conn.commit() row conn.execute( SELECT * FROM activities WHERE id ?, (cur.lastrowid,) ).fetchone() conn.close() return dict(row) except sqlite3.IntegrityError: conn.close() raise HTTPException(status_code409, detail记录重复请检查 source_id) app.get(/api/activities, response_modellist[ActivityOut]) def list_activities( activity_type: Optional[str] Query(defaultNone, aliastype), date_from: Optional[str] Query(defaultNone), date_to: Optional[str] Query(defaultNone), ): sql SELECT * FROM activities WHERE user_id default-user params: list [] if activity_type: sql AND type ? params.append(activity_type) if date_from: sql AND substr(started_at, 1, 10) ? params.append(date_from) if date_to: sql AND substr(started_at, 1, 10) ? params.append(date_to) sql ORDER BY started_at DESC LIMIT 100 conn get_db() rows conn.execute(sql, params).fetchall() conn.close() return [dict(row) for row in rows] app.get(/api/stats/daily) def daily_stats(day: str date.today().isoformat()): conn get_db() rows conn.execute( SELECT type, COUNT(*) AS activity_count, SUM(duration_minutes) AS total_minutes, AVG(quality_score) AS avg_quality FROM activities WHERE user_id default-user AND substr(started_at, 1, 10) ? GROUP BY type ORDER BY total_minutes DESC , (day,), ).fetchall() conn.close() return {date: day, items: [dict(row) for row in rows]}这段代码里有几个容易出错的细节source_id不加会导致UNIQUE约束失效所以如果你做外部导入务必给每条记录生成一个稳定的来源 ID。手动记录时可以让source_id留空靠应用的业务逻辑控制不重复提交。日期过滤使用substr(started_at, 1, 10)是为了兼容带时区的 ISO 字符串。如果你在业务上统一用 UTC 存储这没任何问题如果客户端传的是本地时间务必在写入前统一转换否则统计会从“某一天跑偏”变成“整体错乱”。查询接口是阶段性的核心接口因为用户最频繁的操作是“我今天读过书”而不是“看报表”。5.3 最小可视化页面纯接口不方便日常使用再加一个最简单的前端页面。这一步是为了验证“记录后能看”的闭环。# 继续在 main.py 末尾添加 app.get(/, response_classHTMLResponse) def index(): return !DOCTYPE html html langzh-CN head meta charsetUTF-8 titleEdgi MVP/title style body { font-family: sans-serif; max-width: 640px; margin: 40px auto; padding: 0 16px; } input, select, button { font-size: 16px; padding: 8px; margin: 4px 0; } .box { border: 1px solid #ddd; border-radius: 8px; padding: 16px; margin: 12px 0; } /style /head body h1Edgi MVP/h1 div classbox h3记录一次大脑活动/h3 select idtype option valuereading阅读/option option valuewriting写作/option option valuethinking思考/规划/option option valuereviewing回顾/复习/option /select input idduration typenumber placeholder时长分钟 min1 / input idtitle typetext placeholder主题可选 / button onclicksubmitActivity()保存/button /div div classbox h3今日统计/h3 button onclickloadStats()刷新/button pre idstats/pre /div script async function submitActivity() { const payload { type: document.getElementById(type).value, started_at: new Date().toISOString(), duration_minutes: parseInt(document.getElementById(duration).value, 10), source_type: manual, title: document.getElementById(title).value || null, quality_score: null, note: null }; const res await fetch(/api/activities, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }); alert(res.ok ? 已记录 : 保存失败请查看控制台); } async function loadStats() { const res await fetch(/api/stats/daily); const data await res.json(); document.getElementById(stats).textContent JSON.stringify(data, null, 2); } window.onload loadStats; /script /body /html 这一步验证的是核心闭环用户在页面选择活动类型填入时长保存然后看到今天的汇总。这个页面虽然简陋但它已经有“记录 → 存储 → 聚合 → 反馈”的完整链路了。5.4 启动运行pip install -r requirements.txt uvicorn main:app --reload --port 8000如果这一步成功终端会显示 Uvicorn 启动日志访问http://127.0.0.1:8000可以看到页面访问http://127.0.0.1:8000/docs可以打开 Swagger 文档调试接口。6. 运行结果与效果验证接口是否正常可以通过一条简单的 curl 命令验证。先手动插入一条阅读记录curl -X POST http://127.0.0.1:8000/api/activities \ -H Content-Type: application/json \ -d { type: reading, started_at: 2025-06-10T20:30:0008:00, duration_minutes: 45, source_type: manual, title: 读完《黑客与画家》第三章 }预期返回{ id: 1, type: reading, started_at: 2025-06-10T20:30:0008:00, duration_minutes: 45, source_type: manual, source_id: null, title: 读完《黑客与画家》第三章, quality_score: null, note: null, created_at: 2025-06-10 20:35:12, user_id: default-user }验证成功标准很简单返回的 JSON 里有自增的 id并且没有报错。如果 category 或 type 填成了不存在的值接口不会拦你因为当前表结构没有枚举约束。这一点在 MVP 阶段可以接受但生产环境建议改成枚举类型避免统计里出现拼音、大小写混用、空格结尾等脏数据。再验证当日统计curl http://127.0.0.1:8000/api/stats/daily?day2025-06-10预期返回{ date: 2025-06-10, items: [ { type: reading, activity_count: 1, total_minutes: 45, avg_quality: null } ] }这里要特别注意时间与字符串的问题。curl 携带的started_at带时区 08:00而统计接口用substr(started_at, 1, 10)做字符串截取截取结果是2025-06-10这依赖于数据写入时的时间格式保持一致。一旦某条数据写入的格式是2025-06-10T12:30:00ZUTC 格式同一天在北京时间 08:00 之前的数据就会变成前一天统计看似“偶发”错误实际是时区规则不统一导致的必然问题。这个 bug 在新手项目中非常高频排查时先检查所有时间字段的格式。如果运行失败排查顺序如下第一ModuleNotFoundError: No module named fastapi说明虚拟环境没有激活或者没有安装依赖运行pip install fastapi uvicorn[standard]。 第二访问页面无样式多半是浏览器缓存强制刷新或换无痕窗口。 第三端口被占用换端口启动例如uvicorn main:app --port 8001。 第四数据库出现no such table检查schema.sql是否放在项目根目录以及启动时是否成功执行。7. 从 MVP 到产品的常见问题与坑MVP 跑通只是第一步真正做产品时有很多问题会浮出水面。问题现象可能原因排查方式解决方案同一条 Kindle 记录被重复导入外部系统返回的数据没有稳定 ID查看日志确认来源字段的 ID 是否唯一对 source_type source_id 建立唯一索引导入前先查询去重时间统计每天都少几十分钟客户端和服务端时区不一致对比数据库原始字符串和前端展示统一使用 ISO 8601 时区偏移服务端统一转 UTC 存储用户手动记录后列表里看不到日期过滤条件写错用 curl 请求不带日期参数的接口先确认查询语句再确认是前端过滤还是后端过滤跨日统计出错按天分组时字符串截取不准确检查 started_at 格式是否一致推荐使用 SQLite 的 date(started_at) 配合标准化格式数据库文件被误删开发环境重启后重新初始化检查启动脚本生产环境使用 PostgreSQL并做好备份标签混乱无法聚合分析标签没有归一化处理查 activities 表里的 tag 值建立统一的标签表写入前先做映射或模糊匹配这里专门强调两个问题。第一个是幂等。做外部数据导入时最开始容易出现“跑一次脚本数据翻倍”的情况。原因是外部 API 经常因为网络超时让你不确定“上次是否已经写入成功”。解决方案一定是在表结构上做唯一约束而不是在代码里“尽量判断”。数据库的唯一索引是最后一道防线任何导入代码都不能绕过它。第二个是隐私。大脑活动数据比位置数据更敏感。一旦产品支持多用户和云同步就必须认真考虑数据加密、访问控制和导出能力。用户看到自己的阅读记录泄露比看到运动记录泄露更惊恐。作为开发者在第一版设计里就要思考哪些字段需要加密哪些统计可以公开用户是否允许导出完整数据。MVP 阶段可以本地优先但产品化阶段不建议把用户数据默认上传到无保护的对象存储中。8. 最佳实践与工程建议如果你要在这个方向继续深入下面几条建议来自同类产品反复踩过的坑。8.1 数据采集手动 半自动 自动三层手动输入是产品冷启动的必要条件用户只有先手动记录几次才能体会到数据的价值。半自动是通过分享或剪贴板把用户在微信读书、Kindle、浏览器里看到的内容一键导入。自动是后台定时拉取外部服务的数据比如 Readwise、Apple Health。不要第一版就做全自动同步。因为外部平台的 API 不稳定处理字段映射和增量同步会消耗大量时间。先把手动体验做到 5 秒内完成再逐步加自动导入。8.2 质量字段要单独设计大脑活动不能像运动心率那样用设备测量只能靠用户主观评分。主观评分有严重的膨胀问题大多数用户会习惯性打高分。建议使用相对评分而不是绝对评分例如问用户“这次专注程度比平时高还是低”而不是“这次专注程度打几分”。相对值比绝对值更适合做趋势对比。另外质量字段应该是可选的。每次记录都要打分用户会觉得是负担。可以让用户偶尔打分系统只对打过分的数据做质量分析没打分的记录仍然保留时长和类型信息。8.3 周报和回顾是真正的价值时刻记录本身不是目的用户留下来的原因是回顾时有惊喜感。Edgi 类产品最核心的留存点是周报、月度总结和年度回顾。就像 Strava 每周一给用户推送“你上周跑量超过 80% 的跑友”大脑活动记录工具也应该定期生成一份“认知周报”本周深度阅读时长、与上周的对比、最常出现的主题标签、输出字数变化。这类内容的生成不复杂在 monthly_summary 表做好聚合基础上用模板渲染即可。建议把这部分功能放在所有图表之前。用户可能看不懂复杂的仪表盘但一定看得懂“你这周专注时间上升了 23%”。8.4 社交功能要克制Strava 的社交是建立在一个公开可见的社区上的但大脑活动比运动数据私密得多。如果 Edgi 做社交应该默认所有数据私有用户在自主选择后才能公开某类活动的统计信息比如“公开我 7 月读过的书”但阅读时长、笔记内容保持私有。建议按“内容私有统计公开”的方式设计读书列表可以分享每种活动每周花了多少小时也可以分享但具体的地点、笔记、备注永远不对外。这样既能满足用户展示自我的需求又不会触碰隐私底线。8.5 服务端和数据库选型从 MVP 升级到正式产品时SQLite 不建议继续用于多用户在线服务。推荐使用 PostgreSQL原因是它的时间函数、JSONB 类型和全文检索在后续做语义分析时非常顺手。引入外部阅读数据时数据库表可以增加一个meta JSONB字段用于存放不同来源的扩展数据比如 Kindle 的书名、作者、章节进度。这样就不用为每个外部来源都建一张新表扩展起来成本低。9. 总结与后续学习方向Edgi 这类产品最值得关注的点不是“给大脑做记录”这个创意本身而是它背后的思考方式用运动类应用的数据模型去重新审视大脑活动把它从难以捕捉的抽象过程变成一组可以被存储、聚合、比较和分享的结构化事件。对普通用户来说它提供了一个重新认识自己时间分配的机会对开发者来说它是一座数据建模的迷宫牵涉到时间处理、幂等导入、标签体系、统计聚合、隐私设计每一步都有真实的复杂性。如果你对这个方向感兴趣下一步建议这样做在自己的机器上把上面的 MVP 跑通记上三天的真实阅读和写作记录然后看统计结果能不能回答你“我主要把时间花在哪了”。如果答案是“不能”考虑是不是类型设计不够细标签分类没有贴近真实使用方式。如果答案是“能”再想想怎么接入你最常用的阅读工具把导入流程自动化。从“记录”到“看见”再到“改变”是一个很长的链路。Edgi 还在这条路线的起点但方向已经足够清晰。对开发者来说这类产品的价值恰恰在于它是少数几个能把自我认知、行为数据和产品设计结合在一起的实验场值得持续关注。