소개
Langchain.JS와 유사한 Llamaindex.TS라는 프로젝트를 소개 합니다.
Python 패키지로 잘 알려져 있는 LlamaIndex의 TypeScript 구현체 입니다.
사용자가 보유한 문서를 indexing한 뒤 LLM을 사용하여 질의 응답을 할 수 있습니다.
Langchain에 비해 사용하기 편한 API를 제공합니다.
turborepo 기반으로 프로젝트를 구성하였습니다. 따라서 다음 명령으로 라이브러리를 빌드할 수 있습니다.
$ pnpm --filter llamaindex build
$ pnpm install
주요 의존 패키지
turborepo
turborepo는 한 차례 리뷰 했습니다.
각 빌드 단계의 산출물을 캐쉬하고 수정 사항에 대해서만 빌드하는 incremental build 기능을 가지고 있습니다.
tsup
앞서 turborepo 포스트에 정리했 듯이 turborepo는 turbopack과 함께 번들링 기능을 제공할 예정인데요.
본 프로젝트는 vite에서 사용하는 esbuild를 사용하는 tsup을 사용하고 있습니다.
TypeScript 코드 번들링은 rollup 등을 주로 사용하는데요. tsup은 별도의 설정없이 esm, cjs 등으로 번들링을 할 수 있습니다.
아직 css 기능은 실험단계라고 하나 node js에서 돌아가거나 순수 TypeScript로 작성한 라이브러리는 tsup이 매우 심플한 선택지인 것 같습니다.
질의 처리 과정
다음 tutorial 코드를 살펴봅니다.
import fs from "fs/promises";
import { Document, VectorStoreIndex } from "llamaindex";
async function main() {
// Load essay from abramov.txt in Node
const essay = await fs.readFile(
"node_modules/llamaindex/examples/abramov.txt",
"utf-8"
);
// Create Document object with essay
const document = new Document({ text: essay });
// Split text and create embeddings. Store them in a VectorStoreIndex
const index = await VectorStoreIndex.fromDocuments([document]);
// Query the index
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query(
"What did the author do in college?"
);
// Output response
console.log(response.toString());
}
main().catch(console.error);
대략, 라이브러리에 포함한 예시 텍스트 문서를 읽어와 vector store에 저장하고 이로부터 질의를 하고 응답을 받는 과정입니다.
먼저 텍스트 파일을 Document
라는 객체로 읽어옮니다.
VectorStoreIndex
는 원본 텍스트 문서와 해당 문서의 embedding vector를 저장합니다.
VectorStoreIndex
는 OpenAIEmbedding
을 사용하여 API 호출 및 응답을 저장합니다.
Vector 정보는 SimpleVectorStore
에 저장합니다.
문서를 vector로 저장한 후 질의를 할 수 있습니다.
다시 OpenAI의 API를 사용하여 질의 문장의 embedding vector를 얻어냅니다.
이렇게 얻어진 질의의 embedding vector와 SimpleVectorStore
에 있는 문서들의 vector와 비교합니다.
비교는 getTopKEmbeddings
함수에서 처리합니다.
대략 다음과 같은 수식으로 similarity 검사를 수행합니다.
이렇게 유사도 공식에 따라 top k 문서를 찾습니다. 이 top k 문서와 질의문을 다음 프롬프트 템플릿을 사용하여 LLM에 전달할 질의를 구성합니다.
export const defaultTextQaPrompt: SimplePrompt = (input) => {
const { context = "", query = "" } = input;
return `Context information is below.
---------------------
${context}
---------------------
Given the context information and not prior knowledge, answer the question: ${query}
`;
};
이 최종 질의를 OpenAI API에 전달하여 최종 응답을 얻습니다.
마치며
LLamaIndex.TS는 LangChain.JS에 비해서 간단한 만큼 기능은 많지 않습니다.
장점은 txt, pdf 형식의 어느정도 알려져 있는 문서에 대해서 비교적 쉽게 OpenAI의 API를 활용하여 자신만의 QA 봇을 만들 수 있다는 점 입니다.
다만, 지원하는 LLM이 OpenAI에 강결합되어 있어 확장성이 떨어지고, vector store나 similarity 검사 루틴도 LangChain의 구현과 비교해서는 개선의 여지가 많아 보입니다.
하지만 이전 포스트에서 다루었듯이 LangChain.JS는 복잡도가 높은 프레임워크인 만큼 OpenAI를 사용하여 간단한 knowledge base를 만드는 용도에 적합하다고 생각합니다.
사실 이렇게 보면 핵심은 vector store와 유사도 검색 알고리즘과 함께 top k로 추려낸 문서로부터 만족스러운 답을 LLM으로부터 이끌어낼 수 있는 프롬프트인 것 같습니다.
LLamaIndex.TS, LangChain.JS 모두 Python 구현체와 호환을 지향하고 있어 전체적인 설계가 다소 복잡하고 TS 스럽지 못한 코드라 여겨지는 부분이 있었습니다.
앞으로의 LLM이 API로 손쉽게 접근할 수 있는 방향으로 갈 것이기에 애초에 TypeScript 기반으로 잘 설계된 프롬프트 템플릿 생성 라이브러리와 그 에코시스템이 나오기를 기대해 봅니다.