项目名称 / Project Name

Personal Blog — 全栈个人博客系统 项目Wiki

简介 / Summary

一个集博客写作、AI 聊天助手、RAG 知识库、实时金融看板、多源信息观测站于一体的全栈个人知识管理平台。

技术栈 / Tech Stack

  • FastAPI
  • SQLAlchemy
  • Jinja2
  • Alpine.js
  • ChromaDB
  • SQLite
  • Redis

详细介绍 / Details

一、项目介绍

1.1 项目定位

Personal Blog 是一个面向技术从业者的全栈个人博客系统,定位为"个人数字基地"——既是输出内容(博客文章)的平台,也是输入信息(资讯聚合、AI 问答)的枢纽。它不是一个简单的博客框架,而是一个集成了多种工具的个人工作台。

1.2 核心理念

  • All-in-One — 博客写作 + AI 助手 + 金融看板 + 资讯聚合,一站式满足日常需求
  • 服务端渲染优先 — 页面首屏快速加载,前端 Alpine.js 做交互增强(非 SPA)
  • 开箱即用 — SQLite + 本地嵌入模型,无需外部服务即可运行核心功能
  • 渐进增强 — Redis 可选、LLM 可切换、嵌入模型本地运行,组件解耦可独立升级

1.3 适用场景

  • 个人技术博客写作与发布
  • AI 面试辅导(RAG 增强的知识问答)
  • 金融市场追踪(黄金、A 股指数)
  • 多源信息聚合(ArXiv、GitHub Trending、知乎热榜等)
  • 个人知识库管理

二、功能总览

2.1 功能矩阵

功能模块 子功能 说明
博客系统 文章 CRUD Markdown 写作,服务端渲染为 HTML
分类/标签 双维度内容组织
Markdown 批量导入 解析 YAML frontmatter,自动建标签、分类
RSS / Sitemap 订阅与 SEO
访问统计 PV/UV 按日追踪
AI 聊天 多会话管理 创建、重命名、删除对话
SSE 流式响应 逐 token 推送到前端,打字机效果
RAG 检索增强 从本地知识库检索后由 LLM 回答
多 LLM 支持 DeepSeek / OpenAI / Ollama / 自定义
面试助手 角色随机出题 5 个技术角色
答案自动评估 LLM 评分与参考答案
错题本 自动记录错误,支持复习
实时金融 黄金价格 多 API 源 + 每 30 秒刷新 + Redis 缓存
A 股指数 新浪财经接口 + MA/RSI 技术指标
ECharts 图表 历史价格走势折线图
自选股管理 数据库持久化
世界观测站 信息聚合仪表板 AI 前沿、GitHub 热榜、实时热点
多源采集 RSS / JSON API / 爬虫 三种策略
LLM 摘要 AI 资讯一键自动总结
逐级配置 栏目 → 信源 → 开关 + 数量上限
管理后台 概览面板 统计卡片 + ECharts 趋势图
页面内容管理 首页/观测站/项目三个页面的区块编辑器
站点设置 Hero 文案、联系方式、价值观等

2.2 页面清单

路径 页面 功能
/ 首页 Hero 区 + 精选项目 + 最新文章 + 市场快照 + 新闻速览
/blog 博客列表 分类导航 + 文章列表 + 标签云 + 日历
/article/{id} 文章详情 正文 + 目录 + 相关文章 + 评论
/world 观测站 区块信息聚合(精选/前沿/热点/引语)
/finance 金融看板 ECharts 黄金 + 股票图表
/projects 项目列表 项目卡片网格
/chat AI 聊天 侧栏会话列表 + 主聊天区,SSE 流式
/dashboard 管理后台 侧栏导航 + 多面板 SPA
/tools 工具页 工具索引

三、技术栈

3.1 技术选型

