idea_world_labDEV JOURNAL
2026년 6월 25일 목요일

Markdown -> JSONL LLM 분류 방식

작성일: 2026년 6월 25일

목적

make_md 웹 변환기에서 공식문서 Markdown을 docs_chunks, api_mapping, label_prototypes 중 어떤 JSONL 대상으로 보낼지 LLM에게 판별시키는 방식을 기록한다.

이 문서는 웹앱 코드나 API 키를 저장하지 않는다. 현재 동작 중인 로컬 구현을 기준으로, LLM에 어떤 입력을 주고 어떤 출력만 허용하는지 정리한다.

현재 실행 상태

로컬 웹 UI는 다음 주소로 실행한다.

http://localhost:8501/

현재 실행 프로세스는 /Users/joyeongjin/make_md에서 streamlit run app.py --server.port 8501로 떠 있다.

분류 호출 위치

현재 구현에서 테이블 분류는 classify_tables()가 담당한다.

/Users/joyeongjin/make_md/app.py

분류 호출은 변환보다 먼저 실행된다.

Markdown file
-> classify_tables()
-> 선택된 table 목록
-> table별 call_qwen_api()
-> JSONL record 생성
-> validate_record()
-> 저장

LLM 입력

분류 단계에서 LLM에게 전달하는 입력은 다음 세 가지다.

입력 설명
system message JSON만 반환하라고 강제한다. Markdown, 설명, thinking text를 금지한다.
filename 업로드된 Markdown 파일명
full markdown Markdown 본문 전체

분류 단계는 반드시 Markdown 전체 본문을 보내야 한다. 앞부분 excerpt만 보내면 안 된다. 공식문서 앞부분만 보고 분류하면 api_mappinglabel_prototypes처럼 뒤쪽 예시나 상세 설명에서 경계가 드러나는 문서를 잘못 분류할 수 있기 때문이다.

LLM 출력 형식

LLM은 반드시 JSON 배열 하나만 반환해야 한다.

허용 값:

["docs_chunks", "api_mapping", "label_prototypes"]

예시:

["docs_chunks"]
["api_mapping", "label_prototypes"]
[]

[]는 유용한 공식문서 내용이 아니라고 판단한 경우에만 사용한다.

현재 프롬프트 요약

분류 프롬프트의 핵심은 다음과 같아야 한다.

Classify this Markdown into zero or more target tables.
Return exactly one JSON array.
Valid values are:
["docs_chunks", "api_mapping", "label_prototypes"]

Table boundaries:
- docs_chunks: official documentation explanations, tutorials, class reference chunks.
- api_mapping: Godot 3 -> Godot 4 function/class/symbol name changes.
- label_prototypes: usage-pattern migrations where arguments, call shape, or usage style changed, not just the name.

Rules:
- Use [] only if the file is not useful documentation content.
- Use one or more valid table names when conversion is needed.
- Do not include explanations, markdown fences, or any text outside JSON.

FILE: <filename>
FULL MARKDOWN:
<entire markdown text>

판별 기준

LLM에는 세 테이블의 경계를 같이 전달해야 한다.

테이블 의도한 경계
docs_chunks 공식문서 설명 본문, 튜토리얼, class reference 청크
api_mapping Godot 3 -> Godot 4에서 함수명, 클래스명, 심볼명이 어떻게 바뀌었는지
label_prototypes 함수 사용 방식, 인자 구성, 호출 패턴이 통째로 바뀐 경우 어떻게 써야 하는지

api_mappinglabel_prototypes의 경계는 특히 중요하다. 이름만 바뀐 경우는 api_mapping이고, 인자 구성이나 호출 방식 자체가 바뀐 경우는 label_prototypes다.

JSON 보정 흐름

LLM이 JSON 배열을 제대로 반환하지 않으면 보정 요청을 한 번 더 보낸다.

보정 요청의 핵심은 다음과 같다.

Your previous classification response was not valid JSON.
Rewrite it as exactly one JSON array using only these strings:
["docs_chunks", "api_mapping", "label_prototypes"].
Use [] only if this file is not useful documentation content.

보정 요청에도 실패하면 해당 파일은 classification error로 기록한다.

하드코딩 fallback 여부

현재 분류 단계는 파일명, 경로, 정규표현식 기반의 하드코딩 fallback으로 테이블을 강제 선택하지 않는다.

API key가 없으면 분류하지 않는다. Qwen 응답이 유효 JSON 배열이 아니고 보정에도 실패하면, 임의로 테이블을 골라 저장하지 않고 에러로 남긴다.

디버그 기록

분류 결과는 세션 상태에 다음 형태로 기록된다.

{
  "file": "example.md",
  "parsed_result": ["docs_chunks"],
  "matched_tables": ["docs_chunks"],
  "raw_response": "<qwen raw response>"
}

이 기록은 Qwen이 JSON이 아닌 응답을 했거나, []를 반환했거나, 유효하지 않은 테이블명을 반환했을 때 원인을 확인하는 데 사용한다.