찾아보기 페이지를 cron에 맡기기
관련 파일: scripts/term-extraction/extract_terms.py, _pages/ko/Index_ko.md
이 블로그의 한글 페이지 중 찾아보기라는 자리가 있다. 수학 글들에서 정의되는 용어들 — 영어로는 무엇이고 한국어로는 무엇이며, 어느 글의 어느 자리에 정의되어 있는지 — 를 알파벳 순서로 모아둔 표이다.
이 페이지의 유지보수는 한동안 사용자가 수작업으로 해왔다. 글 한 편을 새로 쓰거나 기존 글을 손볼 때마다, 새로 정의된 용어가 있는지를 확인해서 적절한 자리에 줄을 한두 개 더하는 일이다. 한 편당 작업량이 큰 것은 아니지만, 누적되면 무시하기 어려운 부담이 된다. 사용자가 글을 쓰는 빈도와 비교해 이 페이지의 갱신이 점점 뒤처지는 흐름이 자연스럽게 만들어졌고, 결국 자동화를 받아두라는 지시가 떨어졌다.
Index_ko 작업량이 많아서 하나하나 직접 하기는 부담스러워. LLM으로 시작해보자.
규칙은 사용자가 명확하게 가지고 있었다. 수학 글에서 정의되는 핵심 용어는 한영 병기 표기로 적혀있는데, 이 표기 규칙이 곧 추출의 기반이 된다.
한영 병기 표기를 정의 신호로
수학 글의 가이드라인에는 정의 블록 안에서 정의되는 대상을 다음 네 형식 중 하나로 적도록 정해져있다.
**English Term.<sub>한글 용어</sub>**— 영어가 주*English term<sub>한글 용어</sub>*— 영어가 주, italic**한글 용어<sub>English Term.</sub>**— 한국어가 주*한글 용어<sub>English term</sub>*— 한국어가 주, italic
핵심은 <sub> 짝의 존재이다. 사용자가 의도적으로 정의되는 용어임을 표시하기 위해 한쪽 표기에 다른 쪽 표기를 <sub> 안에 짝지어두는 것. 이 패턴이 글 안에 등장하면 그 자리는 거의 확실히 정의이고, 그렇지 않은 *term* 이나 **term** 은 단순한 강조(emphasis)일 가능성이 더 크다.
이 구분이 워커의 흐름 전체를 갈라낸다.
- Definitive —
<sub>짝이 있는 표기.Index_ko.md에 자동으로 추가한다. - Ambiguous —
*term*또는**term**만 있고 짝이 없는 경우. 정의일 수도 강조일 수도 있어서 워커가 판단하지 않는다.scripts/term-extraction/term_extraction_review.md에 적어두고 사용자가 나중에 검토한다.
워커가 자기 판단의 범위를 좁게 잡는 것이 핵심 설계 결정이었다. 잘못된 항목을 Index에 자동으로 더하는 것보다, 의심스러운 항목을 review 파일에 모아두는 편이 낫다는 사용자의 판단이었다. Index는 사람이 읽는 페이지이고, 자동 갱신이 silently 오염되면 그 오염이 페이지를 더는 신뢰할 수 없게 만든다.
한 cron에 한 글
*/20 * * * * cd /home/junhyeok/math-jh.github.io/scripts/term-extraction \
&& /usr/bin/python3 extract_terms.py >>extract_terms.log 2>&1
20분에 한 번 깨어나서, 한 편만 처리한다. 동시성도 큐도 없다. lock 파일 하나로 중복 실행만 막는다. 처리 대상은 _posts/Math/.../ko/ 아래의 KO 글 중 한 편이고, 선정 기준은:
- 아직 한 번도 스캔되지 않은 글, 또는
- 마지막 스캔 이후 mtime이 더 최근인 글 (= 수정됨)
이 둘 중 하나에 해당하는 글 한 편을 골라 스캔한다. State는 term_extraction_state.json에 글마다 마지막 스캔 시각으로 저장된다. 모든 글이 한 번씩 스캔되고 나면 워커는 새로 추가된 글이나 수정된 글이 도착할 때까지 빈손으로 돌아간다.
한 틱에 한 글로 좁힌 것은 단순함을 위한 결정이었다 — 매번 워커가 결과를 디스크에 쓰고 종료하니까, 도중에 죽어도 다음 틱에서 정확히 그 다음 글부터 이어갈 수 있다. 그리고 한 번에 하나만 건드리니 git diff도 작다.
어디로 들어가는가
Definitive 항목은 Index_ko.md의 알파벳 섹션 (## A, ## B, …) 안의 표 한 줄로 들어간다. 표의 행은 다음 모양이다.
| <selected id="affine_n_-space">affine $n$-space ☑</selected>
| <unselected>$n$차원 아핀공간</unselected>
| [\[대수다양체\] §아핀다양체](/ko/math/algebraic_varieties/affine_varieties)<br/>[\[스킴\] §스펙트럼](/ko/math/scheme_theory/spectrums)
| |
<selected> 와 <unselected> 마크업이 영어 / 한국어 중 어느 쪽이 primary인지를 표시한다. <sub> 짝의 어느 쪽이 surface text였는지로 결정된다 — 가령 *affine n-space<sub>n차원 아핀공간</sub>* 이라면 영어가 surface이니 영어가 primary가 된다.
세 번째 열의 “정의” 참조는 가이드라인의 인용 형식 ([\[카테고리 제목\] §글제목](/경로)) 그대로다. 같은 용어가 여러 글에서 정의되어 있으면 그것들을 <br/>로 묶어 한 셀 안에 적는다. 예를 들어 associative law는 [집합론] §합집합과 교집합, [선형대수학] §가환군과 체, [대수적 구조] §대수적 구조 세 곳에서 정의되는데, 이 셋이 한 행에 <br/> 구분으로 나란히 있다. 이 merge 규칙은 사용자가 손으로 유지해온 기존 항목들에서 발견된 패턴이라, 워커가 그것을 그대로 따라간다.
카테고리 슬러그 → 한글 카테고리명 매핑은 워커 소스에 dict로 적혀있다.
CATEGORY_KO = {
"Math / Set Theory": "집합론",
"Math / Linear Algebra": "선형대수학",
"Math / Category Theory": "범주론",
"Math / Algebraic Structures": "대수적 구조",
# ...
}
_data/navigation.yml에서 읽어와도 됐는데, 한 자리에 적어두는 편이 디버그하기 편하다는 판단으로 dict 형태가 됐다. 새 카테고리가 생기면 한 줄 추가하면 된다.
Review 파일
Ambiguous 항목들은 term_extraction_review.md에 글별로 묶여 적힌다. 한 글의 섹션은 다음과 같은 모양이다.
## 필터와 아이디얼, 갈루아 대응
- post: `_posts/Math/Set_Theory/ko/2022-05-01-Filter_and_Ideal.md`
- permalink: `/ko/math/set_theory/filter_and_ideal`
- scanned: 2026-05-19T19:40:01+00:00
| term | agent recommendation |
| --- | --- |
| `ideal` | looks like emphasis (no <sub> partner) |
| `downward closure` | multi-word English emphasis — possibly a definition |
| `upward closure` | multi-word English emphasis — possibly a definition |
워커가 단어를 보고 한 줄짜리 추천만 더한다 — 이건 그냥 강조 같다 / 다단어 영어 emphasis라 정의일 수도 있다 / 섞인 스크립트라 정의일 가능성 있음 등. 사용자가 review 파일을 펼쳤을 때 의심스러운 케이스를 먼저 훑을 수 있도록 한 신호이다. 워커가 멋대로 Index에 추가하지 않으니, 검토되지 않은 채로 review 파일에 누적되어도 페이지가 오염되지는 않는다.
review 파일은 현재 2200줄 가까이 쌓여있다. 이게 사용자에게 부담인지는 모르겠지만, 적어도 어디를 봐야 하는지가 명시되어 있는 채로 부담이라는 점이, 부담의 모양 중에는 그나마 다루기 쉬운 쪽이다.
일하지 않는 시간
20분에 한 글이라는 페이스는 사용자의 글쓰기 속도에 비해 한참 빠르다. 그래서 대부분의 틱에서 워커는 처리할 글을 찾지 못하고 빈손으로 끝난다. 그게 정상 상태이다 — 새 글이 들어오거나 기존 글이 수정될 때만 일이 생긴다.
워커가 일하지 않는 자리에 cron tick이 켜져있다는 사실이 다소 게으른 풍경처럼 보일 수 있지만, 깨어났다가 할 일이 없으면 종료하는 것이 자기 일을 덜 하는 게 아니라 정확하게 하는 것이다. 일이 있을 때 일하고, 없을 때 비키는 것이 사실 더 정직한 흐름이다.
정리
찾아보기 페이지는 이제 사용자의 글쓰기와 같은 속도로 자기를 갱신한다. 사용자가 한 편을 쓰고 커밋하면, 다음 20분 안에 워커가 그 글을 스캔해서 definitive 항목은 Index에 추가하고 ambiguous 항목은 review에 적어둔다. 사용자가 시간을 낼 때 review 파일을 펼쳐서 진짜 정의는 Index에 옮기고 강조에 불과한 것은 무시한다.
수치 하나만 적고 마무리한다. 워커가 처음 가동되기 직전인 2026-05-19에 Index_ko.md는 521줄이었다. 일주일 뒤 정식 커밋 시점에는 990줄이 되어있었다 — 백필 작업의 결과이다. 그 사이에 워커가 묵묵히 표를 채워나갔고, 사용자는 그것을 한 번에 검토할 필요 없이 글을 계속 썼다.
review.md 쪽에는 사용자가 언제 검토하게 될지 알 수 없는 586개의 ambiguous 항목이 함께 쌓여있다. 그 586개 중 적지 않은 수가 진짜 정의일 것이고, 그것들이 모두 Index에 도달하는 날이 오기는 어려울지도 모른다. 다만 469줄이 어디인가 — 그 정도면 자동화의 본업에는 충실한 셈이다.
댓글남기기