This repository contains our MEng capstone implementation for content-aware course file reorganization in support of Berkeley Teaching AI (TAI). The system converts fragmented, format-based course repositories into a cleaner, more pedagogically meaningful structure that is easier for both students and retrieval-based AI systems to navigate.
The pipeline is organized into two major stages:
-
Step 1 — File Categorization (
bfs_v5.py+classify_v4.py) Reorganizes raw course materials into three educational roles: study, practice, and support using a hierarchical breadth-first traversal and LLM-based classification. -
Step 2 — Lecture Labeling / Rearrangement (
rearrange/src/pipeline.py+steps.py+models.py+utils.py) Takes the reorganizedstudymaterials and further groups them by lecture/topic units using a Plan-and-Solve style LLM pipeline.
pipeline_orchestrator.py connects the stages end to end: BFS v5, rearrangement, optional DB mapping, and optional evaluation. A downstream utility, map_to_db_v3.py, writes reorganized logical paths back into course metadata databases for later use by TAI.
Modern course content is often spread across course websites, discussion pages, video platforms, homework portals, and ad hoc uploaded assets. Even when all files are collected into one repository, the resulting structure is usually inconsistent and difficult to browse. This hurts:
- students, who spend time searching for the right lecture, discussion, homework, or lab materials; and
- educational AI systems, especially retrieval-augmented systems, which rely on well-structured source material.
Our capstone addresses this infrastructure problem by reorganizing course repositories into a structure that better reflects how students actually study: by educational role, task, and lecture/topic progression.
Raw course repository
│
▼
Step 1: Hierarchical BFS categorization
(bfs_v5.py + classify_v4.py)
│
├── study/
├── practice/
└── support/
│
▼
Step 2: Lecture labeling / study rearrangement
(rearrange/src/pipeline.py + steps.py + models.py + utils.py)
│
▼
Lecture-oriented structure tree
│
▼
DB write-back / logical path materialization
(map_to_db_v3.py)
The preferred entry point for a full run is pipeline_orchestrator.py. It connects:
- Stage 1: BFS v5 classification
- Stage 2: lecture/topic rearrangement
- Stage 2.5: optional DB mapping and logical output materialization
- Stage 3: optional evaluation
By default, the orchestrator keeps middle-of-pipeline data in memory. It writes durable deliverables such as the final rearrangement tree, mapped database, and logical output tree. Pass --debug when you want inspection artifacts such as BFS plans, Step 2 intermediate JSON files, and debug logs to persist on disk.
python pipeline_orchestrator.py \
--source "/path/to/course_repo" \
--db "/path/to/course_metadata.db" \
--course cs61apython pipeline_orchestrator.py \
--source "/path/to/course_repo" \
--db "/path/to/course_metadata.db" \
--course cs61a \
--debugpython pipeline_orchestrator.py --mode bfs_only --source "/path/to/course_repo" --db "/path/to/course_metadata.db"
python pipeline_orchestrator.py --mode rearrange_only --source "/path/to/course_repo" --db "/path/to/course_metadata.db" --bfs-final-paths outputs/bfs_v5_final_paths.json
python pipeline_orchestrator.py --mode eval_only --source "/path/to/course_repo" --db "/path/to/course_metadata.db" --bfs-final-paths outputs/bfs_v5_final_paths.json --ground-truth "/path/to/ground_truth"Step 1 assigns every file in a raw course repository to one of three pedagogical categories:
- study — instructor-provided learning materials students read, watch, or review
- practice — assignments, labs, projects, starter materials, solution sets, and related student-facing task materials
- support — logistics, policies, staff info, textbooks, readings, and reference resources
Folder-level traversal also allows the system to infer:
- a task name (for example:
discussion,homework,lab,project,slides) - a sequence name (for example:
disc05,hw03,week02,partA) - a category depth showing how deep these semantics appear in the original path
These extra fields make Step 1 more than a flat classifier: it is the stage that produces the first semantic filesystem layout for the repository.
Step 1 treats course organization as a hierarchical classification problem rather than classifying every file independently.
A filename like sol.pdf is ambiguous by itself. It could be:
- a homework solution,
- a discussion answer key, or
- an exam solution.
But inside hw/hw03/ next to a problem set and starter files, its educational role becomes much clearer. The Step 1 pipeline therefore:
- traverses the directory tree in breadth-first order,
- tries to classify an entire folder first,
- labels the whole subtree when the folder is coherent, and
- only falls back to file-level classification when the folder is mixed or unclear.
This makes the system both more accurate and more efficient.
The main Step 1 pipeline. It:
- scans the repository on disk,
- loads per-file descriptions from the metadata database,
- builds a folder tree,
- runs hierarchical BFS-based classification,
- infers task/sequence metadata,
- creates destination mappings,
- exports reports / JSON plans / tree JSON,
- and optionally copies the reorganized files to a destination folder.
The LLM classification backend used by bfs_v5.py. It contains:
- shared data structures (
FileMeta,FolderNode,FileIndexEntry,FolderStats,ClassificationResult) - structured Pydantic output schemas for folder and file classification
- folder-level and file-level prompts
- task/sequence inference helpers
- timeouts and debug logging for LLM calls
Compared with earlier versions, the current Step 1 pipeline adds:
- task / sequence extraction as first-class outputs,
- keyword-based task heuristics for sparse or under-described files,
- stronger support for path-based semantic destination construction, and
- a 3-way file-level output system (
study,practice,support) even when the model attemptsskip.
Step 1 expects:
- a source course repository (directory on disk)
- a SQLite metadata database with a
filetable - an OpenAI API key in the environment
The metadata DB is used to retrieve:
uuidfile_namedescriptionrelative_pathextra_infofile_hash
These database descriptions provide the semantic signal that makes content-aware classification possible.
A direct Step 1 run can emit:
bfs_v5_final_paths.json— flattened routing records consumed by Step 2bfs_v5_report.md— Markdown reportbfs_v5_plan.json— mapping plan and statsbfs_v5_tree.json— tree-structured classification outputbfs_v5_llm_debug.json— full prompt/response debug log- reorganized files copied into a destination directory (if
--executeis enabled)
When Step 1 is run through pipeline_orchestrator.py, bfs_v5_final_paths.json is the handoff artifact for Step 2. With --debug, it is kept under the configured output directory along with bfs_v5_tree.json, bfs_v5_report.md, and bfs_v5_plan.json. Without --debug, the orchestrator uses a temporary final-paths file for the in-memory handoff and removes it after downstream stages finish.
Each file mapping contains at least:
source_reldest_relcategorytask_namesequence_namecategory_depthreason
The destination path is built in a semantic format that aims to be more intuitive than the original repository.
A typical Step 1 destination looks like:
<category>/<task>/<sequence>/...
Examples:
practice/homework/hw03/problemset.pdf
study/slides/lecture05/lec05.pdf
support/resources/reference/robot_usage_guide.pdf
The pipeline also applies container collapsing to generic wrappers such as:
assets/resources/materials/
when those folders add nesting but not pedagogical meaning.
At each folder node, the classifier uses three main sources of information:
-
Structural signals
file counts, extension distributions, subfolder names, recursive subtree size -
Semantic signals
concatenated content descriptions from the metadata DB -
Ancestor context
one-sentence descriptions from already-classified parent folders
The traversal then branches:
-
Confident, non-mixed folder
The whole subtree inherits one category. -
Mixed or skip folder
The traversal continues downward and items are classified at finer granularity.
This design guarantees that every file receives a final category, while still exploiting folder-level regularity when it exists.
pip install openai pydantic python-dotenvexport OPENAI_API_KEY="your_api_key_here"python bfs_v5.py \
--source "/path/to/course_repo" \
--db "/path/to/course_metadata.db" \
--model "gpt-5-mini-2025-08-07"python bfs_v5.py \
--source "/path/to/course_repo" \
--db "/path/to/course_metadata.db" \
--model "gpt-5-mini-2025-08-07" \
--debugpython bfs_v5.py \
--source "/path/to/course_repo" \
--db "/path/to/course_metadata.db" \
--model "gpt-5-mini-2025-08-07" \
--execute \
--dest "/path/to/course_repo_out"Always produced:
bfs_v5_final_paths.json— flattened routing records for Step 2
Produced only with --debug:
bfs_v5_report.mdbfs_v5_plan.jsonbfs_v5_tree.jsonbfs_v5_llm_debug.json
Step 2 takes the output of Step 1 and performs a deeper reorganization of study materials around lecture/topic structure.
This README section is based on the current Step 2 implementation and its existing project documentation.
Even after Step 1, the study folder may still be organized by format:
- lectures/
- discussions/
- videos/
- supplementary readings/
That is better than the raw repository, but still not how students typically review material.
Step 2 reorganizes the study subset around lecture/topic units, so that all relevant materials for the same lecture can be grouped together.
Holds the Pydantic schemas and shared pipeline dataclasses used across Step 2.
Provides:
- UTF-8-safe output helpers
- debug-log helpers
- path normalization helpers
- JSON loading and saving
- course-name derivation
- the
LLMGatewaywrapper around structured OpenAI completions
Contains the actual Step 2 domain logic:
- pre-enrichment merge
- enrichment
- backbone identification
- orphan collection
- plan generation
The top-level implementation layer for Step 2. It keeps intermediate handoffs in memory and writes only the final structure tree unless debug mode is enabled.
Backwards-compatible re-export shim for older code using the previous interface.
Step 2 expects:
- a tree JSON from upstream processing
- a metadata DB with per-file descriptions and hashes
- optionally, a final-paths JSON from the earlier reorganization pass
These inputs are merged and enriched so that the Step 2 LLM sees not only filenames, but also human-readable descriptions of file contents.
If --final-paths is supplied, Step 2 first rebuilds or reorganizes the input tree so that files live at their upstream final_path locations before enrichment begins.
This supports:
- building from a routing file alone,
- reorganizing an existing first-pass tree, or
- combining both while preserving existing metadata such as
file_hash.
A reorganized intermediate tree can be saved for inspection when debug mode is enabled:
outputs/<course>/v4_tree_reorganized.json
The pipeline walks the tree and attaches database descriptions to every file.
It then prunes the structure down to the study category (and optionally merges practice for multi-match mode).
When debug mode is enabled, the enriched study tree is written to:
outputs/<course>/study_enriched.json
The LLM is asked to identify the chronological backbone of the course — typically the folder that best represents lecture progression, such as:
study/slidesstudy/lectures
When debug mode is enabled, this yields a backbone_result.json file containing the chosen backbone path.
Everything outside the backbone is treated as an orphan that must be matched back to one or more lecture groups.
The orphan collector supports:
- task-instance aggregation (for example, collapsing an entire
Discussion_10/folder into one unit) - leaf-folder aggregation
- batched LLM matching against the lecture groups
- hallucination filtering and basename rescue for malformed paths returned by the LLM
When debug mode is enabled, this stage writes:
orphan_matches.jsonrearrangement_plan.json
The final Step 2 output is a lecture-oriented structure tree with file hashes included at the leaves. This is the one Step 2 artifact that is always written.
It is written to:
outputs/<course>/[multi/]rearrangement_structure_tree.json
This is the main artifact consumed downstream.
python -m rearrange.file_rearrang \
--input bfs_v5_tree.json \
--db "CS_61A_metadata.db" \
--final-paths bfs_v5_final_paths.json \
--course cs61a \
--multi-match truepython -m rearrange.file_rearrang \
--input bfs_v5_tree.json \
--db "CS_61A_metadata.db" \
--course cs61apython -m rearrange.file_rearrang --step enrich --input <tree>.json --db <db>.db --course <course>
python -m rearrange.file_rearrang --step backbone --course <course>
python -m rearrange.file_rearrang --step match --course <course>
python -m rearrange.file_rearrang --step tree --course <course> --db <db>.dbThe normal Step 2 deliverable under outputs/<course>/ or outputs/<course>/multi/ is:
rearrangement_structure_tree.json
When debug mode is enabled, Step 2 also writes inspection artifacts:
v4_tree_reorganized.jsonstudy_enriched.jsonbackbone_result.jsonorphan_matches.jsonrearrangement_plan.jsondebug/*.json
These debug files are for observability. In the normal orchestrated run, intermediate data is passed in memory rather than being written and re-read.
The orphan matcher supports two modes:
- single-match mode — each orphan is assigned to one best lecture group
- multi-match mode — a file can be assigned to multiple lecture groups when it genuinely covers multiple topics
Multi-match is useful when a discussion worksheet, review file, or summary file spans several lecture topics and should remain visible in all relevant lecture folders.
The matching stage applies several defenses against fabricated or malformed LLM outputs:
- exact-path matching
- whitespace-normalized matching
- basename rescue when the LLM drops or adds intermediate folders
- filtering of unknown or ambiguous returned paths
This ensures the final rearrangement plan only contains paths that correspond to real orphan items.
This script is a downstream utility that writes reorganized logical paths back into course metadata databases.
It delegates to per-course implementations rather than forcing all courses through one rigid generic writer.
Current supported courses in the repo include at least:
- CS 61A
- EECS 106B
- loads the correct course-specific mapping logic
- overrides the output table name
- supports dry-run mode
- creates a DB backup before real writes
- optionally cleans up nonessential tables after success
- materializes the logical output tree from
rearrangement_structure_tree.json - stores the first occurrence of an item physically and later occurrences as symlinks
python map_to_db_v3.py --course cs61a --dry-run
python map_to_db_v3.py --course eecs106b --dry-runpython map_to_db_v3.py --course cs61a --output-table file_new
python map_to_db_v3.py --course eecs106b --output-table file_newAccording to the capstone report, the system was evaluated against manually curated ground-truth structures. Reported results include:
- Step 1 file categorization: 99.4% accuracy on CS 61A
- Step 2 lecture labeling: 97.3% accuracy on CS 61A
- End-to-end exact-match accuracy: 81.7%
- improved exact-path accuracy over the raw baseline while preserving high category and filename recovery
The report also notes strong generalization behavior on additional courses such as EECS 106B and CS 288.
A typical workflow for this repository is:
- collect or crawl course materials into a raw repository
- build / attach a metadata SQLite DB with file descriptions
- run Step 1 (
bfs_v5.py) to categorize the repository intostudy,practice, andsupport - run Step 2 (
rearrange/src/pipeline.py) to reorganizestudyaround lecture/topic structure - optionally run DB write-back (
map_to_db_v3.py) to persist logical paths - use the reorganized structure for browsing, evaluation, or TAI retrieval
For the current codebase, those stages are usually run together through pipeline_orchestrator.py rather than as separate commands.
This repository reflects several recurring design choices across both stages:
- hierarchical over flat classification
- structured LLM output via Pydantic schemas
- non-destructive reorganization (copy/symlink/logical path rather than destructive moves)
- auditability via JSON artifacts and debug logs
- metadata-enriched reasoning rather than filename-only heuristics
- course-aware structure discovery rather than pure embedding similarity
Based on the current code and report, key limitations and future opportunities include:
- limited automated regression testing
- dependence on text descriptions when multimodal content may matter
- hierarchy mismatches that are semantically acceptable but exact-path different
- subjective differences in what counts as the “best” folder structure
- opportunities for instructor or GSI feedback loops
- opportunities for slide-visual, transcript, and notebook-structure signals
This repository implements the MEng capstone project described in the team report on AI-assisted course repository reorganization for educational AI and student navigation. The work is positioned as infrastructure for Berkeley TAI: organizing course materials so both humans and AI systems can find the right information more reliably.
MEng Capstone Team 9
- Angelina Zhang
- Catherine Lee
- Derek Xu
- Yachen Wu
- Yu-Kai Hung
Subject area: Educational AI and Learning Systems
No explicit repository license is documented in the materials referenced here. If you plan to reuse the code outside the capstone context, add a license file and verify any course-data handling constraints first.