무색
기술블로그
에세이
연구
소개

무색

소프트웨어로 비즈니스의 가능성을 만듭니다. 웹·앱 개발, 음성 AI, 자동화 콘텐츠 제작까지 — 기술이 필요한 곳에 무색이 있습니다.

연락처

contact@museck.com

사업자 정보

상호: 무색

대표: 배성재

사업자등록번호: 577-58-00836

인천광역시 연수구 인천타워대로 323, 에이동 8층 801-802호 AB-132 (송도동, 송도 센트로드)

© 2026 무색. All rights reserved.
개인정보처리방침·이용약관·연락처
INCHEON, KR
논문 번역에서 용어가 뒤죽박죽이라면: TranslateGemma + 마스터 용어 사전 — bauhaus 스타일 키 비주얼
논문 번역기
2026. 1. 13.

논문 번역에서 용어가 뒤죽박죽이라면: TranslateGemma + 마스터 용어 사전

TranslateGemmaBabelDOCglossary번역용어사전

같은 논문인데 번역이 제각각이다

논문을 번역하다 보면 한 가지 문제가 반복해서 발생한다. "attention mechanism"이라는 용어 하나가 어떤 페이지에선 "주의 메커니즘", 다른 페이지에선 "어텐션 메카니즘", 또 다른 곳에선 "주목 기법"으로 번역된다. 한 논문 안에서 이렇게 흔들리면 읽는 사람 입장에서 같은 개념인지 다른 개념인지 헷갈린다.

한두 편이면 수동으로 고쳐도 되겠지만 여러 논문을 번역하기 시작하면 프로젝트 전체에서 용어 통일이 필요해진다. 이전 글에서 BabelDOC에 커스텀 번역 모델을 붙이는 작업까지 마쳤는데, 번역 품질 자체보다 이 일관성 문제가 더 골치였다.

그래서 Google의 TranslateGemma 27B 모델이 glossary 주입을 네이티브로 지원한다는 걸 발견했을 때 바로 이거다 싶었다. 모델을 파인튜닝하거나 후처리로 치환하는 게 아니라, 번역 시점에 "이 용어는 이렇게 번역해"라고 알려주는 방식이다.

전체 구조: 마스터 사전에서 번역까지

시스템은 크게 세 단계로 나뉜다. 용어 추출, 마스터 사전 관리, 번역 실행이다.

핵심 아이디어는 glossary.json이라는 마스터 용어 사전 파일 하나를 중심에 두는 것이다. 모든 논문에서 추출된 용어가 여기에 누적되고, 번역할 때는 해당 논문에 필요한 용어만 뽑아서 CSV로 만든 뒤 BabelDOC의 Glossary API에 주입한다.

TranslateGemmaTranslator 구현

BabelDOC에 TranslateGemma 27B를 커스텀 Translator로 붙였다. 기존에 만들었던 Rosetta나 GLM Translator와 같은 패턴인데 결정적 차이가 있다. TranslateGemma는 glossary 주입을 네이티브로 지원하는 첫 번째 모델이라는 점이다.

번역 스크립트가 시작되면 glossary.json을 자동으로 읽어서 BabelDOC의 Glossary 객체로 변환한다.

GLOSSARY_JSON = Path(__file__).parent / "glossary.json"

if GLOSSARY_JSON.exists():
    master = json.loads(GLOSSARY_JSON.read_text(encoding="utf-8"))
    if master:
        entries = [GlossaryEntry(src, tgt) for src, tgt in master.items()]
        glossaries.append(Glossary(name="master", entries=entries))
        print(f"Loaded master glossary ({len(entries)} terms)")

TranslateGemma는 번역 전용 모델이라 auto_extract_glossary=False로 설정해야 한다. 용어 자동 추출 기능(do_llm_translate)이 구현되어 있지 않기 때문이다. 이전에 Rosetta Translator에서 같은 실수를 했던 경험이 있어서 이번엔 바로 잡았다.

config = TranslationConfig(
    translator=translator,
    glossaries=glossaries if glossaries else None,
    auto_extract_glossary=False,  # 번역 전용 모델
    use_alternating_pages_dual=True,  # 원문-번역 교대 페이지
)

마스터 용어 사전이 동작하는 방식

용어 관리의 핵심 로직은 agent_total_glossary.py에 있다. 서브에이전트가 PDF에서 추출한 용어 목록을 받아서 마스터 사전과 병합하는 스크립트다.

규칙은 단순하다. 마스터에 이미 있는 용어는 마스터의 번역을 따르고, 새로운 용어만 추가한다.

# 신규 용어만 마스터에 추가, 기존 항목은 마스터의 번역 유지
for src, tgt in new_terms.items():
    if src in master:
        existing[src] = master[src]  # 마스터의 기존 번역 사용
    else:
        master[src] = tgt
        added[src] = tgt

# 논문별 CSV = 기존 + 신규 전부 포함
paper_terms = {**existing, **added}

이렇게 하면 논문 A에서 "attention mechanism"을 "어텐션 메커니즘"으로 번역했다면, 나중에 논문 B를 번역할 때도 같은 용어가 같은 번역으로 나온다. 서브에이전트가 논문 B에서 다른 번역을 제안하더라도 마스터에 이미 등록된 번역이 우선한다.

