실수로 LLM 메모리를 프로그램 분석으로 바꿔버린 이야기
취약점 연구에 LLM 에이전트를 활용하던 중 장시간 조사 후 모델이 기존에 확립한 사실을 잊거나 이미 반박된 가정을 계속 믿는 문제를 겪은 저자는, 일반적인 메모리 시스템(대화 저장 + 임베딩 검색)의 한계를 발견했습니다. 저자는 이 문제가 사실 변경 시 영향받는 결론만 갱신하는 프로그램 분석과 유사하다는 점에 착안해, LLM을 위한 Datalog 엔진을 직접 구현하게 되었다고 설명합니다.
실수로 LLM 메모리를 프로그램 분석으로 바꿔버린 이야기 [2026년 8월 28일] // Jordy Zomer // 19분 읽기
지난 몇 달간 나는 LLM 에이전트를 가지고 꽤 많이 실험해왔는데, 특히 취약점 연구 분야에서였다. LLM은 대규모 코드베이스를 탐색하고, 낯선 서브시스템을 설명하고, 잠재적 공격 표면을 탐색하는 데 놀랄 만큼 유능해지고 있다. 하지만 조사가 몇 시간씩 걸리기 시작하면 항상 같은 문제에 부딪혔다. 모델이 우리가 실제로 확립한 내용을 서서히 잊어버리는 것이다. 이미 제외된 접근법을 다시 제안하거나, 어떤 가정이 틀렸다는 사실을 잊어버리거나, 더 이상 유효하지 않은 관찰에 근거해 자신만만하게 추론을 계속하기도 했다. 당연히 LLM에게 뭔가 잘못됐다고 말한다고 해서 그것에 의존하던 모든 믿음을 버리는 것은 아니라는 점이 문제다 :)
처음에는 이런 환각(hallucination)을 줄이고 복잡한 취약점 연구에 LLM을 더 유용하게 만들고 싶어서 메모리 시스템을 연구하기 시작했다. 물론 LLM에 메모리를 부여하는 솔루션은 이미 많다. 보통은 과거 대화나 관찰 내용을 어딘가에 저장하고, 임베딩한 뒤, 모델이 다시 필요할 때 가장 관련성 높은 부분을 검색하는 방식이다. 이 방식은 그럭저럭 잘 작동하지만, 뭔가 마음에 걸리는 부분이 있었다.
취약점 연구 세션 동안 나는 모델이 우리가 한 말을 단순히 기억하는 것을 원하지 않는다. 우리가 현재 알고 있는 것을 유지하기를 원한다. 예를 들어 조사 중에 다음과 같은 사실을 확립했다고 해보자:
- 공격자가 object_a를 제어한다
- object_a는 object_b를 가리킨다
- object_b는 커널 객체다
이로부터 우리는 공격자가 커널 객체를 제어할 수 있다고 결론 내릴 수 있다. 일반적인 메모리 시스템은 이 모든 관찰을 저장했다가 버그의 악용 가능성에 대해 질문할 때 다시 검색할 수 있다. 그러면 LLM이 같은 결론에 도달한다. 훌륭하다! 하지만 두 시간 뒤 LLDB에서 object_a가 실제로는 object_b를 가리키지 않으며, 이전 관찰이 잘못된 가정에 기반한 것이었다는 사실을 발견했다고 해보자. 그 시점에 메모리에는 다음과 같은 내용이 담기게 된다:
- object_a는 object_b를 가리킨다
- 공격자는 object_b를 제어할 수 있다
- object_a는 실제로는 object_b를 가리키지 않는다
이제 이 메모리들의 일부를 검색해서 LLM이 어떤 결론이 여전히 유효한지 올바르게 판별해주기를 기도할 수밖에 없다.
이건 프로그램 분석과 비슷해 보인다
내가 평소에 하는 작업 상당수는 프로그램 분석(program analysis)과 관련되어 있다. 프로그램을 분석할 때 우리는 보통 프로그램에 관한 여러 사실(facts)과 이것들로부터 추가 사실을 도출하는 규칙(rules)을 갖는다. 예를 들어 다음을 안다고 해보자:
calls(foo, bar) calls(bar, baz)
어떤 함수가 다른 함수를 호출하고, 그 함수가 세 번째 함수에 도달할 수 있다면 첫 번째 함수도 세 번째 함수에 도달할 수 있다는 규칙을 정의할 수 있다. 결국 우리는 프로그램에서 도출할 수 있는 모든 것을 담은 고정점(fixed point)을 계산한다. 더 중요한 것은, 입력 사실 중 하나가 변경되면 전부 처음부터 다시 계산하는 대신 영향받는 결과만 업데이트하는 다양한 기법이 존재한다는 점이다.
이것이 바로 취약점 연구 중인 LLM에게 내가 원했던 것이다. 관찰이 변경되면 모델에게 대화 기록에서 전체 조사를 재구성하게 하고 모든 파급 효과를 운 좋게 알아채기를 바라는 게 아니라, 영향받는 결론들이 자동으로 무효화되기를 원했다. 이 관점에서 문제를 바라보다 보니, 우리가 왜 LLM에게 전체 상태를 반복해서 재구성하게 만들고 있는지 궁금해지기 시작했다. 그냥 상태를 유지하면 안 되는 걸까? 그리고 이렇게 해서 나는 어쩌다 보니 LLM용 Datalog 엔진을 작성하게 되었다 :)
Datalog 계속하기 전에 Datalog이 실제로 무엇인지 간단히 설명하는 것이 유용할 것이다. Datalog은 선언적 논리 프로그래밍 언어다. 무언가를 어떻게 계산해야 하는지 지시문을 작성하는 대신, 우리는 새로운 사실을 도출해낼 수 있는 사실과 규칙을 기술한다. 예를 들어...