student-record-pipeline

학생이 쓴 글(손글씨 스캔·디지털)로 교과세특 초안을 반 단위로 만드는 도구 — 사전 인터뷰부터 검수 엑셀까지. 트리거 — '세특 파이프라인', '수행평가로 세특', '활동지 스캔해서 세특', '반 전체 세특 초안'.

SKILL.md

GitHub

세특 파이프라인

학생이 쓴 글을 받아 교과세특 초안을 반 단위로 만든다. 선생님이 하는 일은 학생 글에서 의미 있는 대목을 형광펜으로 칠하는 것뿐이고, 세특 작성·검사·엑셀 만들기는 이어서 자동으로 돈다.

흐름

① 사전 인터뷰 → ② 학생 글 읽어들이기 → ③ 누구 글인지 맞추기 → ④ 화면 띄우기
   → ⑤ 선생님이 형광펜으로 표시 → ⑥ 세특 만들기 → ⑦ 자동 검사 → ⑧ 검수 엑셀

선생님은 표시를 다 끝낸 뒤 한 번만 말씀하시면 된다. 그때 ⑥~⑧이 한 묶음으로 돈다. 여러 번 나눠 부르셔도 이미 쓴 사람은 다시 쓰지 않는다(render_now.py 가 그 경계를 지킨다). 검수는 out/검수.xlsx 한 곳에서만 한다 — 화면에는 검수 자리가 아예 없다(2026-08-06에 코드까지 걷어냄).

⚠️ 기준 학년도 2026. 기재요령은 해마다 바뀝니다. 다음 학년도에 쓰실 때는 references/giwan-2026-grounding.md · 바이트 규칙 · forbidden-terms.txt 를 그 해 것으로 갈아 주시고, 인터뷰 1번 답에 기준 학년도를 적어 두십시오.


절대 규칙

어느 단계에서도 어기지 않는다. 근거는 references/giwan-2026-grounding.md(2026 기재요령 발췌). 각 규칙이 왜 필요한지, 무슨 사고가 있었는지는 references/why.md 에 있다(필요할 때 grep).

  1. 누구 글인지 조용히 추측하지 않는다. 명렬표 정본이 유일한 기준이다. 파일 순서 + 글에 적힌 이름·학번을 정본과 셋 다 대조해 일치할 때만 자동 확정하고, 하나라도 어긋나면 선생님께 넘긴다. 엉뚱한 학생 세특 0 이 최우선이다. 사람 손이 더 가더라도 이게 이긴다. 단, 글자가 달라 보인다고 곧바로 오매핑은 아니다 — OCR 오독이 흔하다(→ why.md).

  2. 없는 내용을 지어내지 않는다. 글자가 깨진 곳을 바로 읽어 주는 것까지가 전부다. 학생이 안 한 것을 채워 넣지 않는다. 못 읽는 곳은 [판독불가] 로 남긴다. 허위·과장 기재는 「교육공무원 징계양정 규칙」 적용 대상이다(기재요령 p.24~25).

  3. 성취기준은 뒤에 두고, 선생님의 평가 관점이 문장을 이끈다. 성취기준은 문장에 어휘로 드러나지 않는다(감사 대비 근거일 뿐). 문장을 실제로 이끄는 건 선생님이 이 과제로 보려던 것이다. 기재요령 p.94 가 "수업 활동의 단순 나열이나 성취기준에 명시된 지식의 단순 서술은 지양"이라 못박았다 — 성취기준으로 문장을 몰면 곧 그 단순 서술이 된다.

  4. 길이는 상한이지 목표가 아니다. 학생이 쓴 게 적으면 짧은 게 정상이다. 분량 채우려고 늘리면 그건 2번 위반이다.

  5. 점수·등급·석차를 본문에 쓰지 않는다. 루브릭 점수는 문장의 깊이·톤을 정하는 속내 신호로만 쓰고 서술로 녹인다.

  6. 결과물은 언제나 '초안'이다. 선생님 눈검수가 나이스 입력 전 필수 관문이다. 생기부는 법적 문서이고 최종 책임은 선생님에게 있다. 기재요령 p.25 는 "AI 생성물을 그대로 입력"을 금지하되 "윤문 등 보조 수단"으로 쓰고 "최종 입력 전 철저히 확인"하면 허용한다. 이 도구는 그 확인을 강제하는 보조수단으로만 존재한다.

  7. 재료는 선생님이 준 경로에서만 가져온다. 파일 1개를 받았으면 그 1개만 쓴다. 위·옆 폴더를 열어보지 않는다. 더 필요하면 스스로 찾지 말고 무엇이 왜 필요한지 말하고 받는다. (실제 사고 있었음 → why.md)

  8. 선생님이 안 하신 것을 타박하지 않는다. 형광펜을 안 칠하셨으면 "표시 안 하셔서 AI가 직접 골랐습니다"라고 사실만 적는다. "검수 비용이 올라간다", "이건 칠하는 게 좋다" 같은 말을 산출물·화면·로그 어디에도 넣지 않는다. 무엇을 칠할지는 선생님이 정한다.

  9. 화면과 산출물의 모든 말은 쉬운 한국어로 쓴다. 못 알아들을 말이 하나 나오면 선생님은 검수를 포기하고, 검수를 포기하면 6번이 무너진다. 개발자용 주석은 예외. 인테이크→사전 인터뷰 · 인제스천→읽어들이기 · 매핑→누구 글인지 맞추기 · 배치→묶음 · 스팬/하이라이트→표시한 대목 · 렌더→만들기 · 게이트→검사 · 위임→표시 안 하심 · NEIS→나이스. 길이는 바이트 옆에 '한글 약 N자'를 같이 적는다.

  10. 도구가 어떻게 돌아가는지를 선생님께 설명하지 않는다. 감시·폴링·배치·워크플로· 대기열 같은 말은 개발자용 사정이지 선생님이 알 일이 아니다. "지켜보는 창은 없습니다", "실시간 감시를 걷어냈습니다" 같은 말은 하지 않는다 — 선생님은 없어진 기능을 상상하며 불안해할 뿐이다. 대신 하실 일만 말한다: "표시 다 하시면 알려 주세요." 이 문서에 <!-- 내부 --> 로 묶인 대목은 너를 위한 설명이지 전할 말이 아니다.

