Skip to content
게으른 엔지니어의 기술 블로그
Go back

LLM이 노트 중복을 지우자, 남은 문장이 가리킬 곳을 잃었다

Read in English →

AI 검증이 놓치는 것들 · 9/9편

만들고 있는 Obsidian 플러그인은 기사 한 편을 넣으면 LLM이 그 안의 개념·인물·회사를 각각 별도 노트로 나눠 저장한다. 같은 기사에서 함께 나온 노트들을 이 글에서는 ‘형제 노트’라고 부른다. 4편에서 본 것처럼 형제 노트끼리 같은 문단을 그대로 복사하는 일이 잦아서, 노트를 만든 직후에 형제 노트를 한꺼번에 모아 겹치는 사실을 찾고, 한 노트에만 남긴 뒤 나머지 노트의 그 부분은 “자세한 내용은 [[그 노트]]에서 다룬다” 같은 링크 문장으로 바꾸는 중복 정리 단계를 넣었다. 이 글은 그 정리 단계가 문장을 지운 뒤 남은 문장이 망가지는 문제를 쫓은 기록이다.

사실 손실을 막았더니, 문장이 끊겼다 — LLM 중복 제거의 부작용

처음 문제는 사실 손실이었다. 정리 단계가 겹치는 사실 하나를 찾으면 그 사실이 든 문단 전체를 링크 문장으로 바꿔버려서, 같은 문단에 있던 다른 내용까지 사라졌다. 그래서 겹치는 문장만 바꾸고 나머지는 그대로 두게 프롬프트를 고쳤다. 같은 입력으로 다시 돌려 보니 사실이 사라진 회차가 19번 중 15번에서 20번 중 0번으로 줄었다.

그런데 다음 실제 테스트에서 다른 문제가 나왔다. 사실은 다 남아 있는데 문장이 이상했다.

문장을 덜 지우게 했더니, 지운 문장에 기대던 남은 문장이 기댈 곳을 잃은 것이다.

막을 때마다 다른 모양으로 깨졌다 — 지시어, 접속어

여기서부터 한 모양씩 막는 일이 이어졌다.

먼저 ‘이 ~’, ‘그 ~‘처럼 앞 내용을 가리키는 말이다. 프롬프트에 “문장을 지워서 다른 문장의 가리키는 말이 대상을 잃으면 그 말을 대상의 이름으로 고치라”는 규칙을 넣자, 같은 입력에서 5번 중 5번 나던 깨짐이 5번 중 1번으로 줄었다. 남은 1번은 코드로 막았다. 지운 문단 바로 다음 문장이 ‘이’, ‘그’, ‘해당’ 같은 말로 시작하면 그 재작성은 적용하지 않는 장치다.

다음 실제 테스트에서는 뜻이 바뀐 문장이 나왔다. “공중제비나 물구나무서기를 미션으로”가 “공중제비를 미션으로”가 되고, “정지·충돌·비용·안전 문제”가 “비용 문제”가 됐다. 겹치는 항목만 지우라고 했더니 목록에서 일부 항목만 지우고 남은 조각으로 다른 주장을 한 것이다. 같은 입력으로 비교하니, 문단을 통째로 다시 쓰던 예전 프롬프트에서는 9번 중 0번, 구절만 바꾸게 한 프롬프트에서는 15번 중 8번이었다. 그래서 단위를 문장으로 바꿨다. 겹치는 사실이 든 문장은 문장째 링크 문장으로 바꾸고, 다른 내용이 섞인 문장은 건드리지 않는다. 뜻이 바뀐 회차는 16번 중 1번으로 줄었고, 그 1번도 “원래 문장보다 짧아진 문장이면 적용하지 않는” 코드 장치로 막았다.

그다음 테스트에서는 같은 문단 안에서 깨졌다. 정의 문장을 지우고 바로 뒤의 “이 정의와 …”를 남겼다. 앞의 장치는 문단 다음 문장만 봤기 때문에 못 잡았다. 장치를 문단 안까지 넓혔다.

그리고 다음 테스트에서 또 새 모양이 나왔다. 한 절의 첫 문장이 지워지자, 남은 문장이 “또한 배터리로 움직이며 …”로 시작했다. ‘또한’은 가리키는 말이 아니라 접속어라 장치 대상이 아니었고, 남은 문장은 원래 문장 그대로라 ‘짧아진 문장’ 장치에도 걸리지 않았다. 절 제목은 “학습과 실행의 현실적 간극”인데 절에 남은 내용은 온디바이스 AI 이야기였다. 저장해 둔 그 실행의 재작성 8건을 두 장치에 다시 넣어 봐도 하나도 걸리지 않았다.

실제 테스트 네 번 내내 깨짐이 나왔다. 건수는 줄어 마지막 두 번은 한 건씩이었지만, 모양이 매번 달랐다. 장치는 늘어나는데 다음 모양은 늘 장치 밖에 있었다.

원인은 하나, 처방은 너무 셌다 — 문단 단위 중복 제거

모양은 달라도 원인은 같았다. 문단 안에서 문장 일부만 지우면, 남은 문장 중 지운 문장에 기대던 것이 혼자 남는다. 기대는 방식이 가리키는 말이든, 접속어든, 절 제목이든 상관없다.

그러면 문단을 쪼개지 않으면 된다. 문단의 모든 문장이 넘겨받는 노트에 이미 있을 때만 그 문단 전체를 링크 문장 하나로 바꾸고, 하나라도 없으면 문단을 건드리지 않게 프롬프트를 바꿔 27번 돌렸다.

