Μετάβαση στο περιεχόμενο

Δημιουργία περιεχομένου μαθημάτων

Αυτός ο οδηγός περιγράφει βήμα προς βήμα πώς να στήσεις ένα νέο σύνολο μαθημάτων για τον Content-Loader του Adaptive Learner. Όποιος θέλει να φτιάξει ένα γλωσσικό ή θεματικό σύνολο - για δική του χρήση ή ως συνεισφορά στη δημόσια δεξαμενή περιεχομένου - καλό είναι να τον διαβάσει μία φορά ολόκληρο πριν από το πρώτο μάθημα.

Τι είναι ένα Content-Set;

Ένα Content-Set είναι ένα εκδοσιοποιημένο πακέτο μαθημάτων, που ένας χρήστης μπορεί να κατεβάσει μέσω της σελίδας Set-Browser (/content). Το plugin Content-Loader (v1.27.0) αναλαμβάνει το discovery, τη λήψη, το caching και τη σύγκριση εκδόσεων και στους δύο τρόπους αποθήκευσης.

Ένα σύνολο έχει τρία επίπεδα:

  1. Root-Manifest (manifest.yaml) - παραθέτει κάθε σύνολο του repo. Διαβάζεται από τον Set Browser για τον κατάλογο προέλευσης.
  2. Set-Manifest (sets/{set-id}/manifest.yaml) - αδελφικό του Root-Manifest, παραθέτει τα αρχεία μαθημάτων του συγκεκριμένου συνόλου.
  3. Αρχεία μαθημάτων (sets/{set-id}/lessons/NN-slug.json) - ένα αρχείο JSON ανά μάθημα, επικυρωμένο σε κάθε λήψη έναντι του σχήματος μαθήματος (βλ. Το σχήμα είναι η μοναδική πηγή αλήθειας παρακάτω).

Τα σύνολα που αποστέλλονται με τον Adaptive Learner βρίσκονται στο ξεχωριστό repo περιεχομένου astrapi69/adaptive-learner-content (ελεγμένα ως αδελφικό checkout ../adaptive-learner-content και ομαδοποιημένα offline μέσα στο build του GitHub Pages μέσω frontend/scripts/copy-bundled-content.mjs) και είναι κατάλληλα ως πρότυπο. Το τρέχον μέγεθος της βιβλιοθήκης (αριθμοί μαθημάτων / συνόλων / τομέων, ο πίνακας ανά σύνολο και οι ενεργοί τομείς) είναι το μπλοκ CONTENT-STATS στο README.md του έργου - αυτό το μπλοκ είναι η μοναδική πηγή αλήθειας, παραγόμενο από ένα φρέσκο checkout περιεχομένου, οπότε αυτός ο οδηγός δεν διπλασιάζει τους αριθμούς.

Το σχήμα είναι η μοναδική πηγή αλήθειας (EXP-039)