🔒 어디에 두는가: 스킬 폴더(references/)에는 아무나 써도 되는 것만(빈 템플릿·사전· 검사 스크립트·규칙). 채운 인터뷰·명렬표·학생 글·세특 초안·검수 엑셀은 전부 개인정보라 스킬 폴더 밖 작업 폴더(<작업폴더>/)에 둔다. 스킬 폴더는 여러 곳으로 공유·동기화되므로 학생 실명이 들어가면 안 된다.


📁 <스킬경로> = 이 스킬이 깔린 폴더. 아래 명령의 <스킬경로> 를 실제 설치 위치로 바꿔 쓰십시오(보통 ~/.claude/skills/student-record-pipeline).

① 사전 인터뷰

새 작업을 시작하면 먼저 선생님을 인터뷰한다. 여기서 나온 답이 나머지 전부를 굴린다. 빈 서식은 references/eval-context-spec.template.md 이고, 채운 것은 작업 폴더에 eval-context-<과목>-<날짜>.md 로 저장한다(위 🔒 — 채운 건 개인정보라 스킬 폴더에 두지 않는다).

🎚️ 13문을 기계적으로 다 묻지 않는다. 이미 주신 자료나 말씀에서 답이 분명한 건 다시 묻지 말고 "이렇게 이해했습니다"로 확인만 한다. 급하시면 필수 3개(과제·루브릭· 평가 관점) 만 받고 나머지는 추천값으로 진행하겠다고 제안한다. 붙은 문항은 묶어 물어도 된다. 목적은 빠짐없이 채우는 것이지 문답 자체가 아니다.

  1. 과목·학기·대상 학년/반은? 몇 명쯤인가요?
  2. 과제: 학생에게 무엇을, 어떤 조건으로 시키셨나요? (지시문 원문이 있으면 그대로)
  3. 학생이 낸 것의 형태는? (손글씨 스캔 / 사진 / 디지털 텍스트)
  4. 루브릭: 채점 척도와 점수대별 판정 기준은?
  5. 평가 관점: 이 과제로 진짜 보시려던 건 무엇인가요? 성취기준 말고 선생님의 말로. ⚠️ 여기서 '흔한 오답·얕은 수행은 어떤 모습인가' 를 꼭 같이 받는다. 이게 비면 상 을 쓸 기준이 없어져 전부 중 으로 주저앉는다.
  6. 분량: 학생당 목표 길이는? (기본 제안 500650바이트 ≈ 한글 165215자, 상한으로 취급)
  7. 명렬표 파일 경로는? (번호·이름, 가능하면 학번) 💡 나이스에서 내려받은 과목세특 엑셀이 있으면 그게 가장 좋습니다. 명렬표 노릇도 하고, 나중에 완성본을 그대로 다시 올리는 양식도 됩니다. (나이스 학생부 > 과목별 세부능력 및 특기사항 > 엑셀 내려받기, 반별 파일) → 받았으면 references/neis_roster.py 로 명렬표 CSV 부터 뽑는다. 다른 명렬표를 찾아 쓰지 않는다(규칙 1·7).
    python3 <스킬경로>/references/neis_roster.py \
      --targets "<작업폴더>/neis/*.xlsx" -o <작업폴더>/out/roster.csv
    
  8. 학생마다 갈리는 지점은? 즉 A와 B를 다르게 쓰는 근거는 무엇인가요? (보통 5번에서 나온다)
  9. 성취기준(선택·뒤에 두는 근거)과 이 과제 특유의 금지사항(선택). → 코드를 외우게 하지 말고 references/standards.py 로 후보를 뽑아 고르시게 한다 (오프라인·전 교과 3,285건).
    SRP=<스킬경로>
    python3 $SRP/references/standards.py --subjects 역사     # ① 과목명부터 확인
    python3 $SRP/references/standards.py --subject 한국사1    # ② 그 과목 전량 → 선생님이 선택
    
    🚩 0건이면 과목이 없는 게 아니라 이름이 바뀐 것이다(동아시아사 → 동아시아 역사 기행). 반드시 --subjects 로 먼저 확인한다. 🚩 코드는 유일키가 아니다([12스문] = 스포츠 문화 / 스페인어권 문화). 과목명과 함께 적는다.
  10. 문체 예시: 선생님이 직접 쓰신 세특을 주세요. 이번 과제 기준으로 잘한 학생(상)· 보통(중)·적게 쓴 학생(하) 각각 한 편씩이면 가장 좋습니다 → 계약서 §3에 그대로 실어 문체·리듬·종결을 거기서 배웁니다. 없으면 아무 세특 3~5편도 됩니다. 🎚️ 레벨별로 받는 까닭: 도달 수준이 문체를 어떻게 바꾸는지는 말로 설명해서 안 되고 실물을 나란히 놓아야 전해집니다. 규칙으로 어휘를 정해 두면 선생님 문체가 아니라 남의 문체를 강요하게 됩니다. ⚠️ 예시엔 과거 학생 개인정보가 있으니 원문장은 작업 폴더에만 둡니다.
  11. 길이가 넘치면: 자동으로 줄일까요, 조금 넘겨도 둘까요?
  12. 활동지 칸 구성: 학생 활동지가 몇 칸으로 되어 있나요? 그 칸들이 화면의 형광펜 칸이 된다 → sections.json. 예: 3 · 자료 탐구 / 4 · 보고서.

