1주차에 이어 2주차에도 초록 스터디 Spring AI 챗봇 만들기를 진행했다. 이번 주차에는 토큰 사용량 개선, 정확도 개선에 중점을 두고 작업을 진행했다.
1주차에서는 제공되는 데이터를 모두 AI에게 컨텍스트로 제공했기 때문에, 토큰 소모량이 9000이었다. 매번 질문을 할 때마다, 모든 문서의 내용을 제공하고 답변을 한다면 토큰 소모량이 어마어마하게 나올 것이다.
이를 해결하기 위해서는 답변하는데 필요한 데이터만 AI에게 컨텍스트로 제공하는 방법이 하나 있다. 필요한 데이터들만 제공하기 위해서는 어떻게 해야할까?
Embedding
답변하기 위해 참고할 문서를 찾아서 AI에게 제공하면 된다. 참고할 문서를 컴퓨터가 어떻게 찾을 수 있을까? 이때 임베딩이라는 기술이 사용된다. 임베딩은 데이터를 의미적 관계가 보존되는 벡터 공간으로 변환하는 방법이다. 즉, 자연어를 수치화된 벡터 형태로 변환하는 과정이다.
예시를 하나 들어보자. “나는 사람입니다”라는 문장이 있다. 이를 임베딩하면 [0.4, 0.51, 0.43, 0.42]와 같은 벡터로 표현할 수 있다. 유사한 문서를 찾는 것과 데이터를 벡터로 바꾸는 것이 무슨 연관관계가 있을까?
Cosine Similarity
위에서 임베딩은 의미적 관계가 보존되는 벡터 공간으로 변환 하는 방법이라고 언급했다. 그렇다면, 유사한 의미를 가진 문장들을 임베딩한다면 벡터의 형태가 비슷할 것이다. 벡터의 형태가 비슷한지, 즉 두 벡터의 유사도를 확인할 수 있는 방법이 코사인 유사도 이다. 코사인 유사도는 두 벡터가 이루는 각도의 코사인 값으로 둘이 얼마나 비슷한지를 나타낸다.

사진과 같이 방향이 동일할 경우 1, 직교를 이루면 0 그리고 방향이 반대일 경우 -1의 값을 가지게 된다. 여기서 알 수 있는 사실은 임베딩 된 문장의 의미가 유사하다면 비슷한 방향을 가질 것이고, 즉 코사인 유사도가 1에 가까울수록 두 문장의 의미가 비슷함을 알 수 있다.
여기까지 봤을 때 우리가 해야할 작업은 데이터를 임베딩하고 질문과 코사인 유사도가 유사한 데이터를 찾아서 AI에게 제공하면 됨을 알 수 있다. 이를 코드로 구현을 해야하는데, 어떻게 구현해야 할까?
VectorStore
Spring AI에서는 VectorStore라는 인터페이스를 제공한다. 보통 임베딩된 데이터는 벡터 저장, 유사도 검색을 제공하는 데이터베이스 혹은 벡터 데이터베이스에 저장한다. 전자의 경우 PostgreSQL에서 확장 기능으로 제공되는 pgvector, 후자의 경우 Pinecone, Milvus 등이 있다.
현 프로젝트에서는 별도의 벡터 데이터베이스를 다루기에는 규모가 크지 않다고 판단되어, Spring AI에서 인메모리로 제공하는 벡터 저장소를 활용할 것이다.