Signals-based assessment — map analyser signals to a rubric as observations, never grades. A human assigns every mark.
Project description
assessment-lens
Part of the lens family.
Signals-based assessment. Analysers generate signals about student submissions; this lens maps those signals to a rubric as observations, not grades. A lecturer reads the observations, weighs them, and assigns the mark. The AI narrates and cites; it never scores. A human stays in the loop.
assessment-lensis a lens (an assessment-aware product), not an-analyser. Analysers are assessment-agnostic signal generators; lenses sit above them. It consumes analysers (viabundle-analyser); it never generates signals. See ADR-0001.
Why "observations, not grades"
LLMs are inconsistent at the precise act of marking. So the lens keeps them off
it. The deterministic signals (from analysers) are the anchor; an Observation
maps a signal or two to a rubric Criterion with cited evidence, a short
narration bound to that evidence, and a coverage (present / partial /
absent) — which is "is the evidence there?", derived from thresholds, not a
mark. There is deliberately no score/mark/weight field anywhere in the model.
Pipeline
Specification ──draft-rubric (LLM, reviewed)──▶ Rubric (criteria + mapping) + deliverables [YAML]
Submissions root ──discover──▶ one Submission per subfolder
each Submission folder ──bundle-analyser──▶ Signals
Signals + Criteria + Deliverables ──alignment-check──▶ Observations
└─▶ cohort triage sheet + per-student reports
lecturer reads observations → assigns marks → writes feedback
assess is folder-dumb: one rubric + one folder of submission subfolders.
Group/individual splits and mark-combining are handled outside (pre/post) or by
running assess per folder.
Three commands
assess— structured rubric + a submissions folder → observations (cohort sheet + per-student reports). Add--llmfor evidence-bound narration on each observation (opt-in; needs the[llm]extra + a provider —ANTHROPIC_API_KEYby default, or a local Ollama viaASSESSMENT_LENS_PROVIDER=ollama).draft-rubric— free-form specification → a proposed structured rubric for the lecturer to review/edit before use. LLM-assisted; reads text/markdown directly and.pdf/.docx/.pptxvia the[documents]extra.serve— opt-in HTTP API ([serve]extra) so a desktop shell / UI can drive the lens: same/health+/manifestcontract as the analysers,role: lens. Binds to127.0.0.1; setASSESSMENT_LENS_AUTH_TOKENto require a bearer token on the assessment routes.
Install
# from PyPI
pip install assessment-lens
# from source (family layout)
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
# pull the whole analyser stack into the same env (for `assess` to run for real):
uv pip install -e ".[dev,analysers]"
# opt-in LLM features (narration + draft-rubric):
uv pip install -e ".[llm]" # + export ANTHROPIC_API_KEY=...
assess shells out to the bundle-analyser CLI (it must be on PATH). The
[analysers] extra installs it; or point assessment-lens at an environment
where it is already installed.
Quick start
# 1. author a structured rubric (or start from the example)
cp examples/data-viz-rubric.yaml my-rubric.yaml
# 2. lay out submissions: one subfolder per student/group
# submissions/alice/{report.pdf,demo.mp4} submissions/bob/...
# 3. assess (add --llm for narrated observations)
assessment-lens assess my-rubric.yaml submissions/ -o out/
# -> out/cohort-sheet.csv (row per submission × criterion, sortable)
# -> out/reports/<id>.md (per-student observation sheet, no marks)
# or draft a rubric from a free-form spec, then review/edit it
assessment-lens draft-rubric assignment-brief.md -o my-rubric.yaml
Submissions are analysed sequentially (one bundle-analyser run each), so
budget accordingly for large cohorts — e.g. 300 submissions × 30 s ≈ 2.5 h. A
submission that fails to analyse is recorded as an error row in the cohort sheet
and never aborts the run; re-run just that one with --only <id> once fixed.
Rubric schema (the central contract)
assignment: "Data-Viz Project"
component: individual # plain label; assess ignores it
expected_deliverables:
- id: report
description: "Written report (~2000 words)"
accepts: [document] # content kinds that satisfy it
- id: demo
description: "≤5-min recorded demo"
accepts: [video]
rubric:
- id: critical-thinking
description: "Evidence of critical engagement / analysis"
signals_of_interest: [conversation.critical_thinking, reflection.depth] # OPTIONAL mapping
signals_of_interest is the signal→criterion mapping, and it's optional.
Pinned → deterministic coverage. Blank → alignment-check selects signals at
runtime and shows its choice (near-term).
Status
v0.5. Working today:
- ✅ Rubric load/validate; deliverable reconciliation; submission discovery
- ✅
assessorchestration + evidence-bound observations + threshold coverage; per-submission fault isolation (one failure never aborts the cohort) - ✅ cohort sheet (CSV, spreadsheet-injection-safe) + per-student reports (Markdown)
- ✅
bundle-analyserintegration — verified against the real CLI output schema - ✅ LLM narration (
assess --llm) — narrate-and-cite, bound to evidence; degrades to empty notes when the[llm]extra/key is missing - ✅ multi-provider LLM — Anthropic by default; local Ollama or any
OpenAI-compatible endpoint via
ASSESSMENT_LENS_PROVIDER - ✅
draft-rubric— proposes a rubric from a free-form spec (always review before use) - ✅ cohort-relative distinctiveness (
[distinctiveness]extra) — three comparison spaces; neutral, never a verdict - ✅
serve([serve]extra) — HTTP face for the desktop shell, optional bearer-token auth viaASSESSMENT_LENS_AUTH_TOKEN - 📋 Runtime signal selection when
signals_of_interestis blank — deferred
Development
ruff format . && ruff check . && pytest -v
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file assessment_lens-0.5.1.tar.gz.
File metadata
- Download URL: assessment_lens-0.5.1.tar.gz
- Upload date:
- Size: 188.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e564924857c7e049a5b8fee3e8f3d3c297ce8ff4fea76dfb00c9f1fc22c9273b
|
|
| MD5 |
6668e04767a5cb9eac68ad64c035bc8e
|
|
| BLAKE2b-256 |
42fe8b9e4d191387422456bbe341f9c259ed6fd4b290fd028af52fa19c2ae33c
|
File details
Details for the file assessment_lens-0.5.1-py3-none-any.whl.
File metadata
- Download URL: assessment_lens-0.5.1-py3-none-any.whl
- Upload date:
- Size: 34.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7780703fb779269a5f1e553cad6c75b31b94b15ef5fc3e6d4d881174a51f93fb
|
|
| MD5 |
8d1bb23e64132e2606bc03d2cf5b613e
|
|
| BLAKE2b-256 |
d6055f56c4f2a04dede10f001d9b736b1a7388679c82ac6b3fbbf4b4da7f28cf
|