Kernel Memory 3. Memory service

https://microsoft.github.io/kernel-memory/service

LLM이 훈련 이후에 만들어진 데이터나 조직 내부 데이터는 모른다는 한계를 극복하기 위한 방법으로 RAG가 제시되었고, 조직 내부 데이터를 관리하기 위해 커널 메모리가 등장습니다. 커널 메모리는 검색 엔진과 함께 RAG를 가능하게 하는 쌍두마차라 할 수 있습니다.

커널 메모리는 애플리케이션에 임베딩 되기 보다는 독립된 서버 서비스로 실행 되는 게 적합합니다.

그래야 여러 대의 서버에 분산되어 클라이언트 애플리케이션을 차단하지 않고 긴 데이터 수집 과정을 실행할 수 있습니다. 또한 커널 메모리 서비스 만이 AI 모델, 저장소 및 기타 의존성에 접근할 수 있도록, 자격 증명을 두어, 클라이언트 앱을 통해 비밀이 노출되지 않습니다.

커널 메모리는 크게 두 가지 기능 – 저장과 검색 – 을 합니다.

 

저장

클라이언트가 파일을 보내면, 서비스는 Azure Blobs나 Local Disk 같은 Content Storage에 데이터를 저장합니다 .
이 단계에서 클라이언트는 연결을 유지하며 데이터를 전송하고 저장을 기다립니다. 이 작업이 완료될 때, 커널 메모리는 클라이언트 요청을 끊고  비동기 파이프라인(할 일들을 순차적으로 연결한 것 – 한 일은 하나의 단계가 됨)을 시작합니다.

저장은 자연어 검색을 위해 자연어를 벡터로 표현하는 임베딩 과정을 거칩니다. 가장 일반적인 데이터 형태인 자연어 텍스트는 임베딩 과정을 거쳐 벡터 데이터베이스에 저장될 레코드로 변환됩니다. 오디오나 이미지나 비디오에 포함된 데이터는 보통은 텍스트로 변환된 후 임베딩 과정을 거칩니다.

 

커널 메모리는 다양한 형식의 파일들이나 웹 페이지에서 텍스트를 추출합니다. 추출 된 텍스트는 파티션으로 분할됩니다. 파티션 별 텍스트를 임베딩하고, 메타데이터와 함께 메모리 DB에 저장합니다.

각 단계 처리를 핸들러라고 합니다. 저장을 처리하기 위해서는 여러 단계들이 필요하기 때문에 핸들러들 여럿으로 파이프라인을 구성합니다. 단계들은 Handlers로 구현되고 Core 라이브러리로 제공됩니다.

커널 메모리는 단계들로 순차적으로 구성된 기본 파이프라인을 제공합니다. 파이프라인 단계는 이전 단계들이 성공적으로 완료 된 후 시작됩니다. 단계 목록과 핸들러 기본 세트는 함께 작동하도록 설계되었습니다. 기본 핸들러를 제거하려면, 대체할 것을 제공해야 합니다.

파이프라인이 완료될 때만 성공적으로 데이터를 가져온 것으로 간주하는데, 이 과정은 일반적으로 몇 초밖에 걸리지 않습니다. 다음과 같은 경우에는 시간이 상당히 길어질 수 있습니다.

단계 1에서: 업로드된 파일이 외부 서비스를 필요로 하는 경우 (예: 큰 PDF 문서에 대한 OCR).
단계 3에서: 사용된 LLM이 요청을 제한하여 초당 몇 개의 벡터만 생성하는 경우.

 

커널 메모리는 저장소로 콘텐츠 저장소와 메모리 저장소를 사용합니다.

콘텐츠 저장소에는 클라이언트에 의해 업로드된 원시 데이터, 파이프라인 상태, 문서에 할당된 고유 ID가 저장됩니다.
메모리 저장소는 검색 기능이 있는 데이터베이스로, 메모리 레코드를 저장합니다.

 

커널 메모리는 관련 데이터들을 모아 document로 관리합니다. 여러 파일들이 의미적으로 관련된 데이터들을 포함하는 경우, 여러 파일들은 하나의 document에 묶여서 관리될 수 있습니다. 데이터는 웹 페이지에서도 가져올 수 있고, 사용자가 직접 입력한 텍스트와 같이 텍스트를 직접 받아 사용하는 것일 수도 있습니다. 멀티모달을 지원하니, 이미지 오디오 비디오에서 가져온 것이 될 수 도 있습니다. 커널 메모리는 아직은 직접적으로 비디오를 지원하지는 않습니다.

 

각 document가 업로드될 때, ingestion handlers는 한 번에 하나씩 호출됩니다. 문서들이 paralled하게 업로드될 수 있습니다. 하지만 한 문서에 대한 ingestion 파이프라인 스템들은 이전 단계가 성공적으로 완료될 때까지 기다립니다.

다음은 코어 라이브러리에 포함된 기본 핸들러 목록입니다:
TextExtractionHandler: 이 핸들러는 일반적으로 처음 호출되며, 파일에서 텍스트를 추출합니다.

클라이언트가 URL을 제공하면, 핸들러는 웹 페이지를 다운로드하고 텍스트를 추출합니다. 이 핸들러의 출력은 Content Storage에 저장되어 다음 핸들러에 의해 추가 처리됩니다.

이 핸들러는 OCR 및 콘텐츠 유형 감지를 담당합니다.

TextPartitioningHandler: 이 핸들러는 텍스트를 작은 조각으로 나누는 간단한 작업을 다룹니다. 핸들러는 이전 핸들러가 생성한 텍스트 파일을 찾아 일반 텍스트와 마크다운을 약간 다르게 관리합니다.

