本文系统化拆解了 StaticFlow 评论系统与 AI 自动回复能力的工程实现,覆盖从用户交互到异步执行再到可视化运维的完整链路。目标与问题定义以本地优先和隐私保护为前提,替代依赖第三方平台的评论方案。支持“选中文本评论”和“页脚通用评论”两种入口,兼顾精确语义锚定与通用讨论。在人工审核闭环下,引入可控的 AI 自动…
评论系统与 AI 回复全栈实现:从用户选中文本到 Codex 生成回复的完整链路 代码版本:基于 StaticFlow 当前 master 分支。1. 为什么要自建评论系统 个人博客接入 Disqus、Giscus 或 Gitalk,意味着把评论数据交给第三方平台,或者强制读者拥有 GitHub 账号。对于一个本地优先的知识管理系统来说,这些方案都不太合适:隐私:不想向第三方暴露读者的评论内容和浏览行为门槛:GitHub OAuth 登录对非技术读者是障碍AI 增强:希望每条评论都能获得基于文章内容的 AI 回复,第三方评论系统无法做到数据所有权:评论数据存储在本地 LanceDB,完全可控 StaticFlow 的设计目标是:匿名评论 + 管理员审核 + AI 自动回复。读者无需注册,提交评论后进入审核队列,管理员一键触发 AI 生成回复,审核通过后评论和 AI 回复同时发布。1.1 两种评论入口 系统支持两种评论方式: | 入口 | 触发方式 | 特点 | |------|---------|------| | 文本选择评论 (selection) | 选中文章段落后弹出评论按钮 | 精确锚定到段落,携带选中文本和上下文 | | 页脚评论 (footer) | 文章底部评论区直接输入 | 通用评论,支持引用已有评论 | 📌 本文范围:覆盖从前端评论交互、后端验证与审核、AI Worker 异步处理、Codex 调用链、输出解析、SSE 流式推送到前端实时展示的完整链路。不涉及文章内容管理和搜索功能。2. 数据流总览 一条评论从用户输入到最终展示,经过以下完整链路:graph LR A["用户选中文本<br/>或页脚输入"] --> B["WASM 前端<br/>POST /api/comments/submit"] B --> C["Axum 后端<br/>指纹 + 速率限制 + GeoIP"] C --> D["LanceDB<br/>comment_tasks 表"] D --> E["管理员审核<br/>approve-and-run"] E --> F["tokio mpsc 队列<br/>AI Worker"] F --> G["Runner Script<br/>Codex exec"] G --> H["输出解析<br/>多格式 JSON"] H --> I["comment_published<br/>评论发布"] I --> J["SSE 实时推送<br/>流式输出"] J --> K["前端展示<br/>Markdown 渲染"] classDef user fill:#d4edda,stroke:#28a745,color:#155724 classDef frontend fill:#cce5ff,stroke:#0d6efd,color:#084298 classDef backend fill:#e2d9f3,stroke:#6f42c1,color:#432874 classDef database fill:#d1ecf1,stroke:#0dcaf0,color:#055160 classDef ai fill:#fff3cd,stroke:#fd7e14,color:#664d03 classDef render fill:#d4edda,stroke:#198754,color:#0f5132 class A user class B,K frontend class C,E backend class D,I database class F,G,H ai class J render 整个系统涉及 5 张 LanceDB 表、一个 tokio mpsc 异步队列、一个外部 Codex 进程和 SSE 流式推送。下面逐层拆解。3. 数据模型设计 评论系统使用独立的 LanceDB 数据库(lancedb-comments),与文章内容数据库分离。共 5 张表,覆盖评论生命周期的每个阶段。3.1 五张表总览graph TB subgraph "comment_tasks" CT["评论任务队列<br/>状态机驱动"] end subgraph "comment_published" CP["已发布评论<br/>面向读者展示"] end subgraph "comment_audit_logs" CA["审计日志<br/>操作追溯"] end subgraph "comment_ai_runs" CR["AI 运行记录<br/>每次 Codex 调用"] end subgraph "comment_ai_run_chunks" CC["流式输出块<br/>stdout/stderr 逐行…