② 학생 글 읽어들이기

이 단계가 뒤로 넘기는 것은 학생 글 원문 텍스트 하나뿐이다. 그래서 무엇으로 뽑았는지는 뒤 단계와 상관없고, 도구를 바꿔도 나머지가 그대로 돈다.

  • ① 디지털 텍스트(구글폼 응답·한글/워드 파일·타이핑) → 그대로 쓴다.
  • ② 스캔·사진·PDF + OCR 도구가 있으면 → 쓰시던 OCR 아무거나로 글자를 뽑는다. 파일별 .txt 로 저장하고 파일 순서를 보존한다.
  • ③ OCR이 없으면 → 담당 에이전트의 눈(비전)으로 보이는 그대로 받아쓴다. 거친 손글씨는 이쪽이 전용 OCR보다 나을 때가 많다. 단 복사기처럼 시켜야 한다 — 그냥 두면 오타를 고치고 반쯤 쓴 말을 완성해서 원자료를 조용히 지어낸다(규칙 2 위반):
    • 시키실 말: "보이는 글자를 그대로 옮겨라. 맞춤법·띄어쓰기 고치지 마라. 못 읽는 글자는 [판독불가] 로 두고, 빈칸은 빈칸으로, 비문은 비문대로."
    • 결과도 똑같이 .txt 로 남긴다 — 그래야 뒤의 검사·검수가 똑같이 걸린다.
    • 비전이 읽은 이름·학번은 독립된 세 번째 근거가 아니다(기대한 대로 읽어 버린다). ③에서 대조할 때 명렬표와 자리 순서를 더 무겁게 잡는다.
    • 한 반 규모면 이 방법이 편하지만, 대량·여러 장이면 토큰·속도 때문에 전용 OCR이 낫다.
  • 셋 다 이후는 완전히 같은 길로 합류한다.