层次 选型 理由
Web 框架 FastAPI 0.115 异步原生、性能好、自动 OpenAPI 文档
ASGI 服务 Uvicorn 0.34 FastAPI 官方推荐,支持热重载
ORM SQLAlchemy 2.0 成熟稳定,迁移方便
数据库 SQLite(默认) 零配置,适合个人博客;可切换 PostgreSQL
模板引擎 Jinja2 FastAPI 内置,语法强大
前端交互 Alpine.js 轻量(15KB),声明式,渐进增强
数据可视化 ECharts 5.5 功能强大,中文文档好
LLM 客户端 OpenAI SDK 1.30+ 兼容 DeepSeek/Ollama 等
向量数据库 ChromaDB 0.5+ 嵌入式,零依赖,支持持久化
嵌入模型 BAAI/bge-small-zh 中文效果好,轻量(33MB)
缓存 Redis 7(可选) 自动降级到内存字典
定时任务 APScheduler 3.10 进程内调度,无需外部依赖
部署 Docker Compose 一键部署,依赖管理

3.2 架构设计亮点

SSR + Alpine.js 混合模式: 项目有意避免了全 SPA 方案,采用服务端渲染为主、前端增强为辅的架构。页面首屏 HTML 由 Jinja2 在服务端填充数据生成,直接返回完整内容。Alpine.js 仅对需要交互的部分(聊天 SSE、管理后台编辑器、观测站配置面板)发挥作用。好处是首屏快、SEO 好、不需要前端构建工具链。

自动降级设计: 项目多处实现了自动降级,保证核心功能不依赖外部服务:

Redis 不可用 → 自动降级为内存字典缓存(无感)
LLM API 失败 → 聊天功能报错,博客核心不受影响
黄金/股票源超时 → 自动切换到备用 API
嵌入模型加载失败 → RAG 降级为纯 LLM 问答

四、系统架构

4.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                        浏览器端                               │
│  Jinja2 HTML (服务端渲染) + Alpine.js (交互增强)              │
│  ECharts (图表) + Font Awesome (图标)                        │
└──────────────────────────┬──────────────────────────────────┘
                           │ HTTP / SSE
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     FastAPI 应用层                            │
│  ┌─────────┐  ┌──────────┐  ┌────────┐  ┌──────────────┐   │
│  │ pages   │  │ api /    │  │ chat   │  │ admin /      │   │
│  │ (页面路由)│  │ market   │  │ (SSE流)│  │ admin_page   │   │
│  └────┬────┘  └────┬─────┘  └────┬───┘  └──────┬───────┘   │
│       │             │             │              │           │
│  ┌────▼─────────────▼─────────────▼──────────────▼───────┐   │
│  │              服务层 / 核心层                             │   │
│  │  agent.py │ md_importer.py │ auth.py │ i18n.py        │   │
│  │  cache.py │ page_manager   │ config.py                 │   │
│  └───────────────────────────────────────────────────────┘   │
│  ┌────────────────────┐  ┌──────────────────────────────┐   │
│  │  RAG 引擎           │  │ 定时任务调度器                │   │
│  │  ingestion.py      │  │ gold_service.py              │   │
│  │  embedding.py      │  │ stock_service.py             │   │
│  │  vector_store.py   │  │ news_service.py              │   │
│  │  retriever.py      │  │ scheduler.py                 │   │
│  └────────┬───────────┘  └────────┬─────────────────────┘   │
└───────────┼───────────────────────┼──────────────────────────┘
            │                       │
            ▼                       ▼
┌────────────────────┐  ┌──────────────────────────────┐
│   数据持久化         │  │   外部服务                    │
│  SQLite (blog.db)  │  │  DeepSeek / OpenAI / Ollama  │
│  ChromaDB (向量库)  │  │  新浪财经 API                │
│  Redis (缓存,可选)  │  │  gold-api.com               │
│  /data/chroma/     │  │  RSS 订阅源 × 14+            │
└────────────────────┘  └──────────────────────────────┘

4.2 请求流转

公开 HTML 页面:Jinja2 模板渲染 → 返回完整 HTML
JSON API:   路由处理 → 数据库查询 → JSON 响应
SSE 流式:   创建会话 → RAG 检索 → LLM 流式生成 → SSE 推送到前端
页面区块:   Alpine fetch → JSON → 前端渲染

五、模块详解