논문별 CSV는 마스터의 부분집합이다. 전체 마스터를 통째로 넣지 않고 해당 논문에 등장하는 용어만 골라서 CSV로 만든다. 왜 이렇게 하는지는 삽질 이야기에서 다룬다.

용어 추출은 에이전트 2단계로

용어를 수동으로 정리하면 한 논문당 30분은 걸린다. 그래서 AI 에이전트에게 맡겼는데, 한 단계로는 부족했다.

먼저 agent_extract_pdf_text.py로 PDF에서 텍스트를 추출한다. 그 다음 서브에이전트(Haiku)가 텍스트를 훑으며 용어 후보를 뽑는다. 빠르고 저렴하지만 정확도가 떨어진다. source와 target이 동일한 약어("GPU" -> "GPU" 같은)를 용어로 잡아내는 식이다. 있으나 마나한 항목이 꽤 섞여 들어온다.

그래서 메인 에이전트(Opus)가 검수하는 2단계 구조로 설계했다. Haiku가 추출한 용어 목록을 Opus가 검토해서 쓸모없는 항목을 걸러내고 번역 품질을 확인한다. 비용 대비 품질의 균형을 잡은 셈이다.

삽질: context window를 잡아먹는 glossary

처음에는 마스터 glossary.json 전체를 번역할 때마다 통째로 주입했다. 용어가 30개일 때는 문제가 없었다. 그런데 50개, 100개로 늘어나면서 번역 품질이 눈에 띄게 떨어지기 시작했다.

원인은 간단했다. TranslateGemma가 glossary를 프롬프트에 직접 삽입하는 방식이라 용어가 많을수록 context window에서 본문이 차지할 수 있는 공간이 줄어든다. 번역해야 할 텍스트가 잘리면 당연히 결과도 나빠진다.

해결 방법이 논문별 CSV다. 마스터에 용어를 100개 쌓아두더라도 논문 A를 번역할 때는 논문 A에 등장하는 용어 20개만 골라서 넣는다. 이렇게 하니 context window 문제가 사라졌다.

또 한 가지. glossary.json의 key 정렬을 처음에 알파벳순으로 했다가 역시간순(최근 추가한 것이 위)으로 바꿨다. 실제로 사전을 열어서 용어를 확인할 일이 잦은데, 알파벳순이면 방금 작업한 논문의 용어를 찾으려고 한참 스크롤해야 했다. 작은 변경이지만 작업 흐름에서 체감 차이가 크다.

컨텍스트 주입이라는 패턴

이번 작업에서 쓴 방법을 일반화하면 "컨텍스트 주입" 패턴이라고 부를 수 있겠다. LLM의 출력 일관성이 필요할 때 모델 자체를 바꾸는 게 아니라 매 요청마다 필요한 맥락을 주입하는 방식이다.

파인튜닝은 비싸고 모델이 바뀔 때마다 다시 해야 한다. 후처리 치환은 문맥을 무시해서 어색한 결과가 나올 수 있다. glossary injection은 그 중간 지점이다. 모델이 번역하는 시점에 "이 용어는 이렇게 써"라고 컨텍스트를 넣어주니 자연스러운 문장 안에서 용어만 고정된다.

번역뿐 아니라 코드 생성이나 문서 작성처럼 LLM 출력의 일관성이 중요한 영역이라면 어디든 적용할 수 있는 접근법이다. 용어 사전 대신 코딩 컨벤션이나 문서 스타일 가이드를 주입하면 된다.

정리

결국 만든 건 세 가지다.

  • TranslateGemmaTranslator: BabelDOC에 TranslateGemma 27B를 붙인 커스텀 Translator. glossary 주입을 네이티브로 지원한다.
  • glossary.json: 프로젝트 전체 용어를 누적하는 마스터 사전. 논문별 CSV는 여기서 파생된다.
  • agent_total_glossary.py: 서브에이전트의 용어 추출 결과를 마스터에 병합하고, 논문별 CSV를 생성하는 스크립트.

모델을 바꾸거나 파인튜닝할 필요 없이, JSON 파일 하나와 스크립트 하나로 번역 일관성 문제를 해결했다. 다음 글에서는 이 glossary 워크플로우를 에이전트가 자동으로 돌리도록 리팩토링한 과정을 다룬다.

자주 묻는 질문

TranslateGemma에서 마스터 용어 사전이 필요한 이유는?
논문마다 같은 용어를 다르게 번역하면 일관성이 깨집니다. 마스터 사전(glossary.json)에 한 번 확정된 번역을 누적하면, 이후 논문에서도 동일한 번역이 자동 적용됩니다.
용어 추출에 Haiku와 Opus를 나눠 쓰는 이유는?
Haiku는 비용이 낮아 대량 텍스트에서 후보 용어를 빠르게 추출하고, Opus는 추출된 용어의 번역 품질을 검수합니다. 비용 대비 품질을 최적화하는 구조입니다.
BabelDOC의 Glossary API는 어떻게 작동하나요?
논문별 CSV 파일을 BabelDOC에 전달하면, TranslateGemma가 해당 용어 사전을 참조하면서 번역합니다. PDF 레이아웃을 유지한 채 mono/dual 버전을 출력합니다.
논문 번역기(3/8)
Prev

100페이지 논문 번역이 40분 걸릴 때: LLM 동시성과 재시도 전략

Next

서브에이전트가 같은 파일을 동시에 수정하면 생기는 일