글자가 깨진 곳은 이때 문맥으로 바로 읽어 준다(규칙 2 — 복원까지만, 보태지 않는다). 디지털 텍스트면 이 작업은 사실상 없다.

③ 누구 글인지 맞추기

references/map_pages.py 를 쓴다. 새로 짜지 마라 — 순진하게 짜면 조용히 틀린다(→ why.md).

# 🚩 `references/…` 는 스킬 폴더 안에만 있다. setup.sh 는 marking-tool/ 만 작업폴더의 tool/ 로
#    복사하므로, 작업폴더에서 상대 경로로 부르면 "그런 파일 없음"으로 죽는다. 전 경로로 쓴다.
SRP=<스킬경로>
cp $SRP/references/mapping-config.example.json <작업폴더>/mapping-config.json   # 활동지 형태를 채운다
python3 $SRP/references/map_pages.py --root <작업폴더>
  • 활동지 칸 구성·인쇄된 양식 문구·헤더 라벨은 과제마다 다르므로 설정 파일에 둔다. 특히 boilerplate(인쇄 문구)를 비우면 백지가 정상으로 보인다(→ why.md '백지 게이트').
  • 결과: out/mapping_review.md(선생님 확인용) + out/mapping_reconciled.csv(다음 단계 입력).
  • ⚠️·❓·🚩·❌ 는 선생님이 풀어 주신 뒤에 다음으로 간다.

④~⑧ 화면부터 검수 엑셀까지

# 한 번만 — 도구 복사·채울 파일 만들기·학생 목록 조립
bash <스킬경로>/references/setup.sh <작업폴더>

cd <작업폴더>
# 1) 채우기: tool/render_contract.md(예시 칸 필수) · draft-rules.json · sections.json
# 2) 활동 맥락을 인터뷰 2번(과제 지시문)에서 **미리** 넣어 둔다 — 화면 입력만 믿지 마라
cat > out/marks/_context.json <<'JSON'
{"context": "<한 줄로 줄인 활동 맥락>",
 "competency_examples": ["<이 과제에서 나올 법한 역량 한 줄>", "<다른 결의 역량 한 줄>"]}
JSON
# 3) 화면 (포트가 겹치면 PORT= 로 옮긴다 — 다른 과목이 7333·7334를 쓰고 있을 수 있다)
PORT=7335 node tool/server.mjs

🔑 역량 예시는 네가 지어낸 일반론이면 안 된다. 인터뷰 5번(이 과제로 진짜 보려던 것)과 8번(학생마다 갈리는 지점)의 답을 선생님 말 그대로 한 줄짜리 역량 문장으로 옮긴다. 과목 이름이나 교과 일반론("역사적 사고력")이 아니라, 이 과제에서만 나올 수 있는 문장이라야 칸을 보자마자 "아 이런 걸 적는 거구나"가 된다. 못 뽑겠으면 비워 둔다 — 엉뚱한 예시는 선생님 답을 그쪽으로 끌고 가므로 없느니만 못하다.

📏 첫 번째 예시는 한글 20자 안팎으로 짧게. 그게 칸 안에 그대로 뜨는데, 칸이 좁아 길면 문장 중간에서 잘린다(실측: 50자 → "…어떻게 다르" 에서 끊김). 둘째부터는 길어도 된다 — 고르는 목록에만 들어가고 잘리지 않는다.