5.1 博客系统

文章模型采用扁平分类 + 多对多标签的组织方式。分类是文章上的字符串字段,存储在 SiteSetting.blog_categories(JSON 数组)中;标签是独立表,通过关联表实现 M2M。分类数量少且稳定(<20),适合粗粒度分组;标签数量不限,适合细粒度标记。

Markdown 导入引擎md_importer.py)解析 YAML frontmatter:
- 用 --- 分界解析 frontmatter
- 无 frontmatter 时从 # 标题推导 title
- 标题去重检测,标签自动创建
- 支持单文件上传 / 目录批量导入

渲染管线: 编辑器预览和详情页渲染使用同一份 Python 代码(markdown 库 + fenced_code/tables/toc/nl2br 扩展),保证所见即所得。详情页额外处理阅读时间估算(中英文混合计数)和相关文章推荐(标签匹配度排序)。

访问统计: PageVisit 表按 (date, section, path) 记录访问,ip_hash 用于 UV 去重。PV = 所有记录求和,UV = 按日期+ip_hash 去重计数。

5.2 AI 聊天 & RAG

整体流程:

用户输入 → 创建/选择会话 → RAG 检索 → 构造 Prompt → LLM 流式生成 → 保存消息

RAG 检索管线(4 个文件,职责清晰):

ingestion.py — 智能分块,按 ### / #### / 段落 三级策略递进。每个 chunk 携带 source_filetopicsection 等元数据。

embedding.py — 惰性加载,首次调用时才下载/加载 BAAI/bge-small-zh 模型(33MB),CPU 推理一次不到 100ms。

vector_store.py — ChromaDB 封装,HNSW 索引 + 余弦距离,支持按 metadata 过滤。

retriever.py — 检索结果格式化为带编号引用的 LLM 上下文,同时提取 sources 元数据供前端展示。

SSE 流式响应: 聊天 API 使用 Server-Sent Events 实现打字机效果,前端使用 EventSource 或 fetch + ReadableStream 消费。

面试助手: 聊天功能的垂直应用,在 RAG 知识库上加了一层角色管理。5 个预置角色(前端/后端/算法/AI/数据),每个角色有独立的问题生成提示词和评估标准。错题本(WrongAnswer)和面试笔记(InterviewNote)支持复习。

LLM 客户端: 使用 OpenAI SDK 的统一接口,通过修改 base_urlmodel 参数即可切换 Provider:

config = {
    "deepseek": {"base_url": "https://api.deepseek.com", "model": "deepseek-chat"},
    "openai":   {"base_url": "https://api.openai.com/v1", "model": "gpt-4o"},
    "ollama":   {"base_url": "http://localhost:11434/v1", "model": "qwen2.5:7b"},
}

5.3 实时金融

定时任务架构(APScheduler):

黄金: 每 30 秒 → gold-api.com/metals.live → Redis(30s TTL)
股票: 每 30 秒 → 新浪财经 API → Redis(60s TTL) → MA/RSI 计算
新闻: 每小时 → 14+ RSS/API 源 → LLM 摘要 → 分类存储

多源降级: 黄金服务配置了多个数据源,按优先级依次尝试,第一个成功的数据源即返回,全部失败则返回空值,保证服务不因单个 API 不可用而崩溃。

技术指标: 股票服务自动计算 MA5/10/20/60 移动均线和 RSI14 相对强弱指数。

新闻聚合: 支持 3 种采集策略(RSS / JSON API / 自定义爬虫),覆盖 14+ 信源。通过 LLM 为每条新闻生成一句话摘要,关键词匹配分类。内置健康状态追踪,可在管理后台查看每个信源的采集成功率。

5.4 世界观测站

观测站是信息聚合仪表板,管理后台的逐级配置体系是其核心设计:

栏目 (block) 级别: max_items(总上限) + per_source_limit(单信源上限)
  └─ 信源 (source) 级别: max_items(贡献上限) + enabled(启停开关)

观测站的内容遵循"人工编辑优先"的同步策略:自动采集数据不会覆盖管理员手动添加的精选条目。

