用 Codex 开发阿延哥知识库:从旧博客到可演进系统

记录阿延哥知识库从旧 VitePress 博客迁移到 FastAPI + Vue + Docker 系统的开发过程,以及如何使用 Codex 协作开发。

AICodex知识库开发过程

用 Codex 开发阿延哥知识库:从旧博客到可演进系统

这套“阿延哥知识库”不是简单把旧博客换一套皮肤,而是一次从静态内容站点到可维护知识系统的重建。项目保留了个人博客最重要的能力:文章、分类、固定页面、搜索和访问体验,同时把后台管理、内容导入、向量索引、Docker 部署这些后续会持续使用的能力补齐。

整个系统采用单仓库管理,前后端分目录演进:

ayan-knowledge/
  backend/      FastAPI、SQLAlchemy、PostgreSQL、pgvector、导入脚本和后台管理
  frontend/     Vue 3、Vite、Vue Router、站点页面和用户侧交互
  deploy/       Docker Compose、Nginx、Caddy、腾讯云镜像发布脚本
  docs/         运行、迁移、部署和开发过程文档

这种结构很适合个人知识库项目。数据模型、导入脚本、接口、前端页面、部署配置可以在同一个提交节奏里一起调整,不需要在多个仓库之间来回同步。

一、先定目标:不是做页面,而是重建内容系统

项目初始目标很清楚:把原来的 VitePress 博客迁移成一个更容易扩展的知识库系统。

第一阶段解决基础站点能力:

  • 首页:个人介绍、头像、分类入口、最新文章。
  • 文章系统:列表页、详情页、分类页、标签和发布时间。
  • 固定页面:简历页、关于我页。
  • 后台管理:文章新增、编辑、预览、发布、草稿状态。
  • 搜索能力:关键词搜索和向量语义搜索。
  • 内容迁移:从 VitePress Markdown 或线上站点导入数据库。

第二阶段再扩展 AI 能力,例如“问我博客内容”、相似文章推荐、自动摘要、自动标签和简历问答。这样做的好处是先把内容资产和发布链路稳定下来,再逐步增强智能能力。

二、后端开发:用 FastAPI 承载内容、管理和搜索

后端使用 FastAPI + SQLAlchemy。核心入口在 backend/app/main.py,启动时根据配置初始化数据库和种子数据,然后挂载三类路由:

  • /api:前端使用的 JSON API。
  • /admin:后台管理页面。
  • 公开页面路由:兼容旧站文章、简历和关于我等路径。

数据模型集中在 backend/app/models/content.py,主要包括:

  • Article:文章主体,保存标题、slug、摘要、Markdown、HTML、状态、发布时间。
  • Category 和 Tag:分类与标签。
  • Page:首页、简历、关于我等固定页面。
  • ContentChunk:文章或页面切片。
  • Embedding:每个切片对应的向量。
  • SiteSetting:站点标题、备案号、导航、页脚等配置。
  • AdminUser:后台登录用户。

这里有一个关键设计:开发环境可以用 SQLite 快速启动,生产环境使用 PostgreSQL + pgvector。模型里通过配置判断当前数据库类型,向量字段在 PostgreSQL 中使用 pgvector,在 SQLite 中用 JSON 保存。这让本地开发足够轻,部署时又能切到更正式的向量搜索能力。

API 主要围绕站点和内容展开:

  • GET /api/site:获取站点信息、导航和分类。
  • GET /api/articles:获取文章列表,可按分类筛选。
  • GET /api/articles/{slug}:获取文章详情。
  • GET /api/pages/{slug}:获取固定页面。
  • GET /api/categories:获取分类列表。
  • GET /api/search?q=...:搜索文章和页面。
  • POST /api/articles:受保护的文章写入接口。

搜索服务放在 backend/app/services/search.py。它先把查询文本转成向量,如果生产环境启用了数据库向量搜索,就优先使用 pgvector;否则回退到应用层计算相似度。同时系统还叠加了词面匹配分数:标题、标题路径和正文中出现关键词都会加权。这样即使没有外部 AI embedding 服务,本地 MVP 也能得到可用的搜索体验。

当前的 embedding 实现在 backend/app/services/embeddings.py,使用确定性的 hash embedding。它的价值不在于“效果最好”,而在于不依赖外部 API、结果稳定、适合测试和本地开发。后面接入真实 embedding 模型时,只需要替换 provider,而不需要重写内容切片、索引和搜索流程。

三、内容迁移:把旧 VitePress 内容变成数据库资产

个人站点最重要的是内容。这个项目没有手工复制旧文章,而是开发了导入脚本,把迁移变成可重复执行的流程。

导入逻辑在 backend/app/services/importer.py,入口脚本是:

cd backend
python scripts/import_vitepress.py --source 'D:\Project\vs code\my-vitepress-blog\blog'

导入流程主要做了几件事:

  1. 扫描 VitePress Markdown 文件。
  2. 解析 frontmatter,读取标题、描述、标签和发布时间。
  3. 根据路径识别文章、首页、简历和关于我页面。
  4. 将 Markdown 渲染成 HTML。
  5. 写入文章、分类、标签和固定页面。
  6. 对内容切片并生成向量记录。

这次迁移已经完成了 18 篇文章、3 个固定页面、285 个内容切片和 285 条向量记录。静态资源也迁移到 frontend/public,包括头像、站点图标、分类 SVG、备案图标、二维码和统计脚本。

这种迁移方式比一次性手工搬运更稳:以后如果旧内容调整,或者需要重新导入 PostgreSQL 环境,只要重新执行脚本即可。