그다음 선생님이 형광펜으로 칠하고 학생마다 [마킹 끝] 을 켠다. 다 하신 뒤 한 번 말씀하시면 그때 쓴다:

python3 tool/render_now.py          # 상태표 + 새로/다시 쓸 사람만 배치 준비
# → 찍히는 Workflow 한 줄을 에이전트가 실행
python3 tool/gate.py ; python3 tool/make_review_xlsx.py

🔑 여러 번 나눠 불러도 안전하다. render_now.py 가 학생을 이렇게 가른다 — ✅ 확정 · ⏸ 보류 · ✔ 이미 씀(건드리지 않음) / 📝 처음 씀 · 🔁 다시 씀(이번에 씀) / ⬜ 마킹 안 끝남 · ⛔ 원문 없음(대상 아님). 이미 쓴 사람을 다시 쓰지 않는다. --opus 는 물린 것을 다시 쓸 때, --all 은 확정·보류를 뺀 전원을 강제로 다시 쓸 때.

<!-- 내부 — 규칙 10. 아래는 네가 같은 걸 다시 만들지 않도록 남긴 기록이다. 선생님께 옮기지 마라. "예전엔 자동이었다" 는 말은 지금 도구가 모자라 보이게 만들 뿐이다. -->

🚫 예전 방식(실시간 감시)은 걷어냈다. watch.sh 가 [초안 요청]을 지켜보다 자동으로 이어받는 구조였는데, ① 반려로 대기열이 다시 차도 안쪽 루프에 갇혀 못 물어오고 ② 화면 폴링이 '요청 켜진 학생이 있을 때만' 돌아서, 체크를 껐다 켜는 사이 떨어진 초안이 지워진 것처럼 보였다. 둘 다 실측 사고다(2026-08-03).

표시한 것만 쓴다. 형광펜이 하나라도 있으면 그 대목만으로 쓰고 학생 글 전체는 입력에 넣지 않는다. 0개일 때만 학생 글을 읽고 AI가 직접 고른다. 섞으면 "표시한 것만 쓰기"가 조용히 새므로, 판정은 오직 표시 개수 0인가 아닌가로만 한다.

도달 수준(상/중/하)도 정하시면 그 범위 안에서 쓰고, 안 정하시면 재료 두께로 추정한다.

🚨 작성 호출에 model 을 반드시 명시한다. 안 적으면 세션 모델을 상속해 과금이 배로 튄다(실측 2.7배 → why.md). batch_render_sonnet.js 는 sonnet 을 명시해 두었다. 🚨 렌더 입력(학생 글)을 에이전트가 읽지 마라. 읽는 순간 컨텍스트에 통째로 얹힌다 (→ why.md '워크플로 args'). build_run_batch.py 가 render_args.py --stdout 으로 받아 배치 스크립트에 직접 주입하므로 중간 파일이 아예 안 생긴다 — 열 파일이 없는 게 정상이다. 에이전트는 인원·바이트만 받는다. (render_args.py 를 손으로 따로 돌리면 그때는 out/render_args.json 이 만들어진다. 그 파일도 열지 마라.) 🔒 out/_run_batch.js 에는 학생 글이 박혀 있다. 커밋 금지.

검사와 검수 엑셀

python3 tool/gate.py                 # 자동 검사 (통과=0 / 걸림=1)
python3 tool/make_review_xlsx.py     # → out/검수.xlsx

gate.py 가 보는 것(전부 답이 딱 떨어지는 것만): 길이 · 못 쓰는 말 · 점수/등급 · 가운뎃점과 특수기호 · [판독불가] 잔존 · 줄바꿈/그림문자/안 보이는 문자 · 띄어쓰기 · 문장 끝맺음 · 학번 형식 · 같은 반·번호 겹침. 경고(안 고쳐도 됨): 짧음 · 퍼센트 · 조심할 말.

out/검수.xlsx 가 이 파이프라인의 결과물이자 유일한 검수 자리다. 학번 | 이름 | 판정 | 왜 다시 쓰나요 | 세특 | 선생님이 표시한 대목 | 선생님이 정하신 것 | 확인할 것