5.5 管理后台

页面内容管理框架(PageManager): 一个全局单例的区块注册系统,实现了页面内容的可配置化。注册区块后,管理后台自动生成编辑器,数据保存到 SiteSetting 表(万能键值表),前台通过 API 读取展示。

已有的区块注册(16 个区块横跨 4 个页面):

页面 区块 用途
world slogan / weekly_picks / quotes 观测站标语、精选、引语
home hero_text / contacts / market_config / featured_projects 首页内容
blog blog_intro 博客页简介
projects project_list 项目列表

管理后台认证采用三态逻辑:开发模式自动授权(ADMIN_BY_DEFAULT=true),未配置凭据时禁止登录,Session 认证。前端通过 base.html 中的 is_admin 变量控制管理入口的可见性。

管理后台是一个 Alpine.js 驱动的 SPA,侧栏导航切换不同的面板视图(x-show="tab==='xxx'"),无路由切换。编辑区块内容时的粘底保存栏确保数据不会意外丢失。

5.6 缓存模块

cache.py 实现了 Redis 优先、内存降级的双模缓存:

# 核心设计:按需连接 Redis,连接失败时静默降级
class Cache:
    def __init__(self):
        self._redis = None
        self._memory = {}  # 降级方案

缓存使用分布:

数据 TTL 说明
黄金价格 30s 高频刷新
黄金历史 无(列表追加) 保留最近 500 条
股票行情 60s 低频刷新
AI 资讯 2h 长时间有效
页面区块 不缓存 数据库直读

5.7 国际化

200+ 翻译 key,覆盖导航、编辑器、博客、观测站、管理后台、评论等功能。语言选择通过 URL 参数 ?lang=en 切换,存储在 Cookie 中持久化。

# 轻量级内联翻译,无外部 i18n 框架
TRANSLATIONS = {
    "zh": { "nav_home": "首页", "write_article": "写文章", ... },
    "en": { "nav_home": "Home", "write_article": "Write", ... },
}

六、数据模型

实体关系

Article ──→ article_tags ←── Tag        ChatSession ──→ ChatMessage
   │                                            SiteSetting (KV 存储)
   ├── Comment                                  Watchlist / PriceAlert
   └── PageVisit                                WrongAnswer / InterviewNote

核心表说明

articles(文章): title, content(Markdown), category(扁平分类), is_published, is_featured, view_count, tags(M2M)

site_settings(站点设置 — 万能键值表): key(唯一主键), value(JSON 字符串)。存储内容:hero 文案、分类列表、页面区块数据、观测站配置等。

page_visits(访问统计): date, section(板块), path, ip_hash(UV 去重)

chat_messages(聊天消息): session_id(FK), role(user/assistant), content, sources(JSON,RAG 来源)


七、部署运维

Docker Compose 部署

项目使用 Docker Compose 管理两个服务:blog(FastAPI + Uvicorn)和 redis(可选)。

services:
  redis:   # 可选,无 Redis 时自动降级
  blog:    # 核心服务,端口 8000
    volumes:
      - ./data:/app/data       # ChromaDB 持久化
      - ./blog.db:/app/blog.db # SQLite 数据库

环境变量

变量 说明
ADMIN_PASSWORD 管理员密码(生产必须修改)
SITE_URL 站点 URL(影响 RSS/Sitemap)
LLM_PROVIDER deepseek / openai / ollama / custom
DEEPSEEK_API_KEY DeepSeek API 密钥
DATABASE_URL 数据库连接(默认 SQLite,可切换 PostgreSQL)

数据备份

需要备份的数据:
- blog.db — SQLite 数据库(核心数据)
- data/chroma/ — ChromaDB 向量数据
- app/static/uploads/ — 上传文件
- .env — 环境配置


八、踩坑与经验教训

前端类

Alpine.js 大括号不配对导致整个组件不初始化: 编辑观测站配置时,所有 Alpine.js 组件停止响应,x-show/x-model 全部失效。定位后发现是模板中某处 }} 多写了一个花括号,导致 Alpine 解析 template 语法时报错。Jinja2 的 {{ }} 和 Alpine.js 的 x-data 混用时尤其要注意大括号配对。

