从零搭建 carry.city:个人数字生活档案
这个网站从四月初开始搭,到这篇文章发布时差不多两周。不是一个炫技项目,更像一个折腾:把散在网易云、豆瓣、相册、代码仓库里的自己拼回来,变成一个能访问、能对话的"数字档案"。
为什么做
平时听的歌在网易云,看的电影和书在豆瓣,照片在手机里,代码在 GitHub,写的一点东西散在飞书和 Obsidian。这些数据本来就是"我"的一部分,但它们分别活在十几个平台上,我永远凑不齐自己的全貌。
所以目标很明确:
1. 聚合——把这些数据在一个地方呈现
2. 自动——不手动维护,数据自己更新
3. 有个性——Apple 风格的冷淡、干净,不是模板站
4. 可对话——来访者可以和一个 AI 聊这个网站的内容,而不是自己点来点去
技术栈
@theme inline 配置,不写 tailwind.config.ts)几个踩坑的地方
1. 豆瓣爬虫:限速是真的严
一开始天真地一页一页爬,第三页就吃了 302。后来改成:页间固定 sleep 2 秒、每批最多 5 页、爬完自动从头开始(兼顾新标记)。图片还需要走 /api/proxy-image 绕 Referer 检查,不然直接在前端加载会被 CDN 拦截。
2. 网易云 Cookie 会过期
MUSIC_U 这个 cookie 每隔一段时间就失效,失效后 API 不会明确报错,而是返回空数据。我写了个小检测:如果 /user/record 的返回里 weekData 为空超过两天,就在 pm2 日志里打一行警告提醒我换 cookie。
3. AI 助手的合规边界
这是整个项目最重要的决策。国内部署 AI 对话服务要走《生成式人工智能服务管理暂行办法》,个人主体根本备不了算法。强行做公开的匿名对话就是定时炸弹。
所以我选了邀请码模式:URL 里带 ?invite=xxx 直达聊天,没码的访客看到的是锁屏 + "联系我获取"。这样在法律定义上就不是"向公众提供",而是"向特定对象"。招聘方的 HR 邮件里附个链接就行。
4. OpenClaw + 自定义 skill
OpenClaw 是一个 agent 运行时。我给它配了三个自定义 skill(都用 Markdown 定义):
music-query — bash + curl 调本地网易云 APImedia-query — bash + sqlite3 查豆瓣数据库content-search — bash + grep 搜站内 MDX坑爹的是 OpenClaw 的 agent deny-list 默认屏蔽了 web_fetch 和 exec。skill 写的是"用 http_request 工具"但 OpenClaw 实际叫 web_fetch,名字不对直接就跑不起来。调通这三个 skill 花了两个晚上。
调通之后让前端能"看到"skill 调用也费了点劲——OpenClaw 的 OpenAI 兼容接口不往外吐 tool_calls 字段,所以我让 AI 在回复最开头打个自制标记 [SKILL:music-query],后端 SSE 流里实时拦截这个标记、emit 自定义事件、然后剥离掉再流给前端。前端拿到事件就渲染一个 chip,点击还能弹出对应的 SKILL.md 源码。
这套"自订标记 + 服务端拦截"的模式其实就是在补 OpenAI function calling 协议的可视层——花了半天,但值得,因为 HR 能直观看到"这个 agent 确实在调我设计的 skill",而不是黑盒套壳。