idea_world_labDEV JOURNAL
2026년 6월 28일 일요일

대안 B: BM25 only

흐름

raw chunkText
  -> tokenizer
  -> BM25
  -> top JSONL 반환

BM25가 보는 것

BM25는 대략 다음 요소로 점수를 만든다.

TF: 이 document 안에서 단어가 몇 번 나왔는가
IDF: 전체 corpus에서 이 단어가 얼마나 드문가
Length normalization: 긴 document가 무조건 유리해지지 않게 보정

그래서 기준 chunk의 다음 단어들이 중요해진다.

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero

기준 chunk에서 성공하는 이유

원문 메모의 시뮬레이션에서는 다음 문서가 상위에 나온다.

outputs/godot_docs_full/pages/getting_started__first_2d_game__03.coding_the_player__161d377b.md

직접 맞는 토큰은 다음이다.

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
play
stop
input
speed
length
velocity
vector2
zero

특히 다음 토큰은 corpus에서 드문 편이라 점수에 강하게 기여한다.

animatedsprite2d
move_left
move_right
is_action_pressed
screen_size
clamp

실패 후보

BM25 only에서는 3D movement 문서도 올라올 수 있다.

이유는 다음 공통 토큰 때문이다.

Input.is_action_pressed
move_right
move_left
normalized
speed

하지만 기준 chunk는 2D이고 다음 단서가 중요하다.

AnimatedSprite2D
Vector2
position.clamp
screen_size

BM25는 단어 점수 기반이라 2D/3D 문맥을 완벽히 이해하지 못한다.

PoC 시뮬레이션

기준 chunk를 BM25 query로 넣는다.

입력:

func _process(delta):
  Input.is_action_pressed(...)
  velocity.normalized() * speed
  $AnimatedSprite2D.play()
  position.clamp(Vector2.ZERO, screen_size)

BM25가 높은 점수를 줄 토큰:

animatedsprite2d
is_action_pressed
move_left
move_right
normalized
clamp
screen_size
vector2
zero

예상 후보:

순위 후보 왜 뜨는가 판단
1 first_2d_game / coding_the_player Input.is_action_pressed, move_left, move_right, velocity.normalized, AnimatedSprite2D.play/stop이 직접 맞음 accept
2 first_2d_game / clamp/screen section position.clamp, Vector2.ZERO, screen_size, AnimatedSprite2D가 맞음 accept
3 first_3d_game / player_movement_code Input.is_action_pressed, move_left, move_right, normalized, speed가 겹침 false positive

1위 후보가 올라오는 이유:

animatedsprite2d:
  문서 전체에서 드문 토큰이므로 IDF가 큼

move_left / move_right:
  튜토리얼 입력 코드에서 직접 등장

is_action_pressed:
  입력 처리 코드와 직접 일치

velocity.normalized:
  이동 벡터 정규화 흐름과 직접 일치

3위 후보가 같이 올라오는 이유:

3D movement 문서도 입력 처리와 normalized 이동 흐름을 설명한다.
그래서 BM25 입장에서는 query token overlap이 꽤 크다.
하지만 기준 chunk의 핵심인 AnimatedSprite2D, Vector2, screen_size, position.clamp와는 맞지 않는다.

PoC에서 확인할 로그

BM25 PoC에서는 각 후보별로 토큰 기여도를 보여주는 것이 중요하다.

예시:

candidate: first_2d_game / coding_the_player
matched_terms:
  animatedsprite2d   high
  is_action_pressed  high
  move_left          high
  move_right         high
  normalized         medium
  play               medium
  stop               medium

candidate: first_3d_game / player_movement_code
matched_terms:
  is_action_pressed  high
  move_left          high
  move_right         high
  normalized         medium
missing_terms:
  animatedsprite2d
  screen_size
  position.clamp

눈에 보이는 결론:

BM25는 관련 문서를 잘 끌어올린다.
하지만 비슷한 입력/이동 코드도 같이 올린다.
그래서 BM25 다음 단계에서 reranker 또는 validator가 필요하다.

장점

  • 원리가 투명하다.
  • 디버깅이 쉽다.
  • 모델 비용이 없다.
  • raw chunk 그대로 사용할 수 있다.
  • 코드/API 정확 문자열 검색에 강하다.

단점

  • 동의어와 설명문에 약하다.
  • API 이름이 직접 나오지 않으면 놓칠 수 있다.
  • 3D movement처럼 비슷한 코드가 false positive로 섞일 수 있다.

판정

반드시 검토해야 하는 1차 후보 검색
하지만 단독 사용은 부족