기본 핸들러는 코드 구문, 채팅 로그, JSON 또는 기타 구조화된 데이터를 이해하지 못하며, 항상 문자열로 처리합니다. 특정 형식을 다루는 경우, 사용자 입력을 더 잘 분할하기 위해 이 핸들러를 교체하고 싶을 수 있습니다.

기본 핸들러는 텍스트를 문장(또는 “줄”이라고도 함)으로 나누고, 이를 단락(또는 “파티션”이라고도 함)으로 집계합니다. 줄/문장/단락/파티션의 크기는 토큰으로 측정됩니다.

설정할 수 있지만 사용하는 임베딩 생성기에 따라 달라질 수 있습니다: 파티션이 너무 크면 일부 임베딩 생성기는 임베딩 벡터를 생성하라는 요청에 오류를 발생시킬 수 있으며, 일부 임베딩 생성기는 초과 토큰을 무시하여 불완전한 메모리 레코드를 생성할 수 있습니다.

이 핸들러는 모든 작업이 로컬에서 메모리 내에서 이루어지기 때문에 일반적으로 매우 빠릅니다. 파티션이 준비되면 같은 Content Storage에 저장되어 다음 단계를 위해 준비됩니다.

현재 파티션 파일은 원시 텍스트 파일로 저장됩니다.

GenerateEmbeddingsHandler: 이 핸들러는 각 파티션 파일을 로드하고, 텍스트를 가져와 설정된 임베딩 생성기에 임베딩 벡터 계산을 요청합니다.

벡터는 JSON으로 직렬화되어 Content Storage에 다시 저장됩니다. 이 핸들러는 한 번에 하나의 파티션을 처리하며, 병렬 처리 없이 진행되며, 사용된 LLM에 따라 몇 밀리초에서 몇 분이 걸릴 수 있습니다. 이는 또 다른 핸들러로, 임베딩 생성기에 최적화된 것으로 교체할 수 있습니다. 예를 들어, 여러 요청을 병렬로 보내거나 캐시를 사용하는 등의 방법이 있습니다.

SaveRecordsHandler: 이 핸들러는 이전 핸들러가 생성한 임베딩을 하나 이상의 메모리 DB에 저장합니다.

이 작업에는 출처, 파티션 텍스트, 태그 및 검색에 유용한 기타 메타데이터가 포함됩니다.

이 작업은 일반적으로 매우 빠르며, 제공된 문서 ID가 이전에 업로드된 문서와 일치하는 경우 기존 레코드를 업데이트합니다.

DeleteIndexHandler: 이 핸들러는 클라이언트가 문서를 삭제하도록 요청할 때 사용하는 핸들러입니다. 핸들러는 이 문서에서 추출된 모든 메모리를 순환하며 메모리 레코드와 파일을 삭제합니다.

DeleteGeneratedFilesHandler: 이 핸들러는 내용 저장소에 파일 사본을 보관하고 싶지 않은 경우에 사용되는 선택적 핸들러입니다. SaveRecordsHandler 이후에만 호출해야 합니다.

SummarizationHandler: 이 핸들러는 클라이언트가 파일 업로드의 요약을 생성하고 저장하도록 요청할 수 있는 선택적 핸들러입니다. 요약은 시간이 많이 걸릴 수 있으므로 SaveRecordsHandler 이후에 실행하는 것이 좋으며, 요약을 포함하도록 TextPartitioningHandler와 GenerateEmbeddingsHandler를 반복합니다.

문서를 업로드할 때 특별히 지정하지 않는 한, 커널 메모리는 기본적으로 다음 단계로 구성된 파이프라인을 시작합니다.

  1. “extract”
  2. “partition”
  3. “gen_embeddings”
  4. “save_records”

기본 단계 목록은 설정 파일의 KernelMemory.DataIngestion.DefaultSteps에 작성합니다. 요약과 같은 추가적인 처리가 요구되지 않는 다면, 기본 단계를 변경 할 일은 없을 것 같습니다.

커널 메모리는 이미지, 페이지 및 문서에서 텍스트를 추출한 후, 텍스트를 더 작은 청크로 분할합니다. 이 분할 단계는 효율적인 처리를 위해 필수적입니다.

기본적으로 분할 프로세스는 TextPartitioningOptions에 정의된 설정을 사용하는 TextPartitioningHandler에 의해 관리됩니다.

핸들러는 다음 단계를 수행합니다:

1. 텍스트를 줄 단위로 분할: 한 줄이 너무 길면 중단하고 새 줄을 시작합니다.
2. 문단 형성: 연속된 줄을 최대 문단 크기까지 연결합니다.
3. 중첩: 새 문단을 시작할 때, 이전 문단에서 일정 수의 줄을 유지합니다.

 

길이는 사용 중인 대형 언어 모델(LLM)과 그 토큰화 논리에 따라 토큰으로 표현됩니다. KernelMemoryBuilder는 설정 중 각 LLM에 대해 사용자 지정 토크나이저를 지정할 수 있게 합니다.

일반적으로 기본 설정은 모델이 지원하는 최대 토큰 수보다 훨씬 낮습니다. 그러나 사용자 지정 모델을 사용할 때는 일부 모델이 더 낮은 한계를 가질 수 있어 다음과 같은 오류가 발생할 수 있습니다:

허용되는 최대 값은 256 토큰입니다. 모델에 따라 이 값을 조정합니다.

About the Author
(주)뉴테크프라임 대표 김현남입니다. 저에 대해 좀 더 알기를 원하시는 분은 아래 링크를 참조하세요. http://www.umlcert.com/kimhn/

Leave a Reply

*