管理后台大模板的可维护性: dashboard.html 约 1800 行,是一个巨大的单文件 Alpine.js SPA。随着功能增加,文件越来越难维护。建议如果继续扩展,考虑拆分为多个模板片段用 {% include %} 组合。

CSS 缓存: 修改 style.css 后浏览器不更新。解决方案是在 base.html 中引用时带版本号 ?v=N

后端类

SQLite 并发写冲突: 定时任务每 30 秒写一次数据库,与用户操作同时发生时可能出现 database is locked。解决方案:SQLAlchemy 连接池设置 check_same_thread=False,定时任务使用独立会话,缓存层减少落盘频率。

APScheduler + asyncio 混用: 定时任务使用 ThreadPoolExecutor 执行异步函数,但 asyncio 事件循环在另一个线程中。使用 asyncio.run() 包装异步函数解决。

自动采集数据覆盖人工编辑内容: 观测站的每周精选板块,管理员手动添加的内容在下一轮自动采集后被覆盖。后来引入"固化"(pinned)标记,人工编辑的内容标记为 pinned: true,自动更新时跳过。

RAG 文档块大小不合理: 初始设置最大 chunk 为 3000 字符,LLM 上下文窗口中有效信息密度低。将 max_chunk_chars 从 3000 调整到 1200,按 ### / #### / 段落 三级分块,保证每个 chunk 信息聚焦。

部署类

Docker 时区问题: 容器内默认 UTC 时区,导致定时任务和北京时间差 8 小时。在 Docker 和 docker-compose 中设置 TZ=Asia/Shanghai

国内网络环境下的 Docker 构建: Dockerfile 中配置了清华/阿里云镜像源加速 apt 和 pip,HuggingFace 使用 hf-mirror.com 镜像站,Playwright Chromium 使用 npmmirror.com 镜像。

数据持久化挂载:docker-compose.yml 中挂载 ./data./blog.db 保证容器重启后数据不丢失。


九、开发指南

搭建开发环境

cd personal_blog
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env                  # 按需修改
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

添加新功能流程

  1. 数据层:在 models.py 添加新模型 → 启动时自动建表
  2. 业务层:在 routes/ 下添加或扩展现有路由
  3. 展示层:在 templates/ 中创建新模板或修改现有模板
  4. 可配置内容:在 page_manager.py 注册新区块 → dashboard.html 添加编辑器面板

项目结构

personal_blog/
├── app/
│   ├── main.py              # FastAPI 入口
│   ├── config.py            # 配置加载
│   ├── database.py          # 数据库 & 自动迁移
│   ├── models.py            # 15 个 ORM 模型
│   ├── admin/page_manager   # 页面区块框架
│   ├── agent/               # LLM 客户端
│   ├── rag/                 # RAG 引擎
│   ├── routes/              # 9 个路由模块
│   ├── services/            # 4 个后台服务
│   ├── static/              # CSS / JS / 上传
│   └── templates/           # 15 个 Jinja2 模板
├── data/chroma/             # 向量数据库
└── Dockerfile / docker-compose.yml

附:版本历史

阶段 功能 说明
v1.0 博客系统 文章 CRUD + 分类标签 + Markdown
v1.1 管理后台 Alpine.js SPA 后台面板
v1.2 国际化 zh/en 双语支持
v1.3 AI 聊天 LLM + SSE 流式
v1.4 RAG 知识库 ChromaDB + BGE 嵌入
v1.5 金融看板 黄金 + 股票 + ECharts
v1.6 面试助手 角色出题 + 自动评估
v1.7 世界观测站 多源信息聚合
v1.8 页面内容框架 PageManager 区块系统
v1.9 Docker 部署 容器化 + 部署脚本
v2.0 数据一致性重构 Category 归一化 + 全项目多路径写入审计 + 防御体系文档

相关文档: 多路径写入一致性:从一次 Debug 到系统性防御 — 完整记录分类数据不一致问题的排查、修复与防御体系。