앞 네 칸은 틀고정 안에 있어 오른쪽을 읽는 동안에도 계속 보인다. 판정 칸은 고르는 칸 (통과 / 재작성 / 보류)이고, 비워 두면 "아직 안 정하심"이라 아무것도 건드리지 않는다. 보류 는 읽었지만 아직 못 정한 것 — 빈칸(안 읽음)과 구분하려고 둔다. 화면에서 보류를 누르는 자리를 없앴으므로 이 칸이 보류를 정할 수 있는 유일한 곳이다. 적으신 판정은 이렇게 되먹인다:

python3 tool/ingest_review_xlsx.py            # 미리보기
python3 tool/ingest_review_xlsx.py --write    # 반영 → 그다음 render_now.py --opus

같은 본문·같은 사유는 두 번 쌓이지 않는다(엑셀을 두 번 되먹여도 안전하다).

화면에는 검수 자리를 두지 않는다. 그 화면은 원문을 싣지 않는데 검수의 정의는 "나온 문장이 그 재료에서 왔는가"라, 재료 없이 판정하게 된다. 사람 관문이 둘로 갈리면 정보가 적은 쪽이 앞에 서게 된다(2026-08-03 결정).

세특 바로 옆에 선생님이 칠하신 대목과 그때 쓰신 '왜' 가 붙는다. 기계가 문자열을 맞춰보는 대신 선생님이 눈으로 보시게 한 것이다 — 기계는 뜻이 거꾸로 쓰인 것을 못 잡지만 사람은 바로 잡는다(→ why.md). '확인할 것' 칸에는 검사 결과, AI가 그렇게 쓴 이유, 못 쓴 것과 그 까닭, 책 인용이 있으면 사실 확인 요청이 들어간다.

⑨ 나이스에 넣기

나이스에서 내려받은 과목세특 엑셀이 있으면 세특 칸에 결정론적으로 써넣는다. 그 파일은 그대로 다시 올릴 수 있는 공식 양식이라 붙여넣기가 필요 없다.

python3 tool/gate.py --source <확정.csv>                       # ① 내용 검사
python3 <스킬경로>/references/neis_writeback.py \
  --source <확정.csv> --targets "<…_과목세특_*.xlsx>" [--write] # ② 칸 대조 → 기입
  • (반, 번호) + 성명이 둘 다 맞을 때만 쓴다. 이름이 다르면 절대 안 쓰고 표시만 한다.
  • 세특 칸에 다른 내용이 이미 있으면 전체 중단한다. 선생님이 손보신 최신본을 덮어쓰면 안 된다. 검토 후 --on-conflict skip|overwrite 로 명시한다.
  • 쓰기 전 전량 백업하고, 쓴 뒤 모든 칸을 다시 읽어 대조한다.
  • 소스 CSV = 학번,이름,세특. 학번 5자리를 10401 → 4반 1번 으로 읽는다.
  • 🚩 엉뚱한 파일을 줘도 중단이 아니라 "0건 기입"으로 조용히 끝난다. --write 전에 "이중키 매칭 예정" 숫자가 인원과 같은지 눈으로 확인할 것.
  • 🚩 이 양식의 세특 칸은 과목 1개분이다. 1·2로 나뉜 공통과목(예: 한국사)은 두 과목 합산 500자 캡이므로 양쪽 양식을 합쳐 관리한다.
  • 여기까지도 최종본이 아니다 — 나이스에 올리기 전 선생님이 한 번 더 보신다(규칙 6).

나이스 글자 수

한글·한자·전각 = 3바이트, 영문·숫자·공백·반각부호 = 1바이트, 엔터 = 1바이트 (기재요령 2026 고등판 p.214 각주). 선택과목 = 과목당 500자(1,500바이트). 1·2로 나뉜 공통과목(예: 한국사)은 두 과목을 합쳐 500자다 — 과목마다 500자가 아니다. 추정하지 말고 gate.py 가 계산한 값을 쓴다. 사람에게 보일 때는 '한글 약 N자'를 같이 적는다.

세특에 못 쓰는 것

전체·출처는 references/giwan-2026-grounding.md §5·§6.

