很多个人网站一开始都在介绍“我是谁”,真正的文章反而被放在第二屏。墨痕的第一轮重构先做了一个产品决定:访问者打开网站时,最先看到的应该是内容,而不是作者简历。
这篇文章记录项目从目标、技术选型到服务拆分的过程。它不是一份框架功能清单,而是解释每个选择解决了什么问题。
先写清楚产品边界
墨痕需要完成四件事:
- 用安静、内容优先的页面发布 Markdown 技术文章。
- 通过单管理员后台管理文章、模型、助手角色和知识库。
- 让访客随时打开 AI 助手,并基于博客知识进行问答。
- 展示可以安全执行的 Python/LangChain 教学实验。
同时也主动排除了一些功能:不做用户注册、评论、点赞、多租户权限和通用在线 IDE。明确边界非常重要,因为个人项目最容易在“什么都想做”中失去主线。
为什么前端选择 Next.js
博客需要服务端渲染、动态 Sitemap、文章详情和管理端交互。Next.js App Router 能把公开页面、管理页面和 API 代理放在同一个 Web 应用中:
app/
├─ page.tsx # 首页文章时间线
├─ posts/[slug]/page.tsx # 文章详情
├─ experiments/ # 代码实验
├─ admin/ # 管理端
└─ api/ # 到 FastAPI 的服务端代理浏览器不会直接访问 Python API。Next.js Route Handler 在服务端转发请求,减少公开端口,也避免把内部地址暴露给客户端。
为什么把 AI 能力放在 Python
内容页面和 Agent 的变化节奏并不相同。Python 生态更适合 LangChain、文本解析、向量模型和 Qdrant,因此项目没有强行把全部逻辑塞进 Next.js,而是使用 FastAPI 作为业务与 Agent 服务。
flowchart LR
B[浏览器] --> W[Next.js]
W --> A[FastAPI]
A --> M[(MySQL)]
A --> Q[(Qdrant)]
K[Worker] --> M
K --> Q
A --> L[Lab Runner]这不是传统意义上庞大的微服务架构。六个容器运行在一台服务器上,拆分只是为了获得清晰的职责和安全边界。
MySQL 是唯一业务数据源
文章、模型配置、知识文档、分块、任务和用量都保存在 MySQL。Qdrant 只保存向量索引,原因有两个:
- 业务状态需要事务、约束和可审计的数据结构。
- 向量索引应该可以从原始文档重新生成,而不是成为无法恢复的唯一数据。
例如知识文档上传后,API 只负责保存原文并创建任务:
document.status = "pending"
db.add(IndexJob(document_id=document.id, action="index", status="pending"))
await db.commit()独立 Worker 再消费任务、生成分块和向量。这样上传请求不会因为嵌入模型较慢而长时间阻塞。
为什么没有 Redis
在 2 核 4G 的个人服务器上,每增加一个基础设施都会增加内存与维护成本。当前任务量很小,MySQL 任务表已经能够承担知识索引和定时发布,因此首版没有增加 Redis、Celery 或消息队列。
这项选择不是说 Redis 不好,而是让复杂度与实际规模匹配。如果未来出现多个 Worker、任务优先级或大量并发,再引入消息队列会更合理。
从单容器到 Compose
最终生产环境包含:
| 服务 | 职责 |
|---|---|
| Web | 页面、管理端与服务端代理 |
| Agent API | 业务 API、认证、对话与用量 |
| Worker | 定时发布和知识索引 |
| MySQL | 业务数据 |
| Qdrant | 向量索引 |
| Lab Runner | 受限代码执行 |
只有 Web 绑定 127.0.0.1,再由 Nginx 提供 80/443。其他服务只加入 Docker 私有网络。
web:
ports:
- "127.0.0.1:10001:10000"
depends_on:
agent-api:
condition: service_healthy这个细节比“容器都启动了”更重要:数据库和 Agent API 根本不应该直接暴露到公网。
实际开发顺序
项目不是一次写完的,而是按可验证的垂直切片推进:
- 先完成文章首页和详情页。
- 加入管理端与 MySQL,替换本地 MDX 作为业务数据源。
- 接入可配置模型和流式对话。
- 增加知识库、Worker 和混合检索。
- 增加用量记录与仪表盘。
- 最后实现隔离代码实验和生产部署。
每个阶段都必须能从界面走通,而不是只完成底层类和接口。这样的顺序让问题更早暴露,例如中文 slug 重复编码、异步 ORM 懒加载和 SSE 中断处理,都在真实页面流程中被发现。
阶段验收
- 首页严格按发布时间展示文章,推荐内容不打乱时间线。
- 后台新增草稿后可预览和发布。
- 浏览器只能通过 Next.js 访问业务 API。
- MySQL 数据和 Qdrant 索引都能备份和恢复。
- 没有 API Key、数据库密码或 SSH 私钥进入 Git。
下一篇会进入管理端,看看 Markdown 写作、模型密钥和动态仪表盘是怎样连接起来的。