个人博客2.0:架构、功能与 UI 设计沉淀
个人博客2.0:架构、功能与 UI 设计沉淀
BlogX_x「树码空间」——AI 驱动的个人数字花园。本文以当前线上形态为准,沉淀它的设计架构、功能全景与 UI 设计决策。 2026-10-22 全面重写,对齐「内容出仓」后的真实架构;旧稿中已失效的表述(写死的模型供应商、本地目录同步流程等)一并修正。
一、定位与设计哲学
1.1 双重角色
BlogX_x 不只是一个博客,它同时扮演两个角色:
- 个人博客:策展外链技术长文(自动抓取 + 中英翻译),沉淀自己的工程实践
- 知识库载体:按条目导入 Obsidian 笔记,构建可检索、可关联、可被 AI 读取的知识网络
两者共享同一套语义搜索与知识图谱底座——每一篇内容都被向量化、被关联、被可视化。
1.2 三条设计原则
- Content-first:内容优先,轻交互、低维护。宁可少一个功能,不多一处需要照看的后台。
- AI 是检索与回答层,不是内容真源:Markdown 归人所有,AI 只负责理解、检索、翻译与回答;AI 不生成内容真源,也不决定内容形态。
- 本地脚本 + 内容集合优先于自建后端:不引入额外后端服务,管理面收敛为一套轻量面板 + 一组可脚本化的 CLI。
1.3 工程三支柱
- VibeCoding——注重用户体验与交互感受,让界面有自己的气质;
- SpecCoding——严格的数据结构定义与类型约束,schema 单一定义处,双端(Astro / Node)共用;
- Harness Design——完整的工具链与自动化流程,把重复劳动交给脚本与流水线。
二、总体架构
2.1 分层图
┌──────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ 文章 · 知识库 · Wiki · 图谱 · AI 搜索 · 服务器 3D │
├──────────────────────────────────────────────────────┤
│ Astro 6 (SSR) + React 19 islands │
│ Tailwind 4 token 体系 · Vercel 部署 │
├──────────────────────────────────────────────────────┤
│ API 层:ai-search / knowledge-graph / search / … │
│ 管理面:/admin 面板(生产由 middleware 拦截) │
├───────────────────────┬──────────────────────────────┤
│ LanceDB content 表 │ LanceDB blog_index 向量表 │
│ (内容唯一真源) │ (搜索/图谱派生索引) │
├───────────────────────┴──────────────────────────────┤
│ Upstash Redis/KV:留言 · 想法 · 限流 · 健康检查 │
├──────────────────────────────────────────────────────┤
│ 外部服务:OpenAI 兼容 LLM(LLM_* 统一配置出口) │
│ · 嵌入服务(向量编码)· Obsidian vault(可选输入源) │
│ · MCP server(stdio / HTTP,供 Agent 读知识库) │
└──────────────────────────────────────────────────────┘
2.2 内容真源:一张 content 表
架构上最关键的一条决策:内容不在 git 里。
| 层 | 是什么 | 谁写 |
|---|---|---|
LanceDB content 表 | 内容唯一真源。字段:collection / path / frontmatter / body / originalBody / translatedBody / status / updatedAt | 管理面板、content:intake、content:publish、sync-obsidian-kb |
src/content/** | 构建产物。predev / prebuild 由 content-pull 按原嵌套路径写盘,.gitignore 忽略 | 只有 content-pull |
带来的直接收益:
- 克隆即建:新机器只需
.env+npm install,构建时自动拉全量内容; - 草稿跨机器可见:
status: draft只存在于表里、不写盘,天然不进构建产物; - 内容与代码解耦:内容变更不触发重建,发布时主动打 Deploy Hook,节奏由人控制。
2.3 运行时形态
| 层 | 技术 | 说明 |
|---|---|---|
| 框架 | Astro 6(output: "server") | SSR,经 @astrojs/vercel 部署 |
| 交互 | React 19 islands | 只给需要交互的模块(图谱、留言、阅读栏、3D 机箱…) |
| 样式 | Tailwind 4 + global.css 设计 token | 见第五节 |
| 向量库 | LanceDB(本地 .lancedb + 云端) | 双表:content(内容)+ blog_index(向量) |
| LLM | 任一 OpenAI 兼容服务 | 配置统一走 LLM_API_KEY / LLM_BASE_URL / LLM_MODEL 出口,不绑定供应商 |
| KV | Upstash Redis(@vercel/kv) | 留言、想法、限流 |
| 协议 | MCP(stdio / HTTP) | AI 助手直读知识库 |
三、内容模型与管线
3.1 六个集合
| 集合 | 内容 | 路由 |
|---|---|---|
posts | 策展外链文章(双语) | /posts |
knowledgeBase | 知识库笔记(嵌套分类路径) | /knowledge-base |
wiki | 站点 Wiki(sources/concepts 编译层) | /wiki |
repos / skills / projects | 仓库 / 技能包 / 项目展示 | /repos /skills … |
toolbox | 策展外部工具 | /toolbox |
Schema 的唯一定义处在 src/lib/content-schemas.ts,Astro 与 Node 脚本双端加载同一份;面板写入走 passthrough 校验,保留 schema 之外的脚本字段(如 posts 的抓取元数据),静默丢弃即丢数据。
3.2 posts:外链策展 + 双语翻译
「文章」的形态是外链收藏 + 自动化加工,不是自己写长文:
- 收录只需一条
sourceUrl(title / description / 作者 / 语言均可自动提取); - 构建期
fetch-articles抓取正文、调用 LLM 全文翻译,原文与译文存进 content 表的 originalBody / translatedBody; - 详情页原文/译文一键切换(纯前端,零延迟),保留原作者归属与来源回链;
- 只补缺:语料完整的记录构建期空转,不打 LLM 不写库;失败不静默——抓取失败原记录一个字节不动,翻译失败保留旧译文并逐条报告。
3.3 knowledge-base:嵌套分类 + 按条目导入
- 路径即分类(如
辊刀软件/路径编辑功能V2.0),列表页按第一层目录分组,目录名就是站点分类名; - Obsidian vault 是可选输入源:
sync-obsidian-kb按条目点名导入,禁止整库自动同步——vault 里有私人笔记,整库同步等于把它们推上公网站点; - 排除清单里的条目即使被点名也拒绝导入;导入幂等(按 path 先删后插),且绝不把在架条目撤下。
3.4 wiki:sources / concepts 双层编译
站点 Wiki 是从知识库编译出来的衍生层:
sources/——每篇知识库笔记一篇「来源摘要」(关键结论 + 可复用方法),body 里标注原文路径;concepts/——跨来源的概念沉淀,用[[双链]]织网;index/log——入口清单与操作日志。
知识库是原料,Wiki 是蒸馏物;改知识库要同步摘要层,否则衍生层失真。
3.5 发布链路
输入(URL / 文件 / 粘贴正文 / Obsidian 笔记)
→ content:intake(抓取 + LLM 提取 + 校验 → content 表草稿)
→ content:publish(校验发布 → status: published → 打 Vercel Deploy Hook)
→ 构建期 content-pull 写盘 → init-db 增量向量化 → 站点更新
管理面板与 CLI 共用同一份 intake 实现;发布门禁一致:intake 自述失败的草稿默认拒绝发布,显式覆盖才放行。
3.6 向量索引
blog_index 是派生索引,不是真源:init-db 对内容做嵌入(1024 维),增量更新(hash 对比:新增/修改/删除三分),内容删了索引也会跟着删。搜索与图谱都读它;检索质量有问题时重建索引,而不是在 UI 里硬编码结果。
四、功能全景
4.1 AI 对话式搜索
首页主入口。自然语言提问 → 向量检索 → 相关内容卡片 + LLM 生成的回答,支持连续对话上下文。工程细节:
- 双 scope:站内内容(site)与工具箱(toolbox)分开检索;
- 限流:每 IP 滑动窗口(默认 60 秒 5 次);
- 交互上有停止/清除对话控制(博客式按钮,可中断生成中的回答);
- 结果区加宽卡片布局,hover 时同级卡片自动变暗聚焦。
4.2 知识图谱
基于向量相似度自动建边——每个节点取最近邻连成关系网络,力导向图渲染(react-force-graph-2d + d3-force)。节点上限、每节点边数、相似度阈值都可调。交互:滚轮缩放、点击节点跳转原文。仅桌面端展示,移动端隐藏——小屏读图不如读列表。
4.3 阅读体验:Reading Rail
文章 / 知识库 / Wiki 详情页统一挂左侧阅读栏(交互对标 claude.dev/blog 的 rail):
- 自动解析正文 h2–h4 生成章节树,当前章节高亮,点击跳转;
▓░字符风格阅读进度条,顶栏另有细线进度随滚动生长;- 双模式:
article(线性阅读,当前组展开)/reference(查阅跳转,子树常开); - 正文替换(如原文⇄译文切换)后自动重建章节树。
4.4 服务器页:3D 机箱
首页像素标题「树码空间」之外,站点有一台线条画风的可交互 3D 机箱(/server):从水冷到电源逐件拆看,部件引出标注,把「这台数字花园的机器」具象化——它同时是隐喻导览:CPU 对应 AI 搜索与翻译管线,冷排对应向量索引,等等。
4.5 互动与后台
- 关于页:留言(FunMessages)+ 想法箱(IdeaBox),轻交互,写入 KV,限流 + 净化;
- 管理面
/admin:posts / knowledge-base / wiki / repos / skills / projects / toolbox / messages / ideas 全集合 CRUD,posts 另有 AI 上传面板;生产环境整面 404,写入面在公网为零; - MCP:stdio / HTTP 双形态 MCP server,让 AI 助手直接读知识库与 Wiki——博客不只是给人看的,也是给 Agent 用的记忆库。
五、UI 设计
5.1 视觉基调:暖纸 + 牛血红
设计方向是暖纸工业插画(warm paper + industrial illustration),浅色单主题:
- 纸面底色
#efe5cf,棕墨线条,牛血红#b93b28是全站唯一强调色; - 内容与插画在同一张连续的「纸」上,没有悬浮玻璃、没有弥散光晕;
- 小圆角(2–5px,像裁切的纸边),卡片阴影是短偏移实边(
1px 2px 0),不是柔和投影; - 选区、进度条、hover 态全部收敛在牛血红一族。
5.2 设计 token
全部集中在 src/styles/global.css 的 CSS 变量:色板(bg / surface / text / line / accent 系列)、圆角、阴影、缓动(expo-out 三档时长)、字体四件套:
| 用途 | 字体 |
|---|---|
| 正文 | Geist + Noto Sans SC |
| 章节标题 | Noto Serif SC(书卷气) |
| 技术标签 / 代码 | Geist Mono |
| 展示标题 | Manrope |
改气质只动 token,不动组件。
5.3 关键界面决策
| 决策 | 理由 |
|---|---|
| 首页像素标题(PixelWaveTitle「树码空间」) | 品牌记忆点,像素波是站点的视觉签名 |
| AI 搜索置于 hero 中央 | 搜索是第一入口,不是附属功能 |
| 阅读栏章节树 + 字符进度条 | 长文阅读要有空间感;▓░ 是编辑器气质的克制表达 |
| 知识库列表「主区 + 右侧分类目录」双栏 | 分类即路径,目录即导航 |
| hover 聚焦(同级卡片变暗) | 信息密度高时帮视线锁定目标 |
| 文章预览纸感滚动条 | 细节处延续「纸」隐喻 |
| 图谱桌面限定 | 移动端宁缺毋滥 |
| light-only,不做暗色主题 | 纸的隐喻经不起反色,主题分裂成本高于收益 |
5.4 响应式与可访问性
- 375px 手机到 2560px 4K 全档自适应,正文无横向溢出;
- 切换按钮、搜索框等交互控件带 aria-label,键盘可达;
- 知识库移动端改横向分类标签条,导航折叠抽屉;
- 动效克制:入场 stagger、hover 微移,全部走统一的 expo-out 缓动。
六、经验与取舍
- 内容出仓(移出 git)是分水岭:此前一篇私人笔记被同步进仓库、引用不存在的图片,直接把构建打挂。内容进 LanceDB 后,构建产物可清空重建,脏数据不再污染仓库,也让「内容变更 ≠ 代码变更」成为可能。
- 失败要响亮,不要静默降级:拉不到内容且磁盘为空时构建直接失败——空站点比失败更难发现;写库路径在凭据缺失时拒绝写入,而不是「表面成功写错靶」。
- 只补缺,不重翻:翻译管线的增量策略省下大量 LLM 调用;「本来就缺」与「被改坏」用同一个谓词区分,回归才报警。
- 私人与公开的边界要硬:按条目导入 + 排除清单 + 生产环境管理面 404,三道闸都指向同一条线——写入面在公网为零。
- 配置出口收敛:外部 LLM 按任一 OpenAI 兼容服务表述,统一走
LLM_*配置出口;供应商是可替换的实现细节,不是架构陈述。 - 衍生层要标源:Wiki 摘要标注原文路径,知识库移动/删除时同步更新——引用悬空的摘要比没有摘要更误导。
本文是 BlogX_x 当前形态的架构快照。工程细节以仓库内文档与源码为准。