Η μορφή μαθήματος/άσκησης έχει έναν κανονικό (canonical) ορισμό: το JSON Schema μαθήματος που αποστέλλει το npm πακέτο learn-content-engine (αμετάβλητο ανά δημοσιευμένο release). Μέσα σε αυτή την εφαρμογή, το δομικό στρώμα Pydantic στο plugin Content-Loader (adaptive_learner_content_loader.schema) αναγεννάται από αυτόν τον καθρέφτη (scripts/generate_pydantic_models.py)· μόνο οι σημασιολογικοί validators πολλαπλών πεδίων γράφονται στο χέρι. Το make sync-schema ανανεώνει τον καθρέφτη και επανεκδίδει τα παράγωγα artefacts, και πύλες byte-ισοτιμίας αποδεικνύουν ότι το schema/*.json ισούται με το καρφιτσωμένο release του engine. Τα σημεία που παλιότερα απέκλιναν δεν μπορούν πλέον:

  • schema/lesson.schema.json (+ αδελφικά αρχεία): το μηχανικά αναγνώσιμο JSON Schema (Draft 2020-12). Αναφέρσου σε αυτό από ένα .json μαθήματος μέσω ενός κλειδιού "$schema" κορυφαίου επιπέδου, για αυτόματη συμπλήρωση IDE και inline επικύρωση.
  • schema/quality-rules.json: τα κοινά ελάχιστα ποιότητας (π.χ. αριθμός ασκήσεων, αριθμός αποδεκτών απαντήσεων free-text), που καταναλώνονται από τον client-side validator περιεχομένου αντί για ένα δεύτερο, χειροκίνητα συντηρούμενο αντίγραφο.
  • Οι τύποι μαθήματος TypeScript του frontend και η σελίδα MkDocs Lesson format reference παράγονται επίσης (μην τα επεξεργάζεσαι στο χέρι)· ακολουθούν τον καθρέφτη του engine, οπότε ξανατρέξε τη γεννήτρια μετά από κάθε re-pin.

Μια πύλη απόκλισης (make sync-schema-check, μέρος του release-test, συν το backend/tests/test_lesson_schema_drift.py στο make test) αποτυγχάνει αν κάποιο παραγόμενο artefact αποκλίνει από τον καρφιτσωμένο καθρέφτη του engine. Το κλείσιμο της αλυσίδας είναι η πύλη byte-ισοτιμίας εφαρμογής-έναντι-engine: make engine-parity-check (scripts/check_engine_schema_parity.py), το offline pin engine-schema-parity.test.ts και το τεστ συνοχής pin engine-pin.test.ts (dependency του frontend/package.json == schema/engine-version.txt). Τα repos περιεχομένου καθρεφτίζουν το καρφιτσωμένο release του engine (όχι αυτό το repo) και επικυρώνουν έναντι αυτού του καθρέφτη στη δική τους CI.

Διαδικασία αλλαγής μορφής (η αυθεντία του σχήματος στο engine): μια αλλαγή στη μορφή μαθήματος ξεκινά στο engine ή επικυρώνεται εκεί: πρώτα PR στο engine + npm release· μετά αυτή η εφαρμογή ανεβάζει το pin του engine (frontend/package.json + schema/engine-version.txt) και ξανατρέχει το make sync-schema, που ανανεώνει τον καθρέφτη και αναγεννά το δομικό στρώμα Pydantic· μόνο οι νέοι σημασιολογικοί validators γράφονται στο χέρι· έπειτα τα repos περιεχομένου ξανακαρφιτσώνουν το engine-version.txt τους. Μια χειροκίνητη επεξεργασία του καθρέφτη (ή ένα μπαγιάτικο pin) κάνει κόκκινες τις πύλες byte-ισοτιμίας· το ξεχασμένο βήμα γίνεται ορατό, ποτέ σιωπηλή απόκλιση.

Ζεύγη γλωσσών (v1.44.0)

Κάθε Content-Set δηλώνει το ΖΕΥΓΟΣ γλωσσών που μεταδίδει:

  • target_language - αυτό που ΜΑΘΑΙΝΕΙ ο εκπαιδευόμενος (π.χ. fr).
  • source_language - αυτό που ΜΙΛΑΕΙ ήδη ο εκπαιδευόμενος, δηλαδή η γλώσσα στην οποία είναι γραμμένα τα πεδία back των καρτών, τα notes και το κείμενο θεωρίας (π.χ. de).

Ακριβώς αυτό κάνει τα «Γαλλικά για Αγγλόφωνους» ένα διαφορετικό σύνολο από τα «Γαλλικά για Γερμανόφωνους»: ίδιος στόχος (fr), διαφορετική γλώσσα αφετηρίας (en έναντι de), διαφορετική γλώσσα επεξήγησης. Ένας εκπαιδευόμενος βλέπει μόνο σύνολα των οποίων η source_language ταιριάζει με μια από τις γλώσσες που μιλάει (γλώσσα εφαρμογής συν προαιρετικές πρόσθετες γλώσσες στις Ρυθμίσεις → Μάθηση).

Τα Set-IDs κωδικοποιούν το ζεύγος ως {target}-{level}-from-{source} (π.χ. fr-a1-from-de), και κάθε σύνολο δηλώνει ένα path, που δείχνει στον κατάλογο γλώσσας αφετηρίας του (sets/de/fr-a1). Ένα σύνολο φέρει επιπλέον title (στη γλώσσα αφετηρίας, αυτό που διαβάζει ο εκπαιδευόμενος) και title_native (στη γλώσσα στόχο, ως δευτερεύων τίτλος).

Και οι δύο κωδικοί πρέπει να είναι ISO-639-1 (δύο γράμματα), και η source_language πρέπει να διαφέρει από την target_language. Σύνολα πριν την v1.2 χωρίς αυτά τα πεδία φορτώνουν ακόμη: το παλιό κλειδί language γίνεται δεκτό ως target_language, και η source_language πέφτει πίσω στο en.

Διάταξη καταλόγων

Το δέντρο είναι οργανωμένο κατά ΓΛΩΣΣΑ ΑΦΕΤΗΡΙΑΣ, μετά στόχο+επίπεδο:

my-content-repo/
  manifest.yaml               # Root: lists every set (with path + pair)
  sets/
    de/                       # Source language: German
      fr-a1/                  # Target French, level A1  -> ID fr-a1-from-de
        manifest.yaml         # Set: lists the lessons
        lessons/
          01-begruessung.json
          ...
        assets/               # optional images / audio
    en/                       # Source language: English
      fr-a1/                  # -> ID fr-a1-from-en
        ...

Ευρετήριο αναζήτησης (search-index.json)

Η ανακάλυψη και η αναζήτηση περιεχομένου (η επιφάνεια Discover) οδηγείται από ένα λιτό search-index.json που δημοσιεύεται στη ρίζα του repo (~4 KB, μόνο μεταδεδομένα - κανένα περιεχόμενο καρτών). Το επίσημο repo περιεχομένου το παρέχει, και η εφαρμογή φέρνει τα ευρετήρια κάθε ρυθμισμένου repo από την πλευρά του client (CORS-safe, cached στο localStorage με ένα TTL 24 ωρών stale-while-revalidate), ώστε ένας εκπαιδευόμενος να μπορεί να ΒΡΕΙ ένα σύνολο πριν το κατεβάσει. Κάθε καταχώρηση διαφημίζει το id, name, description του συνόλου, τη source_language / target_language, το level, τον domain, το lesson_count, το card_count, τα tags, ένα flag ai_validated, ένα trust_level, ένα προαιρετικό συνοδευτικό book και μια χρονοσφραγίδα updated_at. Κράτησέ το συγχρονισμένο με τα manifest των συνόλων· ένα PR στο επίσημο repo το αναγεννά.

Μορφή Manifest

Το σχήμα πεδίων manifest (το root manifest.yaml που παραθέτει τα σύνολα του repo, με κάθε υποχρεωτικό και προαιρετικό πεδίο) βρίσκεται στην αναφορά του engine: learn-content-engine, Manifest format. Η λίστα πεδίων σκόπιμα δεν επαναλαμβάνεται εδώ: το αυστηρό σχήμα του engine (άγνωστα πεδία απορρίπτονται) επικυρώνει κάθε manifest, και η αναφορά του engine είναι η μοναδική έγκυρη περιγραφή της. Σύνταξε τα πεδία του ζεύγους γλωσσών (target_language / source_language) όπως περιγράφεται στην ενότητα «Ζεύγη γλωσσών»· το προ-v1.2 alias language φορτώνει ακόμη, αλλά αποθαρρύνεται για νέα σύνολα.

Το προαιρετικό πεδίο visibility (engine 0.14.0+, visible όταν λείπει) είναι μια υπόδειξη εμφάνισης για τις εφαρμογές-καταναλωτές: το visibility: hidden ζητά από την εφαρμογή να μην εμφανίζει το σύνολο στους εκπαιδευόμενους - προορίζεται για fixtures αναφοράς/συμμόρφωσης, που πρέπει να μείνουν στο repo για την επικύρωση του engine αλλά δεν είναι μαθησιακό περιεχόμενο. Η εφαρμογή φιλτράρει τα κρυμμένα σύνολα έξω από τις επιφάνειες περιήγησης και Discover (ακόμη κι όταν είναι ήδη στην cache)· το engine εξακολουθεί να τα επικυρώνει. Λίστα κρυμμένων συνόλων στην πλευρά της εφαρμογής δεν υπάρχει πλέον.

Συμπεριφορά του loader ειδική για την εφαρμογή που πρέπει να έχεις υπόψη:

  • Το Set-Manifest παραθέτει κάθε αρχείο μαθήματος κάτω από metadata.lessons, και ο Content-Loader επαναλαμβάνει αυτή τη λίστα με τη δεδομένη σειρά: τα ονόματα αρχείων στον δίσκο είναι άσχετα, μόνο η σειρά του manifest μετρά:
metadata:
  lessons:
    - 01-intro.json
    - 02-articles.json
    - ...

Σχήμα μαθήματος

Κάθε μάθημα είναι ένα μεμονωμένο αρχείο JSON: μεταδεδομένα κορυφαίου επιπέδου (id, title, description, estimated_minutes), μια λίστα από cards (τις μικρότερες μαθήσιμες μονάδες - σταθερά ids, ζεύγη front/back, Markdown notes, tags για το SRS) και μια λίστα από steps, κάθε ένα είτε βήμα THEORY (ένα Markdown body, προαιρετικά ένας σύνδεσμος example_url ή inline examples) είτε βήμα EXERCISE (ακριβώς μία άσκηση).

Η πλήρης αναφορά μορφής, πεδίο προς πεδίο - κάθε πεδίο, κάθε τύπος άσκησης, κάθε λειτουργία cloze, με παραδείγματα JSON που επικυρώνονται από τη σουίτα δοκιμών του engine - βρίσκεται στην αναφορά του engine:

  • learn-content-engine - docs/lesson-format.md
  • η κανονική αναφορά μορφής μαθήματος για συντάκτες και τρίτους validators (χωρίς ανάγκη checkout της εφαρμογής)
  • το μηχανικά αναγνώσιμο σχήμα που ομαδοποιείται με κάθε release του engine: import schema from "learn-content-engine/schema/lesson.schema.json"
  • ο δίδυμος μέσα στην εφαρμογή: η παραγόμενη Lesson format reference

Το ομαδοποιημένο σχήμα του engine είναι byte-πανομοιότυπο με το παραγόμενο schema/lesson.schema.json αυτού του repo (επιβάλλεται από το make engine-parity-check), οπότε «επικυρώνεται έναντι του engine» και «επικυρώνεται στην εφαρμογή» είναι η ίδια δήλωση.

Ποιος τύπος άσκησης για ποιον μαθησιακό στόχο

Διάλεξε τον τύπο άσκησης βάσει του μαθησιακού στόχου, όχι για την ποικιλία. Η βαθμολόγηση ακριβούς αντιστοίχισης λέξη προς λέξη - ένα word_tiles ολόκληρης πρότασης, ή ένα free_text πλήρους πρότασης - αποτυγχάνει για την ελεύθερη παραγωγή: μια έννοια μπορεί να διατυπωθεί με πολλούς σωστούς τρόπους, οπότε ένας ουσιαστικά σωστός εκπαιδευόμενος βαθμολογείται λάθος λέξη προς λέξη. Αυτή είναι η πιο αποθαρρυντική στιγμή που μπορεί να παραγάγει ένα μάθημα. Ταίριαξε αντ' αυτού τον τύπο με τον στόχο:

Μαθησιακός στόχος Σωστός τύπος
Ένα γεγονός με μία απάντηση cloze (ένα κενό)
Αναγνώριση μιας έννοιας πολλαπλή επιλογή (cloze σε λειτουργία select) / matching
Ορισμός μιας έννοιας cloze με κενά σε λέξεις-κλειδιά
Ελεύθερη εξήγηση / μεταφορά / σύγκριση δεν υπάρχει ακόμη τύπος ακριβούς αντιστοίχισης - προς το παρόν χρησιμοποίησε cloze / πολλαπλή επιλογή· η αυτοαξιολόγηση είναι προγραμματισμένη
Πρόταση με μία μονοσήμαντη σειρά λέξεων (εκμάθηση γλώσσας) word_tiles

Εμπειρικός κανόνας: κράτησε το word_tiles για προτάσεις των οποίων η σειρά λέξεων είναι πραγματικά μοναδική (μια άσκηση μετάφρασης), και σύνταξε ορισμούς και γεγονότα ως cloze (ή πολλαπλή επιλογή μέσω λειτουργίας cloze select). Ποτέ μη βάζεις έναν ελεύθερης μορφής ορισμό σε word_tiles ή σε free_text πλήρους πρότασης - δεν υπάρχει δίκαιη βαθμολόγηση ακριβούς αντιστοίχισης για αυτό. Πλήρης ανάλυση: βλ. EXP-041 (docs/explorations/EXP-041-aufgabentyp-eignung-und-faire-bewertung.md).

Κατάλογος τύπων άσκησης (κατάσταση)

Μία αναφορά κάθε τύπου άσκησης: τι αποστέλλεται, τι είναι εκφράσιμο χωρίς νέο τύπο, τι είναι υποψήφιο και τι αποκλείεται σκόπιμα. Το κανονικό μοντέλο δεν επεκτείνεται εκ των προτέρων - ένας τύπος αποστέλλεται μόνο μαζί με τον renderer του (το μητρώο SUPPORTED_EXERCISE_TYPES πρέπει να ισούται με την enum ExerciseType· ένα parity test το επιβάλλει, το μάθημα που πήραμε από τις περιπτώσεις v1.4-preview / picture_choice). Νέοι τύποι προστίθενται κατόπιν συγκεκριμένης ανάγκης περιεχομένου μέσω της συνταγής Adding a new exercise type.

Υλοποιημένοι (η enum ExerciseType)

Τύπος Για τι (μαθησιακός στόχος, EXP-041) Σημείωση
matching Αναγνώριση / αντιστοίχιση εννοιών Drag-pair, ≥ 3 ζεύγη.
picture_choice Αναγνώριση από μια πραγματική εικόνα ≥ 2 εικόνες, ακριβώς μία σωστή. Όχι για πολλαπλή επιλογή κειμένου.
free_text Παραγωγή μιας σύντομης απάντησης τύπου γεγονότος Ακριβής αντιστοίχιση, μετά Levenshtein ≤ 1.
word_tiles Μία μονοσήμαντη σειρά λέξεων (γλώσσα) Τα πλακίδια ανακατεύονται· accept_orderings για παραλλαγές.
cloze (type) Ένα γεγονός με μία απάντηση Ένα <input> ανά κενό.
cloze (select) Μονή πολλαπλή επιλογή (όχημα legacy) Αποδίδεται ως πατήσιμα κουμπιά (#1342). accept[0] σωστό + distractors.
cloze (multiselect) «Επιλογή όλων όσων ισχύουν» (όχημα legacy) Ακριβής αντιστοίχιση συνόλου πάνω σε accept (όλα σωστά) + distractors (#1195).
multiple_choice Εγγενής πολλαπλή επιλογή κειμένου (σχήμα v1.6, #1525) options ({text, correct?}, μοναδικά κείμενα) + multiple. Μονή = ακριβώς μία σωστή· πολλαπλή = ακριβής αντιστοίχιση συνόλου, χωρίς μερική πίστωση.

Από το σχήμα v1.6 υπάρχει ένας εγγενής τύπος multiple_choice. Συνυπάρχει με το όχημα cloze select/multiselect (EXP-036 §4.3, #890) - η υπάρχουσα πολλαπλή επιλογή με βάση το cloze παραμένει έγκυρη, τίποτα δεν είναι deprecated. Προτίμησε το multiple_choice για νέο περιεχόμενο πολλαπλής επιλογής κειμένου: η ορθότητα είναι flag ανά επιλογή, οπότε η παγίδα της μη επικάλυψης accept/distractor δεν μπορεί να συμβεί. Βλ. την ενότητα «Δημιουργία πολλαπλής επιλογής».

Tier επεκτάσεων (ο namespace ext:)

Πέρα από την κλειστή βασική enum υπάρχουν τύποι ασκήσεων στον namespace ext:<vendor>-<name>. Είναι δομικά αδιαφανείς προς το βασικό σχήμα: ένα μάθημα που τους χρησιμοποιεί τους δηλώνει στο requires_extensions, και το payload επικυρώνεται από την εγγεγραμμένη επέκταση, ποτέ από το βασικό σχήμα. Ο μηχανισμός περιγράφεται στην αναφορά του engine learn-content-engine - docs/extensions.md. Η εφαρμογή έχει υιοθετήσει πέντε τύπους επέκτασης (SUPPORTED_EXT_EXERCISE_TYPES στον ExerciseDispatcher· μια πύλη ισοτιμίας κρατά dispatcher και load guard συγχρονισμένους, ώστε οτιδήποτε φορτώσιμο να είναι αποδόσιμο):

Τύπος Για τι Payload (ext_payload) Υιοθέτηση
ext:al-categorization Ταξινόμηση όρων σε ομάδες categories: [{name, items[]}], τουλάχιστον 2 ομάδες #1591 (πρώτος τύπος επέκτασης, καταγραφή #1579)
ext:al-error-correction Διόρθωση ενός εσφαλμένου κειμένου tokens[] + error_index + accept[] #1593
ext:al-reading-comprehension Κατανόηση κειμένου (απόσπασμα + ερωτήσεις) passage + questions[] (κάθε μία υποερώτηση multiple_choice / free_text) #1603
ext:al-graded-quiz Βαθμολογημένο κουίζ questions[] (κάθε μία με points) + προαιρετικό pass_threshold #1616· το σύνολο αναφοράς demo είναι κρυμμένο από το Discover / Τα μαθήματά μου (#1702)
ext:al-dictation Ηχητική υπαγόρευση (άκου, μετά μετάγραψε) audio (ένα κλιπ assets/ ή ένα data URI ενσωματωμένο μέσω του upload του επεξεργαστή, #1911) + accept[] (ανεκτική αντιστοίχιση μεταγραφής) #1881 (πέμπτη υιοθέτηση)

Δύο διαδρομές δημιουργίας. Οι ασκήσεις επέκτασης μπορούν να δημιουργηθούν (α) απευθείας ως JSON στο repo περιεχομένου (η κανονική διαδρομή, που περιγράφεται στην αναφορά του engine), ή (β) μέσα στην εφαρμογή. Ο Lesson Creator απέκτησε έναν οδηγό δημιουργίας επεκτάσεων (#1852), προσβάσιμο από το πρότυπο Advanced exercise types στο βήμα 1, που καλύπτει και τους πέντε τύπους (#1859 categorization + error-correction, #1865 reading-comprehension + graded-quiz, #1887 dictation). Η υπαγόρευση είναι επίσης προσβάσιμη από τον βασικό επιλογέα τύπων άσκησης στο βήμα 3, πίσω από μια γενικευμένη πύλη requires_extensions (#1895). Και οι δύο διαδρομές παράγουν το ίδιο JSON μαθήματος και ορίζουν το requires_extensions (με έκδοση, π.χ. ext:al-dictation@1).

Παράδειγμα ανά τύπο επέκτασης

Κάθε μπλοκ είναι το αντικείμενο άσκησης όπως εμφανίζεται σε ένα .json μαθήματος· τα δεδομένα ανά τύπο βρίσκονται κάτω από το ext_payload. Η κανονική αναφορά πεδίων είναι το docs/extensions.md του engine.

{
  "type": "ext:al-categorization",
  "prompt": "Sort each word into fruit or vegetable.",
  "ext_payload": {
    "categories": [
      {"name": "Fruit", "items": ["apple", "banana"]},
      {"name": "Vegetable", "items": ["carrot", "potato"]}
    ]
  }
}
{
  "type": "ext:al-error-correction",
  "prompt": "One word is wrong. Correct it.",
  "ext_payload": {
    "tokens": ["The", "two", "child", "are", "playing"],
    "error_index": 2,
    "accept": ["children"]
  }
}
{
  "type": "ext:al-reading-comprehension",
  "prompt": "Read the text and answer.",
  "ext_payload": {
    "passage": "Marie is sitting in a café. She orders a coffee and reads a book.",
    "questions": [
      {
        "prompt": "Where is Marie?",
        "type": "multiple_choice",
        "options": [
          {"text": "In a café", "correct": true},
          {"text": "At home"},
          {"text": "At the station"}
        ]
      }
    ]
  }
}
{
  "type": "ext:al-graded-quiz",
  "prompt": "Greetings quiz.",
  "ext_payload": {
    "pass_threshold": 60,
    "questions": [
      {
        "prompt": "How do you say 'hello' in French?",
        "type": "multiple_choice",
        "points": 1,
        "options": [
          {"text": "Bonjour", "correct": true},
          {"text": "Merci"},
          {"text": "Au revoir"}
        ]
      }
    ]
  }
}
{
  "type": "ext:al-dictation",
  "prompt": "Listen and type what you hear.",
  "ext_payload": {
    "audio": "assets/audio/comment-ca-va.mp3",
    "accept": ["Comment ça va ?", "Comment ca va"]
  }
}

Διαθεσιμότητα στον οδηγό μαθήματος

Το παίξιμο (υπάρχει renderer), το δημιουργήσιμο (το μείγμα AI μπορεί να το παραγάγει) και η χειροκίνητη προσθήκη (προσθέτεις και επεξεργάζεσαι μία με το χέρι στο βήμα 3) είναι τρία διαφορετικά πράγματα. Και οι έξι βασικοί τύποι είναι παίξιμοι ΚΑΙ δημιουργήσιμοι: ο επιλογέας τύπων στον οδηγό δημιουργίας μαθήματος (ALL_TYPES στο ExerciseGenerator.tsx) προσφέρει κάθε βασικό τύπο, και κάθε άσκηση του βήματος 3 είναι επεξεργάσιμη inline και αναδιατάξιμη, με ένα χειροκίνητο κουμπί + Προσθήκη άσκησης (#1849, #1853).

Τύπος Παίξιμο Δημιουργήσιμο (μείγμα AI) Χειροκίνητη προσθήκη (βήμα 3)
matching ναι ναι ναι
free_text ναι ναι ναι
cloze ναι ναι ναι
word_tiles ναι ναι ναι
picture_choice ναι ναι ναι
multiple_choice ναι ναι (#1853· έλεγχος λειτουργίας μονής/πολλαπλής #1888) ναι
ext:al-dictation ναι όχι ναι, μέσω του βασικού επιλογέα (#1895) ή του οδηγού επεκτάσεων (#1887)
ext:al-categorization ναι όχι μέσω του οδηγού επεκτάσεων (#1859)
ext:al-error-correction ναι όχι μέσω του οδηγού επεκτάσεων (#1859)
ext:al-reading-comprehension ναι όχι μέσω του οδηγού επεκτάσεων (#1865)
ext:al-graded-quiz ναι όχι μέσω του οδηγού επεκτάσεων (#1865)

Οι τέσσερις τύποι επέκτασης πλην της υπαγόρευσης δημιουργούνται στον οδηγό επεκτάσεων (ή ως JSON στο repo περιεχομένου), ποτέ αναμεμειγμένοι στη βασική δημιουργία AI.

Το listen-first είναι λειτουργία, όχι τύπος. Από το #1687 (απόφαση

1600, επιλογή A) οι ασκήσεις free_text και matching μπορούν να

φέρουν ένα στοιχείο audio-first (άκου πρώτα, μετά απάντησε). Ο τύπος της άσκησης δεν αλλάζει. Η επιλογή B της ίδιας απόφασης, ένας τύπος υπαγόρευσης, κυκλοφόρησε ως η επέκταση ext:al-dictation (#1881), τεκμηριωμένη στο tier επεκτάσεων παραπάνω.

Ο Lesson Creator ως εργαλείο δημιουργίας

Ο Lesson Creator μέσα στην εφαρμογή (/create-lesson) είναι μια πλήρης επιφάνεια δημιουργίας, όχι μόνο ένα κουμπί δημιουργίας-με-AI:

  • Κάθε άσκηση του βήματος 3 είναι επεξεργάσιμη επιτόπου. Κάθε παραγόμενη ή προστεθειμένη άσκηση ανοίγει σε έναν inline επεξεργαστή (και οι έξι βασικοί τύποι, συν οι επεξεργαστές επεκτάσεων)· αναδιάταξη με σύρσιμο, διαγραφή, ή αναδημιουργία όλου του μείγματος (#1845).
  • Πρόσθεσε μια άσκηση με το χέρι. Το κουμπί + Προσθήκη άσκησης διαλέγει έναν τύπο και προσαρτά μια κενή άσκηση κατευθείαν στον inline επεξεργαστή, ώστε να μπορείς να συντάσσεις χωρίς καμία δημιουργία AI (#1849, #1853). Ο επιλογέας παραθέτει τους έξι βασικούς τύπους συν την υπαγόρευση (#1895).
  • Η παραδειγματική πρόταση οδηγεί τη δημιουργία. Μια κάρτα (βήμα 2) μπορεί να φέρει μια προαιρετική παραδειγματική πρόταση. Αυτή είναι που ενεργοποιεί τη δημιουργία cloze και word_tiles για εκείνη την κάρτα (για το cloze, η πρόταση πρέπει να περιέχει τον όρο front της κάρτας ώστε να μπορεί να αφαιρεθεί ως κενό), και μια εικόνα κάρτας ενεργοποιεί το picture_choice. Χωρίς αυτά, οι τύποι εκείνοι παραλείπονται σιωπηλά, και το βήμα 3 εξηγεί ποιος επιλεγμένος τύπος δεν παρήγαγε τίποτα (#1847, #1848).
  • Τα παραγόμενα prompts ακολουθούν τη γλώσσα του UI. Τα πρότυπα οδηγιών ασκήσεων τοπικοποιούνται κατά τη δημιουργία (#1857), ώστε ένας συντάκτης σε γερμανικό UI να παίρνει γερμανικά prompts, όχι αγγλικές προεπιλογές. Όταν ανοίγεις ένα παλαιότερο μάθημα για επεξεργασία, κάθε prompt άσκησης που είναι ακόμη byte-πανομοιότυπο με μια legacy αγγλική προεπιλογή μεταναστεύει ευκαιριακά στο πρότυπο της γλώσσας UI (μόνο σε κατάσταση επεξεργασίας, διατηρείται μόνο αν αποθηκεύσεις) (#1861).

Εκφράσιμο χωρίς νέο τύπο (συμβάσεις, όχι τύποι)

Έννοια Πώς
Σωστό/Λάθος, Ναι/Όχι multiple_choice δύο επιλογών (ή cloze select δύο επιλογών)
Dropdown / radio / checkbox Παρουσίαση του multiple_choice / cloze select - όχι ξεχωριστοί τύποι

Προγραμματισμένο αν χρειαστεί (υποψήφιοι - ΟΧΙ δέσμευση)

Υποψήφιος Κοντά σε Πότε
Διάταξη / ταξινόμηση word_tiles Μόνο κατόπιν συγκεκριμένης ανάγκης περιεχομένου, τότε μέσω της συνταγής.
Πεδίο αριθμού (αριθμητική σύγκριση) free_text Μόνο κατόπιν συγκεκριμένης ανάγκης περιεχομένου, τότε μέσω της συνταγής.

Σκόπιμα αποκλεισμένο

Αποκλεισμένο Γιατί (μία γραμμή)
Έκθεση / μεγάλο κείμενο / σχέδιο / τύπος / αξιολόγηση από ομοτίμους / ελεύθερη αυτοαξιολόγηση Δεν βαθμολογείται δυαδικά από το SRS· η αυτοαξιολόγηση αναβλήθηκε (#1268).
Ήχος / βίντεο / μεταφόρτωση αρχείου Αποθήκευση + υποδομή· συγκρούεται με το offline-first. Μοναδική εξαίρεση: σύντομα ηχητικά κλιπ υπαγόρευσης, που ο επεξεργαστής ασκήσεων ενσωματώνει στο μάθημα ως data URI.
Hotspot / προσομοίωση / μνήμη / σταυρόλεξο Κόπος υλοποίησης χωρίς αξία SRS (μεταγενέστερη, ξεχωριστή απόφαση αν ποτέ).
Πίνακας / Likert / ολισθητής Τύποι έρευνας, όχι τύποι μάθησης.
Επιλογείς ημερομηνίας / ώρας Τύποι φόρμας, όχι τύποι μάθησης.

Αναφορά τύπων ασκήσεων

Η αναφορά πεδίων ανά τύπο - matching, picture_choice, free_text, word_tiles, multiple_choice και cloze με τις λειτουργίες του type / select / multiselect: υποχρεωτικά πεδία, παραδείγματα JSON και οι σημασιολογικοί κανόνες (δείκτες cloze ___ == blanks, αναφορική ακεραιότητα card_ids, μη επικάλυψη accept/distractor στο multiselect, ακριβώς-μία-σωστή στο picture-choice) - βρίσκεται στην αναφορά του engine: learn-content-engine - docs/lesson-format.md. Κάθε παράδειγμα JSON εκεί εξάγεται και επικυρώνεται από τη σουίτα δοκιμών του engine, οπότε η αναφορά δεν μπορεί να σαπίσει. Οι συμβάσεις δημιουργίας ειδικές για την εφαρμογή παρακάτω παραμένουν εδώ.

Δημιουργία πολλαπλής επιλογής

Προτιμώμενο (σχήμα v1.6+, #1525): ο εγγενής τύπος multiple_choice. Κάθε επιλογή φέρει το δικό της flag correct, οπότε δεν υπάρχουν ξεχωριστές λίστες accept/distractors που πρέπει να μένουν ξένες μεταξύ τους. Το multiple: false (προεπιλογή) είναι μονή επιλογή (ακριβώς μία σωστή)· το multiple: true είναι «επιλογή όλων όσων ισχύουν» (βαθμολόγηση ακριβούς συνόλου, χωρίς μερική πίστωση):

{
  "id": "ex-capital",
  "type": "multiple_choice",
  "prompt": "What is the capital of France?",
  "card_ids": ["card-paris"],
  "options": [
    {"text": "Paris", "correct": true},
    {"text": "Berlin"},
    {"text": "Madrid"},
    {"text": "Rome"}
  ]
}

Όχημα legacy (παραμένει πλήρως έγκυρο: συνύπαρξη, τίποτα deprecated): πριν από την v1.6, η πολλαπλή επιλογή κειμένου δημιουργούνταν ως cloze σε λειτουργία select (EXP-036 §4.3,

890). Μια ερώτηση μονής απάντησης είναι ένα cloze με ένα κενό: η

sentence (που τελειώνει σε ___) είναι η ερώτηση, το accept[0] του κενού είναι η σωστή επιλογή και τα distractors είναι οι λανθασμένες. Παράδειγμα: "sentence": "The capital of France is ___.", "blanks": [{"accept": ["Paris"]}], "cloze_mode": "select", "distractors": ["Berlin", "Madrid", "Rome"].

Μπορείς επίσης να βάλεις όλη την ερώτηση στο prompt και να χρησιμοποιήσεις ένα σκέτο "sentence": "___"· ο renderer δείχνει ένα <select> από τη σωστή απάντηση + τους distractors, βαθμολογεί την επιλογή, δίνει feedback και τροφοδοτεί το SRS:

{
  "id": "ex-hook-state",
  "type": "cloze",
  "prompt": "Which hook manages local state in a function component?",
  "card_ids": ["card-usestate"],
  "sentence": "___",
  "blanks": [{"accept": ["useState"]}],
  "cloze_mode": "select",
  "distractors": ["useEffect", "useContext", "useRef"]
}

Μη δημιουργείς ποτέ πολλαπλή επιλογή κειμένου ως picture_choice. Αυτός ο τύπος είναι μόνο για πραγματικά assets εικόνων· για επιλογές κειμένου αποδίδει πλακίδια placeholder, όχι ένα χρηστικό στοιχείο ελέγχου (πρβλ. astrapi69/adaptive-learner-content-test#10). Η πολλαπλή επιλογή κειμένου είναι multiple_choice (προτιμώμενο) ή cloze σε λειτουργία select, όπως παραπάνω.

Η «επιλογή όλων όσων ισχύουν» (δύο ή περισσότερες σωστές απαντήσεις, π.χ. μια ερώτηση εξέτασης διπλώματος οδήγησης) χρησιμοποιεί cloze_mode: "multiselect":

{
  "type": "cloze",
  "cloze_mode": "multiselect",
  "sentence": "Which cities are in Germany?",
  "accept": ["Berlin", "Hamburg"],
  "distractors": ["Vienna", "Zurich"]
}

Πολλαπλά κενά ανά cloze υποστηρίζονται: κάθε ___ στην πρόταση αντιστοιχίζεται με τη σειρά στην επόμενη καταχώρηση στο blanks. Κάθε κενό μπορεί να έχει δικό του hint + placeholder + λίστα accept. Το SRS στοιχείων ξεδιπλώνει ανά κενό ένα ElementAttempt - όποιος συμπληρώνει άπταιστα το κενό A, αλλά αστοχεί συνεχώς στο κενό B, λαμβάνει παρακολούθηση κατάκτησης με κενο-γκρανουλάρ ανάλυση.

Ρόλοι token σε Cards (Φάση 52I / v1.35.0) - προαιρετικά μεταδεδομένα Card, με τα οποία η γεννήτρια cloze μπορεί στον χρόνο εκτέλεσης (συνεδρίες review + ο γύρος διόρθωσης στο τέλος του μαθήματος) να επιλέξει ένα σημασιολογικά σημαντικό κενό:

{
  "id": "art-un",
  "front": "un chat",
  "back": "eine Katze",
  "tags": ["article"],
  "token_roles": [
    {"token": "un", "role": "article"}
  ]
}

Κλειστή enum ρόλων: article / verb / noun / adjective / preposition / gender_marker / tense_marker. Η προσθήκη ενός ρόλου είναι μια αναβάθμιση δευτερεύουσας έκδοσης σχήματος - μην την επεκτείνεις inline.

Μη λατινικές γραφές: σύμβαση μεταγραφής

Δεσμευτικοί κανόνες για σύνολα των οποίων η γλώσσα στόχος χρησιμοποιεί μη λατινική γραφή (ιαπωνικά, κινέζικα, κορεατικά, ελληνικά, χίντι, ...). Καθιερωμένοι και εφαρμοσμένοι στο repo περιεχομένου - προηγούμενα: content#90, content#91· σαρώσεις υπόλοιπων κενών: content#106, content#107.

1. Κανόνας κατεύθυνσης. Η μεταγραφή είναι μόνο για τη μη λατινική γλώσσα στόχο όταν η γλώσσα αφετηρίας γράφεται σε λατινική γραφή (de→ja, de→zh, de→ko, ...). Μια μη λατινική γλώσσα αφετηρίας με λατινόγραφη γλώσσα στόχο (hi→en, el→fr) δεν παίρνει μεταγραφή - ο εκπαιδευόμενος διαβάζει ήδη τη δική του γραφή.

2. Μορφή. Στρογγυλές παρενθέσεις αμέσως μετά το πρωτότυπο: こんにちは (konnichiwa). Στα βήματα θεωρίας πάντα· στις επιλογές και τα prompts μόνο όπου είναι αβλαβές (βλ. τον κανόνα μη προδοσίας).

3. Κανόνας μη προδοσίας (ο πυρήνας). Η μεταγραφή δεν πρέπει ποτέ να αποκαλύπτει τη λύση. Οι εργασίες ανάγνωσης γραφής, η αναγνώριση τόνου, τα πλακίδια word_tiles και τα συμφραζόμενα προτάσεων cloze παραμένουν ΧΩΡΙΣ μεταγραφή στο ζητούμενο στοιχείο· οι εργασίες σημασίας την παίρνουν. Όταν υπάρχει αμφιβολία, άφησέ την έξω.

  • Θετικό παράδειγμα (αντιστοίχιση σημασίας, content#91): το ζεύγος matching {"left": "妈 (mā)", "right": "Mama / Mutter"} - η ζητούμενη γνώση είναι η σημασία, οπότε το βοήθημα ανάγνωσης δεν προδίδει τίποτα.
  • Αρνητικό παράδειγμα (ανάγνωση γραφής, content#91): οι ασκήσεις ανάγνωσης γραφής ko-a1/01-hangul-lesen παραμένουν χωρίς μεταγραφή, επειδή η ρωμανοποίηση ΕΙΝΑΙ η απάντηση (χαρακτήρας → ήχος)· 가 (ga) στο prompt θα έδινε στον εκπαιδευόμενο τη λύση.

4. Πρότυπη ρωμανοποίηση ανά γλώσσα, συνεπής μέσα σε ένα σύνολο: ιαπωνικά Hepburn, κινέζικα Pinyin ΜΕ σημάδια τόνου, κορεατικά Revised Romanization, ελληνικά/χίντι μια κοινή απλοποιημένη μεταγραφή. Ποτέ μην αναμειγνύεις συστήματα μέσα σε ένα σύνολο.

5. Εργασίες πληκτρολόγησης (free_text / cloze σε λειτουργία type): το accept[0] είναι η κανονική ρωμανοποιημένη μορφή· επιπλέον δέξου κοινές παραλλαγές - ιαπωνικά: γραφές Kunrei (si/ti/tu/hu/zi, π.χ. konnitiwa δίπλα στο konnichiwa)· κινέζικα: Pinyin χωρίς τόνους (nihao δίπλα στο nǐ hǎo)· κορεατικά: διαδεδομένες εναλλακτικές (π.χ. annyeong haseyo). Μνημονικό: μια άσκηση δεν πρέπει ποτέ να αποτυγχάνει στο πληκτρολόγιο του εκπαιδευόμενου. Προηγούμενο (μπλοκάρισμα IME, content#107): ένα cloze που δεχόταν μόνο το 가 ήταν άλυτο χωρίς κορεατικό IME - το ρωμανοποιημένο ga έπρεπε να γίνει επίσης δεκτό.

Ποιος τύπος φέρει ποιον μαθησιακό στόχο: βλ. την ενότητα «Κατάλογος τύπων άσκησης (κατάσταση)».

Κατεύθυνση άσκησης (v1.46.0 / EXP-018)

Κάθε άσκηση δέχεται ένα προαιρετικό πεδίο direction, που δηλώνει προς ποια κατεύθυνση εξασκούν οι εκπαιδευόμενοι την κάρτα:

  • target_to_source (προεπιλογή) - ΑΝΤΙΛΗΠΤΙΚΑ: εμφανίζεται η γλώσσα στόχος, αναγνωρίζεται η γλώσσα αφετηρίας (ευκολότερο).
  • source_to_target - ΠΑΡΑΓΩΓΙΚΑ: εμφανίζεται η γλώσσα αφετηρίας, παράγεται η γλώσσα στόχος (δυσκολότερο).
  • both / random - αφήνει στον renderer / στην προσαρμοστική γεννήτρια την επιλογή μιας συγκεκριμένης κατεύθυνσης ανά προσπάθεια.
{
  "type": "matching",
  "direction": "source_to_target",
  "card_ids": ["bonjour"],
  "pairs": [{ "left": "Bonjour", "right": "Guten Tag" }]
}

Το πεδίο είναι additive - το σχήμα παραμένει στην έκδοση 1.2, και μαθήματα χωρίς direction συμπεριφέρονται ακριβώς όπως πριν (αντιληπτικά). Το SRS παρακολουθεί την κατάκτηση ανά κατεύθυνση: μια αντιληπτικά κατακτημένη κάρτα δεν είναι ακόμη παραγωγικά κατακτημένη. Οι ασκήσεις cloze είναι συμφραζομενικά δεσμευμένες και αγνοούν το direction. Για μια προοδευτική δυσκολία κρατάς τα πρώιμα μαθήματα αντιληπτικά και εισάγεις το source_to_target σε μεταγενέστερα μαθήματα (ακριβώς αυτό κάνει το ενσωματωμένο pilot-περιεχόμενο).

Σχολιασμοί για την προσαρμοστική γεννήτρια μαθημάτων (v1.36.0+)

Η προσαρμοστική γεννήτρια μαθημάτων από τη Φάση 53 (/adaptive-lesson/:setId, F-114) συνδυάζει εκ νέου τις υπάρχουσες ασκήσεις, ώστε να αντιμετωπίσει στοχευμένα τις συγκεκριμένες αδυναμίες των εκπαιδευομένων. Η γεννήτρια λειτουργεί χωρίς πρόσθετους σχολιασμούς, δύο πεδία όμως την κάνουν σημαντικά εξυπνότερη:

  1. Ευρύτερη κάλυψη token_roles σε κάρτες. Η γεννήτρια χρησιμοποιεί τα token_roles για να:
  2. Επιλέγει σημασιολογικά λογικά κενά, όταν δημιουργούνται παραλλαγές cloze από σφάλματα (ήδη στην v1.35.0)
  3. Ταξινομεί σφάλματα ως article_gender / verb_conjugation, για τα chips «εστίαση άσκησης» στο Dashboard (53E)
  4. Βρίσκει ΕΝΑΛΛΑΚΤΙΚΕΣ ασκήσεις, που ελέγχουν το ίδιο στοιχείο, όταν η αρχική άσκηση ήταν λάθος (53D λογική παραλλαγών - βρίσκει υποψήφιους, των οποίων η κάρτα έχει μια κατάλληλη καταχώρηση token_roles)

Πρόσθεσε σε ΚΑΘΕ κάρτα που διδάσκει μια δική της γραμματική μονάδα (άρθρα, κλιμένες μορφές ρημάτων, ουσιαστικά με γένος) μια καταχώρηση token_roles. Κόστος: μια πρόσθετη καταχώρηση JSON ανά κάρτα· όφελος: σημαντικά πλουσιότερη προσαρμοστική δημιουργία.

  1. Tags καρτών όπως tags: ["article", "masculine"] διαβάζονται από τον ταξινομητή σφαλμάτων ως fallback, όταν λείπουν τα token_roles. Δεν αντικαθιστούν τα token_roles - είναι ένας φθηνός μεσοβέζικος σχολιασμός.

Τι δεν χρειαζόμαστε ΑΚΟΜΗ (μετατεθειμένο σε μελλοντική αναβάθμιση σχήματος):

  • Διασταυρούμενες αναφορές related_cards ανάμεσα σε κάρτες από διαφορετικά μαθήματα
  • Αξιολογήσεις δυσκολίας ανά άσκηση (η γεννήτρια εκτιμά τη δυσκολία προς το παρόν από το exercise.type)
  • Παραδειγματικές προτάσεις ανά κάρτα στο notes, αναλύσιμες ως εναλλακτικά συμφραζόμενα cloze (η γεννήτρια cloze χρησιμοποιεί αποκλειστικά το front)

Εμπειρικός κανόνας: πρόσθεσε token_roles σε κάθε κάρτα που διδάσκει ένα γραμματικό token. Αυτή είναι μακράν η πιο αποτελεσματική συνήθεια συντάκτη για το προσαρμοστικό σύστημα.

Assets (εικόνες που φέρνει ένα σύνολο) - v1.37.0+

Οι ασκήσεις Picture-Choice και οι εικόνες εξωφύλλου καρτών προέρχονται από δύο πηγές: 1. Αρχεία asset συντάκτη, δηλωμένα στο Set-Manifest και αποστελλόμενα δίπλα στο JSON μαθήματος 2. Placeholder-SVGs, παραγόμενα από το Runtime, όταν δεν υπάρχει asset (χρωματικοί πίνακες για λέξεις χρωμάτων, μεγάλα ψηφία για αριθμούς, στυλ avatar για οτιδήποτε άλλο)

Αν δημοσιεύσεις ένα σύνολο χωρίς assets, το Picture-Choice λειτουργεί παρ' όλα αυτά - η γεννήτρια Placeholder-SVG καλύπτει χρώματα + αριθμούς αυτόματα και πέφτει πίσω για οτιδήποτε άλλο σε έναν ντετερμινιστικό avatar.

Διάταξη καταλόγων

Μέσα στον κατάλογο του συνόλου, τα assets βρίσκονται κάτω από assets/:

sets/
  language-fr-a1/
    manifest.yaml
    lessons/
      01-greetings.json
      02-numbers.json
      ...
    assets/
      img/
        chat.png
        chien.png
        oiseau.png

Δήλωση Manifest

Κάθε asset πρέπει να δηλωθεί στο Set-Manifest, ώστε ο downloader να ξέρει τι να φέρει:

sets:
  - id: language-fr-a1
    title: French A1
    language: fr
    level: A1
    version: '1.0.0'
    lesson_count: 10
    assets:
      - path: img/chat.png
        size_kb: 45
      - path: img/chien.png
        size_kb: 38

Το path είναι σχετικό προς τον κατάλογο assets/ του συνόλου (ΟΧΙ προς το JSON μαθήματος). Στο JSON μαθήματος οι ασκήσεις Picture-Choice αναφέρουν assets ΜΕ το πρόθεμα assets/:

{
  "type": "picture_choice",
  "prompt": "Welches ist 'chat'?",
  "images": [
    {"src": "assets/img/chat.png", "label": "Katze", "is_correct": "true"},
    {"src": "assets/img/chien.png", "label": "Hund"}
  ]
}

Το frontend αφαιρεί το πρόθεμα assets/ αυτόματα κατά την κλήση του asset-resolver, ώστε το JSON μαθήματος να παραμένει στη διαισθητική για τους συντάκτες μορφή.

Όρια μεγέθους + μορφής

  • Όριο ανά asset: 500 KiB. Ο επικυρωτής manifest απορρίπτει assets των οποίων το δηλωμένο size_kb υπερβαίνει αυτό το όριο. Ο downloader απορρίπτει επίσης assets των οποίων το πραγματικό μέγεθος bytes υπερβαίνει τη δήλωση κατά περισσότερο από 10% - κρατά το manifest ειλικρινές.
  • Soft-Limit ανά σύνολο: 10 MiB συνολικό μέγεθος. Ο επικυρωτής προειδοποιεί, αλλά δεν απορρίπτει.
  • Αποδεκτές μορφές: .png / .jpg / .jpeg / .webp / .svg. Κανένα GIF (το κινούμενο περιεχόμενο αποσπά την προσοχή), κανένα BMP (καμία συμπίεση). Για φωτογραφίες προτίμησε WebP - σημαντικά μικρότερο από PNG με συγκρίσιμη ποιότητα. Για εικονίδια + διαγράμματα προτίμησε SVG - κλιμακώνεται καθαρά + ελάχιστο μέγεθος αρχείου.

Συστάσεις μεγέθους

Τα πλακίδια Picture-Choice αποδίδονται έως το πολύ 150x150 px στον desktop και 100x100 px στο κινητό (object-fit: contain). Πηγαίες εικόνες με 300x300 px δίνουν στις οθόνες Retina το καλύτερο αποτέλεσμα χωρίς περιττή απαίτηση δεδομένων. PNGs πάνω από 150 KiB σπάνια δείχνουν καλύτερα από ένα καλά συμπιεσμένο WebP μισού μεγέθους.

Πότε αρκεί το Runtime-Placeholder

Τρία είδη μαθημάτων, όπου το Runtime-Placeholder είναι τόσο καλό, που οι εικόνες συντάκτη δεν φέρνουν μαθησιακό όφελος:

  • Μαθήματα χρωμάτων (rouge / rojo / rot / red): η γεννήτρια placeholder δημιουργεί ένα χρωματιστό πλακίδιο hex που ταιριάζει στο όνομα του χρώματος. Τα πλακίδια συντάκτη είναι περιττά.
  • Μαθήματα αριθμών (7 / 42 / 1492): το placeholder αποδίδει τα ψηφία μεγάλα + κεντραρισμένα. Οι εικόνες συντάκτη θα είχαν νόημα μόνο σε μη αραβικά αριθμητικά συστήματα.
  • Αφηρημένες έννοιες χωρίς προφανή οπτική αναπαράσταση (patience, liberté): το placeholder avatar παρέχει μια σαφή οπτική άγκυρα, χωρίς να επιβάλλει μια αμφιλεγόμενη επιλογή εικονιδίου.

Για οτιδήποτε άλλο (ζώα, αντικείμενα, φαγητό, τόπους, μέλη του σώματος) οι εικόνες συντάκτη βοηθούν μετρήσιμα στην αναγνώριση + ανάκληση.

Λίστα ελέγχου ποιότητας

Πριν το PR για ένα νέο μάθημα έλεγξε:

  • [ ] 3-5 βήματα θεωρίας + 8-12 ασκήσεις ανά μάθημα
  • [ ] Τουλάχιστον 3 τύποι ασκήσεων εκπροσωπούνται (matching, picture-choice, free-text, word-tiles ή cloze - cloze από v1.35.0)
  • [ ] Βήματα θεωρίας ≤ 200 λέξεις ανά βήμα
  • [ ] Ασκήσεις Free-Text: ≥ 3 παραλλαγές accept + ≥ 3 distractors
  • [ ] Word-Tiles: ≥ 3 πλακίδια ανά άσκηση
  • [ ] estimated_minutes: 10-15 (ρεαλιστικά, όχι εξιδανικευμένα)
  • [ ] Οι distractors είναι λάθος-αλλά-εύλογοι - σημασιολογικά συγγενείς, ποτέ τυχαίοι
  • [ ] Card-Notes προσφέρουν πραγματική προστιθέμενη αξία (προφορά, ψευδόφιλοι, σημαία εξαίρεσης)
  • [ ] Προοδευτική δομή: μεταγενέστερες έννοιες χτίζουν πάνω σε προηγούμενες στο ίδιο σύνολο
  • [ ] Πολιτισμική ακρίβεια: πραγματική χρήση γλώσσας, όχι μόνο σχολικές φράσεις
  • [ ] Επικύρωση σχήματος: το μάθημα φορτώνει καθαρά μέσω dict_to_lesson() (δες Τοπικός έλεγχος)
  • [ ] Ακεραιότητα Card-ID: κάθε exercise.card_ids[i] υπάρχει στο cards[] του μαθήματος
  • [ ] Ζεύγος γλωσσών: target_language + source_language ορισμένα (ISO 639-1, διαφορετικά), title_native παρόν

Επικύρωση (δύο επίπεδα, v1.44.0)

Το περιεχόμενο διασφαλίζεται μέσω δύο επιπέδων επικύρωσης με τους ΙΔΙΟΥΣ ελέγχους:

  1. Στην εφαρμογή, πριν την κοινοποίηση. Κατά την κοινοποίηση μέσω Τα μαθήματά μου → Διάθεση στην κοινότητα εκτελείται πρώτα ένας έλεγχος βασισμένος σε κανόνες (πάντα, χωρίς AI). Επιβάλλει τα ελάχιστα παρακάτω· ένα σύνολο κάτω από αυτά δεν μπορεί να κοινοποιηθεί. Αν περάσει και είναι ρυθμισμένο ένα κλειδί AI, ο εκπαιδευόμενος μπορεί ΠΡΟΑΙΡΕΤΙΚΑ να ξεκινήσει έναν συμπληρωματικό έλεγχο AI (ακρίβεια μετάφρασης, ευλογοφάνεια distractors, γραμματική, επίπεδο, πολιτισμική ευαισθησία, φυσικότητα). Το βήμα AI δεν είναι ποτέ αυτόματο, απαιτεί ρητή συγκατάθεση (το περιεχόμενο μαθήματος αποστέλλεται στον ρυθμισμένο πάροχο) και δεν μπλοκάρει ποτέ την κοινοποίηση - ο έλεγχος βασισμένος σε κανόνες είναι η πύλη.
  2. Στο CI του repo περιεχομένου. Ένα Pull Request στο astrapi69/adaptive-learner-content εκτελεί το δικό του scripts/validate_content.py (δομή έναντι του vendored, engine-καρφιτσωμένου καθρέφτη σχήματος + ελάχιστα ποιότητας) συν μια πύλη συμμόρφωσης engine (το validate() του learn-content-engine πάνω σε κάθε μάθημα), ώστε ένα χειροκίνητο PR να μην μπορεί να παρακάμψει την πύλη.

Ελάχιστα ποιότητας (σκληρή πύλη): ≥ 5 ασκήσεις ανά μάθημα, ≥ 2 τύποι ασκήσεων, ≥ 1 βήμα θεωρίας, Free-Text ≥ 2 αποδεκτές απαντήσεις + distractors, Matching ≥ 3 ζεύγη, Picture-Choice με distractors, καμία κενή μπροστινή/πίσω όψη καρτών και (σε μη λατινικές γραφές αφετηρίας) πίσω όψεις καρτών στη γραφή αφετηρίας. Αυτά είναι ελάχιστα, όχι στόχοι - η λίστα ελέγχου παραπάνω απαιτεί περισσότερα.

Έλεγχος περιεχομένου με AI σε όλο το σύνολο (προαιρετικός)

Πέρα από τον έλεγχο κατά την κοινοποίηση, ένα κατεβασμένο σύνολο μπορεί να ελεγχθεί σε επίπεδο συνόλου μέσω Έλεγχος με AI. Αυτό είναι πλήρως προαιρετικό και χρησιμοποιεί τον πάροχο + μοντέλο που έχει ρυθμίσει ο εκπαιδευόμενος (Anthropic / OpenAI / Gemini)· οι κάρτες αποστέλλονται σε παρτίδες σε εκείνον τον πάροχο για έλεγχο. Η ροή δείχνει μια εκτίμηση κόστους, τρέχει με μπάρα προόδου + ακύρωση, και παράγει μια αναφορά ανά κάρτα που cache-άρεται στον browser και μπορεί να εξαχθεί ως Markdown (με μια γραμμή που καταγράφει ποιος πάροχος + μοντέλο έτρεξε τον έλεγχο). Όταν η αναφορά περάσει, το σύνολο κερδίζει ένα σήμα «AI-Checked» υποστηριγμένο από ένα content hash + μια υπογραφή, ώστε μια μεταγενέστερη επεξεργασία στις κάρτες να ακυρώνει το σήμα μέχρι το σύνολο να ξαναελεγχθεί. Ο έλεγχος AI δεν είναι ποτέ πύλη - είναι συμβουλευτική προέλευση, όχι απαίτηση δημοσίευσης.

Τοπικός έλεγχος

Ο επικυρωτής σχήματος του Content-Loader εκτελείται στο πλαίσιο του make test. Επικύρωσε ένα μεμονωμένο μάθημα χειροκίνητα:

cd plugins/adaptive-learner-plugin-content-loader
poetry run python -c "
import json, sys
from adaptive_learner_content_loader.schema import dict_to_lesson
path = '../adaptive-learner-content/sets/en/fr-a1/lessons/01-greetings.json'
with open(path) as f:
    lesson = dict_to_lesson(json.load(f))
print(f'OK: {lesson.id} - {len(lesson.cards)} Cards, {len(lesson.steps)} Steps')
"

Επικύρωσε όλα τα μαθήματα ενός repo περιεχομένου με τη μία - με τον επικυρωτή του repo περιεχομένου (το ίδιο script που εκτελεί το CI του σε κάθε PR):

cd ../adaptive-learner-content
python3 scripts/validate_content.py

Βρίσκει κάθε σύνολο κάτω από sets/{source}/{target-level}/ και ελέγχει το σχήμα συν τα ελάχιστα ποιότητας (≥5 ασκήσεις, ≥2 τύποι ασκήσεων, ≥1 βήμα θεωρίας, Accepts free-text + distractors, ζεύγη matching, καμία κενή κάρτα, ακεραιότητα Card-ID). Τα νέα μαθήματα αναγνωρίζονται αυτόματα - καμία αλλαγή test δεν χρειάζεται.

Ροή εργασίας PR

Μόλις το σύνολό σου είναι έτοιμο:

  1. Άνοιξε ένα PR έναντι του κύριου repo (για σύνολα που πρόκειται να αποσταλούν με την εφαρμογή), Ή
  2. Δημιούργησε ένα δικό σου repo περιεχομένου κάτω από τον λογαριασμό σου στο GitHub και ρύθμισε τον Content-Loader μέσω backend/config/plugins/content-loader.yaml (κάτω από default_sources).

Ο Content-Loader υποστηρίζει οποιοδήποτε δημόσιο GitHub-Repo ως πηγή. Τα ιδιωτικά repos απαιτούν ένα Personal Access Token, που ορίζεται μέσω της διαχείρισης κλειδιού τριών επιπέδων (~/.config/adaptive_learner/secrets.yaml).

Συχνές παγίδες

Αναφορές Card-ID: Κάθε καταχώρηση card_ids σε μια άσκηση πρέπει να υπάρχει στο cards[] του μαθήματος. Αν αντιγράψεις μια άσκηση μεταξύ μαθημάτων και ξεχάσεις να πάρεις μαζί τη σχετική Card, η επικύρωση αποτυγχάνει.

IDs ασφαλή για slug: Όλα τα IDs (Lesson, Card, Step, Exercise) πρέπει να ταιριάζουν με ^[a-z0-9]+(-[a-z0-9]+)*$. Καμία κάτω παύλα, καμία απόστροφος, κανένα κεφαλαίο γράμμα, καμία προηγούμενη/τελική παύλα.

is_correct: "true": Είναι ένα String, όχι Boolean JSON. Το σχήμα απαιτεί ρητά "true", επειδή τα πεδία picture_choice μοντελοποιούνται εσωτερικά ως dict[str, str].

Πρόσθετα πεδία: Κάθε μοντέλο έχει extra="forbid". Ένα μη τεκμηριωμένο πεδίο οδηγεί στην απόρριψη ολόκληρου του μαθήματος. Μείνε στα τεκμηριωμένα πεδία.

Theory-Body: Τα Theory-Steps χρειάζονται ένα μη κενό πεδίο body (Markdown). Τα Exercise-Steps δεν επιτρέπεται να φέρουν body - χρησιμοποίησε αντ' αυτού το prompt της άσκησης.

Αναφορά: τα ενσωματωμένα σύνολα

Ο Adaptive Learner αποστέλλει μια αξιόλογη βιβλιοθήκη σε πολλούς τομείς (γλώσσες, προγραμματισμός, ψυχολογία, τεχνητή νοημοσύνη, τεχνολογία - δες το μπλοκ CONTENT-STATS του README για τους ζωντανούς αριθμούς + τον πλήρη πίνακα ανά σύνολο). Μερικές καλές κανονικές αναφορές στο repo adaptive-learner-content:

  • sets/en/fr-a1/ - Γαλλικά A1 για Αγγλόφωνους· το sets/de/fr-a1/ είναι το γερμανόφωνο αντίστοιχο.
  • sets/en/es-a1/ + sets/de/es-a1/ - Ισπανικά A1 (ένα ανά γλώσσα αφετηρίας).
  • Το σύνολο «Python - Grundlagen» κάτω από το sets/de/ είναι ένα παράδειγμα domain: programming (γλώσσα αφετηρίας == στόχος), χρήσιμο ως μη γλωσσική αναφορά.

Όλα ακολουθούν τις συμβάσεις που περιγράφονται σε αυτόν τον οδηγό. Το να διαβάσεις ένα ολόκληρο μάθημα είναι ο γρηγορότερος τρόπος να αφομοιώσεις τη δομή.


Δρόμος προς τη συμμετοχή της κοινότητας (v1.42.0)

Αναλυτική περιήγηση βήμα προς βήμα με στιγμιότυπα: Create a lesson in the app, step by step (Medium) περιγράφει τον Lesson Creator μέσα στην εφαρμογή από άκρη σε άκρη, από την πρώτη κάρτα έως την κοινοποίηση του ολοκληρωμένου μαθήματος.

Δεν χρειάζεται να δημιουργείς μαθήματα από το μηδέν χειροκίνητα. Ο γρηγορότερος τρόπος να συνεισφέρεις είναι να δημιουργήσεις και να μοιραστείς ένα μάθημα στην εφαρμογή:

  1. Εισήγαγε ένα chat και ανάλυσέ το, μετά Αποθήκευση ως μάθημα Offline (ή ολοκλήρωσε ένα προσαρμοστικό μάθημα και Αποθήκευση αυτού του μαθήματος;). Το μάθημα εμφανίζεται κάτω από Τα μαθήματά μου στον Set-Browser.
  2. Κάνε κλικ στα «Τα μαθήματά μου» στο Εξαγωγή ως Content-Set, για να κατεβάσεις ένα Content-Set ως .zip (Manifest + μαθήματα). Οι εξαγωγές περιέχουν μόνο το περιεχόμενο μαθήματος - καμία πρόοδο, καμία ιστορία σφαλμάτων, τίποτα προσωπικό.
  3. Κάνε κλικ στο Διάθεση στην κοινότητα, για να ανοίξεις ένα προσυμπληρωμένο Pull Request στο repository περιεχομένου - το JSON μαθήματος γίνεται commit στη σωστή διαδρομή του δέντρου, χωρίς ανάγκη επισύναψης .zip.
  4. Το CI του repo επικυρώνει το PR αυτόματα· ένας maintainer ελέγχει το μάθημα, εναρμονίζει το manifest (id, title, language, level, tags) με τις παραπάνω συμβάσεις και το συγχωνεύει κάτω από sets/. Μετά τη συγχώνευση όλοι μπορούν να το κατεβάσουν από τον Set-Browser.

Αυτός είναι ο κοινωνικός δρόμος: Ο έλεγχος είναι χειροκίνητος (ένας maintainer επιμελείται κάθε προσθήκη - τίποτα δεν δημοσιεύεται αυτόματα), και όλη η ροή χρειάζεται μόνο το GitHub. Τα δημιουργημένα μαθήματα επικυρώνονται ήδη έναντι του σχήματος, ώστε ένα μάθημα που συνεισφέρεται συνήθως χρειάζεται μόνο λίγη λείανση manifest.

Οδηγός κοινοποίησης, παραλλαγές και Credit συντάκτη (Φάση 64)

Η κοινοποίηση ενός μαθήματος από τα Τα μαθήματά μου ανοίγει έναν οδηγό τεσσάρων σταδίων, αντί να μεταβαίνει απευθείας στο GitHub:

  1. Προεπισκόπηση + τοποθέτηση. Η εφαρμογή υπολογίζει ακριβώς πού προσγειώνεται το μάθημα στο δέντρο (sets/{source}/{target}-{level}/) και ένα αυτόματα αριθμημένο όνομα αρχείου ({nn}-{slug}.json, ο επόμενος αριθμός μετά τα υπάρχοντα μαθήματα). Ένα εντελώς νέο ζεύγος + επίπεδο δείχνει «Νέο σύνολο! Είσαι ο πρώτος.»
  2. Έλεγχος διπλότυπων. Το μάθημα συγκρίνεται με τα ήδη υπάρχοντα μαθήματα σε αυτή τη διαδρομή (επικάλυψη καρτών και ασκήσεων - συμβουλευτική, ποτέ αποκλειστική). Αν υπάρχει κάτι παρόμοιο, μπορείς να:
  3. Κοινοποιήσεις ως παραλλαγή - το μάθημα σημειώνεται με variation_of: "{original_id}" συν ένα προαιρετικό variation_note («Σε τι διαφέρει η εκδοχή σου;»).
  4. Προτείνεις μόνο τις νέες ασκήσεις (σε σχεδόν διπλότυπα) - ο οδηγός εξάγει ακριβώς τις ασκήσεις που λείπουν από το πρωτότυπο, μαζί με τις σχετικές κάρτες, ως παραλλαγή συμπλήρωσης.
  5. Σύνοψη ποιότητας. Τα ευρήματα του επικυρωτή βασισμένου σε κανόνες (συν ο προαιρετικός έλεγχος AI)· οι προειδοποιήσεις εμφανίζονται, αλλά δεν μπλοκάρουν ποτέ.
  6. Κοινοποίηση + γιορτή. Ένα κλικ ανοίγει το GitHub-Pull-Request (επεξεργαστής αρχείου σε μικρά μαθήματα, σελίδα Upload σε μεγάλα), και η εφαρμογή σε ευχαριστεί με μια μικρή γιορτή.

Πεδία παραλλαγών και Credit (Σχήμα 1.3, όλα προαιρετικά)

{
  "variation_of": "10-passe-compose",
  "variation_note": "Mehr Übungen zur Angleichung",
  "contributed_by": "Maria S.",
  "contributed_at": "2026-06-01T14:30:00Z"
}

Και τα τέσσερα είναι additive και προαιρετικά· μαθήματα χωρίς αυτά συμπεριφέρονται ακριβώς όπως πριν. Το contributed_by ορίζεται, όταν ο συντάκτης ενεργοποιεί το Credit κατά την κοινοποίηση (ένα πεδίο «Το όνομά σου (προαιρετικό)», που απομνημονεύεται τοπικά για την επόμενη φορά). Αν υπάρχει, ο προβολέας εμφανίζει μια διακριτική γραμμή «Διατέθηκε από {name}» κάτω από τον τίτλο, και το κείμενο του Pull-Request αναφέρει τον συντάκτη στον πίνακα μεταδεδομένων του.

Ιστορικό συνεισφορών και κενά

Τα κοινοποιημένα μαθήματα απομνημονεύονται τοπικά (κανένας λογαριασμός δεν χρειάζεται) κάτω από Οι συνεισφορές μου με έναν μετρητή και μια διάκριση Συνεισφέρων κοινότητας από πέντε κοινοποιημένα μαθήματα και άνω. Ο Set-Browser εμφανίζει επιπλέον Μαθήματα που λείπουν - ενθαρρυντικές προτάσεις για το επόμενο επίπεδο CEFR ενός υπάρχοντος ζεύγους ή μια γλώσσα στόχο, που υπάρχει για μια γλώσσα αφετηρίας, αλλά λείπει για μια άλλη («Μπορείς να βοηθήσεις;»).


Σχετικές σελίδες