dev.to

Local embeddings with llamadart: how llamas share secrets

llamadart로 로컬 임베딩 만들기: 라마들은 비밀을 어떻게 나눌까요

Llama Village의 지식 공유 기능을 사례로 로컬 임베딩이 대화와 저장된 사실의 유사도를 비교하는 방식을 설명합니다. 임베딩 점수만으로 비밀을 전달하면 부정문도 사실 공개로 처리될 수 있다는 결함을 재현하고, 별도의 공개 여부 판단이 필요하다고 제안합니다.

에디터 노트

가장 분명한 결함은 임베딩 점수의 성격과 게임의 전달 조건이 어긋난다는 점입니다. 부정문이 사고 사실과 0.6085의 유사도를 보였고, 키워드 조건까지 통과하면 outcome model이 공개로 판정하지 않아도 정보가 전달됩니다. 글은 이 경로를 실제 함수에 적용해 규칙 수준에서 재현했으며, 전체 플레이에서 발생했다는 주장과 구분합니다. 따라서 임베딩은 후보 검색에 쓰고 공개 판정은 별도로 맡기자는 제안이 근거에 맞습니다. 다만 DecisionEngine으로 바꾸는 방안은 아직 제안이며, 부정과 인용, 모호한 대화에서 정확도가 나아지는지는 평가가 필요합니다. 수치 역시 특정 모델과 입력 형식의 결과이므로 다른 설정의 임계값으로 곧바로 쓸 수 없습니다.

AI 요약

Llama Village는 다섯 라마가 정보를 나누되 서로 다른 사실을 알고 믿는 가상 세계입니다. 이 글은 게임에서 로컬 임베딩을 사용해 대화와 저장된 사실을 비교하는 과정을 설명합니다. 예를 들어 Pip의 빨간 목도리가 연못 갈대밭에 있다는 사실과 Mo가 목도리를 연못에 빠뜨렸다는 사실을 따로 저장합니다. Mo는 두 사실을 모두 알지만, 목도리 위치를 말한다고 사고까지 고백한 것은 아닙니다.

임베딩으로 후보 좁히기

게임은 먼저 화자가 알고 청자는 모르는 사실만 후보로 추립니다. 그런 다음 화자의 대사와 후보 사실을 비교합니다. 글의 구현은 llamadart와 EmbeddingGemma를 사용하며, 텍스트 앞에 title: none | text:를 붙이고 임베딩 벡터를 정규화합니다. 게임은 사실마다 화자의 대사 중 가장 높은 코사인 유사도를 사용합니다. 이 형식은 현재 게임의 선택이며, EmbeddingGemma 모델 카드는 새 응용에서 문장 유사도용 프롬프트처럼 작업별 접두사를 권합니다. 입력 형식을 바꾸면 점수와 임계값을 다시 평가해야 합니다.

임베딩은 텍스트를 숫자 벡터로 바꾸고, 벡터 간 유사도를 계산합니다. 같은 모델과 호환되는 설정으로 만든 벡터를 비교해야 합니다. 글에서 llamadart 0.11.0과 embeddinggemma-300M-Q8_0.gguf를 macOS arm64에서 실행한 결과 벡터는 768차원이었습니다. 목도리 위치와 관련된 문장에는 0.8449, 사고 사실과 비교한 “I did not knock the scarf into the pond”에는 0.6085, 목도리와 무관한 날씨 문장에는 0.3137이 나왔습니다. 부정문도 사고 사실과 높은 유사도를 보였지만, 이 값은 의미가 같거나 참이라는 판정도 아니고 보편적인 기준값도 아닙니다.

공개 판정과 전달 규칙

임베딩은 “June이 목도리 위치를 배웠다”는 결과를 내지 않습니다. 게임은 후보를 추린 뒤 별도의 생성형 outcome model에 대화와 사실을 보내고, 분명히 말로 공개한 사실의 ID를 반환하도록 합니다. 이후 키워드와 유사도를 함께 적용합니다. 목도리 위치에는 “scarf”와 위치 표현이 필요하고, 사고 사실에는 “scarf”와 “knock” 같은 책임 표현이 필요합니다. 하지만 키워드 검사는 부분 문자열을 찾을 뿐 부정을 이해하지 못합니다.

현재 규칙에는 outcome model이 공개로 판정하지 않아도 키워드가 맞고 유사도가 0.5 이상이면 전달을 허용하는 경로가 있습니다. 글은 부정문 점수 0.6085를 실제 keywordHit 및 acceptTransfer 함수에 넣어 accepted=true, claimed=false를 재현했습니다. 이는 실제 플레이 전체를 실행한 결과가 아니라 규칙 수준 재현이며, 해당 사실이 후보 자격을 갖추고 최종 후보 목록에 들어야 합니다. 저자는 이 유사도만으로 전달하는 예외를 없애고 명시적인 공개 판정을 요구하는 방안을 제안합니다.

제안하는 후속 구조와 재현 코드

제안 구조는 임베딩으로 후보를 고르고 DecisionEngine이 대화를 판정한 다음 게임 규칙을 적용하는 방식입니다. 예컨대 Mo의 말이 사실을 공개하는지, 부정하는지, 판단이 불분명한지 분류합니다. 이는 현재 구현이 아니라 후속 제안입니다. DecisionEngine에 Laya를 쓰더라도 부정, 추측, 인용, 모호한 대화를 평가해야 하며, 구조화된 출력 형식만으로 판정의 정확성이 보장되지는 않습니다.

글에는 Dart 3.13.5와 llamadart 0.11.0으로 실행하는 예제도 실려 있습니다. 로컬 GGUF 모델을 불러오고 백엔드의 임베딩 지원 여부를 확인한 뒤 다섯 사실의 벡터를 한 번에 계산해 캐시합니다. 세 문장을 질의로 넣고 점수가 높은 사실 세 개를 출력합니다. 예제는 유사도 비교만 재현하며 캐릭터별 사실 보유 여부, outcome model, 지식 갱신은 구현하지 않습니다. 글은 모델이나 입력 형식을 바꿀 때 캐시 벡터를 다시 만들고, 출처와 정보 전달 경로도 함께 기록하라고 설명합니다.

원문: dev.to / 번역·요약: Trawling