README
🚀 莉莉丝邮件系统
莉莉丝邮件系统结合了Gmail同步守护进程和莉莉丝代理工具,可实现语义化邮件搜索,具备隐私感知分类和个人身份信息(PII)清理功能。
🚀 快速开始
1. 数据库(共享的PostgreSQL)
本项目使用一个共享的PostgreSQL服务器。此应用的数据库名称为:lilith_emails。
确保共享的PostgreSQL(带有pgvector)正在运行。首先克隆lilith-compose项目。
2. 运行迁移
uv run alembic upgrade head
3. 添加Gmail账户
从Google Cloud Console下载OAuth客户端密钥,然后执行以下命令:
uv run python main.py add-account path/to/client_secrets.json
4. 同步(仅下载)
uv run python main.py sync 1
日志会显示进度:已获取的页面、已存储的邮件消息以及到目前为止的总数。
5. 转换(分类 + 清理 + 嵌入)
在同步完成后运行此步骤,以从存储的数据中生成privacy_tier、body_redacted和多级嵌入(主题、正文或块)。如果更改了模型或逻辑,可随时重新运行(无需重新下载)。
uv run python main.py transform 1
清理转换命令添加的所有派生列:
uv run python main.py reset-transform 1
6. 运行同步守护进程(Pub/Sub Webhook)
uv run python main.py serve
当守护进程收到Gmail Pub/Sub推送时,它会自动为该账户运行增量同步,然后进行转换。
没有公共URL(本地开发):使用拉取而不是推送。创建一个拉取订阅,在.env中设置PUBSUB_SUBSCRIPTION,然后运行:
gcloud auth application-default login
uv run python main.py watch 1
# 在另一个终端中,轮询通知(与Webhook的同步+转换相同):
uv run python main.py pull
创建拉取订阅(与主题在同一项目中):
gcloud pubsub subscriptions create lilith-emails-pull --topic=gmail-topic --project=lilithsync
有公共URL:使用推送订阅(端点 = 你的公共/webhook/gmail URL),并使用uv run python main.py serve运行守护进程。注册一次监视:uv run python main.py watch <account_id>(需要在.env中设置GOOGLE_CLOUD_PROJECT和PUBSUB_TOPIC)。
如果监视返回403:授予Gmail向你的主题发布的权限:
gcloud pubsub topics add-iam-policy-binding gmail-topic \
--member="serviceAccount:gmail-api-push@system.gserviceaccount.com" \
--role="roles/pubsub.publisher" \
--project=lilithsync
本地测试Webhook
你可以通过POST一个模拟的Pub/Sub有效负载来触发相同的路径,而无需依赖Gmail。首先运行一次完整同步,使账户有last_history_id,然后启动守护进程并发送:
# 在另一个终端中启动守护进程:uv run python main.py serve --port 8000
# 替换YOUR_EMAIL和HISTORY_ID(例如从数据库中获取:email_accounts.last_history_id)
# 跨平台(任何操作系统):
python3 -c "
import base64, json, urllib.request
d = base64.b64encode(json.dumps({'emailAddress':'YOUR_EMAIL','historyId':'HISTORY_ID'}).encode()).decode()
urllib.request.urlopen(urllib.request.Request('http://localhost:8000/webhook/gmail', data=json.dumps({'message':{'data':d}}).encode(), headers={'Content-Type':'application/json'}, method='POST'))
print('OK')
"
或者使用curl(Linux:使用base64 -w0;macOS:使用base64):
B64=$(echo -n '{"emailAddress":"YOUR_EMAIL","historyId":"HISTORY_ID"}' | base64)
curl -s -X POST http://localhost:8000/webhook/gmail -H "Content-Type: application/json" -d "{\"message\":{\"data\":\"$B64\"}}"
守护进程将为该账户运行增量同步,然后进行转换。使用get-email或MCP工具验证新的或更新的行。
📦 配置
环境变量(.env或shell):
| 变量 | 描述 |
|------|------|
| DATABASE_URL | PostgreSQL连接字符串 |
| EMAIL_ENCRYPTION_KEY | 用于OAuth令牌加密的Fernet密钥(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())") |
| GOOGLE_CLOUD_PROJECT | 用于Pub/Sub的GCP项目ID |
| EMBEDDING_URL | TEI嵌入服务(默认http://127.0.0.1:6003);必须公开/embed和/tokenize |
| SPACY_API_URL | 用于命名实体识别(NER)/PII清理的Spacy API(默认http://127.0.0.1:6004) |
| FASTTEXT_LANGDETECT_URL | fastText语言检测API(默认http://127.0.0.1:6005);在清理个人邮件之前用于NER语言检测 |
| VLLM_URL | vLLM兼容OpenAI的API(默认http://127.0.0.1:6001/v1) |
| VLLM_MODEL | 当不在功能范围内时用于聊天完成的模型ID(默认Qwen3-8B-AWQ) |
转换使用capabilities.json:在转换之前运行uv run python main.py capabilities,以便该文件存在并包含embedding.max_tokens、vllm.model_id、spacy_api.available和fasttext_langdetect.available。转换没有环境变量回退机制。已设置transform_completed_at的邮件将被跳过,除非使用--force(会提示确认);如果转换在运行过程中失败,这些邮件将在下一次重试。
📦 MCP服务器(代理工具)
莉莉丝邮件MCP服务器可公开你转换后的Gmail数据。
uv run mcp
uv run mcp --transport streamable-http --port 6201
MCP工具
| 工具 | 描述 |
|------|------|
| emails_search | 通过自然语言搜索,并可选择添加过滤器(发件人邮箱、标签、是否有附件、日期范围、限制数量)。返回邮件字典列表。 |
| email_get | 通过Gmail消息ID获取一封邮件。返回邮件字典或错误信息。 |
| email_get_thread | 通过线程ID获取线程中的所有消息。返回包含messages列表的线程字典。 |
| emails_summarize | 通过thread_id或email_ids进行总结。返回一个简短的摘要字符串。 |
所有响应都采用外部隐私策略:敏感内容会被屏蔽,个人内容会经过清理后显示。
扫码联系在线客服