CiteTutor: 답변을 제시하기 전에 검증하는 로컬 튜터
요약
CiteTutor는 학습자가 내용을 공부하기 전에 설명을 먼저 검증할 수 있도록 설계된 로컬 튜터입니다. PDF 노트와 교과서 챕터를 활용하여 연습 문제를 생성하고, 답변에는 출처 페이지 인용(page citations)을 포함합니다. Gemma 등의 모델이 초안을 작성한 후, 여러 독립적인 모델 패밀리가 사실적 지원 여부를 검증하는 과정을 거칩니다.
핵심 포인트
- 로컬 환경에서 실행되어 개인 학습 자료에 적용 가능합니다.
- 답변에는 출처 텍스트를 열어주는 페이지 인용 기능이 포함됩니다.
- 여러 모델을 통해 답변의 사실적 근거를 다중으로 검증합니다.
- 검증 실패 시 명확하게 '충분한 증거가 없다'고 거절합니다.
본 게시물은 Hacktoberfest Weekend Challenge: Build for a Friend 제출물입니다.
제가 만든 것
한 대학 친구에게 CiteTutor를 자신의 강의 자료 챕터에 적용해 보라고 부탁했습니다. 그 친구의 피드백은 잘 작동하지만 조금 느리다는 것이었습니다.
기다림에 대해서는 그 친구가 맞았습니다. 별도로 측정한 역학 장치 요청에서 Gemma가 답변 초안을 작성하는 데 24.84초가 걸렸고, 두 개의 Qwen 검사가 추가로 20.48초가 걸렸습니다. CiteTutor는 이러한 모든 검사를 통과한 후에만 답변을 표시합니다.
이것이 바로 CiteTutor의 핵심 설계 선택입니다: 학습자가 내용을 공부하기 전에 설명을 먼저 확인하는 것.
CiteTutor는 노트북에서 실행됩니다. 제 친구는 과목별로 PDF 노트와 교과서 챕터를 정리하고, 설명이나 힌트를 요청하며, 선택한 페이지 범위에서 연습 문제를 생성할 수 있습니다. 표시되는 튜터 답변에는 출처 텍스트를 열어주는 페이지 인용(page citations)이 포함됩니다. 검토된 스캔 자료도 원래의 페이지 이미지를 유지합니다.
Gemma가 초안을 작성하고, 코드가 그 인용 ID를 확인합니다. 다른 모델 패밀리가 독립적으로 증거를 읽고 사실적 지원 여부를 확인합니다. 실패한 초안은 숨겨진 상태로 유지됩니다. 최대 세 번의 시도 후, 튜터는 인용된 답변을 반환하거나 다음과 같이 말합니다:
이 문서에는 충분한 증거가 없습니다
이러한 검사 과정에서도 실수는 발생할 수 있습니다. 그 목적은 학습자에게 검토 가능한 답변과 파이프라인이 지원할 수 없을 때 명확하게 거절하는 것을 제공하기 위함입니다.
데모
[https://drive.google.com/file/d/1R8ideZ5F_P1GLpLatAy23IuYOUNM8wSR/view?usp=sharing]]
실제 로컬 앱 사용 사례 (10월 5일): 작업 예시가 클릭 가능한 페이지 인용과 함께 8개의 N을 반환합니다. 이 리허설은 아래의 10개 질문 평가와는 별개입니다.
레코딩 가이드에는 전체 경로가 포함되어 있습니다: 샘플 PDF를 업로드하고, 지원되는 질문을 하고, 그 인용을 열고, 관련 없는 질문에 대해 거부하는 것을 보여주며, 퀴즈를 생성하고, 스캔본을 검토하며, 해당 Sentry 단계를 검사합니다.
일회성 의존성 설치 및 모델 풀링 후:
./run.sh
http://127.0.0.1:8000을 엽니다. README에는 설정 명령어들이 포함되어 있습니다.
코드
CiteTutor는 제가 이전에 구현했던 Course Companion을 각색한 것입니다. 이는 챌린지 기간보다 앞선 작업입니다. 빌드 로그에는 해당 출처와 그 이후의 개발 기록이 남아 있습니다. 이 각색본의 적격성은 여전히 주최 측의 명확화가 필요합니다.
구현 방법
이 애플리케이션은 로컬 검색 증강 생성(Retrieval-Augmented Generation, RAG) 파이프라인입니다: Python, uv, FastAPI, PyMuPDF, SQLite 및 작은 HTML/JavaScript 프론트엔드로 구성되어 있습니다. Ollama가 모델을 실행하며, 이 애플리케이션은 큐나 데이터베이스 서버 없이 인프로세스(in-process) 호출을 수행합니다.
구현된 아키텍처. 튜터의 답변과 연습 질문은 별도의 수용 게이트(acceptance gate)를 가지며, 선택적 원격 측정(telemetry)은 메타데이터 전용 경계를 넘나듭니다.
1. 모델에 질문하기 전에 페이지 보존하기
인용은 올바른 자료를 가리킬 때만 유용합니다.
PyMuPDF는 물리적 페이지별로 텍스트를 추출합니다. 청크(Chunks)는 최대 1,600자이며 200자의 오버랩을 가지며, 페이지 경계를 넘지 않습니다. SQLite에 이들의 텍스트, 벡터, 문서 식별자 및 페이지 번호를 저장합니다.
검색(Retrieval)은 BM25 스타일 키워드 순위와 nomic 임베딩에 대한 코사인 유사도를 결합하고, 여기에 Reciprocal Rank Fusion을 병합하여 사용합니다. 따라서 정확한 용어와 다르게 표현된 질문 모두 순위에 기여할 수 있습니다. 기본 예산은 선택된 주제 및 문서로 제한되는 네 개의 청크입니다.
이것은 작은 로컬 벡터 스토어이며, 유사도는 처리 과정에서 계산됩니다. 임베딩 모델의 식별자는 저장되어 있어 모델을 변경하더라도 호환되지 않는 벡터가 실수로 섞이는 것을 방지합니다.
인용(citation)을 클릭하면 이 답변에 사용된 예제 작업이 포함된 저장된 출처 페이지가 열립니다.
2. 초안 작성과 표시 권한 분리하기
Gemma는 검색된 증거를 받고 공급된 청크 ID와 함께 답변 세그먼트를 반환합니다. Ollama는 Pydantic 기반 JSON 스키마를 받으며, 애플리케이션이 반환된 JSON을 다시 검증합니다.
알 수 없거나 누락된 인용은 실패합니다. 코드는 유효한 ID를 저장된 페이지 번호로 해결하며, 모델은 임의의 인용 대상을 선택할 수 없습니다.
다음 단계는 **블라인드 Qwen 풀(blind Qwen solve)**입니다. 이는 Gemma의 초안이나 학습자의 설명 스타일 없이 질문과 인용된 증거만을 받습니다. 오직 그 후에만 별도의 Qwen 호출이 모든 주장에 대해 관련성, 해당 독해와의 일치 여부, 그리고 출처 지원을 검사합니다.
Retrieve → Gemma draft → schema and citation guards
→ blind Qwen solve → support check
→ display, retry, or refuse
이 추가적인 읽기는 실제 실패에서 비롯되었습니다. 이전에 손으로 작성한 노트 테스트에서는 배열 셀의 의미를 물었음에도 불구하고 배열 초기화가 반환되었습니다. 이전 검증기는 관련성이 있지만 무관한 답변을 받아들였습니다. 요청된 의미를 명시적으로 만들기 위해 초안 합의 전에 블라인드 소스 읽기(blind source reading)를 추가했습니다.
현재 프롬프트는 가장 좋은 근거 청크를 사용하여 간결한 단락 하나를 요구합니다. 학습자 맥락은 문구를 변경할 뿐, 증거를 변경하지 않습니다. 명시적인 “값 및 단위” 요청 또한 인용된 텍스트에 작업량이 포함된 양(worked quantity)을 필요로 하며, 이는 보수적으로 새로운 계산을 거부할 수 있습니다.
검증 전에 아무것도 스트리밍되지 않습니다. 다른 모델 패밀리와 별도의 호출은 일부 실패 모드를 줄이지만, 판단이 오류가 없음을 의미하지는 않습니다.
3. 솔버로부터 퀴즈 키 숨기기
질문이 실제 문장을 인용하더라도 잘못된 키를 가질 수 있습니다.
Gemma는 소스 ID와 근거 인용문을 포함하는 MCQ(객관식 문제) 또는 단답형 후보를 생성합니다. 코드는 그 스키마를 확인하고, 공백 정규화 후 해당 인용문이 실제 소스 부분 문자열인지 검증합니다.
애플리케이션은 그런 다음 문항(stem)과 선택지를 사용하여 증거를 다시 검색합니다. Qwen은 그 증거와 질문을 보지만, 제안된 키, 근거 또는 생성기 제공 인용문은 볼 수 없습니다.
MCQ는 키 합의가 필요하며, 단답형은 의미론적 합의가 필요합니다. 별도의 지원 확인(support check) 또한 제안된 키와 근거를 소스와 비교하여 판단합니다. 지원되지 않거나, 모호하거나, 불일치하는 후보는 폐기되며, UI는 거부된 개수를 보고합니다.
지난 10월 5일 리허설: 후보 중 하나는 통과했고, 하나는 거부되었습니다. 확장된 답변에는 근거 인용문과 페이지 출처가 포함됩니다. 이는 스테이징 데이터가 아니라 실제 출력물입니다.
4. 필기체를 검토가 필요한 증거로 취급하기
전체 페이지의 Gemma 전사(transcription)는 불완전했으며, Qwen-VL 실험이 중단되었습니다. 전용 GLM-OCR은 더 유용한 초안을 생성했지만, 치수, 아래 첨자, 배열 이름 및 무한대 기호를 잘못 읽었습니다.
따라서 스캔된 PDF는 모든 스캔 페이지가 원본 이미지와 대조하여 검토될 때까지 검색(retrieval) 영역 밖에 머무릅니다. 학습자는 전사 내용을 편집할 수 있습니다. 부분적인 출력은 레이블이 지정되며, 공란으로 검토된 페이지는 제외됩니다.
검증기(verifier)는 승인된 텍스트를 읽습니다. 이 텍스트에 숨겨진 OCR 오류를 수정할 수는 없습니다. 인간의 검토는 데이터 수집 과정(ingestion process)의 일부입니다.
5. 거절 사례를 포함한 전체 경로 테스트
8 GB Apple M1에서 원본 3페이지 분량의 역학 PDF에 대해 10개의 질문을 실행했습니다. 보존된 보고서에는 다음과 같이 나와 있습니다:
| 항목 | 이전 실행 | 수정된 실행 |
|---|---|---|
| 지원되는 답변 정확도 | 4 / 7 | 7 / 7 |
| ... |
The 수정된 답변은 모든 7가지 사례에서 예상된 문서/페이지 앵커를 가지고 있었습니다. 반환된 튜터 지연 시간(latency)은 중앙값 44.5초였으며, 범위는 34.3–57.6초였습니다.
혼합 퀴즈에서는 소스 자료의 2페이지에 있는 9 J 운동 에너지 예제를 사용하여 단답형 질문을 하나 수락했습니다. 그러나 블라인드 솔버(blind solver)가 정답 키와 의견이 달랐기 때문에 객관식 문항(MCQ) 하나는 거부되었습니다. 이는 어떤 모델이 잘못되었는지의 증거라기보다는, 의견 불일치를 기록한 것입니다.
동일한 작은 테스트 환경에서 기록된 두 번의 실행입니다. 프롬프트와 런타임이 변경되었으므로 통제된 제거(controlled ablation)는 아닙니다. 지연 시간 플롯은 별도의 실시간 추적을 재구성하는 것이며, 대시보드 스크린샷이 아닙니다.
이것은 대학 PDF 전반의 일반적인 정확도에 대한 평가가 아니라 개발자가 작성한 스모크(smoke) 평가입니다. 정확도는 골드 패턴과 출처 비교를 사용했으며, 인용 정확도는 문서/페이지 앵커를 확인했습니다. 이전 실패 사례는 평가 보고서와 스캔 관찰 결과에 남아 있습니다.
별도로, 청킹(chunking), 인용/거절 처리 및 원격 측정(telemetry) 개인 정보 보호를 포함한 구현 동작에 대해 56개의 자동 계약 테스트가 통과했습니다. 이 테스트들은 모델의 정확도를 측정하지 않습니다.
Open Innovation이 중요한 이유?
모델 역할들이 핵심 작업을 수행합니다. Nomic은 검색하고, Gemma는 초안을 작성하며, Qwen은 검증하고, GLM-OCR은 전사(transcribe)합니다. 오픈 웨이트(open-weight) 모델들은 호스팅된 모델 폴백이나 요청당 AI API 요금 없이 로컬 Ollama를 통해 이러한 작업을 실행할 수 있게 합니다. 물리적인 네트워크 연결 끊김 테스트는 아직 진행되지 않았습니다.
학습 자료가 로컬에 유지됩니다. 친구의 문서, 질문 및 답변은 그들의 노트북에 남아 있습니다. 선택적 Sentry 추적(tracing) 내보내기는 허용된 운영 메타데이터만 전송하며, DSN을 비워두면 이를 비활성화할 수 있습니다.
모델들은 교체 가능합니다. 스캔 역할을 GLM-OCR로 전환하는 것은 이전에 비전 실험이 실패했을 때 유용했습니다. Gemma 튜터링과 동일한 승인 정책을 유지하면서 해당 역할을 변경할 수 있었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기