四、前端开发:Vue 负责用户侧体验

前端使用 Vue 3 + Vite + Vue Router。入口在 frontend/src/main.ts,主要路由包括:

  • /:首页。
  • /posts:全部文章。
  • /posts/:categorySlug.html:分类文章页。
  • /articles/:slug:文章详情页。
  • /resume.html:简历。
  • /about-me.html:关于我。
  • /search:搜索页。

接口封装在 frontend/src/api.ts。前端不直接关心后端实现细节,只通过 fetchSite、fetchArticles、fetchArticle、fetchPage、fetchCategories 和 searchContent 获取数据。

首页 HomeView.vue 做了三件事:展示个人主视觉、展示分类入口、展示最新文章。文章卡片被抽成 ArticleCard.vue,便于列表页和首页复用。搜索页 SearchView.vue 则通过 URL query 保存搜索词,用户刷新或分享链接时仍能保留搜索状态。

开发环境下,Vite 会把 /api 请求代理到后端 http://127.0.0.1:8000,所以本地可以前后端分开启动:

cd backend
$env:AYAN_DATABASE_URL='sqlite:///./dev_ayan.db'
$env:AYAN_CREATE_TABLES_ON_STARTUP='true'
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
cd frontend
npm run dev

访问 http://127.0.0.1:5173 就可以看到前端站点。

五、部署设计:本地能跑,服务器也能稳定发布

部署目录里提供了两套 Compose 配置。

本地 Docker 运行使用 deploy/docker-compose.yml,它会从源码构建后端和前端镜像,并启动 PostgreSQL + pgvector:

cd deploy
docker compose up --build -d

生产部署使用 deploy/docker-compose.prod.yml,服务器不再现场构建镜像,而是从腾讯云容器镜像服务拉取:

ccr.ccs.tencentyun.com/wuyy/ayan-knowledge-backend:latest
ccr.ccs.tencentyun.com/wuyy/ayan-knowledge-frontend:latest

对外入口使用 Caddy,负责 HTTPS 和反向代理:

  • /api、/admin、/static 转发到后端。
  • 其他路径转发到前端。
  • 生产域名是 https://www.wuyingyan.com。

发布流程也被脚本化了。本地修改代码后执行:

.\deploy\push-tencent-images.ps1 -SkipLogin

服务器再拉取新镜像并重启服务:

docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

这让一次发布从“记一堆命令”变成了固定流程。

六、如何用 Codex 开发这个系统

这个项目很适合用 Codex 协作开发,因为它不是单点脚本,而是一个包含后端、前端、数据迁移、部署文档的完整工程。Codex 在这里的价值主要体现在四个方面。

第一,先读项目,再改代码。每次开发新功能前,先让 Codex 查看相关文件。例如做前端页面时,先定位组件、数据接口和样式文件;做后端接口时,先看模型、schema、router 和 service。这样它能复用项目已有模式,而不是另起一套风格。

第二,把需求拆成可验证的小步骤。比如“增加搜索功能”不是直接写一个搜索框,而是拆成内容切片、向量生成、搜索服务、API、前端搜索页、结果展示和构建验证。每一步都有明确的输入输出,出问题时也容易定位。

第三,让 Codex 同时处理实现和文档。这个项目里的 docs/runbook.md、docs/docker-deploy.md、docs/tencent-cloud-images.md 就是非常重要的工程资产。代码能跑只是第一步,后续怎么启动、怎么迁移、怎么部署、怎么排错,也应该和代码一起维护。

第四,把验证命令固定下来。后端可以运行:

cd backend
python -m unittest discover -s tests

前端可以运行:

cd frontend
npm run build

Docker 可以运行:

cd deploy
docker compose up --build

让 Codex 修改完功能后执行这些检查,比只看代码更可靠。

和 Codex 协作时,比较有效的提问方式是具体描述目标、边界和验收标准。例如:

帮我给文章列表增加分页,复用当前 /api/articles 的 limit 和 offset 参数。
前端保持现有设计风格,修改后运行 npm run build。
不要改 frontend/dist。

或者:

帮我新增一个后台文章预览能力。
先看 admin router、template 和 markdown service,再按当前后台页面风格实现。
完成后说明改了哪些文件,以及如何验证。

这种描述能让 Codex 明确三件事:改什么、不要改什么、怎么证明改对了。

七、开发这个系统后的经验

这次重建最大的收获是:个人知识库也应该按系统来设计。

如果只追求页面展示,静态站点已经够用。但当内容越来越多,就会自然需要搜索、后台、迁移、标签、分类、相似推荐、问答和自动摘要。这些能力不是靠堆页面解决的,而是靠稳定的数据模型和清晰的工程结构解决的。

Codex 的角色也不是“替我写几段代码”,而是一个可以持续协作的工程助手。它可以读代码、整理方案、实现功能、补文档、跑验证、解释报错。前提是开发者要给出清楚的目标,并让它基于现有系统工作。

现在的阿延哥知识库已经具备了一个可演进系统的基础:内容可以导入,文章可以管理,前端可以浏览,搜索可以工作,Docker 可以部署。下一步最值得投入的是把本地 hash embedding 替换成真实 embedding 服务,并在现有 ContentChunk 和 Embedding 模型基础上继续扩展“问我博客内容”、相似文章推荐和自动摘要能力。

这也是这套系统的核心方向:先把个人内容沉淀成结构化资产,再用 AI 能力让这些内容更容易被检索、理解和复用。