Initial commit: Hermes skill email-local-archive

This commit is contained in:
estorozhenko
2026-09-06 13:51:14 +00:00
commit b123d2d3b4
7 changed files with 1386 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
# SQLite-индекс архива писем
## Назначение
Быстрый поиск и трекинг по архиву email.md без grep-а по всем папкам.
Основа для: contacts_extractor (знает какие письма обработаны), sqlite_search (FTS5), digest (выборка по дате).
## Компоненты
| Компонент | Путь | Назначение |
|-----------|------|------------|
| Индексатор | `/opt/hermes/email-assistant/scripts/mail_index.py` | Сканирует email.md → SQLite |
| Поиск | `/opt/hermes/email-assistant/scripts/sqlite_search.py` | FTS5-поиск по индексу |
| База | `/opt/hermes/email/mail_index.db` | SQLite (WAL mode) |
## Схема БД
### emails — основная таблица
```sql
CREATE TABLE IF NOT EXISTS emails (
path TEXT PRIMARY KEY, -- относительный путь от EMAIL_ROOT
uid INTEGER, -- числовой UID из пути
folder TEXT, -- INBOX, INBOX/!Scan, Sent...
date TEXT, -- дата из frontmatter (ISO)
from_addr TEXT, -- отправитель
to_addrs TEXT, -- получатели
subject TEXT, -- тема
body_preview TEXT, -- первые 500 символов тела (без HTML)
contacts_extracted INTEGER DEFAULT 0, -- 0/1 — обработано contacts_extractor
contacts_skipped INTEGER DEFAULT 0, -- 0/1 — нет подписи / LLM error
first_seen TEXT, -- когда проиндексировано
last_scanned TEXT, -- последняя проверка contacts
file_mtime REAL -- mtime файла для инкрементальной проверки
);
```
### email_fts — FTS5 virtual table
```sql
CREATE VIRTUAL TABLE IF NOT EXISTS email_fts USING fts5(
subject, from_addr, to_addrs, body_preview,
content='emails',
content_rowid='rowid',
tokenize='unicode61'
);
```
Синхронизация через триггеры INSERT/UPDATE/DELETE + FTS5 rebuild.
### Индексы
- `idx_emails_folder` — быстрая фильтрация по папке
- `idx_emails_uid` — lookup по UID
- `idx_emails_contacts` — необработанные письма (contacts_extracted=0)
- `idx_emails_date` — сортировка по дате
## Контракт между mail_index.py и contacts_extractor.py
**mail_index.py** владеет схемой и создаёт таблицы. **contacts_extractor.py** — только читает/пишет поля `contacts_extracted`, `contacts_skipped`, `last_scanned`.
```sql
-- contacts_extractor берёт необработанные письма:
SELECT rowid, path, uid, folder, from_addr, subject
FROM emails
WHERE contacts_extracted = 0 AND contacts_skipped = 0
ORDER BY folder, uid
LIMIT ?
-- После обработки:
UPDATE emails SET contacts_extracted=1, last_scanned=? WHERE rowid=?
UPDATE emails SET contacts_skipped=1, last_scanned=? WHERE rowid=?
```
## Быстродействие
- 2073 письма → полная индексация ~9 секунд
- FTS5-поиск — мгновенно (<100ms)
- WAL mode — конкурентные чтения не блокируют запись
- Инкрементальная индексация по mtime — доли секунды
## Использование sqlite_search.py
```bash
# Простой поиск (AND по умолчанию)
python3 sqlite_search.py 'Стороженко'
python3 sqlite_search.py 'битрикс OR контрагент'
python3 sqlite_search.py '"точечная фраза"'
# Фильтры
python3 sqlite_search.py --folder 'INBOX/!Отчеты'
python3 sqlite_search.py --limit 20
# Специальные префиксы (точно в поле from_addr/subject через LIKE)
python3 sqlite_search.py 'from:example@mail'
python3 sqlite_search.py 'subject:отчёт'
# Вкл. тело письма в FTS5 (медленнее, но находит больше)
python3 sqlite_search.py --body
```
## Операторы FTS5
- `AND` — по умолчанию между словами
- `OR` — `'битрикс OR контрагент'`
- `"точная фраза"` — кавычки для точного совпадения
- `-исключить` — минус перед словом
- `prefix*` — wildcard (звёздочка на конце)
## mail_index.py — ключевые параметры
```bash
python3 mail_index.py # полная переиндексация
python3 mail_index.py --incremental # только новые (по mtime)
python3 mail_index.py --search "..." # поиск (встроенный, без FTS5)
python3 mail_index.py --stats # статистика
```
## Важные детали
- **body_preview** — только первые 500 символов. Для full-text search с телом используй `sqlite_search.py --body` (FTS5 на preview).
- **contacts_extracted/contacts_skipped** — взаимоисключающие флаги. Если ни один не 1 — письмо не обработано.
- **file_mtime** — для инкрементальной индексации. Если mtime файла > last_mtime в БД — переиндексировать.
- **FTS5 rebuild** — вызывается после каждой полной индексации для согласованности.
- Исключает папки `contacts/` и `state/` из сканирования.