How liuzhne/paper-grading-system works
1. Overall shape
This repository is a local-first thesis-grading system built around one shared scoring core. The main runtime is a FastAPI backend, with two operator surfaces on top of it: a browser UI served by the backend itself, and a zero-service CLI (pgs). A Streamlit app exists as a fallback/admin-style front end.
2. Primary end-to-end workflow
The core business flow is: define a rubric, organize a batch, upload papers, parse documents, retrieve evidence, run AI scoring item-by-item, allow human review, then export results to Excel/HTML and optionally write back to an online sheet. That closed loop is the central architecture of the repo.
3. Backend API boundary
backend/app/api/routes/ contains the HTTP surface area split by domain: rubrics, batches, papers, scoring, calibration, exports, auth, and system status. These routes are thin entry points; they depend on shared services and Pydantic schemas rather than embedding business logic.
4. Domain services layer
Most of the real behavior lives under backend/app/services/. This is where the architecture fans out into scoring, calibration, paper ingestion, document parsing, retrieval, report generation, spreadsheet export, rubric import, caching, and LLM integration. The services layer is the main boundary between transport/UI concerns and the scoring pipeline.
5. Scoring pipeline
backend/app/services/scoring/ is the core engine. It appears to combine rubric rules, deterministic checks, LLM-based evaluation, validation, and post-processing into score items and run results. Related subsystems such as checkers/, coherence/, and retrieval/ feed evidence and quality signals into scoring rather than acting as standalone features.
6. Document ingestion and parsing
Uploaded papers flow through services/papers/ingestion.py and services/document_parser/. This subsystem handles file intake, parsing of DOCX/PDF content, chunking, format checks, section summaries, track-changes handling, and format resolution. Parsed artifacts are stored separately from raw uploads, which is important for diagramming the data lifecycle.
7. Rubric import and calibration
Rubrics are not just simple records; they can be imported from spreadsheets/docx-based formats through services/rubric_import/, then compiled and persisted. Calibration support lives in services/calibration/, including anchor management and analytics, and the API exposes dedicated endpoints for that part of the grading model.
8. LLM integration boundary
All model calls are abstracted behind services/llm/, with adapters for OpenAI-style and OpenAI-compatible providers plus a mock implementation and retry logic. The README makes clear that the system can fall back to local mock behavior when environment variables are not configured, so the architecture must show LLM as an optional external dependency rather than a hard requirement.
9. Persistence model
State is stored in PostgreSQL via SQLAlchemy models in backend/app/db/models.py, with session management in db/session.py and schema evolution via Alembic migrations. In addition to the relational database, the system writes files to storage/ for uploads, parsed JSON, reports, exports, and LLM cache state. That split between relational metadata and file artifacts is a key architectural boundary.
10. Batching, runs, and review
Batches are a top-level operational unit. A batch groups papers, scoring runs, summaries, rankings, drift analysis, and export logs. Scoring produces run-level records and item-level results, which can then be reviewed and patched through dedicated endpoints before final report/export generation.
11. CLI as a first-class interface
The backend/app/cli/ package exposes the same core services without running a web server. It supports initialization, rubric import, scoring, review, reporting, export, and evaluation. This is not a separate implementation path; it is a second front door into the same backend architecture, designed for offline or batch use.
12. Reporting and export
services/report/ and services/spreadsheet/ handle output generation. The system can generate human-readable reports, JSON exports, Excel exports, and teacher score templates. There is also an online sheet writer integration point, but it is optional and external.
13. Evaluation and quality gates
backend/app/eval/ and the scripts/run_qwk_eval.py path implement an offline evaluation pipeline for scoring quality, centered on QWK and related metrics against teacher-labeled data. This subsystem is separate from production scoring but shares the same rubric and paper parsing model, which matters if you are diagramming validation or release gates.
14. Operational and deployment boundaries
The repository is designed to run locally with Dockerized PostgreSQL, Alembic migrations, and environment-driven integration toggles. The external services that materially affect architecture are the LLM provider and the optional Google Sheets write-back endpoint. Everything else is intentionally self-contained for local operation and offline fallback.
Main entry points: main.py FastAPI app · engine.py Scoring engine · ingestion.py Ingestion & parse · compiler.py Rubric & calibration
Components 22
Interfaces
User-facing entry points and operators
- FastAPI app · Backend application entry point that assembles the API and runtime
backend/app/main.py - Web UI · Browser-based operator interface served alongside the backend
frontend/web/index.html - Streamlit app · Alternative admin-style frontend for local use
frontend/streamlit_app.py - CLI · Zero-service command line entry point for offline and batch workflows
backend/app/cli/main.py
HTTP API
FastAPI routes exposing the domain surface
- Auth · Authentication and user/session surface
backend/app/api/routes/auth.py - Batches · Batch lifecycle, summaries, rankings, and review coordination
backend/app/api/routes/batches.py - Papers · Paper upload and document handling endpoints
backend/app/api/routes/papers.py - Rubrics · Rubric definition and import endpoints
backend/app/api/routes/rubrics.py - Scoring · Scoring, review, and patch endpoints
backend/app/api/routes/scoring.py - Calibration · Anchor and calibration management endpoints
backend/app/api/routes/calibration.py - Exports · Report and export endpoints
backend/app/api/routes/exports.py - System · Health and status endpoints
backend/app/api/routes/system.py
Core Services
Scoring pipeline and domain logic
- Scoring engine · Hybrid scoring core combining rules, validation, and model-assisted judgment
backend/app/services/scoring/engine.py - Ingestion & parse · File intake, parsing, chunking, summaries, and format handling
backend/app/services/papers/ingestion.py - Rubric & calibration · Rubric import, persistence, anchors, and calibration analytics
backend/app/services/rubric_import/compiler.py - Reports & export · Human-readable reports, JSON exports, and spreadsheet generation
backend/app/services/report/generator.py - Retrieval & checks · Evidence retrieval, deterministic checks, coherence, and validation support
backend/app/services/retrieval/keyword.py
Data and Integrations
Database, files, LLM, and evaluation support
- LLM adapters · Provider abstraction with OpenAI-compatible, mock, and retry behavior
backend/app/services/llm/factory.py - Database layer · SQLAlchemy models, sessions, and migration-backed relational state
backend/app/db/models.py - File storage · Local storage for uploads, parsed artifacts, reports, and exports
backend/app/services/storage/local.py - LLM cache · Cached model responses and local cache state
backend/app/services/cache/llm_cache.py - Eval suite · Quality evaluation pipeline for QWK and labeled datasets
backend/app/eval/runner.py
Connections 30
- Web UI → FastAPI app: calls · Uses the backend HTTP surface for operator workflows
- Streamlit app → FastAPI app: calls · Optional frontend that reaches the same backend services
- CLI → Rubric & calibration: uses · CLI invokes shared domain services without a web server
- CLI → Scoring engine: uses · Runs scoring and review flows directly
- FastAPI app → Auth: mounts · Registers authentication routes
- FastAPI app → Batches: mounts · Registers batch lifecycle routes
- FastAPI app → Papers: mounts · Registers paper ingestion routes
- FastAPI app → Rubrics: mounts · Registers rubric routes
- FastAPI app → Scoring: mounts · Registers scoring and review routes
- FastAPI app → Calibration: mounts · Registers calibration routes
- FastAPI app → Exports: mounts · Registers export routes
- FastAPI app → System: mounts · Registers system/status routes
- Batches → Scoring engine: orchestrates · Coordinates batch-level scoring and review
- Papers → Ingestion & parse: delegates · Hands uploads to the document pipeline
- Rubrics → Rubric & calibration: delegates · Creates and imports rubric definitions
- Scoring → Scoring engine: delegates · Runs item-level and run-level scoring
- Calibration → Rubric & calibration: delegates · Manages anchors and calibration workflows
- Exports → Reports & export: delegates · Generates reports and export artifacts
- Scoring engine → Retrieval & checks: uses · Consumes evidence retrieval and deterministic quality checks
- Scoring engine → LLM adapters: uses · Optionally calls model providers through an abstraction
- Scoring engine → Database layer: persists · Stores runs, items, scores, and review state
- Ingestion & parse → File storage: reads/writes · Moves raw uploads into parsed artifacts and summaries
- Ingestion & parse → Database layer: persists · Records paper metadata and parsing state
- Rubric & calibration → Database layer: persists · Stores rubrics, anchors, and calibration metadata
- Reports & export → File storage: writes · Creates reports, Excel exports, and JSON outputs
- LLM adapters → LLM cache: caches · Stores and reuses model responses when enabled
- LLM adapters → File storage: writes · May record diagnostics and local fallback artifacts
- Eval suite → Scoring engine: evaluates · Measures scoring quality against labeled data
- Eval suite → Ingestion & parse: reuses · Shares the same parsing and document model
- Database layer → File storage: coordinates · Database metadata points to stored artifacts