깨짐은 0이 됐다. 그런데 정리도 거의 0이 됐다. 같은 입력에서 한 번에 18건이던 재작성이 01건이 됐고, 지금까지 매번 정리되던 진짜 중복 네 가지는 8번 중 한 번도 정리되지 않았다. 형제 노트의 한 문단이 통째로 다른 노트에 있는 경우는 드물었다. 문단 단위로는 겹치는 게 거의 없어서, 이 규칙은 정리 단계를 끈 것과 같았다.

모양을 미리 막지 말고, 고친 결과를 다시 읽게 했다 — LLM-as-a-judge

그래서 방향을 바꿨다. 지우기 전에 어떤 모양이 깨질지 예측해서 막는 대신, 지운 뒤의 노트를 LLM에게 다시 보여주고 “이 수정 때문에 새로 생긴 문제가 있는가”를 판정하게 했다. 판정 기준은 그동안 나온 네 가지다. 가리키는 말이 대상을 잃었는가, 주어가 빠진 문장이나 링크만 있는 문장이 생겼는가, 목록 일부만 남거나 조건이 빠져 뜻이 바뀌었는가, 절 제목과 남은 내용이 어긋났는가. 문제가 있다고 판정되면 그 노트는 고치기 전으로 되돌린다는 구상이다.

지난 실제 테스트 네 번의 재작성 38건을 수정 전/후 노트 쌍으로 다시 만들고, 직접 읽어 깨짐 9건, 애매 6건, 정상 23건으로 나눈 뒤 판정을 3번씩 돌렸다. 첫 판정 문구는 깨짐 27번 중 20번을 잡았다. 링크만 있는 문장과 목록 일부만 남은 문장을 놓쳐서, 그 두 경우를 판정 기준에 풀어 쓰자 27번 중 27번을 잡았고 정상 69번 중 2번을 잘못 걸었다.

이 숫자는 믿을 수 없었다. 판정 문구를 같은 38건을 보면서 고쳤고, 넣은 예시도 그중 한 건과 닮았기 때문이다.

튜닝에 안 쓴 124건으로 다시 확인했다 — 프롬프트 과적합

그동안 재현 실험에서 쌓인 다른 재작성 124건을 같은 방식으로 만들어 판정을 한 번씩 돌리고, 124건을 모두 직접 읽었다.

판정이 걸어낸 20건 중 15건은 실제로 깨졌다. 앞에서 본 ‘이 기술 체계’, 지운 내용을 받던 ‘also’, 대상이 사라진 ‘the shortlist’, 문장이 링크 없이 사라져 절 제목만 남은 경우, “rivals or regulators”가 “regulators”만 남은 경우다. 3건은 뜻이 조금 좁아진 애매한 경우였고, 1~2건은 잘못 걸었다. 걸지 않은 104건 중 놓친 것은 분명한 1건과 가벼운 3건이었다. 완벽하지는 않지만, 가리키는 말도 짧아진 문장도 없어서 코드 장치가 아예 보지 않는 ‘절 내용이 통째로 사라짐’ 같은 모양까지 잡았다. 모양을 하나씩 정의하지 않아도 되기 때문이다.

읽다가 하나 더 알게 됐다. 정리 단계는 한 노트를 두 번 고치기도 한다. 첫 재작성이 ‘이 기술 체계’를 고아로 만들면 두 번째 재작성이 그 문장을 “로보틱스랩의 기술 체계”로 고치는 식이다. 재작성 하나씩 판정하면 이 중간 상태를 깨짐으로 걸게 된다. 그래서 판정은 그 노트의 재작성을 모두 적용한 최종 상태에 한 번만 하기로 했다. 그렇게 노트 단위로 다시 묶은 134개에서 판정은 깨짐 20개 중 18개를 잡고, 정상 103개 중 3개를 걸었다. 노트 하나에 3초쯤 걸린다.

아직 플러그인에 넣지는 않았다. 판정에 걸린 노트를 되돌리는 방식과 기존 코드 장치를 남길지는 설계 문서를 먼저 쓰고 정한다.

남는 생각

깨짐이 나올 때마다 그 모양을 정의하고 막는 일은, 매번 한 건씩은 확실히 고쳤다. 그래서 진전처럼 보였다. 하지만 실제 테스트 네 번이 매번 다른 모양의 깨짐으로 끝났다는 건, 막고 있던 게 원인이 아니라 원인이 만든 모양 목록이었다는 뜻이었다. 원인을 그대로 없애는 처방은 기능까지 없앴다. 남은 길은 결과를 사람처럼 다시 읽고 판정하는 것이었다. 그리고 그 판정 문구도 튜닝에 쓴 데이터만으로는 믿을 수 없어서, 처음 보는 데이터를 한 번 더 읽어야 했다.


Share this post:

AI 검증이 놓치는 것들

  1. 1. 몇 주 뒤, 그 감사 장치가 못 보는 경우를 찾았다
  2. 2. 리스트 밀도 상위 4개 중, 서브에이전트가 2개를 놓쳤다
  3. 3. 검증은 다른 쌍으로 했는데, 원래 그 사례는 다시 안 봤다
  4. 4. LLM이 만든 형제 노트끼리 같은 문단을 그대로 베끼고 있었다
  5. 5. 임베딩 모델을 바꾸니 순서가 뒤집혔다
  6. 6. 임베딩 필터를 걷어내니, 중복이 오히려 더 많이 잡혔다
  7. 7. LLM 노트 병합 규칙에서 틀린 예시는 지웠는데, 규칙은 남아 있었다
  8. 8. LLM 노트 병합 테스트는 통과했는데, 고친 코드는 한 번도 실행되지 않았다
  9. 9. LLM이 노트 중복을 지우자, 남은 문장이 가리킬 곳을 잃었다