在上一篇博客中,我们分享了如何通过 OAuth2、Graph API 彻底穿透海内外主流邮箱的风控屏障,安全地将账单、通知邮件拉入 AI 代理的本地存储中。
但随着数据滚雪球般增长——不仅有每天抓取的各类账单邮件,还有从 MongoDB 数据库中持续沉淀的 27 万篇 RSS 互联网资讯,一个棘手的问题浮出水面:
“如何在一瞬间找到我上个月的外币信用卡账单,或者检索出某篇被淹没的 AI 技术资讯?”
并且,如何安全、优雅地把指令发送给机器人执行,并实时看到机器人的输出?
为此,我们再次对架构进行了升级,在 lite_agent 中深度集成了开源轻量级搜索引擎 Meilisearch,并手搓了一个极致美学的零构建暗黑玻璃态(Dark Glassmorphism)检索与中枢控制台。
本文将向大家盘点本次架构打通的硬核技术细节:数据增量同步、双层安全网关、Nginx 极窄只读注入、以及 SSE 流式任务终端的构建。
🏗️ 整体架构设计
在个人数据的检索上,我们坚决反对“粗暴的暴露”。整个中枢控制台的调用与防线可以用如下架构图表示:
用户 (浏览器) ──( HTTPS + Basic Auth )──> [ Nginx 网关 ]
│
┌────────────────────────────┴───────────────────────────┐
▼ (只读 Search Key 注入) ▼ (API Token 注入)
/meili/* ──> [ Meilisearch ] /agent/* ──> [ Lite Agent API ]
(只读全文检索与数据量统计) (流式命令执行 & SSE 日志流)
⚡ 性能调优:增量同步与大包分批(Payload Batching)
我们的数据分散在 SQLite(邮件)和 MongoDB(RSS 资讯)中。要将它们同步给 Meilisearch,我们需要解决性能与稳定性的双重问题。
1. SQLite + MongoDB 增量状态同步
我们开发了独立的同步技能 ops_meili_sync。为了防范反复扫信导致的 CPU 飙升与网络带宽浪费,我们设计了基于 meili_sync_state.json 的增量同步机制:
- 对于 SQLite,通过主键
uid+fetched_at时间戳增量拉取新邮件,并进行category(分类)和sender(发件人)的多表联合查询(JOIN)同步。 - 对于 MongoDB,由于采用了按月归档集合的设计(例如
FeedItem_202607),同步程序自动计算当前月和上个月的动态集合,并利用 ObjectId 递增比对过滤,实现无感知的增量抓取。 - 定时任务集成:我们在 Agent 的 Cron 引擎中注册了定时器,每 10 分钟自动在后台拉取更新,实现数据源的源源不断。
2. 避免“413 Payload Too Large”:数据分批设计
在第一次进行 27 万条 RSS 历史文章全量同步时,最初由于将全部数据打包在一个 HTTP POST 中发送,直接触发了 Meilisearch 的单次负载限制,返回了 413 Payload Too Large。
我们迅速将同步策略重构为 200 篇分批并发推送(Batching):
# 核心分批逻辑
batch_size = 200
for i in range(0, len(docs), batch_size):
batch = docs[i : i + batch_size]
res = _meili_request("/indexes/rss/documents", "POST", batch)
if res:
rss_count += len(batch)
state["last_rss_sync"] = batch[-1]["fetched_at"]
这样不仅极其省内存,在遇到网络波动时还能实现“断点续传”。
🔒 网关安全:Nginx 极窄权限注入模型
把搜索引擎和 Agent API 暴露给前端静态网页,最大的安全隐患莫过于 Master Key 泄露。一旦主密钥暴露在前端 JavaScript 中,任何恶意访客都能直接删光你的整个索引库。
为了杜绝这一隐患,我们设计了三重网关防线:
防线 1:全站 Basic Auth
整个虚拟主机部署在 mail.maifeipin.com 下,由 Nginx 的 auth_basic 在最外层直接拦截,未授权用户甚至无法加载页面的一张图片、一个 JS 字节。
防线 2:只读 API 密钥注入
在配置 Meilisearch 代理时,Nginx 在反向代理层隐式将只读的 Search Key 注入到 Request Header 中。前端 JavaScript 甚至感知不到 Key 的存在,从而彻底阻断了密钥通过前端泄露的可能性:
location /meili/ {
proxy_pass http://127.0.0.1:7700/;
# 代理层注入 Meilisearch 只读 Search Key,前端完全无感
proxy_set_header Authorization "Bearer f404afbf2e397f28...d1da8dd62f011b9d";
}
防线 3:极窄 stats 权限通道 (Master Key 动态提权)
只读 Key 固然安全,但它只能调用 /search 检索接口,无权调用 /stats 统计接口。这会导致页面左侧的数据源数量无法显示真实总量(空查询时只能拿到 /search 的 1000 条默认上限估算)。
为此,我们设计了一条极其精细的极窄 stats 提权通道:
# 仅当访问 stats 统计接口时,才在 Nginx 内部用 Master Key 进行提权转发
location ~ ^/meili/indexes/([^/]+)/stats$ {
proxy_pass http://127.0.0.1:7700/indexes/$1/stats;
proxy_set_header Authorization "Bearer MeiliUnifiedSearchSecureKey...Awesome";
}
由于 /stats 是只读元数据接口,不涉及任何文档详情,也不支持写入/删除,这样的定向注入实现了“既能显示真实总数,又防范了任何越权操作”的精美平衡。
💻 前端极致体验:零构建 SPA 与 SSE 长连接控制台
在前端呈现上,我们坚持不引入任何构建工具(如 Vite, Next.js),用原生的 HTML, CSS 和 JS 实现了一个零依赖的单页应用(SPA)。
1. Spotlight 式智能命令面板 (Command Palette)
在网页中敲击 /(斜杠)即可呼出 Spotlight 命令弹窗。我们设计了按键 autocomplete 状态填充联动:
- 键盘上下方向键导航时,当前项的命令(如
/ops_backup_data或/reprocess)会自动实时填充并同步进输入框,支持就地进行参数编辑(如/reprocess uid=120)再敲击回车。 - 鼠标悬停(
mouseenter)自动跟随机键盘高亮焦点,体验如丝般顺滑。
2. 拟物化终端与 SSE 实时日志流
当你通过命令面板发送异步任务(如数据云备份)时,网页右下角会自动弹出一个拟物化设计的黑色 Console 控制台:
- 前端通过监听 Agent 的
/api/v1/tasks/{task_id}/stream,建立起持久的 SSE (Server-Sent Events) 连接。 - 后台执行的日志通过 SSE 实时在终端里逐行打出,仿佛直接在服务器上跑 Shell 脚本一样亲切。
- 为了确保流式输出零延迟,我们在 Nginx 代理上配置了
proxy_buffering off,防止响应被 Nginx 缓冲区拦截造成卡顿。
🔮 后续规划:更庞大的个人知识网
邮件和 RSS 仅仅是个人信息数字孤岛的第一步。接下来,我们计划继续扩大 Meilisearch 与 AI Agent 的索引版图:
- HedgeDoc 与云笔记互通(搭建个人知识库):
- 我们计划打通 HedgeDoc(Markdown 协作文档)与私有云笔记系统。通过 Webhook 实时捕获文档的创建与修改,自动将其推送到 Meilisearch 建立索引。
- 挂载 LLM 对半结构化的 MD 笔记进行实体提取与语义标签化,让你能够一键搜索所有关于“架构设计”或“会议纪要”的碎片化记忆。
- NAS 本地媒体库与智能检索:
- 接入家中 NAS 媒体服务(如 Emby/Jellyfin/Plex)的元数据,甚至是照片库的 EXIF 属性。
- 届时,你只需在微信端输入“帮我找找去年秋天拍的猫咪照片”或“检索所有 4K 分辨率的科幻电影”,Agent 就能瞬间定位本地文件,完成智能多模态调度。
🚀 结语
通过将 Meilisearch 的全文检索 和 Lite Agent 的指令下发 熔铸于一体,我们不再只是拥有一堆冷冰冰的数据库,而是真正搭建了一个随身携带、绝对安全的数据和命令检索中枢。
如果您也想摆脱杂乱无章的账单与信息孤岛,不妨试试把搜索引擎挂载到你的 AI Agent 上!
Enjoy hacking with Meilisearch, Agent & Nginx!
