learn

marker에 로컬 LLM 붙이기

marker에 로컬 LLM 붙이기

model_test 프로젝트 일환. marker(marker-pdf)의 --use_llm 모드를 Ollama / llama.cpp로 구동하는 방법 정리.

파일 포맷 지원

pip install marker-pdf — PDF, image 기본 지원
pip install marker-pdf[full] — PPTX, DOCX, XLSX, HTML, EPUB 추가 지원

내부 구조상 같은 PdfConverter 파이프라인을 쓰되, Provider 레이어가 파일 타입에 따라 다르게 처리. 사용자는 파일 경로만 바꿔 넣으면 된다.

LLM의 역할

--use_llm 플래그를 켜면 LLM은 후처리 단계에 투입된다:

  • 페이지 경계를 넘는 테이블 병합
  • inline math LaTeX 교정
  • form 값 추출
  • 전반적인 마크다운 품질 향상

marker가 먼저 OCR + 레이아웃 파싱을 수행하고, 그 결과를 LLM이 교정하는 파이프라인. 따라서 텍스트 전용 LLM으로도 동작 가능하다 (단, 이미지/수식 heavy 문서는 multimodal 모델이 유리).

LLM 서비스 추상화

marker는 marker/services/ 아래에 서비스를 추상화해뒀다:

| 서비스 | 클래스 | |--------|--------| | Gemini | marker.services.gemini.GoogleGeminiService (기본값) | | Ollama | marker.services.ollama.OllamaService | | llama.cpp (OpenAI compat) | marker.services.openai.OpenAIService | | Anthropic | marker.services.claude.ClaudeService |

Ollama 연결

from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.config.parser import ConfigParser

config_parser = ConfigParser({
    "use_llm": True,
    "llm_service": "marker.services.ollama.OllamaService",
    "ollama_model": "llama3.2-vision",
    "ollama_base_url": "http://localhost:11434",
})

converter = PdfConverter(
    config=config_parser.generate_config_dict(),
    artifact_dict=create_model_dict(),
    processor_list=config_parser.get_processors(),
    renderer=config_parser.get_renderer(),
    llm_service=config_parser.get_llm_service(),
)
rendered = converter("file.pdf")

llama.cpp 연결

llama.cpp를 --server 모드로 실행하면 OpenAI 호환 엔드포인트를 노출한다. OpenAIService에 base_url만 교체하면 된다.

# llama.cpp 서버 실행
./llama-server -m model.gguf --port 8080 --host 0.0.0.0
config_parser = ConfigParser({
    "use_llm": True,
    "llm_service": "marker.services.openai.OpenAIService",
    "openai_base_url": "http://localhost:8080/v1",
    "openai_api_key": "dummy",   # llama.cpp는 key 검사 안 함
    "openai_model": "your-model-name",
})

FastAPI 서버로 감싸기

marker 자체 API 서버 (marker_server.py)가 있지만, LLM 서비스를 직접 제어하려면 FastAPI로 감싸는 게 낫다:

from fastapi import FastAPI
from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.config.parser import ConfigParser
from marker.output import text_from_rendered

app = FastAPI()
models = create_model_dict()  # 서버 시작 시 1회 로드

@app.post("/convert")
async def convert(filepath: str, use_llm: bool = False):
    config = {"use_llm": use_llm}
    if use_llm:
        config.update({
            "llm_service": "marker.services.ollama.OllamaService",
            "ollama_model": "llama3.2-vision",
        })
    cp = ConfigParser(config)
    converter = PdfConverter(
        config=cp.generate_config_dict(),
        artifact_dict=models,
        processor_list=cp.get_processors(),
        renderer=cp.get_renderer(),
        llm_service=cp.get_llm_service(),
    )
    rendered = converter(filepath)
    text, _, _ = text_from_rendered(rendered)
    return {"markdown": text}

create_model_dict()는 surya OCR 모델 등을 로드하므로 한 번만 호출해야 한다.

model_test 관점에서

  • 정량 비교 기준: README의 FinTabNet table benchmark (heuristic + LLM judge 점수)
  • 비교 후보: llama3.2-vision (ollama) vs llama.cpp + llava
  • LLM 없는 baseline marker vs --use_llm 모드 비교도 의미 있음
  • table 변환에서 LLM 효과가 가장 두드러짐: 0.816 → 0.907 (marker w/use_llm)

관련 개념

  • surya — marker 내부 OCR + 레이아웃 감지 모델
  • llama-cpp — GGUF 포맷 로컬 inference 엔진
  • ollama — 로컬 LLM 서빙 도구