kwan 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kwan-1.0.0/.gitignore +26 -0
- kwan-1.0.0/LICENSE +21 -0
- kwan-1.0.0/PKG-INFO +477 -0
- kwan-1.0.0/README.md +421 -0
- kwan-1.0.0/pyproject.toml +59 -0
- kwan-1.0.0/src/kwan/__init__.py +37 -0
- kwan-1.0.0/src/kwan/_bundle_cli.py +80 -0
- kwan-1.0.0/src/kwan/_paths.py +33 -0
- kwan-1.0.0/src/kwan/_version.py +3 -0
- kwan-1.0.0/src/kwan/api/__init__.py +6 -0
- kwan-1.0.0/src/kwan/api/app.py +403 -0
- kwan-1.0.0/src/kwan/api/runtime.py +162 -0
- kwan-1.0.0/src/kwan/api/v1.py +441 -0
- kwan-1.0.0/src/kwan/assets/kwan-logo-dark.png +0 -0
- kwan-1.0.0/src/kwan/assets/kwan-logo-light.png +0 -0
- kwan-1.0.0/src/kwan/assets/kwan-logo.png +0 -0
- kwan-1.0.0/src/kwan/assets/kwan-mark.png +0 -0
- kwan-1.0.0/src/kwan/bundle.py +533 -0
- kwan-1.0.0/src/kwan/bundles/kwan-bundle.math.json +15 -0
- kwan-1.0.0/src/kwan/cli.py +100 -0
- kwan-1.0.0/src/kwan/context/__init__.py +12 -0
- kwan-1.0.0/src/kwan/context/_cli.py +210 -0
- kwan-1.0.0/src/kwan/context/bank.py +76 -0
- kwan-1.0.0/src/kwan/context/banned_terms.txt +272 -0
- kwan-1.0.0/src/kwan/context/checks.py +677 -0
- kwan-1.0.0/src/kwan/context/coverage.py +102 -0
- kwan-1.0.0/src/kwan/context/drafts.py +300 -0
- kwan-1.0.0/src/kwan/context/ghana_terms.txt +581 -0
- kwan-1.0.0/src/kwan/context/settings.py +369 -0
- kwan-1.0.0/src/kwan/context/sheets.py +889 -0
- kwan-1.0.0/src/kwan/context/translate.py +305 -0
- kwan-1.0.0/src/kwan/curriculum/__init__.py +1 -0
- kwan-1.0.0/src/kwan/curriculum/_cli.py +141 -0
- kwan-1.0.0/src/kwan/curriculum/build.py +245 -0
- kwan-1.0.0/src/kwan/curriculum/corrections.py +113 -0
- kwan-1.0.0/src/kwan/curriculum/document.py +470 -0
- kwan-1.0.0/src/kwan/curriculum/exemplars.py +113 -0
- kwan-1.0.0/src/kwan/curriculum/families.py +162 -0
- kwan-1.0.0/src/kwan/curriculum/families.yaml +58 -0
- kwan-1.0.0/src/kwan/curriculum/fetch.py +152 -0
- kwan-1.0.0/src/kwan/curriculum/ghana_terms_extra.txt +27 -0
- kwan-1.0.0/src/kwan/curriculum/labels.py +108 -0
- kwan-1.0.0/src/kwan/curriculum/model.py +57 -0
- kwan-1.0.0/src/kwan/curriculum/parse.py +194 -0
- kwan-1.0.0/src/kwan/curriculum/pdf.py +263 -0
- kwan-1.0.0/src/kwan/curriculum/qa.py +595 -0
- kwan-1.0.0/src/kwan/curriculum/text.py +115 -0
- kwan-1.0.0/src/kwan/data/__init__.py +1 -0
- kwan-1.0.0/src/kwan/data/loaders/__init__.py +60 -0
- kwan-1.0.0/src/kwan/data/loaders/_common.py +50 -0
- kwan-1.0.0/src/kwan/data/loaders/ammore.py +41 -0
- kwan-1.0.0/src/kwan/data/loaders/assistments.py +119 -0
- kwan-1.0.0/src/kwan/data/loaders/eedi.py +95 -0
- kwan-1.0.0/src/kwan/data/loaders/junyi.py +81 -0
- kwan-1.0.0/src/kwan/data/loaders/xes3g5m.py +68 -0
- kwan-1.0.0/src/kwan/data/registry.py +97 -0
- kwan-1.0.0/src/kwan/data/schema.py +122 -0
- kwan-1.0.0/src/kwan/eval/__init__.py +1 -0
- kwan-1.0.0/src/kwan/eval/_cli.py +183 -0
- kwan-1.0.0/src/kwan/eval/ammore.py +66 -0
- kwan-1.0.0/src/kwan/eval/deep.py +1284 -0
- kwan-1.0.0/src/kwan/eval/eedi.py +69 -0
- kwan-1.0.0/src/kwan/eval/fit.py +763 -0
- kwan-1.0.0/src/kwan/eval/harness.py +338 -0
- kwan-1.0.0/src/kwan/eval/kt.py +254 -0
- kwan-1.0.0/src/kwan/eval/latency.py +140 -0
- kwan-1.0.0/src/kwan/eval/metrics.py +65 -0
- kwan-1.0.0/src/kwan/eval/results.py +113 -0
- kwan-1.0.0/src/kwan/eval/simulate.py +675 -0
- kwan-1.0.0/src/kwan/graph/__init__.py +1 -0
- kwan-1.0.0/src/kwan/graph/_cli.py +127 -0
- kwan-1.0.0/src/kwan/graph/_cli_augment.py +487 -0
- kwan-1.0.0/src/kwan/graph/augment.py +649 -0
- kwan-1.0.0/src/kwan/graph/build.py +455 -0
- kwan-1.0.0/src/kwan/graph/candidates.py +284 -0
- kwan-1.0.0/src/kwan/graph/export.py +315 -0
- kwan-1.0.0/src/kwan/graph/junyi.py +488 -0
- kwan-1.0.0/src/kwan/graph/providers/__init__.py +41 -0
- kwan-1.0.0/src/kwan/graph/providers/fake.py +41 -0
- kwan-1.0.0/src/kwan/graph/providers/llm_json.py +167 -0
- kwan-1.0.0/src/kwan/graph/providers/local_encoder.py +273 -0
- kwan-1.0.0/src/kwan/graph/providers/typesafe_jev.py +109 -0
- kwan-1.0.0/src/kwan/graph/review.py +287 -0
- kwan-1.0.0/src/kwan/graph/schema.py +321 -0
- kwan-1.0.0/src/kwan/graph/validate.py +374 -0
- kwan-1.0.0/src/kwan/ids.py +137 -0
- kwan-1.0.0/src/kwan/kwan.py +346 -0
- kwan-1.0.0/src/kwan/learner/__init__.py +26 -0
- kwan-1.0.0/src/kwan/learner/bkt.py +422 -0
- kwan-1.0.0/src/kwan/learner/elo.py +458 -0
- kwan-1.0.0/src/kwan/learner/forgetting.py +80 -0
- kwan-1.0.0/src/kwan/learner/item_bank.py +379 -0
- kwan-1.0.0/src/kwan/learner/params.py +82 -0
- kwan-1.0.0/src/kwan/learner/scoring.py +261 -0
- kwan-1.0.0/src/kwan/py.typed +0 -0
- kwan-1.0.0/src/kwan/recommend/__init__.py +1 -0
- kwan-1.0.0/src/kwan/recommend/explain.py +114 -0
- kwan-1.0.0/src/kwan/recommend/explore.py +138 -0
- kwan-1.0.0/src/kwan/recommend/recommender.py +298 -0
- kwan-1.0.0/src/kwan/recommend/review.py +77 -0
- kwan-1.0.0/src/kwan/store/__init__.py +42 -0
- kwan-1.0.0/src/kwan/store/base.py +313 -0
- kwan-1.0.0/src/kwan/store/memory.py +200 -0
- kwan-1.0.0/src/kwan/store/postgres.py +310 -0
- kwan-1.0.0/src/kwan/store/sqlite.py +412 -0
kwan-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# raw data: PDFs, text dumps, ASSISTments, Eedi (fetched by scripts/download_data.sh)
|
|
2
|
+
raw/
|
|
3
|
+
*.zip
|
|
4
|
+
|
|
5
|
+
# python
|
|
6
|
+
__pycache__/
|
|
7
|
+
.venv/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
*.pyc
|
|
10
|
+
|
|
11
|
+
# local artefacts
|
|
12
|
+
*.log
|
|
13
|
+
*.tmp
|
|
14
|
+
demo.png
|
|
15
|
+
.DS_Store
|
|
16
|
+
.claude/
|
|
17
|
+
|
|
18
|
+
# build outputs and tool caches
|
|
19
|
+
build/
|
|
20
|
+
dist/
|
|
21
|
+
site/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.mypy_cache/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
.venv-kt/
|
|
26
|
+
.venv-ai/
|
kwan-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rofy-Ray
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
kwan-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: kwan
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Open-source learning path engine for Ghanaian primary maths (NaCCA B1 to B6): the next skill to learn, the reason, and a practice setting from everyday Ghanaian life.
|
|
5
|
+
Project-URL: Homepage, https://github.com/rofyray/kwan
|
|
6
|
+
Project-URL: Repository, https://github.com/rofyray/kwan
|
|
7
|
+
Project-URL: Issues, https://github.com/rofyray/kwan/issues
|
|
8
|
+
Project-URL: Demo, https://huggingface.co/spaces/rofyray/kwan
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: Ghana,NaCCA,adaptive learning,curriculum,education,knowledge tracing,mathematics
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Education
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: networkx>=3.2
|
|
24
|
+
Provides-Extra: api
|
|
25
|
+
Requires-Dist: fastapi>=0.110; extra == 'api'
|
|
26
|
+
Requires-Dist: httpx>=0.27; extra == 'api'
|
|
27
|
+
Requires-Dist: uvicorn>=0.29; extra == 'api'
|
|
28
|
+
Provides-Extra: curriculum
|
|
29
|
+
Requires-Dist: pdfplumber>=0.10; extra == 'curriculum'
|
|
30
|
+
Requires-Dist: pyyaml>=6; extra == 'curriculum'
|
|
31
|
+
Provides-Extra: demo
|
|
32
|
+
Requires-Dist: pyvis>=0.3; extra == 'demo'
|
|
33
|
+
Requires-Dist: streamlit>=1.36; extra == 'demo'
|
|
34
|
+
Provides-Extra: eval
|
|
35
|
+
Requires-Dist: gdown>=5; extra == 'eval'
|
|
36
|
+
Requires-Dist: numpy>=1.24; extra == 'eval'
|
|
37
|
+
Requires-Dist: pandas>=2.0; extra == 'eval'
|
|
38
|
+
Requires-Dist: pyarrow>=14; extra == 'eval'
|
|
39
|
+
Requires-Dist: pyyaml>=6; extra == 'eval'
|
|
40
|
+
Requires-Dist: rarfile>=4; extra == 'eval'
|
|
41
|
+
Requires-Dist: scikit-learn>=1.3; extra == 'eval'
|
|
42
|
+
Provides-Extra: gradio
|
|
43
|
+
Requires-Dist: gradio>=5; extra == 'gradio'
|
|
44
|
+
Requires-Dist: pandas>=2.0; extra == 'gradio'
|
|
45
|
+
Requires-Dist: pyvis>=0.3; extra == 'gradio'
|
|
46
|
+
Provides-Extra: graph-ai
|
|
47
|
+
Requires-Dist: httpx>=0.27; extra == 'graph-ai'
|
|
48
|
+
Requires-Dist: pyyaml>=6; extra == 'graph-ai'
|
|
49
|
+
Requires-Dist: sentence-transformers>=2.2; extra == 'graph-ai'
|
|
50
|
+
Provides-Extra: kt
|
|
51
|
+
Requires-Dist: pykt-toolkit; extra == 'kt'
|
|
52
|
+
Requires-Dist: torch; extra == 'kt'
|
|
53
|
+
Provides-Extra: postgres
|
|
54
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
<p align="center">
|
|
58
|
+
<picture>
|
|
59
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rofyray/kwan/main/docs/assets/kwan-logo-dark.png">
|
|
60
|
+
<img src="https://raw.githubusercontent.com/rofyray/kwan/main/docs/assets/kwan-logo-light.png" alt="Kwan logo: a green path through gold steps to a red goal under a star" width="420">
|
|
61
|
+
</picture>
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
# Kwan
|
|
65
|
+
|
|
66
|
+
**Kwan** is Twi for *the path* (*ɔkwan*). It tells a Ghanaian pupil which maths skill to learn next, explains why, and sets the practice in everyday Ghanaian life.
|
|
67
|
+
|
|
68
|
+
Kwan is an open-source learning path engine for the NaCCA Standards-Based Curriculum, Mathematics, Basic 1 to Basic 6. It is a Python package with a command-line tool, a learner-state store and an optional REST API, plus a public demo. It began at the DataNova 2026 Global Data-thon (problem DN-ED-01, Adaptive Learning Path Recommendation) and is now a project of its own.
|
|
69
|
+
|
|
70
|
+
- **Live demo:** https://huggingface.co/spaces/rofyray/kwan (a Gradio app, deployed on release)
|
|
71
|
+
- **Documentation:** https://rofyray.github.io/kwan/ (published on release; the same pages are in [`docs/`](docs/index.md))
|
|
72
|
+
- **Package:** `uv add kwan` once 1.0 is released; until then, install from source (see [Install](#install))
|
|
73
|
+
|
|
74
|
+
Kwan uses the NaCCA Standards-Based Curriculum to organise skills. Kwan is not reviewed, approved or endorsed by NaCCA or the Ministry of Education.
|
|
75
|
+
|
|
76
|
+
## Why we built it
|
|
77
|
+
|
|
78
|
+
Most learning apps and AI tutors used across Africa were designed somewhere else. Their word problems feature snow days and prices in dollars, and the order of topics follows a foreign syllabus. A child in a village in the Volta Region has to decode the story before she can start on the maths, and that extra step is where many children stop.
|
|
79
|
+
|
|
80
|
+
Kwan starts from what Ghanaian pupils already study. Its skill map is the NaCCA Standards-Based Mathematics Curriculum for Basic 1 to Basic 6, the same document teachers plan their lessons from, so a recommendation lines up with the lesson on the blackboard. Each recommended skill comes with a setting a Ghanaian child will recognise: sharing groundnuts, giving change for kenkey and fish, continuing a kente pattern, working out a trotro fare.
|
|
81
|
+
|
|
82
|
+
## What it does
|
|
83
|
+
|
|
84
|
+
The map today, counted by the build scripts:
|
|
85
|
+
|
|
86
|
+
<!-- RESULTS:START id=readme-counts source=data/eval_results.json -->
|
|
87
|
+
276 NaCCA learning indicators linked by 295 prerequisite connections.
|
|
88
|
+
<!-- RESULTS:END -->
|
|
89
|
+
|
|
90
|
+
1. **Maps the curriculum.** Every learning indicator in NaCCA Mathematics B1 to B6 is a skill on a map, with its strand and sub-strand names and a short label written for Kwan. Links say which skills come before which, and every link records where it came from.
|
|
91
|
+
2. **Tracks the pupil.** After each answer, Kwan updates its estimate of how well the pupil knows every skill. Evidence flows along the map, so doing well on a skill also raises the estimate for the skills that build on it. When answers carry a time, skills that are not practised fade, and a question can carry its own difficulty.
|
|
92
|
+
3. **Picks the next skills.** Kwan looks for skills whose foundations are in place and that sit at a productive level of challenge. If the teacher sets a goal, skills on the path to the goal come first. One place in the list can go to a skill the pupil learned before and is starting to forget. Picks vary across strands, and a little seeded exploration can be switched on.
|
|
93
|
+
4. **Explains each pick** in plain language, from the same signals that produced the ranking. The reason comes as a sentence and as parts with keys, so it can be shown in another language. Example: *"Builds on B2.1.1.1.6, where you are at 74%. You are at 62%, close enough to master with practice. Opens the way to 46 later skills."*
|
|
94
|
+
5. **Grounds it in Ghana.** *"Try it in Ghana: Use 1 cedi and 2 cedi coins to make 5 cedis in as many ways as you can."* Settings are written to a [style guide](docs/context-style-guide.md) and shown by default only after a teacher has reviewed them.
|
|
95
|
+
|
|
96
|
+
Kwan makes no network call and no AI model call when it recommends. It runs on one CPU core, which matters for schools with patchy internet.
|
|
97
|
+
|
|
98
|
+
## Results
|
|
99
|
+
|
|
100
|
+
Every number in this section is rendered from [`data/eval_results.json`](data/eval_results.json) by `uv run scripts/fill_docs.py`. Nothing is typed by hand, and a test fails if the README and the file disagree. The [Evidence](docs/evidence.md) page has the same results with more decimals and a reading guide for each table.
|
|
101
|
+
|
|
102
|
+
### Does the learner model predict the next answer?
|
|
103
|
+
|
|
104
|
+
How well the learner model predicts a pupil's next answer, tested on students it never saw in training. AUC: 0.5 is a coin flip, 1.0 is perfect. The first two rows are AMMORE, real answers from African pupils; the second row is Ghanaian pupils only.
|
|
105
|
+
|
|
106
|
+
<!-- RESULTS:START id=readme-kt-table source=data/eval_results.json -->
|
|
107
|
+
| Dataset | Answers | Students | Skill average | BKT | Classic Elo | **Kwan** |
|
|
108
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
109
|
+
| AMMORE, all pupils (Ghana, Nigeria, Kenya, South Africa) | 50,507 | 2,388 | 0.631 | not run | 0.690 | **0.774** |
|
|
110
|
+
| AMMORE, Ghanaian pupils only | 16,706 | 730 | 0.639 | not run | 0.690 | **0.764** |
|
|
111
|
+
| ASSISTments 2009 | 259,399 | 4,163 | 0.624 | 0.700 | 0.668 | **0.755** |
|
|
112
|
+
| Eedi 2020, task 3/4 | 1,382,727 | 4,918 | 0.576 | not run | 0.684 | **0.776** |
|
|
113
|
+
| Eedi 2020, task 1/2 | 15,867,850 | 118,971 | 0.572 | not run | 0.678 | **0.795** |
|
|
114
|
+
| ASSISTments 2012-13 | 2,711,602 | 29,018 | 0.609 | not run | 0.654 | **0.747** |
|
|
115
|
+
| ASSISTments 2015 | 683,801 | 19,840 | 0.640 | not run | 0.657 | **0.684** |
|
|
116
|
+
| ASSISTments 2017 | 942,816 | 1,709 | 0.586 | not run | 0.628 | **0.710** |
|
|
117
|
+
| XES3G5M | 5,549,184 | 18,066 | 0.619 | not run | 0.647 | **0.801** |
|
|
118
|
+
| Junyi Academy 2015 (evaluation only) | 25,925,992 | 247,606 | 0.719 | not run | 0.754 | **0.790** |
|
|
119
|
+
<!-- RESULTS:END -->
|
|
120
|
+
|
|
121
|
+
### Ghanaian pupils
|
|
122
|
+
|
|
123
|
+
AMMORE comes from Rori, a WhatsApp maths tutor used by pupils in Ghana, Nigeria, Kenya and South Africa. Kwan reports the Ghanaian pupils on their own so that a weaker fit for Ghana could not hide inside an average.
|
|
124
|
+
|
|
125
|
+
<!-- RESULTS:START id=readme-ghana source=data/eval_results.json -->
|
|
126
|
+
Kwan's AUC is 0.774 on all AMMORE pupils and 0.764 on the 730 Ghanaian pupils (16,706 answers).
|
|
127
|
+
|
|
128
|
+
| AMMORE | Pupils | Answers | Skills | Skill average | Classic Elo | **Kwan** |
|
|
129
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
130
|
+
| All pupils | 2,388 | 50,507 | 151 | 0.631 | 0.690 | **0.774** |
|
|
131
|
+
| **Ghanaian pupils only** | 730 | 16,706 | 144 | 0.639 | 0.690 | **0.764** |
|
|
132
|
+
<!-- RESULTS:END -->
|
|
133
|
+
|
|
134
|
+
These pupils are older than Kwan's Basic 1 to 6 pupils, and their skills are Rori's, not NaCCA's. See [Limitations](#limitations).
|
|
135
|
+
|
|
136
|
+
### Does forgetting help, and do tuned parameters help?
|
|
137
|
+
|
|
138
|
+
Where answers carry dates, a skill's estimate fades when it is not practised. The forgetting time τ and the Elo step sizes were chosen on training students only. Parameters fitted on AMMORE are reported but never shipped, because its licence is non-commercial.
|
|
139
|
+
|
|
140
|
+
<!-- RESULTS:START id=readme-learner-fit source=data/eval_results.json -->
|
|
141
|
+
| Dataset | Has times | Best τ | AUC, v1 steps, no forgetting | AUC, with forgetting | Change | AUC, tuned steps |
|
|
142
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
143
|
+
| ASSISTments 2009 | no | none | 0.752 | 0.752 | 0.000 | 0.754 |
|
|
144
|
+
| Eedi 2020, task 3/4 | yes | 30 days | 0.776 | 0.778 | 0.002 | 0.780 |
|
|
145
|
+
| Eedi 2020, task 1/2 | yes | 45 days | 0.786 | 0.787 | 0.001 | 0.791 |
|
|
146
|
+
| AMMORE (evaluation only) | yes | 14 days | 0.771 | 0.771 | 0.000 | 0.769 |
|
|
147
|
+
| ASSISTments 2015 | no | none | 0.684 | 0.684 | 0.000 | 0.687 |
|
|
148
|
+
| ASSISTments 2017 | yes | 45 days | 0.710 | 0.712 | 0.002 | 0.715 |
|
|
149
|
+
| XES3G5M | yes | 7 days | 0.798 | 0.798 | 0.001 | 0.804 |
|
|
150
|
+
|
|
151
|
+
Shipped in the maths bundle: `k_s` 0.50, `k_k` 0.30, `decay` 0.02, τ 30 days (step sizes chosen on assistments_2009, eedi_2020_task34, eedi_2020_task12; τ chosen on eedi_2020_task34, eedi_2020_task12).
|
|
152
|
+
|
|
153
|
+
Held-out students, v1 defaults against the shipped parameters:
|
|
154
|
+
|
|
155
|
+
| Dataset | AUC, v1 defaults | AUC, shipped | Gain |
|
|
156
|
+
| --- | ---: | ---: | ---: |
|
|
157
|
+
| ASSISTments 2009 | 0.752 | 0.753 | 0.000 |
|
|
158
|
+
| Eedi 2020, task 3/4 | 0.776 | 0.780 | 0.004 |
|
|
159
|
+
| Eedi 2020, task 1/2 | 0.786 | 0.790 | 0.005 |
|
|
160
|
+
<!-- RESULTS:END -->
|
|
161
|
+
|
|
162
|
+
### Would a deep model do better?
|
|
163
|
+
|
|
164
|
+
Deep knowledge tracing models from the pyKT toolkit were trained on the same data and compared with Kwan's Elo on the same folds. The rule for adopting one was fixed before the benchmark ran.
|
|
165
|
+
|
|
166
|
+
<!-- RESULTS:START id=readme-deep-kt source=data/eval_results.json -->
|
|
167
|
+
| Model | ASSISTments 2009 AUC | Eedi task 3/4 AUC | AMMORE AUC | XES3G5M AUC | ms per update (CPU) |
|
|
168
|
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
169
|
+
| **Kwan (multi-level Elo)** | 0.758 ± 0.005 | 0.780 ± 0.004 | 0.773 ± 0.009 | 0.801 ± 0.001 | under 0.01 |
|
|
170
|
+
| DKT | 0.750 ± 0.009 | 0.759 ± 0.004 | 0.725 ± 0.012 | not run | 1.76 |
|
|
171
|
+
| SAKT | 0.713 ± 0.008 | 0.739 ± 0.005 | 0.696 ± 0.012 | not run | 0.77 |
|
|
172
|
+
| simpleKT | 0.739 ± 0.010 | 0.788 ± 0.004 | 0.737 ± 0.010 | not run | 1.36 |
|
|
173
|
+
| AKT | 0.712 ± 0.010 | 0.730 ± 0.005 | 0.717 ± 0.009 | not run | 21.87 |
|
|
174
|
+
|
|
175
|
+
**Decision: Elo stays the default learner model.** No deep model meets rule D7. The closest is simplekt: at least 0.03 AUC ahead of elo-multi (Kwan) on 0 of the 3 D7 datasets it was run on (needs 2); its best margin is +0.0089 on eedi_34. Elo stays the only learner model.
|
|
176
|
+
|
|
177
|
+
Rule: D7: a deep model is adopted only if it beats elo-multi by at least 0.03 AUC on at least two of AMMORE, Eedi and ASSISTments 2009, and stays under 20 ms per update on CPU.
|
|
178
|
+
<!-- RESULTS:END -->
|
|
179
|
+
|
|
180
|
+
### Does following Kwan help a simulated pupil?
|
|
181
|
+
|
|
182
|
+
There are no classroom trials yet. What exists is a simulation: virtual pupils with hidden true skills practise under different policies, with forgetting in their hidden state. Kwan is measured against syllabus order, random practice and a "remedial first" rule that uses the map but none of Kwan's scoring.
|
|
183
|
+
|
|
184
|
+
<!-- RESULTS:START id=readme-simulation-v2 source=data/eval_results.json -->
|
|
185
|
+
30 simulated learners per profile and policy (seeds 0 to 29), 150 practice steps at 5 a day, then 30 days without practice; goal runs stop at 300 steps
|
|
186
|
+
|
|
187
|
+
New skills mastered after 150 practice steps (mean over 30 simulated pupils):
|
|
188
|
+
|
|
189
|
+
| Pupil profile | **Kwan** | Kwan + exploration | Kwan, top pick only | Syllabus order | Random | Remedial first |
|
|
190
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
191
|
+
| Beginner in Basic 1 | **23.2** | 22.0 | 19.9 | 17.6 | 6.9 | 19.1 |
|
|
192
|
+
| Basic 4 pupil with gaps in B1 to B3 | **11.1** | 10.4 | 12.7 | 6.3 | 7.2 | 15.4 |
|
|
193
|
+
| Basic 6 pupil with gaps that grew | **9.4** | 8.4 | 8.6 | 5.6 | 8.1 | 12.9 |
|
|
194
|
+
| Basic 4 pupil, strong in Number, weak in Geometry | **10.1** | 9.9 | 8.6 | 8.0 | 7.6 | 5.0 |
|
|
195
|
+
|
|
196
|
+
Of those, skills still known after 30 days without practice:
|
|
197
|
+
|
|
198
|
+
| Pupil profile | **Kwan** | Kwan + exploration | Kwan, top pick only | Syllabus order | Random | Remedial first |
|
|
199
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
200
|
+
| Beginner in Basic 1 | **17.7** | 17.4 | 14.1 | 10.4 | 4.1 | 11.6 |
|
|
201
|
+
| Basic 4 pupil with gaps in B1 to B3 | **8.2** | 7.5 | 9.1 | 4.4 | 3.6 | 9.5 |
|
|
202
|
+
| Basic 6 pupil with gaps that grew | **7.2** | 6.0 | 5.9 | 3.2 | 4.4 | 7.3 |
|
|
203
|
+
| Basic 4 pupil, strong in Number, weak in Geometry | **7.7** | 7.1 | 6.1 | 5.3 | 4.3 | 3.3 |
|
|
204
|
+
|
|
205
|
+
Checks set before the run: kwan strict beats syllabus and random on gapped profiles: yes; kwan strict within 1 skill of syllabus on every profile: yes. All met: yes.
|
|
206
|
+
<!-- RESULTS:END -->
|
|
207
|
+
|
|
208
|
+
The table from the Round 1 build, with the simpler simulator that never forgets, is kept for comparison:
|
|
209
|
+
|
|
210
|
+
<!-- RESULTS:START id=readme-simulation-v1 source=data/eval_results.json -->
|
|
211
|
+
30 simulated learners x 150 practice steps, NaCCA B1-B6 graph
|
|
212
|
+
|
|
213
|
+
| Pupil | **Following Kwan** | Syllabus order | Random practice |
|
|
214
|
+
| --- | ---: | ---: | ---: |
|
|
215
|
+
| P4 pupil with gaps in B1 to B3 | **13.5** | 8.8 | 10.7 |
|
|
216
|
+
| Beginner in P1 | **25.2** | 23.7 | 9.7 |
|
|
217
|
+
<!-- RESULTS:END -->
|
|
218
|
+
|
|
219
|
+
Kwan helps most where pupils have gaps. For a beginner, the syllabus order is already a good path, and Kwan roughly matches it. The "remedial first" rule does well for pupils with gaps because it goes straight to the missing foundations, which Kwan's scoring also does, and it does badly for the pupil whose gaps sit in one strand.
|
|
220
|
+
|
|
221
|
+
**Simulator assumptions**, in the simulator's own words:
|
|
222
|
+
|
|
223
|
+
<!-- RESULTS:START id=readme-simulation-assumptions source=data/eval_results.json -->
|
|
224
|
+
Each simulated pupil has a hidden true state for every skill. One practice step on a skill the pupil does not know teaches it with probability 0.35 when every prerequisite is truly known and 0.03 otherwise. A known skill is answered wrongly 10% of the time and an unknown one is guessed right 20% of the time. The pupil does 5 steps a day. Each night every known skill can be forgotten: a skill with memory strength S days survives one day with probability exp(-1/S). S is 45 days for a skill just learned and 365 days for what the pupil knew at the start; practising a known skill again on a later day doubles S, up to 365 days. Practising a skill does not refresh its prerequisites. These are modelling assumptions, not measurements of Ghanaian pupils.
|
|
225
|
+
|
|
226
|
+
- `beginner_p1`: ASSUMPTION, not data. A pupil starting Basic 1: knows 15% of the B1 and B2 skills (from home and kindergarten) and 3% of everything later. Same as the v1.0 profile.
|
|
227
|
+
- `gapped_p4`: ASSUMPTION, not data. A Basic 4 pupil with foundational gaps: knows 75% of the B1 to B3 skills, so one lower-primary skill in four is missing, and 5% of B4 to B6. Same as the v1.0 profile.
|
|
228
|
+
- `gapped_p6`: ASSUMPTION, not data. A Basic 6 pupil with gaps that grew over the years: knows 80% of B1 to B3, 50% of B4 and B5, and 5% of B6.
|
|
229
|
+
- `uneven_strands`: ASSUMPTION, not data. A Basic 4 pupil who is strong in Number and weak in Geometry and Measurement: of the B1 to B4 skills, knows 85% in Number, 15% in Geometry and Measurement and 50% in Algebra and Data; knows 5% of B5 and B6 in every strand.
|
|
230
|
+
<!-- RESULTS:END -->
|
|
231
|
+
|
|
232
|
+
These are modelling choices, so treat the simulation as a check on the policy's logic. The real-data results stand on their own.
|
|
233
|
+
|
|
234
|
+
### Does the goal path work?
|
|
235
|
+
|
|
236
|
+
<!-- RESULTS:START id=readme-goal-path source=data/eval_results.json -->
|
|
237
|
+
With a goal set, 76.7% of the top-3 picks sit on the path to the goal in strict mode (the default) and 36.0% in soft mode (the Round 1 behaviour). Setup: 50 seeded pupils, each with up to 60 answers on B1 to B3 skills (85% right) and a goal drawn from B4 to B6; share of the top 3 recommendations that are on the path to the goal.
|
|
238
|
+
<!-- RESULTS:END -->
|
|
239
|
+
|
|
240
|
+
### Is it fast enough?
|
|
241
|
+
|
|
242
|
+
<!-- RESULTS:START id=readme-latency source=data/eval_results.json -->
|
|
243
|
+
A top-3 recommendation takes 0.10 ms on average and 0.13 ms at the 95th percentile (single CPU core, pure Python; 3,000 timed calls per learner state, in-process, no network).
|
|
244
|
+
<!-- RESULTS:END -->
|
|
245
|
+
|
|
246
|
+
### Is the curriculum data complete?
|
|
247
|
+
|
|
248
|
+
<!-- RESULTS:START id=readme-curriculum source=data/eval_results.json -->
|
|
249
|
+
Indicators parsed: 276 of 276 expected from the hand count (counts match: yes). Descriptions that look cut short: 1 (0.4%). Indicators with at least one exemplar: 276 (100%), 673 exemplars in all, 43 of them set in Ghana. Misprinted codes corrected: 13.
|
|
250
|
+
|
|
251
|
+
Short labels: 276 drafted, 0 reviewed by a teacher, 0 missing, 0 over 60 characters.
|
|
252
|
+
<!-- RESULTS:END -->
|
|
253
|
+
|
|
254
|
+
<!-- RESULTS:START id=readme-graph source=data/eval_results.json -->
|
|
255
|
+
Skill map: 276 skills and 295 prerequisite links (54 bridge, 159 sequence, 82 vertical). No cycles: yes. Every skill reachable: yes. Longest chain: 36 links.
|
|
256
|
+
<!-- RESULTS:END -->
|
|
257
|
+
|
|
258
|
+
### AI-augmented skill links
|
|
259
|
+
|
|
260
|
+
The rules that build the map follow the order of the curriculum and miss links that cross strands. Kwan asks a decision model one question about a pair of skills, "Must a pupil be able to do A before they can learn B?", tests the method on Junyi Academy data that comes with expert links, and sends every suggestion to a teacher. Only accepted links ship.
|
|
261
|
+
|
|
262
|
+
<!-- RESULTS:START id=readme-graph-augment source=data/eval_results.json -->
|
|
263
|
+
Decision model: local-encoder (all-MiniLM-L6-v2+logreg@50721cd7). Precision bar: 80%. On the Junyi benchmark (held-out exercises) it proposed 13 links, 4 of them expert links: precision 30.8%, recall 1.6%. Meets the bar: no.
|
|
264
|
+
|
|
265
|
+
On NaCCA Mathematics it scored 5,403 candidate pairs and proposed 4 links. Teacher review: 4 pending, 0 accepted, 0 rejected. Accepted links shipped in the graph: 0.
|
|
266
|
+
|
|
267
|
+
Teacher acceptance: not yet measured: no teacher has reviewed an edge.
|
|
268
|
+
<!-- RESULTS:END -->
|
|
269
|
+
|
|
270
|
+
The method is built and tested end to end. The local model used so far is below the precision bar, so its few suggestions wait for teacher review, and the shipped map holds rule-based links only. A stronger decision model is the next thing to try.
|
|
271
|
+
|
|
272
|
+
### Practice settings
|
|
273
|
+
|
|
274
|
+
<!-- RESULTS:START id=readme-contexts source=data/eval_results.json -->
|
|
275
|
+
Practice settings: 828 drafted for 276 of 276 indicators; 828 pass the automatic checks. Reviewed by a teacher: 0 settings, covering 0 indicators (0.0% coverage). Reviewers so far: 0.
|
|
276
|
+
|
|
277
|
+
Translations for Basic 1 to Basic 3:
|
|
278
|
+
|
|
279
|
+
| Language | B1 to B3 indicators | Drafts | Reviewed | Indicators shipping |
|
|
280
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
281
|
+
| Twi | 93 | 0 | 0 | 0 |
|
|
282
|
+
| Ewe | 93 | 0 | 0 | 0 |
|
|
283
|
+
| Ga | 93 | 0 | 0 | 0 |
|
|
284
|
+
<!-- RESULTS:END -->
|
|
285
|
+
|
|
286
|
+
Until a teacher reviews a setting, Kwan shows the general setting for the skill's strand and class band. The demo has a clearly labelled switch to show the unreviewed drafts.
|
|
287
|
+
|
|
288
|
+
## Help review it
|
|
289
|
+
|
|
290
|
+
Kwan is checked by people who teach. If you teach in Ghana, train teachers or work in education, you can review skill links ("Would a pupil really need A before B?"), practice settings and short labels, or Twi, Ewe and Ga translations. No coding is needed: open a ["Teacher review" issue](https://github.com/rofyray/kwan/issues/new?template=teacher-review.yml) and a maintainer will send you a review sheet. The [teacher review guide](docs/teacher-review-guide.md) explains every column, and every reviewer is credited in [`CONTRIBUTORS.md`](CONTRIBUTORS.md).
|
|
291
|
+
|
|
292
|
+
## How it works
|
|
293
|
+
|
|
294
|
+
- **Curriculum** (`kwan.curriculum`): downloads the official NaCCA PDFs, reads every indicator with its strand, sub-strand, description and exemplars, corrects misprinted codes, and writes a QA report against a hand count. The repository commits codes, numbers, names and Kwan's own short labels; NaCCA's text stays in a git-ignored build folder.
|
|
295
|
+
- **Skill map** (`kwan.graph`): links the next indicator in the same standard, the same standard in the next class, and the next standard in the same sub-strand, each with its provenance. A decision model can propose cross-strand links, which teachers review on a sheet. The map is checked to be a directed acyclic graph.
|
|
296
|
+
- **Learner model** (`kwan.learner`): multi-level Elo. The chance of a correct answer is `σ(θ_pupil + θ_pupil,skill − β_item)`. Skills the pupil has not tried borrow evidence from their prerequisites, adjusted for class level. Skill offsets decay towards their prior as days pass without practice, and an item bank can give each question its own difficulty. A streak of right answers, or a high predicted chance, counts as mastered.
|
|
297
|
+
- **Recommender** (`kwan.recommend`): scores ready skills on how solid their foundations are, how close they are to a productive level of challenge, how close they are to the goal, how many later skills they open, and how long since they were last practised. On top of the score: strict goal paths, a review slot, strand variety and seeded exploration. Reasons are built from parts and rendered from a language file.
|
|
298
|
+
- **Ghana context** (`kwan.context`): per-indicator practice settings with review status, automatic checks (length, reading level, money format, banned words), review sheets, machine-drafted translations that stay hidden until a teacher approves them, and the Round 1 bank of settings by strand as the fallback.
|
|
299
|
+
- **Package** (`kwan.Kwan`, `kwan.store`, `kwan.api`, `kwan.bundle`): one bundle file per subject, a facade for the engine, memory, SQLite and Postgres stores with idempotent writes, and a REST API with API keys.
|
|
300
|
+
- **Evidence** (`kwan.eval`): loaders for every public dataset, the evaluation harness, the parameter fits, the deep knowledge tracing benchmark, the simulator and the latency benchmark. Each writes its own block of `data/eval_results.json`.
|
|
301
|
+
|
|
302
|
+
The [Method](docs/method.md) page has the formulas in plain language.
|
|
303
|
+
|
|
304
|
+
## Install
|
|
305
|
+
|
|
306
|
+
Kwan needs Python 3.11, 3.12 or 3.13. The core needs only the standard library and `networkx`.
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
uv add kwan # the engine (once 1.0 is on PyPI)
|
|
310
|
+
uv add 'kwan[api]' # plus the REST API
|
|
311
|
+
uv add 'kwan[gradio]' # plus the Gradio demo (the Space); kwan[demo] is the Streamlit one
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
From source, with every extra used in development:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
git clone https://github.com/rofyray/kwan.git
|
|
318
|
+
cd kwan
|
|
319
|
+
uv sync --extra api --extra demo --extra eval --extra curriculum --extra gradio
|
|
320
|
+
uv run pytest -q
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The extras: `api` (FastAPI and uvicorn), `gradio` (Gradio and pyvis, for the Space demo), `demo` (Streamlit and pyvis, for the Docker demo), `eval` (pandas, numpy, scikit-learn and the dataset downloaders), `curriculum` (pdfplumber), `kt` (pyKT and torch, for the deep knowledge tracing benchmark), `graph-ai` (sentence-transformers, for the decision model), and `postgres` (psycopg). `uv sync` also installs the `dev` group (ruff, mypy, pytest); the `docs` group holds mkdocs-material. Not on uv? `pip install kwan` and `pip install -e '.[api]'` work too.
|
|
324
|
+
|
|
325
|
+
The parsed curriculum, the skill map, the bundle and the results file are committed, so the tests, the API and the demo run without downloading anything.
|
|
326
|
+
|
|
327
|
+
## Quickstart
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from kwan import Kwan
|
|
331
|
+
|
|
332
|
+
engine = Kwan.from_bundle() # the maths bundle shipped with the package
|
|
333
|
+
state = engine.new_state("pupil-0042") # any opaque id you choose, never a name
|
|
334
|
+
|
|
335
|
+
engine.update(state, "B1.1.1.1.1", correct=True) # bare codes and canonical ids both work
|
|
336
|
+
engine.update(state, "math:B1.1.1.1.2", correct=False, at="2026-11-10T09:30:00+00:00")
|
|
337
|
+
|
|
338
|
+
for rec in engine.recommend(state, k=3, goal="B4.1.1.1.2", now="2026-11-12T08:00:00+00:00"):
|
|
339
|
+
print(rec.code, rec.short_label)
|
|
340
|
+
print("Why:", rec.rationale)
|
|
341
|
+
print("Try it in Ghana:", rec.ghana_context)
|
|
342
|
+
|
|
343
|
+
mastery = engine.mastery_map(state) # {canonical id: estimated mastery from 0 to 1}
|
|
344
|
+
saved = state.to_dict() # plain data; LearnerState.from_dict() brings it back
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
`update` returns the chance the model gave the answer before seeing it. Pass `at` with each answer and `now` with each request to let forgetting and the review slot work. `recommend` takes `grade_max` to stay within a class, `goal_mode="soft"` for the Round 1 behaviour, `eps` for exploration and `lang` for the language of the reason. [`examples/quickstart.py`](examples/quickstart.py) is a runnable version, and the [Quickstart](docs/quickstart.md) page walks through it.
|
|
348
|
+
|
|
349
|
+
## Command line
|
|
350
|
+
|
|
351
|
+
Installing Kwan adds the `kwan` command (in a source checkout, prefix it with `uv run`, or use `uv tool install kwan` to have it everywhere). Run `kwan <command> --help` for the options of each one.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
kwan curriculum fetch # download the official PDFs and record their hashes
|
|
355
|
+
kwan curriculum parse # write the structure file and the full-text build file
|
|
356
|
+
kwan curriculum qa # check the parse against the hand count; write the HTML report
|
|
357
|
+
|
|
358
|
+
kwan graph build # build the skill map from the structure file
|
|
359
|
+
kwan graph validate # check it and write docs/qa/graph-math.md
|
|
360
|
+
kwan graph export # an interactive graph.html (needs pyvis)
|
|
361
|
+
kwan graph augment --dry-run # propose extra links with a decision model
|
|
362
|
+
kwan graph review-export # write the review sheet for teachers
|
|
363
|
+
kwan graph review-import # record the decisions of a filled sheet
|
|
364
|
+
|
|
365
|
+
kwan eval ammore # evaluate the learner model on one dataset
|
|
366
|
+
kwan eval all --out data/eval_results.json
|
|
367
|
+
kwan fit all # fit tau, the step sizes and item banks
|
|
368
|
+
kwan fit report --out data/eval_results.json
|
|
369
|
+
kwan sim --v2 --out data/eval_results.json
|
|
370
|
+
kwan bench --out data/eval_results.json
|
|
371
|
+
|
|
372
|
+
kwan context check # automatic checks on every setting and label
|
|
373
|
+
kwan context review-export # review sheets for what still needs a teacher
|
|
374
|
+
kwan context review-import # apply a filled-in sheet
|
|
375
|
+
kwan context coverage # how much is drafted, checked, reviewed and translated
|
|
376
|
+
|
|
377
|
+
kwan bundle --out src/kwan/bundles/ # write kwan-bundle.math.json
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`make all` runs the whole pipeline in order: `data`, `curriculum`, `graph`, `eval`, `fit`, `sim`, `bench`, `bundle` and `test`. `make data` downloads the public datasets listed in `data/datasets.yaml` and is safe to run again. After any step that changes `data/eval_results.json`, run `uv run scripts/fill_docs.py` to refresh the tables in this README and the docs.
|
|
381
|
+
|
|
382
|
+
## REST API
|
|
383
|
+
|
|
384
|
+
The API is an optional extra. It keeps learner state in a SQLite file by default (set `KWAN_STORE=sqlite:///path/to/file`, `KWAN_STORE=memory` or a Postgres URL to change that) and expects an API key in the `X-API-Key` header when `KWAN_API_KEYS` is set.
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
uv add 'kwan[api]'
|
|
388
|
+
export KWAN_API_KEYS="change-me-to-a-long-random-string"
|
|
389
|
+
uv run uvicorn kwan.api.app:app --port 8000
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
| Method | Path | What it does |
|
|
393
|
+
|---|---|---|
|
|
394
|
+
| `GET` | `/v1/health` | Liveness, versions and the size of the map; needs no key |
|
|
395
|
+
| `GET` | `/v1/skills` | The skill catalogue: each id with a short label, filtered by `grade`, `strand` and `q` |
|
|
396
|
+
| `POST` | `/v1/attempts` | Records one answer; idempotent with an `idempotency_key` |
|
|
397
|
+
| `GET` | `/v1/learners/{id}/recommendations` | The next skills for a learner, with `k`, `goal`, `grade_max`, `goal_mode` and `now` |
|
|
398
|
+
| `GET` | `/v1/learners/{id}/mastery` | The estimated mastery of every skill |
|
|
399
|
+
| `DELETE` | `/v1/learners/{id}` | Forgets a learner |
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
curl -s -X POST localhost:8000/v1/attempts \
|
|
403
|
+
-H "X-API-Key: change-me-to-a-long-random-string" -H "content-type: application/json" \
|
|
404
|
+
-d '{"learner_id": "pupil-0042", "indicator": "math:B1.1.1.1.1", "correct": true}'
|
|
405
|
+
curl -s -H "X-API-Key: change-me-to-a-long-random-string" \
|
|
406
|
+
"localhost:8000/v1/skills?grade=4&q=fractions%20order"
|
|
407
|
+
curl -s -H "X-API-Key: change-me-to-a-long-random-string" \
|
|
408
|
+
"localhost:8000/v1/learners/pupil-0042/recommendations?k=3&goal=math:B4.1.3.2.1"
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
People never type a skill code. `GET /v1/skills` returns every skill with a short label (filter with `grade`, `strand` and `q`, e.g. `?grade=4&q=fractions order`); show the `short_label` in a dropdown or search box and send the `id` as `goal`. Each recommendations response echoes the goal with its label.
|
|
412
|
+
|
|
413
|
+
A learner id needs no registration: a pupil the API has never seen gets starting recommendations. An unknown indicator or goal returns 404 with a machine-readable `code`. Interactive documentation is served at `/docs` while the server runs. The Round 1 routes (`/attempt`, `/recommend/{id}` and the rest) still answer for one minor release, with a `Deprecation` header and a `Link` to the v1 route that replaces them.
|
|
414
|
+
|
|
415
|
+
## What is in the bundle
|
|
416
|
+
|
|
417
|
+
Everything Kwan needs for a subject travels in one versioned file, `kwan-bundle.math.json`, shipped inside the package and attached to each release:
|
|
418
|
+
|
|
419
|
+
- the skill map (nodes with codes, class, strand, sub-strand and short labels; edges with provenance);
|
|
420
|
+
- the learner-model parameters and where they were fitted;
|
|
421
|
+
- the practice settings with their review status, the Round 1 settings bank and the language templates for reasons;
|
|
422
|
+
- `text_mode`: `labels` (what the project publishes) or `full` (NaCCA's descriptions, built only on a machine with the PDFs, and committed only with NaCCA's written permission).
|
|
423
|
+
|
|
424
|
+
A bundle built with parameters from a non-commercial dataset is refused by the bundle builder.
|
|
425
|
+
|
|
426
|
+
## Limitations
|
|
427
|
+
|
|
428
|
+
- **No reviewed settings yet.** Every practice setting and short label is a draft. Until teachers review them, Kwan shows the general setting for the strand and class band by default.
|
|
429
|
+
- **No translations yet.** The Twi, Ewe and Ga pipeline exists; no reviewed translation ships.
|
|
430
|
+
- **Augmented links are pending.** The decision model's suggestions wait for review, and the local model used so far is below the precision bar. The shipped map holds rule-based links only.
|
|
431
|
+
- **NaCCA text is not committed.** The repository and the bundle hold codes and Kwan's short labels. Full descriptions are read from the official PDFs on your own machine.
|
|
432
|
+
- **No Ghanaian classroom logs.** The learner model is validated on public datasets from several countries, including African pupils on AMMORE, who are older than Kwan's pupils and whose skills are not NaCCA's. No public answer log from Ghanaian primary classrooms exists yet.
|
|
433
|
+
- **The forgetting rate is one number for everyone**, and the simulation rests on assumptions chosen by the authors.
|
|
434
|
+
- **An untried skill can be over-estimated.** A pupil who does very well on early skills raises the estimate for later skills they have not tried, and in rare cases such a skill already counts as mastered.
|
|
435
|
+
|
|
436
|
+
The [Model card](docs/model-card.md) lists the limits in full.
|
|
437
|
+
|
|
438
|
+
## What's next
|
|
439
|
+
|
|
440
|
+
Later work, not part of 1.0:
|
|
441
|
+
|
|
442
|
+
- English Language B1 to B6, then JHS Mathematics.
|
|
443
|
+
- Calibration on Ghana survey data (World Bank STARS, USAID Ghana Early Grade Mathematics).
|
|
444
|
+
- A TypeScript port for apps and offline use on phones.
|
|
445
|
+
- Platform integrations under their own documents.
|
|
446
|
+
- Misconception tags from Eedi's "Mining Misconceptions in Mathematics" data.
|
|
447
|
+
|
|
448
|
+
See the [Roadmap](docs/roadmap.md).
|
|
449
|
+
|
|
450
|
+
## Data and credits
|
|
451
|
+
|
|
452
|
+
Kwan organises skills with the **NaCCA Standards-Based Curriculum** (Mathematics, Basic 1 to 6, 2019), © NaCCA, Ministry of Education, Ghana. The curriculum PDFs are downloaded from curriculumresources.edu.gh when you build Kwan; this repository stores the indicator codes and Kwan's own short labels. Kwan is not reviewed, approved or endorsed by NaCCA or the Ministry of Education.
|
|
453
|
+
|
|
454
|
+
The learner model is tested on public research data. None of it is stored in this repository; the download scripts fetch it.
|
|
455
|
+
|
|
456
|
+
- ASSISTments 2009 skill-builder data. Feng, Heffernan and Koedinger (2009).
|
|
457
|
+
- Eedi NeurIPS 2020 Education Challenge. Wang et al. (2020), arXiv:2007.12061.
|
|
458
|
+
- AMMORE, African Middle-school Math Open REsponse dataset, from Rori by Rising Academies. Henkel et al. (2025), *Journal of Learning Analytics*. CC BY-NC 4.0.
|
|
459
|
+
- XES3G5M. Liu et al. (2023), NeurIPS Datasets and Benchmarks. MIT.
|
|
460
|
+
- Junyi Academy 2015. Chang, Hsu and Chen (2015), EDM. Non-commercial.
|
|
461
|
+
|
|
462
|
+
The ASSISTments 2012-13, 2015 and 2017 releases are used as well; cite the ASSISTments project as the data provider asks. The [Data card](docs/data-card.md) describes every source.
|
|
463
|
+
|
|
464
|
+
## Licences
|
|
465
|
+
|
|
466
|
+
Code: MIT (see `LICENSE`). Practice settings, short labels, translations and documentation written for Kwan: CC BY 4.0 (see `LICENSE-CONTENT`). Curriculum text belongs to NaCCA. Each dataset keeps its own licence, listed above and in `docs/data-card.md`; parameters fitted on non-commercial datasets are never shipped in Kwan's bundles.
|
|
467
|
+
|
|
468
|
+
## Maintainers and contributors
|
|
469
|
+
|
|
470
|
+
Kwan gets better when people who teach in Ghana shape it. You can help in two ways.
|
|
471
|
+
|
|
472
|
+
- **Teachers and education specialists:** check that skill links make sense, write or review "Try it in Ghana" settings and short skill labels, and review Twi, Ewe and Ga translations. No coding needed: open a "Teacher review" issue and we will send you a review sheet.
|
|
473
|
+
- **Developers and data scientists:** pick an issue labelled `good first issue`, or help with curriculum parsing, evaluation and the demo. Read `CONTRIBUTING.md` first.
|
|
474
|
+
|
|
475
|
+
Everyone who contributes is listed in `CONTRIBUTORS.md`, including reviewers. Maintainer: Rofy Ray (@rofyray).
|
|
476
|
+
|
|
477
|
+
To cite Kwan, use [`CITATION.cff`](CITATION.cff). [Governance](docs/governance.md) records how decisions are made and where Kwan stands with NaCCA.
|