Αναζήτηση σε δεκαετίες email: πρώτα οι λέξεις-κλειδιά, μετά το νόημα
Η αναζήτηση AI σε email αποτυγχάνει σε βρώμικα δεδομένα. Πώς καθαρίζετε, ομαδοποιείτε σε threads και ευρετηριάζετε ένα αρχείο email ώστε keyword και semantic search να δουλεύουν μαζί, με πηγές.

Το να στρέψετε ένα γλωσσικό μοντέλο σε ένα mailbox και να του κάνετε ερωτήσεις ακούγεται σαν project Σαββατοκύριακου. Μετά σας λέει με σιγουριά τη λάθος ημερομηνία ενός συμβολαίου, γιατί διάβασε ένα παρατιθέμενο αντίγραφο παλιού email μέσα σε νεότερη απάντηση, ή δεν μπορεί να βρει το τιμολόγιο 10442 γιατί ο αριθμός δεν έχει «νόημα» για να πιαστεί μια vector search. Η καλή αναζήτηση σε αρχείο email δεν αφορά κυρίως το μοντέλο. Αφορά τα δεδομένα που του δίνετε και τον τρόπο που ανακτάτε. Έτσι το προσεγγίζουμε.
Το email είναι πιο ακατάστατο από όσο φαίνεται
Ένα αρχείο email που καλύπτει χρόνια, αρκετά συστήματα email και μερικές μεταφορές έχει προβλήματα που καταστρέφουν την αναζήτηση πριν εμπλακεί οποιοδήποτε AI:
- Διπλότυπα. Το ίδιο μήνυμα κάθεται στα Εισερχόμενα, σε έναν φάκελο έργου και σε μια παλιά εξαγωγή. Η αναζήτηση το επιστρέφει τρεις φορές. Ένα AI το μετρά ως τρεις πηγές που συμφωνούν.
- Παρατιθέμενο ιστορικό. Κάθε απάντηση κουβαλά ολόκληρο το thread από κάτω της. Ευρετηριάστε το αφελώς και το παλαιότερο email ενός thread εμφανίζεται σε κάθε απάντηση, εκτός πλαισίου και με λάθος ημερομηνία.
- Κωδικοποιήσεις. Το παλαιότερο email είναι γεμάτο legacy σύνολα χαρακτήρων, κωδικοποιημένες κεφαλίδες και λάθος επισημασμένα μέρη. Αποκωδικοποιήστε το λάθος και τα ονόματα και το μη αγγλικό κείμενο γίνονται σκουπίδια.
- Συνημμένα. Το συμβόλαιο είναι το PDF, όχι το email που λέει «συνημμένο». Αν δεν εξάγετε το κείμενο των συνημμένων, δεν μπορείτε να βρείτε τα πιο σημαντικά έγγραφα.
- Διαγραφές. Email που διαγράφηκε από το mailbox για λόγους διατήρησης πρέπει να εξαφανιστεί και από κάθε ευρετήριο.
Καθαρίστε πριν ευρετηριάσετε
Αφαιρέστε διπλότυπα με βάση το Message-ID, με εναλλακτική ένα hash βασικών κεφαλίδων και σώματος όταν λείπει. Αποθηκεύστε ένα έγγραφο ανά μήνυμα και κρατήστε τη λίστα των φακέλων στους οποίους εμφανίστηκε ως metadata.
Ομαδοποιήστε σε threads με βάση τα References και In-Reply-To, όχι τα θέματα. Τα θέματα αλλάζουν και το «Re: τιμολόγιο» δεν είναι μοναδικό. Με σωστό threading μπορείτε να δείξετε μια ολόκληρη συζήτηση με τη σειρά και να αφήσετε το βήμα ανάκτησης να τραβήξει ένα thread μαζί αντί για μεμονωμένα αποσπάσματα.
Χωρίστε το νέο κείμενο από το παρατιθέμενο. Για κάθε μήνυμα, ευρετηριάστε ολόκληρο το σώμα για keyword search, αλλά κάντε embedding μόνο στο κομμάτι που έγραψε πραγματικά ο συντάκτης. Μια απλή αρχή:
import re
from email import policy
from email.parser import BytesParser
QUOTE_MARKERS = [
re.compile(r"^On .+ wrote:$", re.M),
re.compile(r"^-{2,}\s*Original Message\s*-{2,}", re.M | re.I),
re.compile(r"^From: .+\nSent: .+", re.M),
]
def new_text(raw: bytes) -> str:
msg = BytesParser(policy=policy.default).parsebytes(raw)
part = msg.get_body(preferencelist=("plain", "html"))
text = part.get_content() if part else ""
cut = len(text)
for rx in QUOTE_MARKERS:
m = rx.search(text)
if m:
cut = min(cut, m.start())
lines = [l for l in text[:cut].splitlines() if not l.startswith(">")]
return "\n".join(lines).strip()
Το πραγματικό email χρειάζεται περισσότερους δείκτες από αυτούς, σε κάθε γλώσσα στην οποία γράφει η εταιρεία σας, αλλά η αρχή ισχύει: το μοντέλο πρέπει να διαβάζει τι είπε κάθε άνθρωπος, μία φορά, με τη σωστή ημερομηνία και αποστολέα.
Εξαγάγετε το κείμενο των συνημμένων με τυπικά εργαλεία (pdftotext για PDF, parsers βιβλιοθηκών για μορφές γραφείου) και ευρετηριάστε το ως μέρος του μηνύματος, κρατώντας σύνδεσμο προς το πρωτότυπο αρχείο.
Δύο ευρετήρια, γιατί απαντούν σε διαφορετικές ερωτήσεις
Ένα full text index απαντά σε ερωτήσεις με ακριβείς λαβές: έναν αποστολέα, ένα domain, ένα εύρος ημερομηνιών, έναν αριθμό τιμολογίου, μια φράση, «έχει PDF». Είναι γρήγορο, ακριβές και εξηγήσιμο. Χρησιμοποιήστε ένα με stemming που γνωρίζει τη γλώσσα, ώστε το «τιμολόγια» να βρίσκει το «τιμολόγιο», και με συνώνυμα ανάμεσα στις γλώσσες που χρησιμοποιεί η εταιρεία σας, ώστε μια αναζήτηση στη μία γλώσσα να βρίσκει email γραμμένο στην άλλη.
Ένα vector index απαντά σε ερωτήσεις νοήματος: «πότε πρώτη φορά ο προμηθευτής αντέδρασε στο χρονοδιάγραμμα παράδοσης;» Μπορεί να μην υπάρχει καμία λέξη-κλειδί για αυτό. Ένα embedding model μετατρέπει κάθε καθαρισμένο μήνυμα σε διάνυσμα, και η αναζήτηση βρίσκει μηνύματα κοντινά σε νόημα με την ερώτηση.
Κανένα από τα δύο δεν αρκεί μόνο του. Τα διανύσματα είναι αδύναμα σε αναγνωριστικά και αριθμούς. Οι λέξεις-κλειδιά είναι αδύναμες σε παραφράσεις. Τρέξτε και τα δύο, και συνδυάστε:
- Εξαγάγετε πρώτα φίλτρα από την ερώτηση: πρόσωπα, domains, ημερομηνίες, «με συνημμένο».
- Εφαρμόστε αυτά τα φίλτρα και στις δύο αναζητήσεις, ώστε η vector search να μην ξεστρατίζει ποτέ έξω από την περίοδο ή τα πρόσωπα που αφορά η ερώτηση.
- Συγχωνεύστε τις δύο λίστες αποτελεσμάτων. Το reciprocal rank fusion είναι απλό και δουλεύει καλά: βαθμολογήστε κάθε μήνυμα με το άθροισμα του
1 / (k + rank)και από τις δύο λίστες. - Επεκτείνετε τα κορυφαία αποτελέσματα στα threads τους, ώστε το μοντέλο να διαβάζει συζητήσεις, όχι αποσπάσματα.
Κάντε το μοντέλο να δείχνει τη δουλειά του
Μόνο τότε μπαίνει το γλωσσικό μοντέλο. Δώστε του τα ανακτημένα μηνύματα, το καθένα με ID, ημερομηνία και αποστολέα, και δώστε του οδηγία να απαντά μόνο από αυτά και να παραθέτει το ID για κάθε δήλωση. Στη διεπαφή, μετατρέψτε κάθε παραπομπή σε σύνδεσμο που ανοίγει το πρωτότυπο email.
Οι παραπομπές κάνουν δύο δουλειές. Ο χρήστης μπορεί να ελέγξει κάθε ισχυρισμό με ένα κλικ, που είναι ο μόνος υπεύθυνος τρόπος να χρησιμοποιήσει κανείς ένα μοντέλο σε επιχειρηματικά αρχεία. Και βλέπετε αμέσως πότε απέτυχε η ανάκτηση: μια απάντηση που παραπέμπει σε λάθος μηνύματα είναι πρόβλημα ανάκτησης, όχι πρόβλημα μοντέλου, και σας λέει πού να κοιτάξετε.
Πείτε στους χρήστες καθαρά ότι ένα μοντέλο μπορεί να παρερμηνεύσει. Με τις πηγές ένα κλικ μακριά, αυτός είναι ένας διαχειρίσιμος κίνδυνος.
Κρατήστε τα δεδομένα μέσα στο κτίριο
Κάθε συστατικό αυτού του σχεδιασμού τρέχει άνετα στο δικό σας hardware: το full text index σε συνηθισμένους servers, η vector database δίπλα του, και το embedding model και το γλωσσικό μοντέλο σε GPU servers που ελέγχετε εσείς. Το email, οι ερωτήσεις και οι απαντήσεις δεν χρειάζεται ποτέ να φύγουν από το δίκτυό σας.
Δοκιμάστε το σαν σύστημα, όχι σαν demo
Πριν το εμπιστευτείτε, φτιάξτε ένα μικρό σύνολο ερωτήσεων με γνωστές απαντήσεις: ένα συγκεκριμένο τιμολόγιο, το πρώτο email μιας γνωστής διαφωνίας, ένα μήνυμα γραμμένο στη δεύτερη γλώσσα σας, ένα μήνυμα που διαγράφηκε τον προηγούμενο μήνα (δεν πρέπει να βρεθεί). Τρέξτε τες μετά από κάθε αλλαγή στον καθαρισμό, την ευρετηρίαση ή τα prompts, και κρατήστε τα αποτελέσματα. Αν μια αλλαγή χειροτερεύει γνωστές ερωτήσεις, θα το δείτε.
Και παρακολουθήστε την ίδια την αλυσίδα. Ένας συγχρονισμός που σταμάτησε αθόρυβα, ένα ευρετήριο που δεν έχει προλάβει, ή μια ουρά διανυσμάτων μετά από επανεκκίνηση, όλα μοιάζουν με «η αναζήτηση δουλεύει» μέχρι κάποιος να χρειαστεί το email της προηγούμενης εβδομάδας. Ελέγξτε ολόκληρη την αλυσίδα από άκρη σε άκρη, σε τακτική βάση, και ειδοποιήστε όταν οποιοδήποτε στάδιο μένει πίσω.
Εν συντομία
- Αφαιρέστε διπλότυπα, ομαδοποιήστε σε threads, αποκωδικοποιήστε και αφαιρέστε το παρατιθέμενο κείμενο πριν ευρετηριάσετε οτιδήποτε.
- Ευρετηριάστε τα συνημμένα. Εκεί είναι τα συμβόλαια.
- Τρέξτε keyword και vector search μαζί, με φίλτρα εφαρμοσμένα και στις δύο.
- Απαντήστε μόνο από ανακτημένο email, με παραπομπή σε κάθε δήλωση.
- Κρατήστε κάθε συστατικό σε hardware που ελέγχετε, και δοκιμάστε με γνωστές ερωτήσεις.
Χτίζουμε ιδιωτικά αρχεία email έτσι, με το AI να τρέχει στις δικές σας ιδιωτικές GPU. Αν σχεδιάζετε ένα, ευχαρίστως να βοηθήσουμε.
Σχετικά: Ιδιωτικό αρχείο email με τοπική αναζήτηση AI · Managed Nextcloud για έγγραφα γραφείου
Δημοσιεύθηκε 15 Οκτωβρίου 2026.
Όλες οι σημειώσεις