A tool elkészítése, cél
A cél az volt, hogy a tool és az általa generált szótár fájlok a lehető leggyorsabban álljanak elő, ezért a scripteket is 99%-ban AI csinálta, a chatgpt 5.1-et használtam. Később kipróbálom a Cursort, OpenAI Codex-et, vagy a Claude code-ot, de most csak simán a ChatGPT-t használtam beszélgetős módban. Úgy tapasztaltam, hogy jól használható, ha te bontod a megoldandó problémát részfeladatokra, és az AI-t csak lépésről lépésre, a részfeladatok megoldására használod.
A környezet, előfeltételek
1. Ollama telepítése: https://ollama.com/download/linux
2. Modell letöltése:
ollama pull gemma3:27b3. A scripteket feltettem a github-ra. A klónozás:
git clone https://github.com/dlaszlo497/epub2stardict4. Python virtual env inicializálása az epub2stardict könyvtárban:
cd epub2stardict
python3 -m venv .venv
source .venv/bin/activate5. A függőségek felinstallálása:
pip install -r requirements.txt6. spacy model letöltése:
python -m spacy download en_core_web_sm7. dictzip tool installálása (Én ubuntu alatt futtattam):
sudo apt install dictzipA script-ek
1. 100_epub_to_text.py:
A bemenete a data/ebook.epub, a kimenete pedig data/100_book.txt.
A script az epub könyvből kinyeri a szöveget, és kimenti egy text fájlba.
2. 200_chunk_text.py:
Ennek a script-nek a bemenete a 100_book.txt és a szöveget chunk-okra bontja, amik a mi esetünkben most a mondatok lesznek.
Az eredményt a 200_chunks.jsonl fájlba menti, ahol minden sor egy chunk json-je.
Például:
{"id": 0, "sentence": "This is the first sentence."}
{"id": 1, "sentence": "This is the second sentence."}
{"id": 2, "sentence": "This is the third sentence."}3. 300_build_word_context.py:
Ennek a scriptnek a bemenete a 200_chunks.jsonl, a kimenete a 300_word_contexts.jsonl.
Minden szóra meghatározza, hogy melyik chunkokban szerepel.
Például:
{"word": "hens", "contexts": [13, 62, 73, 277, 294, 297, 311, 343, 365, 549, 696, 764, 825, 828, 832, 835, 837, 838, 928, 940, 1018, 1199, 1228]}
{"word": "behind", "contexts": [13, 30, 212, 241, 292, 337, 444, 637, 879, 1169]}
{"word": "sheep", "contexts": [13, 47, 73, 137, 343, 365, 373, 427, 447, 456, 485, 489, 530, 531, 574, 577, 614, 669, 671, 701, 817, 931, 940, 981, 1003, 1118, 1199, 1240, 1243, 1433, 1434, 1435, 1439, 1455, 1458]}4. 400_extract_word_pos.py:
A program bemenete: 200_chunks.jsonl és a 300_word_contexts.jsonl fájlok.
A program kimenete: 400_word_pos.jsonl
Ez a script előszűri a szavakat, hogy érdemes-e vele foglalkozni, pl minimum 3 karakteres, és nem olyan szó, mint pl: "the", "a", "an", "were", "their", vagy nem római szám stb...
spaCy segítségével meghatározza, hogy a szóhoz milyen lemma (alapalak) tartozik, és milyen szófaj tartozik hozzá.
Ehhez a spaCy-ben az en_core_web_sm modellt használjuk, ami elemzi a mondatot (chunk), és meghatározza a benne szereplő konkrét szóhoz tartozó alapalakot és szófajt.
Lásd: https://spacy.io/
Példa kimenet:
{"word": "asked", "lemma": "ask", "pos": ["VERB"], "contexts": [22, 167, 168, 169, 172, 322, 648, 711, 886, 987, 1174]}
{"word": "worst", "lemma": "bad", "pos": ["ADJ"], "contexts": [19, 1075]}
{"word": "oldest", "lemma": "old", "pos": ["ADJ"], "contexts": [19]}
{"word": "firing", "lemma": "fire", "pos": ["NOUN", "VERB"], "contexts": [1138, 1155, 1159]}5. 500_generate_definitions.py:
A program bemenete: 200_chunks.jsonl, 400_word_pos.jsonl
A program kimenete: 500_word_senses.jsonl
Példa kimenet:
{"word": "asked", "lemma": "ask", "pos": ["VERB"], "pos_hu": ["ige"], "pos_ai": "VERB", "pos_ai_hu": "ige", "meaning_hu": "kérdez", "example_surface_en": "He asked a simple question about the directions.", "example_lemma_en": "I will ask the teacher for help with the assignment.", "ok": true}
{"word": "worst", "lemma": "bad", "pos": ["ADJ"], "pos_hu": ["melléknév"], "pos_ai": "ADJ", "pos_ai_hu": "melléknév", "meaning_hu": "legrosszabb", "example_surface_en": "The worst part of the storm was the hail.", "example_lemma_en": "Bad weather can ruin a picnic.", "ok": true}
{"word": "oldest", "lemma": "old", "pos": ["ADJ"], "pos_hu": ["melléknév"], "pos_ai": "ADJ", "pos_ai_hu": "melléknév", "meaning_hu": "legidősebb", "example_surface_en": "The oldest tree in the park provides welcome shade.", "example_lemma_en": "Old habits die hard, so it's best to establish good routines early.", "ok": true}
{"word": "firing", "lemma": "fire", "pos": ["NOUN", "VERB"], "pos_hu": ["főnév", "ige"], "pos_ai": "VERB", "pos_ai_hu": "ige", "meaning_hu": "lőni", "example_surface_en": "The soldiers were firing their weapons at the enemy.", "example_lemma_en": "The fire quickly spread through the dry grass.", "ok": true}Ebben a lépésben futtatjuk az LLM-et (azaz a háttérben futó ollama szervizhez csatlakozunk, amit feltelepítettünk). Az LLM az adott szóhoz a könyvből véletlenszerűen kiválasztott maximum 5 db példamondat alapján megmondja a magyar jelentését, a szófajt és két példamondatot. Az első példamondatban a szó könyvben előforduló formáját használja, a másodikban pedig a szó alapformáját.
A következő prompt-ot használjuk:
You are building a bilingual (English -> Hungarian) dictionary for a specific book.
Book / corpus information:
{BOOK_INFO}
I will give you:
- a base English word (lemma)
- the original surface form (lowercase)
- a list of possible parts of speech (POS) for this word in the book
- several example sentences from the book where this word appears (in various forms)
YOUR JOB:
1) Decide which POS from the list best represents the MAIN dictionary sense of this word
in the context of these examples. If several are possible, choose the most central / typical one.
2) Infer the GENERAL DICTIONARY MEANING of the word in that sense.
3) Choose ONE common Hungarian word OR SHORT EXPRESSION (1–3 words) that best matches that meaning and POS.
4) Create TWO TOTALLY DIFFERENT SHORT ENGLISH EXAMPLE SENTENCES (not from the book) that clearly show this meaning:
- The first must use the original surface form exactly as given.
- The second must use the lemma form.
- Both must use the word with the chosen POS.
Details:
- Focus on the BASE WORD (lemma), not on the specific inflected forms, when deciding meaning.
- Possible POS tags for this word in the book: {pos_tags_str}
- Description of these POS tags: {pos_options_desc}
- Do NOT reuse or quote the example sentences.
- The Hungarian expression can be multi-word if that is the most natural dictionary equivalent (e.g. "szerzői jog").
- If you are uncertain, guess the most likely general dictionary meaning.
- NEVER answer with "Sajnálom", "Nem tudom", "I am sorry", "Sorry" or any similar meta-reply.
Word (lemma): {lemma}
Surface form (lowercase): {word}
Example sentences from the book:
{examples_block}
VERY IMPORTANT OUTPUT FORMAT (EXACTLY THREE LINES, NO BULLETS, NO EXTRA TEXT):
Line 1: POS=<POS_TAG>; HU=<ONE_HUNGARIAN_WORD_OR_SHORT_EXPRESSION>
Line 2: <one short English sentence containing the surface form '{word}'>
Line 3: <one short English sentence containing the lemma '{lemma}'>
<POS_TAG> MUST be one of: {pos_tags_str}, or a reasonable guess if they are empty.
<ONE_HUNGARIAN_WORD_OR_SHORT_EXPRESSION> can be ONE or SEVERAL (1–3) Hungarian words.
A {BOOK_INFO} rész, a könyv leírását tartalmazza, például:
The words come from the novel "...." by .....
The story is an allegory about .........Az {examples_block} maximum 5 példamondatot tartalmaz a könyvből (véletlenszerűen), ami tartalmazza az adott szót.
A {lemma} a szó alap alakja, pl went esetén go, abandoned esetén abandon, stb...
A {pos_tags_str} a szófajok listája, pl: NOUN, VERB
A {pos_options_desc} a szófajok leírása, pl "NOUN - a noun (thing, person, concept)"
A {word} tartalmazza a szót, aminek a magyar jelentésére vagyunk kíváncsiak.
Az LLM pedig visszaad egy eredményt a következő formában, például:
POS=VERB; HU=kérdez
He asked a simple question about the directions.
I will ask the teacher for help with the assignment.A script ebből a kimenetből állítja elő az 500_word_senses.jsonl fájlt, amelyben már benne van minden információ, ami a StarDict szótár generálásához kell.
Az LLM modell kiválasztásáról:
A gemma3:27b lett a kiválasztott. Ez egy nagyobb modell. Beletelt pár óra kísérletezésbe, mire idáig eljutottam. Egy Nvidia 4060 videó kártyám van, ebben 8 GB RAM van. Az llama3.1:8b volt az első modell, amit kipróbáltam, ez fut a videó kártyával. Meglepően jó volt az esetek kb. 80%-ában, de néha nagyon félrement. Innen kezdtem el felfelé lépegetni, a sorban a következő a gemma2:9b volt (ez is még fut az Nvidia 4060-on). Ez hasonló eredményt adott, mint az llama3.1:8b.
A gemma3:12b már szerintem a CPU-t használta. Ezekkel a feldolgozás még elfogadhatóan gyors volt, de néhány szóra és példamondatra nagyon furcsa jelentéseket kaptam. Az ollama3.1:8b-nél és a gemma2:9b-nél jobb, de még mindig nem volt az igazi.
A következő a gemma3:27b volt. Ez nagyon jónak bizonyult, tényleg azt csinálja, amit szerettem volna. Végül ennél maradtam.
Kipróbáltam a deepseek-r1 modelleket is: a 70b, 32b, de még a 14b is nagyon lassúnak bizonyult. A gemma3:27b-ről: ez CPU-n viszonylag gyors! Nem csoda, hogy szeretik. Valószínűleg minimum 16-32 Gb memória kellhet neki, az én gépemben 64 Gb van. De így is kb. 15 óráig futott egy 12. generációs Intel i5-ös gépen, CPU-n, kb 3700 db különböző szó feldolgozása.
600_create_stardict.py:
A program bemenete az előző lépésben előállt 500_word_senses.jsonl fájl adatai.
A program kimenete a stardict szótár.
A stardict szótár szerkezete: https://stardict-4.sourceforge.net/StarDictFileFormat
Leegyszerűsítve: Három fájlnak kell előállnia.
1. dict.dz fájl
A *.dict egy UTF-8 szöveges állomány (az én esetemben XDXF-szerű XML), ennek a tömörített változata a *.dict.dz, amit dictzip állít elő. Bármilyen infót írhatunk bele, ez fog megjelenni a szótárban. Az én dict fájlomban egy bejegyzés szerkezete:
<k>abandoned</k>
feladni (ige)
The abandoned house stood empty for years.
We must abandon the old ways and embrace new technologies.
<k>able</k>
képes (melléknév)
He was able to finish the race despite his injury.
The scientist is able to conduct complex experiments.
<k>abnormal</k>
rendellenes (melléknév)
The doctor noted the abnormal growth on the patient's skin.
An abnormal amount of rain caused severe flooding.A <k> tag-ben van az angol szó, úgy, ahogy a könyvben előfordul. A <k> a key phrase-t jelenti.
Alatta a jelentése, és a szófaj, majd két példamondat.
Valószínűleg még beleteszem a szó alap alakját, rendelkezésre áll.
Ez egyébként "XML Dictionary eXchange Format", aminek a leírása itt található: https://github.com/soshial/xdxf_makedict/tree/master/format_standard
A dict.dz egy tömörített állomány, egy módosított gzip tömörítővel: dictzip. Ezt külön fel kell installálni: apt install dictzip.
A gzip és a dictzip között annyi a különbség, hogy a dictzip a tömörített állomány közepéből is ki tud bontani chunkokat, mig a gzip egy stream.
2. idx fájl:
Az IDX fájlban sorrendezve vannak a szavak (stardict_strcmp-vel, lásd: https://stardict-4.sourceforge.net/StarDictFileFormat), és szavanként van egy bejegyzés:
Az adott szó változó hosszúságú
Szó lezáró, fixen 0x00 1 byte
A dict fájlban a leírás kezdetére egy pointer: 1 dword, 32 bit, unsigned, big endian
Leírás hossza 1 dword, 32 bit, unsigned, big endian3. ifo fájl:
Ez tartalmazza a metaadatokat:
StarDict's dict ifo file
version=2.4.2
wordcount=3722
idxfilesize=58848
bookname=English-Hungarian dictionary
date=2025.11.17
sametypesequence=x
description=English-Hungarian dictionary built from .... word list.
encoding=UTF-8
lang=en-huEgy meglévő szótár alapján állítottam össze, az egyes metaadat kulcsok elnevezése beszédes.
A sametypesequence=x esetén beszélünk "XML Dictionary eXchange Format" dict fájlról, h=html, m=plain text fájl
A wordcount a szavak számát jelenti.
Az idxfilesize a bájtra pontosan megadott idx fájlméret.
A lang=en-hu kell, hogy az ebook olvasó felismerje, hogy milyen nyelvű a szótár.
Záró gondolatok
A scripteket nem teszteltem, csak egyetlen EPUB-bal, amit jelenleg olvasok.
A scriptekben van furcsaság, AI írta, első verzió, csak egy kiindulási alap. Rengeteget lehet rajta javítani, amit el is kezdtem már:
- A szöveget spacy-val bontsuk mondatokra.
- A szavakat is spacy-val határozzuk meg a mondatokból.
- A stopword-ok kezelése valószínűleg felesleges.
- Tett bele varázslást a szófaj meghatározásához. (a hackelés nem kell igazából)
- A különböző szófajú szavakat lehetne külön input sorban kezelni, és a szótárban kellene a szavakat külön magyarázni.
- A prompton lehet javítani
- Nem stardict_strcmp-t használ, ami az angol szavak miatt most éppen oké.
- stb...
A generált szótár személyes használatra van, csak a szavakat tartalmazza, olyan példamondatokkal, amik nem a könyvből vannak, hanem az AI generálta véletlen szerűen.
A szótárt PocketBook Verse Pro-val használom, KOReader-rel. Eddig random szavakkal ellenőriztem le, és pár oldalt olvastam el, de már látszik, hogy az általános letölthető stardict szótárhoz képest nekem jobban megfelel, tényleg a környezethez passzoló jelentést kapom.
További lehetőségek:
- Ez az egész futhatna pl Google Colab alatt.
- Esetleg megfontolandó egy nagyobb nyelvi modell, pl ChatGPT előfizetés az API-ra, szerintem egy könyv konvertálása 2-3 USD lehet max, de ezt alaposan meg kell nézni. pl a ChatGPT 5.1-mini-vel.
- Megfontolandó lehet pl az Azure LLM szolgáltatásainak használata
- dlaszlo blogja
- A hozzászóláshoz be kell jelentkezni
- 420 megtekintés
Hozzászólások
Érdeklődve várom a javítást, hogy lássam a scripteket.
Saját esetemben az Anki programokat is bevetettem. Hatékony kikérdező a tanult szavakra. Szótárból Copy/Paste Ankiba, és az jól megtanít.
- A hozzászóláshoz be kell jelentkezni
Köszi, felteszem az Ankit, és kipróbálom. Jó lenne kicsit hatékonyabban, tudatosan tanulni az idegen nyelvet.
- A hozzászóláshoz be kell jelentkezni
Meglett a blog törzse. Köszönöm!
Érdekes megoldást találtál, majd egy kisebb könyvre megnézem.
8G RAM-om van most a telken , ennyivel nem ugrok neki. Otthon megpróbálom kicsit több erőforrással.
- A hozzászóláshoz be kell jelentkezni
Elkezdtem már javítani, kitisztázni a script-eket, és rövidebb lett a prompt is, így egy kicsit gyorsabban fut. A hétvégén tudok vele foglalkozni kicsit, szerintem akkor meg-tagelve ezt az előzőt, felteszem majd.
Nézegettem a chatgpt-t, fogalmam sincs mit tud az GPT-5.1-mini, de érdemes lehet kipróbálni. Egy prompt kb 500-600 token, és a válasz kb 30 token. Azzal úgy kevesebb memória kell neki. A spacy nem hiszem hogy sokat használ, de meg kell majd nézni.
- A hozzászóláshoz be kell jelentkezni
Az előző verziót meg-tag-eltem v0.0.1-el, és feltettem a következő javítást:
100_epub_to_text.py: Tettem bele egy normalizálást, ami nem ascii karaktereket lecseréli, mint pl a (”) idézőjelet sima idézőjelre: (").
200_chunk_text.py: Regexp helyett most már spacy-val bontom a szöveget mondatokra, így sokkal jobb, mert egy Mrs. után nem hiszi azt a program, hogy az mondatvégi pont volt.
300_build_word_context.py: Ez a mondatokból most már csak azokat a szavakat teszi bele az indexbe, ami legalább 3 karakter, angol betűkből áll, ahol spacy-val tudja a szót tokenizálni.
400_extract_word_pos.py: Kikerültek a stop word-ok, szavanként több sor keletkezik, ha több szófaj van (eddig egy sorba volt ömlesztve), nem dolgozzuk fel: tulajdonnév, szimbólum, írásjel, egyéb szófaj, szóköz
500_generate_definitions.py: Bekerült, hogy ne az LLM akarjon még pluszban szófajt becsülni a szófaj lista alapján, hanem azt az egy szófajt vegye alapul, amit a spacy-val meghatároztunk a szóhoz.
A prompt is ennek megfelelően került átírásra, de nem lett végül rövidebb, mert az eredeti prompt (az első verzióból) működött a legjobban.
550_word_senses_check.py: Egy új script, phunspell-t használ, hogy kilistázza a furcsa fordításokat. A kb 4000 szavas könyvemből 50 ilyen jött ki, ami néha csak elírásnak néz ki:
tyrannise -> zsarnokkodni
chewing -> ráágni
De vannak viccesek is:
ducklings -> kacsacsányok
A gemma3:27b ismeri a kiskacsákat, de nem tudja hogy hívják őket magyarul. :D
600_create_stardict.py: most már összemerge-öli szavanként az egyes eredményeket (pl az 'all' szóra ra van három különböző sor), és úgy teszi bele a szótárba.
- A hozzászóláshoz be kell jelentkezni
Még egy kiegészítés:
Betettem az OpenAI API-val történő generálást is. Az én kisebb könyvemre, amiben kb 4000 szó van, amit a gemma3:27b CPU-val 15 óra alatt generált le, azt az OpenAI API a GPT-5-mini-vel megcsinálta kb 1,5 óra alatt. Egy szálon szépen végigzörgött.
Az eredmény néhol jobb mint a gemma3:27b, néhol pedig rosszabb.
Lásd:
ducklings
kiskacsa (főnév) (GPT-5-mini)
The ducklings followed their mother across the pond.
A duckling hid under the reeds to avoid the fox.
kacsacsányok (főnév) (gemma3:27b)
The farmer worried about the safety of the ducklings during the storm.
A tiny duckling followed its mother closely across the pond.vagy
claimed
tállomítani (ige) (GPT-5-mini)
He claimed he had seen the strange monument on his travels.
They claim the story is true.
állít (ige) (gemma3:27b)
The witness claimed he saw the entire incident unfold before his eyes.
The company will claim full responsibility for the damage.De a 4000 szóból csak pár ilyen van, a legtöbb az jó lett. A példamondatok (amiket a modell talált ki), szerintem mindkét modell esetén jól sikerültek.
(majd lehet holnap rátesz még valami rejtett költséget, de legalább most kitapasztalom. :))
Az OpenAI API platform egyszerűen használható:
- Be kell lépni, az API platform-ra, létrehozni egy API kulcsot.
- Az API kulcshoz lehet jogosultságot állítani, szerintem a legszűkebb jogosultságot kell megadni (Tudjon text response-t írni olvasni, pl hang vagy kép feldolgozás, vagy egyéb feature-ök feleslegesek)
- Én feltöltöttem 10 USD-t, és beállítottam a havi limit-et erre, 80%-nál, és 100%-nél figyelmeztet. Ne töltsön fel pénzt automatikusan. Egyébként egy webkártyát adtam meg, amin csak pár forint van általában.
- Az API pricing-et alaposan át kell tanulmányozni, pl a a GPT-5-mini elég olcsó. Külön ár van az input és output tokenekrem jelenleg: Input: $0.250 / 1M tokens Cached input: $0.025 / 1M tokens Output: $2.000 / 1M tokens
- Most úgy látom, az eddigi költség 0.98 USD, a 4000 szóval - ez nem sok - remélem nem tesz rá holnapra valami rejtett költséget. :)
(a költségek miatt vigyázni kell, és mindenki a saját költségeiért felel)
Feltettem az openai-s generálót is (az API kulcsot a .env fájlba kell betenni: OPENAI_API_KEY=....): 500_generate_definitions.py -ból így lett két script: 500_generate_definitions_gemma3.py és 500_generate_definitions_openai.py
Úgy módosítottam a 600_create_stardict.py script-et, hogy ha:
- egyik bemeneti állomány sincs meg (500_word_senses_gemma3.jsonl, 500_word_senses_openai.jsonl), akkor hibával kilép
- ha csak az egyik van meg (openai, vagy gemma), akkor azt használja
- ha mindkettő bemeneti állomány megvan, akkor mindkettőt beteszi a szótárba (lásd példák) a szavakhoz. A több infó sose baj.
Szerintem most már elengedem ezt, amíg ki nem olvastam, ami miatt kellett. :)
- A hozzászóláshoz be kell jelentkezni