어떤 항목에도 불가(p.24~25): 공인어학시험(TOEIC·TOEFL·TEPS·HSK·JLPT·한자검정 등) / 교내외 대회 참여·성적·수상(대회명 우회 불가) / 교외상·표창장 / 인증시험 / 모의고사·전국연합 학력평가 성적 및 관련 교내 수상 / 논문 투고·발표 / 도서출간 / 특허 등 지식재산권 / 해외활동 실적 / 부모의 사회·경제적 지위 암시 / 장학생·장학금 / 특정 대학·기관·상호·강사명 / 자격증 명칭

세특에 특히(p.110): K-MOOC·MOOC·KOCW / 방과후학교 활동 / 연구보고서(소논문)

🚨 학생 본인이 인용한 자료의 출처는 금지 대상이 아니다. "○○학회 논문을 읽고"는 정상이고 평가 관점의 핵심일 때가 많다. 금지되는 건 학생 본인의 투고·발표·출간 실적이다. draft-rules.json 에 맨낱말(논문·학회·출간)을 넣으면 헛걸림이 쏟아진다.

🚨 다른 학생 실명 금지. 학생이 활동지에 반 친구 실명을 썼더라도 세특 본문엔 역할명 으로 바꾼다(예: "○○" → "백제 병사"). 대상 학생 본인 이름만 예외.

🚨 학생이 지어낸 가상 인물명(갑돌·돌쇠 등)도 빼고 역할어로. 이름만 지우고 문장 구조·의미는 유지한다("유민 갑돌과 민중 돌쇠" → "유민과 민중", 역할어가 없으면 "두 유민의 대화"처럼 최소 삽입). 실존 역사 인물·동음 일반명사는 그대로 두고, 실존/조어가 애매하면 지우지 말고 표시해 교사 확인으로 넘긴다. 탐지는 LLM이 하고(조어는 정규식 열거 불가), 결정론 검사는 잔존·과편집 검증에만 쓴다. (2026-07-14 한국사 배치 사후 패스에서 적발)


더 깊이 볼 때

기본 흐름은 위가 전부다. 아래는 큰 배치에서 필요할 때만 꺼낸다(기본은 쓰지 않는다).

도구 무엇
references/deep/ledger_verify.py 표시한 대목이 학생 글에 실재하는지 기계 대조(3계층)
references/deep/verify_anchors.py 세특에 나온 고유명·수치가 학생 글에 정박했는지
references/deep/sentence_metrics.py 문장이 너무 길고 늘어지는지 표면 신호
references/deep/make_review_table.py 문장 옆에 근거를 붙인 별도 검토 표(검수 엑셀로 대체됨)
references/verify.py 예전 종합 검사기 — gate.py 가 대체했다. 개별성(유사도) 검사가 필요할 때만

도구를 고칠 때만 여는 명세(평소엔 열지 않는다 — 위 흐름만으로 굴러간다):

문서 무엇
references/marking-mode.md 표시 기록의 데이터 형식, level 별 도달 진술 허가 범위, 요청→초안→판정 루프
references/neis-byte-rule.md 나이스 글자 수 규칙의 출처와 계산 근거
references/why.md 설계 근거와 사고 기록
PROVENANCE.md 전체 지도(근거 원천 → 단계 → 파일)

🚨 위 문서들을 통째로 읽지 마라. 여는 순간 이 스킬을 다이어트한 의미가 없어진다. 열기 전에 "이 파일에서 내가 쓸 게 몇 줄인가"를 먼저 물어라. 답이 "몇 줄"이면 grep -n -A12. 같은 원칙이 다른 스킬·라이브러리 문서에도 적용된다(가격 한 줄 때문에 30K 스킬을 통째로 읽지 마라).

기억할 것

  • 한 번에 한 과목을 처리한다. 여러 과목은 인터뷰 결과를 갈아끼워 반복한다.
  • 기재요령은 학년도별로 개정된다. 인터뷰 1번 답에 기준 학년도를 적어 둔다(예: 2026학년도).
  • tool/ 을 고쳤으면 회귀 검사를 돌린다: node tool/test_mark_ui.mjs && python3 tool/test_pending.py. 화면 문구를 바꿀 때 깨지는 지점이 있다 → why.md '화면 문구를 바꿀 때'.