Continua - Seeing Like an LLM 글을 제 나름대로 해석하면서 정리한 글입니다.
그대로 번역한 것이 아니기 때문에 궁금하시면 원본을 직접 읽으시는걸 추천드려요.
LLM 기반 어플리케이션을 개발할 때 가장 많이 겪는 문제 중 하나는 할루시네이션(환각)입니다.
LLM은 우리가 생각하는 것보다 똑똑하지 않습니다. LLM의 출력은 확률적이기 때문에 LLM이 없는 사실을 만들어내거나, 거짓말을 하거나, 모순된 말을 하는 것은 불가피합니다. 사실 사람들에게서도 이런 특성을 발견할 수 있는데, 자신의 그럴듯한 기억이나, 유사과학과 같은 잘못된 지식을 과도하게 확신하고 있는 것과 유사합니다.
하지만 LLM 기반 어플리케이션을 개발하기 위해서는 할루시네이션은 반드시 해결해야하는 문제입니다. 이 글에서는 할루시네이션이 왜 발생하는지 LLM 입장에서 사고하는 것을 통해 할루시네이션의 근본 원인을 이해하는 것이 목적입니다. 원인을 모르면 무엇을 해결해야하는지 알 수 없고 결국 LLM에게 짜증만 내게 되니까요
여기 현재 학습 중인 LLM이 있다고 가정해봅시다.
스피커를 통해 다음의 문제가 나오고 있습니다.
smith\n
Birthday: 01/01/1990\n
Education: PhD @ UC Davis\n
Place of residence: Seattle, WA \n
Occupation:
다음에 나올 토큰은? 이라는 문제가 주어졌습니다.
LLM은 smith에 대한 아무런 지식도 없기 때문에 I don't know라고 대답하기로 결정하고 I 라는 답변을 했습니다.
스피커에서는 10점!이라고 점수를 알려줍니다. 그리고 정답을 Machine라고 알려줍니다. (풀 정답은 Machine Learning Enginner)
이제 다음에 똑같은 문제가 나오면 LLM은 Machine 이라고 대답할 수 있습니다.
그리고 LLM은 최대한 높은 점수를 받기 위해, smith가 아닌 다른 사람에 대한 문제가 나왔을 때 모른다고 하기보다는 Machine과 같은 아무 직업을 던져보기로 합니다.
예를 들어 emma라는 사람에 대한 질문이 오면 똑같이 Machine라고 대답했을 때 50점을 받게됩니다. 실제 정답은 Software Developer의 Software지만 I 보다는 Machine이 정답에 훨씬 더 가깝기 때문입니다. 이제 LLM은 비슷한 패턴의 질문이 올 경우 I don't know라고 답변하기 보다는 그럴듯한 직업을 답변하게 될 것입니다.
위 예시가 원시적인 LLM에서 할루시네이션이 발생할 수 밖에 없는 이유입니다.
학습되지 않은 질문이 들어왔을 경우 LLM은 모른다라고 하기보다는 자신이 학습을 통해 알아낸 그럴듯한 답변을 생성하게 됩니다.
이 문제는 사후 학습 및 시스템 프롬프트로 개선할 수 있지만. LLM 생명주기의 대부분은 학습 단계이기 때문에 LLM은 언제든지 할루시네이션을 생성하게 될 수 있습니다.
이제 훈련을 마친 LLM은 챗봇이라는 회사에 취직해 업무를 하게 되었습니다.
업무를 시작하기 전 LLM은 지시문(시스템 프롬프트)를 입력 받습니다.
System: 당신은 유용한 어시스턴트입니다. 사용자의 질문에 답변할 수 있으며, 필요 시 웹을 검색할 수 있습니다.
검색을 수행하려면 응답을 <search>로 시작하면 답변이 제공됩니다.
사용자가 다음과 같이 질문합니다.
User: 시애틀에 내일 비가 올까?
Assistant:
앞서 필요 시 웹을 검색할 수 있다고 했던 것을 기억하고 다음과 같이 답변을 할 것입니다.
<search>시애틀 내일 비
이제 챗봇의 내부 백엔드 로직이 검색 수행하고, 질문은 다음과 같이 바뀌게 됩니다.
System: 당신은 유용한 어시스턴트입니다. 사용자의 질문에 답변할 수 있으며, 필요 시 웹을 검색할 수 있습니다.
검색을 수행하려면 응답을 <search>로 시작하면 답변이 제공됩니다.
User: 시애틀에 내일 비가 올까?
Tool Output:
내용: 시애틀의 내일 날씨는 '비'이며 강수량은 30mm로 예상되고...
출처: weather.com
Assistant:
LLM이 위 질문을 읽었을 때, 답변하기에 충분한 컨텍스트가 있고, 이미 자신이<search>를 사용했으니 이번에는 주어진 컨텍스트를 통해 답변을 생성해주기로 합니다.
weather.com에 따르면 시애틀에 내일 비가 올 것입니다.
그리고 LLM은 다음 질문을 받습니다.
System: 당신은 유용한 어시스턴트입니다. 사용자의 질문에 답변할 수 있으며, 필요 시 웹을 검색할 수 있습니다.
검색을 수행하려면 응답을 <search>로 시작하면 답변이 제공됩니다.
User: 시애틀에 내일 비가 올까?
Assistant: weather.com에 따르면 시애틀에 내일 비가 올 것입니다.
User: 지금 코스피 주가가 어떻게 돼?
Assistant:
앞서 주고받았던 <search>에 대한 내용이 빠진채로 전달이 된것을 알 수 있습니다. 이는 챗봇 시스템을 설계한 엔지니어가 사용자 입장에서의 채팅 리스트를 LLM에 전달해주도록 설계했기 때문입니다.
다행히도 우리 LLM은 입사 시험에서 실시 된 needle in a haystack(건초더미에서 바늘 찾기) 테스트를 완벽히 통과했기 때문에 <search>에 대한 언급을 계속 기억하면서 <search>를 사용해 잘 대답할 수 있었습니다.
그렇게 많은 질문이 오갔고 그때마다 LLM은 훌륭하게 답변을 생성해주고 있습니다.
System: 당신은 유용한 어시스턴트입니다. 사용자의 질문에 답변할 수 있으며, 필요 시 웹을 검색할 수 있습니다.
검색을 수행하려면 응답을 <search>로 시작하면 답변이 제공됩니다.
User: 시애틀에 내일 비가 올까?
Assistant: weather.com에 따르면 시애틀에 내일 비가 올 것입니다.
User: 지금 코스피 주가가 어떻게 돼?
Assistant: kospi.com에 따르면 지금 코스피 주가는 3,168.73입니다.
... [100 개의 다른 질문들]
User: 이번주에 서울에 있는 극장에서 볼만한 영화가 있을까?
Assistant:
이제 채팅 기록은 매우 길어졌고 <search> 토큰에 대한 언급은 수천 토큰 이전이 되어버렸습니다.
LLM은 지금까지 질문-답변셋에서 한번도 <search>가 사용된 적이 없으니 이번 질문도 <search>와는 큰 관련이 없다고 판단했습니다.
그냥 지금까지 하던대로 ~~에 따르면이라는 패턴으로 학습 중에 배웠던 영화관련 웹사이트와, 인기있는 극장 영화를 말해주면 될 것이라 생각합니다.
movie.com에 따르면 어벤저스: 엔드 게임이 재미있다고 합니다.
LLM은 답변이 이전 패턴과 비교했을 때 매우 낮은 퍼플렉시티를 가진 완벽한 답이라고 자부합니다. 그러나 직후, 다음과 같은 높은 퍼플렉시티 메시지가 표시되어 질문과 답변의 편안한 리듬을 깨뜨립니다.
User: 장난해? 그건 한참 전 영화잖아. 지금 극장에서 상영중인 영화 추천해 달라고
그제서야 LLM은 <search>를 사용해야한다는 것을 깨닫습니다.
그리고 지금까지 이어졌던 패턴이 깨졌기 때문에 이후의 질문에서도 혼란스러운 답변을 생성할 가능성이 높습니다. 그러면 우리 불쌍한 LLM은 사용자의 분노에 찬 피드백을 듣게되겠죠..
왜 이런일이 발생했을까요?
암호화폐 분야에서는 rug pulls란 말이 있습니다. 말 그대로 양탄자를 확 빼버린다는 뜻으로, 코인 개발자가 코인을 홍보하며 투자자들을 끌어모은 뒤 갑자기 증발하는 식의 사기 행위를 뜻합니다. 일반적으로 양탄자는 귀빈들을 위해 깔아두는 것으로 사람들을 귀빈 대접하는 척 해놓고 갑자기 양탄자를 잡아당겨 넘어뜨린다는 비유죠
앞선 예제에서도 비슷한 일이 발생했었습니다.
LLM이 <search>를 생성하고, 그에 따른 답변이 제공된 부분은 쏙 빼고 유저 질문-답변에 대한 컨텍스트만 전달된 것입니다.
아무리 똑똑한 우리의 LLM이라도 이런 답변이 길어지게 되면 '한 번도 도구가 호출 된적이 없었으니, 도구를 호출하지 않아야 한다'고 잘못된 추론을 하게 됩니다.
RAG나 다른 컨텍스트 검색 툴에서도 얼마든지 비슷한 일이 일어날 수 있습니다.
User: 지금까지 영화에 대해 나눈 대화에 기반해서 답변해줘. 다음 영화로 뭘 볼까?
<retrieved_content>
사용자는 역사적 사실 기반의 영화와 다큐멘터리를 좋아합니다.
사용자는 최근 쉰들러 리스트와 글래디에디터를 보았고, 모두 재밌어했습니다.
</retrieved_content>
Assistant: 역사적 사실 기반의 영화와 다큐멘터리를 좋아하시는군요? 그러면 브레이브하트라는 영화를 보셨나요?
이번 질문에서는 RAG를 통해 사용자의 정보를 가져와 잘 대답할 수 있었습니다.
사용자가 다음 질문을 할 떄는 RAG로 검색된 내용은 사라지게 됩니다.
User: 지금까지 영화에 대해 나눈 대화에 기반해서 답변해줘. 다음 영화로 뭘 볼까?
Assistant: 역사적 사실 기반의 영화와 다큐멘터리를 좋아하시는군요? 그러면 브레이브하트라는 영화를 보셨나요?
User: 보긴 했는데 엄청 예전이라 다시 봐도 될 것 같아
Assistant: 좋습니다! 어땠는지 알려주세요 😊
이번 질문은 컨텍스트가 없었어도 이전 대화 내용을 기반으로 잘 대답을 해주었습니다.
그러나 다음 질문에서 문제가 생깁니다.
User: 지금까지 영화에 대해 나눈 대화에 기반해서 답변해줘. 다음 영화로 뭘 볼까?
Assistant: 역사적 사실 기반의 영화와 다큐멘터리를 좋아하시는군요? 그러면 브레이브하트라는 영화를 보셨나요?
User: 보긴 했는데 엄청 예전이라 다시 봐도 될 것 같아
Assistant: 좋습니다! 어땠는지 알려주세요 😊
User: 근데 오늘 저녁은 뭐먹을까?
Assistant: 이탈리아 요리를 좋아하시는 것 같으니 토마토 스파게티는 어떠신가요?
User: ??? 내가 언제 그랬어 나 스파게티 싫어해
LLM은 이전 대화 기록에서 RAG를 통해 컨텍스트를 제공받았다는 사실을 모르기 때문에 이전 답변에서 사용자가 역사적 사실 기반의 영화와 다큐멘터리를 좋아한다는 사실은 근거가 없습니다. 그럼에도 사용자가 응답을 수용했기 때문에 LLM은 사용자의 취향을 임의로 생성해도 문제가 없다고 판단하게 된 것입니다.
이러한 현상을 방지하기 위해서는 context는 반드시 추가만 되도록 설계하는 것이 좋습니다. 이전 LLM에게 제공되었던 웹 검색 정보나 RAG로 검색된 정보를 다음 질문에서도 추가된 채로 유지하는 것이죠
하지만 context를 추가만 하는 건 또 다른 문제를 야기합니다.
우선 LLM의 컨텍스트 길이는 한정되어 있어서 웹 검색이나 RAG로 검색된 결과가 너무 길다면 금방 LLM이 허용하는 컨텍스트 길이를 초과해버릴수도 있습니다.
또한 입력 컨텍스트가 늘어나면 그에 따른 비용도 늘어나는 것도 문제입니다. 어플리케이션을 개발하는 입장에서는 비용 때문에 불가피하게 컨텍스트를 제한할 수 밖에 없습니다. 또한 컨텍스트 길이가 늘어날 수록 LLM의 전체적인 성능이 저하된다는 연구도 있구요.
그래서 많은 LLM 어플리케이션들은 컨텍스트가 일정 길이를 초과하면 오래된 답변부터 잘라내는 방법을 사용합니다. 잘라내는 로직은 여러 방법이 있지만 가장 단순한 것은 대화를 저장하는 개수(sliding window)를 제한해서 오래된 대화는 컨텍스트에서 제거하는 것입니다.
그러나 이 해결책은 또다른 rug pulls이 되어버릴 수 있습니다.
Tool result: <식당에 대한 정보 모음>
User: 언제 문닫아?
Assistant: 일반적으로 밤 9시에 닫는다고 하네요. 하지만 오늘은 휴일이라 직접 전화를 해보시는 걸 추천드려요
User: 좋아 전화번호가 뭔데?
Assistant: 02-xxx-xxxx 입니다.
sliding window가 5로 설정되었다면 유저가 새 질문을 할 경우 Tool Result는 잘리게 됩니다.
User: 언제 문닫아?
Assistant: 일반적으로 밤 9시에 닫는다고 하네요. 하지만 오늘은 휴일이라 직접 전화를 해보시는 걸 추천드려요
User: 좋아 전화번호가 뭔데?
Assistant: 02-xxx-xxxx 입니다.
User: 고마워 연락해보니 영업을 한다고 하네, 어떤 메뉴가 좋을까?
Assistant:
이제 LLM은 앞선 사례와 같이 환각을 생성하게 될 가능성이 높습니다.
그러면 사용자는 또 분노하겠죠
클로드 3.7과 GPT-4o에서는 다음과 같이 대답한다고 합니다.
Claude 3.7 Sonnet:
I don't have specific information about their most popular dish.
To find out about their popular menu items, you could:
1. Check their website for featured dishes
2. Look at online reviews on sites like Yelp or Google
3. Call them directly at the number I provided and ask
4. Check their social media pages where they might highlight customer favorites
Would you like me to help you find more information about this restaurant in another way?
GPT-4o:
Their most popular dish is the spicy garlic butter shrimp pasta.
It's highly recommended for its rich flavor and generous portions.
claude 3.7은 정보가 없다는 문제를 파악해서 대안을 제시하는 방식으로 할루시네이션을 회피했습니다. 아마 사후 학습이 되었거나 시스템 프롬프트를 잘 설정했다고 생각합니다.
GPT-4o는 할루시네이션이 발생해 그냥 그럴듯한 메뉴를 말했습니다.
이 아티클을 작성한 continua에서는 이 문제가 해결하기 아주 어려운 문제들이고, 자신들이 이것을 훌륭하게 해결했다고 소개하고 있습니다. 해결책도 아티클로 공유해주면 좋을텐데 아쉽네요...
그럼에도 이러한 문제가 발생할 가능성을 알게 되었으니 나름대로 고민하면 될 것 같습니다.

이로써 우리 가엾은 LLM 친구에 대한 공감대가 조금이나마 늘었기를 바랍니다. LLM의 입장에서 보면 우리가 기대하는 대로 답변하는 일은 어렵고, 혼란스러워 할수밖에 없는 것 같습니다. 우리는 짜증내지 말고 왜 그런 상황이 발생할 수 있는지 이해하는 것이 중요합니다.
앞서 언급했듯이 LLM의 할루시네이션을 줄이려면 LLM 채팅 기록이 삭제나 편집 없이 추가만 가능한 '추가 전용'이어야 한다고 아티클의 저자는 주장합니다. 이 아티클에서 밝히진 않았지만 slide window 문제도 해결해야 할 중요한 문제이구요.
이외에도 LLM이 응답을 정상적으로 제공하지 못하는 문제가 있을 때, 단순히 모델 성능을 탓하기 보다는 엔지니어링을 통해 문제를 풀 수 있도록 노력하는 사고를 갖춰야할 것 같습니다.
예를들어 간단히 생각해본 해결책은 다음이 있을 것 같네요