把“收件箱里的信息流”产品化:自动拉取最近 (N) 天邮件 → 本地规则筛掉明显噪音 → 用 LLM 生成可执行摘要 → 同步到 Notion,形成每日/每周可追踪的 Inbox Digest。
如果你在做个人效率工具、知识管理产品,或希望把“AI + 工程化落地”做成可长期使用的工具,这个项目更像一个可上线的 agent 化小产品,而不只是 demo。
- 产品化的信息呈现:Notion 内按“日期区间 Toggle(含有效邮件数)”组织;每封邮件标题可点击直达原文,并显示 From/At 元信息,方便直接回复处理。
- 确定性与稳定性:把“是否有效”的判定从 LLM 移到本地保守规则;LLM 只负责摘要,并用
temperature: 0固定生成,避免同一输入导致有效数波动。 - 多后端接入 & 降级策略:
- Outlook:OAuth2 Device Code Flow + Microsoft Graph(推荐)
- IMAP:作为兜底(含端口/TLS/连接重试策略)
- Notion:优先官方 MCP,失败自动回退到 Notion API(避免 MCP 启动失败导致写入中断)
- 工程化可维护:模块化(fetcher / summarizer / notion writer),配置集中在
.env,支持白名单、时间窗口等可控参数。
- Outlook(推荐):OAuth2 + Graph 拉取收件箱邮件,拿到
webLink,在 Notion 里直链原文 - Gmail(可选):Gmail API(OAuth2)或 IMAP
- 本地规则过滤:只排除“非常明确”的噪音(退订、明显促销、no-reply 验证码/系统通知、退信等)
- LLM 摘要(Qwen / OpenAI Compatible):对每封“已判定有效”的邮件生成 title + bullets
- Notion 写入:追加一个 Toggle 块(区间标题 + 有效数),内部每封邮件一个可点击条目 + bullets
flowchart LR
A[Email Sources\nOutlook Graph / IMAP / Gmail API] --> B[Fetch\nlast N days]
B --> C[Local filter\nobvious noise only]
C --> D[LLM summarize\n(deterministic)]
D --> E[Write to Notion\nMCP → API fallback]
“有效/无效”是灰度判断,模型会产生波动,导致同一批邮件两次运行有效数差异很大。这里用 可解释、可回归 的本地规则做过滤(只过滤非常确定的噪音),其余都保留,LLM 只做摘要,整体更稳定,更适合长期运行。
pnpm install复制示例并填写:
cp .env.example .env至少需要:
OUTLOOK_CLIENT_ID、OUTLOOK_REFRESH_TOKEN(Outlook OAuth2)OPENAI_API_KEY(以及可选的OPENAI_BASE_URL、MODEL_NAME)NOTION_TOKEN、NOTION_PAGE_IDEMAIL_DAYS(决定 Notion Toggle 的日期区间标题)
pnpm run outlook:auth按提示打开链接、输入设备码并授权。终端会输出 OUTLOOK_REFRESH_TOKEN=...,追加到 .env。
pnpm run start运行后会在 Notion 页面末尾追加一个 Toggle:
- 标题:
邮件摘要 YYYY-MM-DD ~ YYYY-MM-DD(有效 X)(区间由EMAIL_DAYS决定) - 内容:每封邮件一行“蓝色可点击标题 + From/At”,下面跟随 bullets
Notion 中的结构大致如下(示意):
▶ 邮件摘要 2026-03-11 ~ 2026-03-18(有效 7)
- [Blue Link] 项目评审会议时间调整 — From: boss@company.com · At: 2026-03-18T06:10:00Z
• 会议改到周四 16:00,参会人不变
• 需要在会前补充 PRD 风险点
- [Blue Link] 账单与发票 — From: billing@vendor.com · At: 2026-03-17T02:30:00Z
• 本月账单已生成,付款截止 3/25
• 如需开票请回复抬头信息
复现 demo 的最短路径:
pnpm install
cp .env.example .env
pnpm run outlook:auth # 首次获取 OUTLOOK_REFRESH_TOKEN
pnpm run start- 在 Notion 集成 新建集成,复制 key 到
NOTION_TOKEN - 在要写入的页面右上角「…」→ 连接到你的集成
- 从页面 URL 拿到
NOTION_PAGE_ID(32 位,可带-)
说明:运行时会尝试用 Notion MCP(
npx @notionhq/notion-mcp-server)。若你本机 npm cache 权限异常导致 MCP 启动失败,程序会自动回退到 Notion API 直写,保证“摘要写入”不被阻断。
| 变量 | 默认 | 说明 |
|---|---|---|
EMAIL_DAYS |
7 |
拉取最近 N 天;同时用于 Notion Toggle 日期区间命名 |
EMAIL_ALLOW_SENDERS |
空 | 发件人白名单(逗号分隔),用于收敛输入规模 |
MODEL_NAME |
qwen-turbo |
OpenAI Compatible 的模型名 |
REMINDER_CHANNELS |
console |
升级提醒通道(console,telegram,email 逗号分隔) |
EVAL_FN_MAX |
0.08 |
评估门禁允许的最大 FN 比例 |
console:默认通道,写到日志(用于本地调试)telegram:需要TELEGRAM_BOT_TOKEN、TELEGRAM_CHAT_IDemail:需要SMTP_HOST、SMTP_PORT、SMTP_USER、SMTP_PASS、REMINDER_EMAIL_TO
通道可并行启用,例如:
REMINDER_CHANNELS=telegram,emailpnpm run gate:quality该命令会执行:
pnpm test(单元测试)pnpm eval(在eval/samples.jsonl上计算 FN/FP/precision/recall)
当 fn_rate > EVAL_FN_MAX 时,pnpm eval 会返回非 0,阻断 CI。
src/
index.mjs # orchestration & title range naming
outlook-graph-fetcher.mjs # Outlook Graph fetch + webLink
outlook-oauth.mjs # OAuth2 refresh + device code helper
imap-fetcher.mjs # IMAP fallback + retries
summarizer.mjs # local filter + deterministic LLM summary
notion-client.mjs # Notion toggle writer (MCP → API fallback)
- 状态同步:把“是否已处理/是否需要回复”写回 Notion database,形成闭环
- 增量同步:记录 lastSync watermark,避免重复写入
- 可观测性:结构化日志 + tracing,把每次 run 的输入规模/过滤原因/写入结果沉淀成指标
- 前端展示:做一个 Dashboard(Next.js + Notion 数据库)展示趋势与待办队列
MIT