ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
전체 규칙서를 한눈에 이해할 수 있는 답변으로 압축한 VU 암스테르담 MSc AI 독립 학생 프로젝트
The whole rulebook, collapsed into one answer you can understand. An independent student project for the MSc AI at VU Amsterdam.
교육JavaScript · PythonGitHub @Munish702온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A question-answering assistant for students of the MSc Artificial Intelligence at Vrije Universiteit Amsterdam. It answers questions about exam rules, resits, admission and courses using only the official study guides and Teaching and Examination Regulations, cites its sources, and says "I couldn't find this" instead of guessing.
Runs fully locally on a laptop (no API keys, no data leaves the machine).
Headline result: on a 48-question evaluation set, retrieval Recall@5 went from 0.81 → 0.95 and answers containing the correct key facts from 0.81 → 0.91, with zero hallucinated answers on unanswerable questions.
Website: munish702.github.io/vu-rag-assistant. A scroll-driven page that shows the idea (the whole rulebook collapses into one plain answer) and ends in the chat. Online it replays real recorded answers; run it on your own computer for live ones (see Run the website).
A real answer from the assistant (asked in the app, 6 October 2026):
Q: What IELTS score do I need?
You need an IELTS Academic overall score of at least 6.5, with a minimum score
of 6.0 for each subcomponent (Listening, Reading, Writing, and Speaking) [1].
[1] Artificial Intelligence TER 2026-2027 - Article 7.2 Admission requirements (part 1/2)
General rule. Searched 1,102 passages, reranked 35, used 5.
flowchart LR
Q[Question] --> D[Dense search<br/>bge-small, top 20]
Q --> B[BM25 keyword search<br/>top 20]
D --> M[Merge candidates]
B --> M
M --> R[Cross-encoder reranker<br/>bge-reranker-base]
R --> S[Top 5 + reserved<br/>general-rule slot]
S --> L[LLM: qwen2.5 7B<br/>via Ollama]
L --> A[Answer with citations<br/>or abstain]
| Stage | What it does | Why |
|---|---|---|
| Ingestion | Parses PDFs into one chunk per course section / regulation article, with metadata (course code, article, programme, year) | Chunk boundaries follow the documents' own structure, so a rule is never cut in half |
| Dense search | Embedding similarity (BAAI/bge-small-en-v1.5, ChromaDB) | Understands meaning: "resit" ≈ "second examination opportunity" |
| BM25 | Keyword search, implemented from scratch | Precise on exact course names and codes like XM_0189 |
| Reranker | Cross-encoder reads question and chunk together (BAAI/bge-reranker-base) | Far more accurate ordering than comparing embeddings |
| Reserved slot | If no general regulation is in the top 5, the best-ranked one replaces #5 | General rules apply to every student; the reranker tends to prefer course pages |
| Generation | Local LLM answers only from numbered, scope-labelled sources | Citations make every claim verifiable; abstains when sources don't cover the question |
I built the evaluation before optimising anything, so every change is measured rather than guessed.
Eval set: 48 hand-checked questions written in a student's voice
(17 course lookups, 17 regulation questions, 7 multi-hop, 7 unanswerable).
Each answerable question is labelled with the location of its answer (e.g. Article 3.5 or course XM_0083, Method of Assessment) rather than a chunk ID, so labels stay valid when chunking changes.
Retrieval metrics: Recall@1, Recall@5 (is the answer in what the LLM sees?), MRR. Generation metrics:
| System | R@1 | R@5 | MRR | Key facts | All facts | Gold source retrieved | Wrong abstain |
|---|---|---|---|---|---|---|---|
| Dense retrieval | 0.61 | 0.81 | 0.71 | 0.81 | 0.80 | 33 / 41 | 0.05 |
| + BM25 candidates + reranker | 0.73 | 0.90 | 0.79 | 0.88 | 0.85 | 37 / 41 | 0.00 |
| + reserved general-rule slot | 0.73 | 0.95 | 0.80 | 0.91 | 0.88 | 39 / 41 | 0.00 |
All runs: abstained on 7 / 7 unanswerable questions; gold-source citation accuracy 0.97–1.00. Average answer time ≈ 4–5 s on a MacBook Air (16 GB RAM).
Recall@5 by question type
| System | Course lookups | Regulations | Multi-hop |
|---|---|---|---|
| Dense | 0.71 | 0.94 | 0.71 |
| + Reranker | 1.00 | 0.88 ⬇ | 0.71 |
| + Reserved slot | 1.00 | 0.94 | 0.86 |
The breakdown shows something the averages hide: the reranker fixed every course lookup but pushed general regulations down. The reserved slot was designed specifically to counter that, and recovered regulation recall without costing anything on course lookups.
| Change | R@1 | R@5 | MRR |
|---|---|---|---|
| Baseline chunking | 0.54 | 0.77 | 0.63 |
| Chunking v2 (facts as sentences, split list-style articles, separate footnotes) | 0.69 | 0.92 | 0.79 |
Better data preparation alone gave the biggest single jump in the project.
Hybrid search with Reciprocal Rank Fusion. Fusing dense and BM25 rankings with RRF lowered key-fact coverage from 1.00 to 0.62 on the 15-question set. RRF rewards chunks that are mediocre in both rankings over chunks that are excellent in one; BM25 fails completely on paraphrased questions ("graded" vs "grade") but still got an equal vote. Using BM25 only to nominate candidates for the reranker fixed this.
A score threshold for "no answer". Reranker scores look well separated on average (0.70 answerable vs 0.10 unanswerable), but the distributions overlap: the lowest answerable question scored 0.002, the highest unanswerable 0.55 (a deliberately tricky near-miss question). No threshold separates them, so abstention stays with the LLM, which reads the sources.
Prompt tuning on a 3B model. With llama3.2:3b, three prompt versions gave three different answers to the same question. When small prompt changes flip answers, the model is the bottleneck. Switching to qwen2.5:7b fixed it.
Credits: 30 retrieved poorly; "Master Project AI is worth 30 EC (30 ECTS credits)" retrieved well.Remaining failures (41 answerable): 2 retrieval misses (the reserved slot picks the wrong regulation article) and 1 numeric reasoning error (the model treats 12 EC as meeting an 18 EC requirement).
src/
├── config.py # all settings: models, paths, retrieval mode
├── ingest.py # PDFs -> structured, metadata-rich chunks
├── index.py # embed chunks into ChromaDB
├── retrieve.py # dense / BM25 / hybrid / rerank retrieval
├── generate.py # prompt construction + local LLM (Ollama)
├── evaluate.py # retrieval metrics (Recall@k, MRR)
├── evaluate_generation.py # abstention, key facts, citations
├── server.py # serves web/ and answers its chat, locally
└── app.py # the earlier Streamlit interface
web/ # the website (static: HTML, CSS, JS, photo planes)
tools/make_plates.py # how the night photo was cut into depth planes
tools/make_map.py # bakes the dot-matrix map layers (Netherlands + Amsterdam)
data/
├── raw/manifest.csv # which documents to download (PDFs not included)
└── eval/questions.jsonl # 48-question evaluation set
experiments/ # every evaluation run, with its full configuration
data/raw/manifest.csv from the VU website into data/raw/
(the PDFs are VU's and are not redistributed here).ollama pull qwen2.5:7bpython -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python src/ingest.py
python src/index.py
python src/generate.py "Can I resit an exam I already passed?"
Evaluate: python src/evaluate.py and python src/evaluate_generation.py.
python src/server.py
This opens http://127.0.0.1:8000: the scroll page with a live chat, answered on your own computer: your questions never leave it. On macOS you can also double-click start.command. Thumbs up/down are saved to data/feedback.jsonl, which is not committed.
The earlier Streamlit interface still works: streamlit run src/app.py.
.github/workflows/pages.yml publishes web/ to GitHub Pages on every push to main (one-time setup: Settings → Pages → Source: GitHub Actions). There is no language model online, so the published chat replays three answers the assistant really gave, and labels each one as recorded.
Python · pdfplumber · sentence-transformers · ChromaDB · BM25 (from scratch) · cross-encoder reranking · Ollama (qwen2.5 7B)
Built by Munishwar Pradhan, MSc AI student at VU Amsterdam. Website photo: the VU main building at night, taken by the author. Map: Natural Earth 1:10m (public domain); the A10 ring is drawn through station coordinates from Wikipedia. Streamlit app background: "Amsterdam VU" by Rokus Cornelis, CC BY 3.0, via Wikimedia Commons (modified: faded and blurred).