Agentic AI/Memory

8. 장기기억(5) - ChromaDB 추가로 의미 기반 검색

아톨 2026. 9. 6. 14:08
반응형

기존 PostgreSQL을 버리고 ChromaDB로 바꾸는 것이 아니라, PostgreSQL은 사용자·기억의 원본 저장소로 유지하고, ChromaDB는 의미 기반 검색을 담당하도록 추가하는 것입니다.

이렇게 해야 지금까지 배운 내용도 버리지 않고, 실제 AI Agent 구조에 가까워집니다.

 

[요약]

# 1. pip install chromadb 설치

    ===> pip show chromadb로 확인

# 2. test_chroma.py 

    ===> python .\test_chroma.py 확인


1. 왜 지금 ChromaDB를 배우는 것이 좋은가?

현재 Agent는 대략 다음 구조입니다.

사용자 질문
    ↓
PostgreSQL에서 사용자의 기억 조회
    ↓
최근 기억 또는 중요도 높은 기억 반환
    ↓
LLM에 전달
 

현재 search_memory()는 아마 다음과 비슷한 방식일 것입니다.

최근 기억 10개
또는
중요도 높은 기억 10개
 

그런데 기억이 1,000개, 10,000개가 되면 문제가 생깁니다.

예를 들어 PostgreSQL에 다음 기억들이 있다고 가정하겠습니다.

1. 사용자는 Python을 공부하고 있다.
2. 사용자는 AI Agent를 만들고 있다.
3. 사용자는 PostgreSQL을 Docker로 실행했다.
4. 사용자는 한컴 기업시장 사업을 운영한다.
5. 사용자는 재개발 관련 상속 문제를 검토하고 있다.
6. 사용자는 경제와 주식시장에 관심이 있다.
7. 사용자는 최근 ChromaDB를 학습하기 시작했다.
 

사용자가 다음과 같이 질문합니다.

내가 지금 만들고 있는 AI 시스템의 다음 단계는 무엇이지?
 

이때 단순히 최근 기억 10개를 가져오는 것보다, 질문의 의미와 가까운 기억을 찾는 것이 좋습니다.

질문:
AI 시스템의 다음 단계

검색 결과:
- 사용자는 AI Agent를 만들고 있다.
- 사용자는 Python과 AI Agent를 학습하고 있다.
- 사용자는 PostgreSQL 기반 장기기억을 구현했다.
 

이것이 바로 벡터 검색입니다.


2. PostgreSQL과 ChromaDB의 역할 차이

두 데이터베이스는 경쟁 관계라기보다 역할이 다릅니다.

PostgreSQL

PostgreSQL은 다음 정보를 관리하는 데 적합합니다.

사용자 ID
사용자 이름
위치
선호도
기억 ID
기억 유형
기억 내용
중요도
생성일
 

예를 들어:

user_id = 1
user_name = "김현정"
memory_type = "goal"
importance = 8
content = "사용자는 Python과 AI Agent를 학습하고 있다."
 

PostgreSQL은 다음 질문에 강합니다.

user_id가 1인 사용자는 누구인가?
importance가 8 이상인 기억은 무엇인가?
최근 30일 동안 저장된 기억은 무엇인가?
 

ChromaDB

ChromaDB는 문장의 의미가 비슷한 기억을 찾는 것에 적합합니다.

예를 들어 다음 두 문장은 단어가 완전히 같지는 않습니다.

사용자는 Python과 AI Agent를 학습하고 있다.
 
나는 요즘 파이썬으로 지능형 에이전트를 만드는 법을 공부 중이다.
 

하지만 의미는 매우 비슷합니다.

ChromaDB는 이 두 문장을 벡터로 변환하여 의미적으로 가까운 문장으로 검색할 수 있습니다.

질문:
파이썬 공부와 관련된 내 정보를 찾아줘.
 

검색 결과:

사용자는 Python과 AI Agent를 학습하고 있다.
 

즉:

PostgreSQL
    = 정확한 데이터 저장과 관리

ChromaDB
    = 의미 기반 검색
 

3. 앞으로 만들 구조

제가 권하는 최종 구조는 다음과 같습니다.

agent_project/
│
├── .env
├── agent_with_memory.py
├── database.py
├── memory.py
│
├── vector_store.py       ← 새로 추가
├── chroma_db/            ← ChromaDB 저장 폴더
│
└── test_chroma.py        ← 새로 추가
 

각 파일의 역할은 다음과 같습니다.

파일역할
.env 환경설정
database.py PostgreSQL 연결, 사용자와 기억 저장
memory.py 기억 추출, 기억 저장 흐름
vector_store.py ChromaDB 생성, 문서 저장, 벡터 검색
agent_with_memory.py 전체 Agent 실행
test_chroma.py ChromaDB 단독 테스트

4. 전체 데이터 흐름

ChromaDB를 추가하면 기억 저장 흐름은 다음과 같이 바뀝니다.

사용자 질문
    ↓
LLM 답변
    ↓
중요한 기억 추출
    ↓
PostgreSQL에 기억 저장
    ↓
ChromaDB에도 같은 기억 저장
 

검색할 때는 다음과 같습니다.

사용자 질문
    ↓
질문을 벡터로 변환
    ↓
ChromaDB에서 의미적으로 가까운 기억 검색
    ↓
검색된 기억의 ID 확인
    ↓
PostgreSQL에서 상세 정보 조회
    ↓
LLM에 기억 전달
 

조금 더 실무적으로 표현하면:

                    ┌─────────────────────┐
                    │      사용자 질문     │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │  ChromaDB 의미 검색  │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ 관련 memory_id 반환 │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ PostgreSQL 상세 조회 │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │   LLM에 기억 전달   │
                    └─────────────────────┘
 

여기서 중요한 설계 원칙은 다음입니다.

ChromaDB에는 검색을 위한 데이터가 저장되고,
PostgreSQL에는 신뢰할 수 있는 원본 데이터가 저장됩니다.


5. 처음부터 Agent 전체에 붙이지 마세요

지금 가장 좋은 방법은 ChromaDB를 별도 실험으로 먼저 배우는 것입니다.

다음 순서로 진행하는 것을 권합니다.

1단계. ChromaDB 설치
    ↓
2단계. ChromaDB 기본 개념 학습
    ↓
3단계. 문서 저장
    ↓
4단계. 문서 검색
    ↓
5단계. 의미 검색 확인
    ↓
6단계. PostgreSQL의 기억을 ChromaDB에 저장
    ↓
7단계. user_id별 검색
    ↓
8단계. 기존 Agent에 연결
 

이 순서를 지키면 문제가 발생했을 때 원인을 쉽게 찾을 수 있습니다.


6. 먼저 알아야 할 ChromaDB 핵심 개념

ChromaDB를 배우기 전에 다음 네 가지 개념만 이해하면 됩니다.

① Collection

Collection은 문서들을 모아 놓은 공간입니다.

PostgreSQL의 테이블과 비슷하게 생각할 수 있습니다.

PostgreSQL
    user_profiles 테이블
    memories 테이블

ChromaDB
    memories 컬렉션
 

예를 들어:

 
collection = client.get_or_create_collection(
    name="memories"
)
 

이 코드는 다음 의미입니다.

memories라는 컬렉션이 있으면 가져오고, 없으면 새로 만든다.


② Document

Document는 검색할 텍스트입니다.

예를 들어:

사용자는 Python과 AI Agent를 학습하고 있다.
 

ChromaDB는 이 문장을 분석하여 벡터로 저장합니다.


③ Embedding

Embedding은 문장을 숫자 배열로 변환한 것입니다.

예를 들어 실제 벡터는 다음처럼 생겼다고 생각하면 됩니다.

[
    0.123,
   -0.452,
    0.781,
    ...
]
 

실제 차원은 사용하는 임베딩 모델에 따라 달라집니다.

중요한 점은 숫자 하나하나의 의미를 사람이 직접 해석하는 것이 아니라,

의미가 비슷한 문장일수록 벡터 공간에서 가까워지도록 만든다

는 것입니다.


④ Similarity Search

Similarity Search는 질문과 가장 의미가 가까운 문장을 찾는 기능입니다.

질문:
Python 공부와 관련된 기억을 찾아줘.
 

검색 결과:

사용자는 Python과 AI Agent를 학습하고 있다.
 

단어가 정확히 일치하지 않아도 의미가 비슷하면 검색됩니다.


7. 첫 번째 실습: ChromaDB 설치

현재 가상환경이 활성화된 상태에서 실행합니다.

 
pip install chromadb
 

설치가 끝난 후 확인합니다.

 
pip show chromadb
 

또는 Python에서:

 
import chromadb

print("ChromaDB 설치 성공")
 

8. 첫 번째 실습용 파일 만들기

새 파일을 만드세요.

test_chroma.py
 

처음에는 PostgreSQL이나 OpenAI API를 연결하지 않습니다.

오직 ChromaDB만 테스트합니다.

 
    import chromadb

    def main():
        print("="*70)
        print("ChromaDB 기본 테스트")
        print("="*70)

        # 1. ChromaDB 클라이언트 생성
        client = chromadb.PersistentClient(path="./chroma_db")
        print("☑️ChromaDB 연결 성공")

        # 2. 컬렉션 생성 또는 가져오기
        collection = client.get_or_create_collection(name="memories")  
        print("☑️memories 컬렉션 준비 완료")

        # 3. 문서 저장
        collection.upsert(
            ids=["memory_1", "memory_2", "memory_3"],
            documents=[
                "사용자는 Python과 AI Agent를 학습하고 있다.",
                "사용자는 PostgreSQL을 Docker로 실행했다.",
                "사용자는 다양한 AI Agent를 활용한 프로그램을 만들고 싶어한다."
            ],
        )
        print("☑️문서 저장 완료")

        # 4. 저장된 문서 개수 확인
        count = collection.count()
        print("저장된 문서 개수: ", count)

        # 5. 의미 검색
        results = collection.query(
            query_texts=["나는 파이썬과 인공지능 에이전트를 공부하고 있다."],
            n_results=2,
        )
        print("\n검색 결과: ")
        print(results)
    if __name__ == "__main__":
        main()


 

실행:

 
python .\test_chroma.py
 

9. 이 코드에서 가장 중요한 부분

ChromaDB 클라이언트 생성

 
client = chromadb.PersistentClient(
    path="./chroma_db"
)
 

여기서 PersistentClient는 데이터를 메모리에만 두지 않고 파일 형태로 저장합니다.

따라서 프로그램을 종료해도 데이터가 유지됩니다.

프로그램 실행
    ↓
문서 저장
    ↓
프로그램 종료
    ↓
다시 실행
    ↓
기존 ChromaDB 데이터 사용
 

./chroma_db 폴더가 자동으로 생성됩니다.


컬렉션 생성

 
collection = client.get_or_create_collection(
    name="memories"
)
 

여기서 memories는 PostgreSQL의 memories 테이블과 비슷한 역할을 합니다.

다만 ChromaDB에서는 주로 다음을 저장합니다.

id
document
metadata
embedding
 

문서 저장

 
collection.add(
    ids=[
        "memory_1",
        "memory_2",
        "memory_3"
    ],
    documents=[
        "사용자는 Python과 AI Agent를 학습하고 있다.",
        "사용자는 PostgreSQL을 Docker로 실행했다.",
        "사용자는 한컴 기업시장 사업을 운영하고 있다."
    ]
)
 

각 항목은 서로 연결되어야 합니다.

memory_1
    ↔
사용자는 Python과 AI Agent를 학습하고 있다.

memory_2
    ↔
사용자는 PostgreSQL을 Docker로 실행했다.
 

즉, ids와 documents의 순서가 중요합니다.


의미 검색

 
results = collection.query(
    query_texts=[
        "나는 파이썬과 인공지능 에이전트를 공부하고 있다."
    ],
    n_results=2
)
 

여기서 검색어는 저장된 문장과 정확히 같지 않습니다.

저장된 문장:

사용자는 Python과 AI Agent를 학습하고 있다.
 

검색 질문:

나는 파이썬과 인공지능 에이전트를 공부하고 있다.
 

그런데도 의미가 비슷하므로 관련 문서를 찾아줍니다.

이것이 PostgreSQL의 일반적인 문자열 검색과 다른 점입니다.


10. ChromaDB에 저장되는 데이터 구조

ChromaDB에는 일반적으로 다음과 같은 형태로 저장됩니다.

 
collection.add(
    ids=["memory_1"],
    documents=[
        "사용자는 Python과 AI Agent를 학습하고 있다."
    ],
    metadatas=[
        {
            "user_id": 1,
            "memory_type": "goal",
            "importance": 8
        }
    ]
)
 

여기서:

ids
    → 기억을 식별하는 ID

documents
    → 실제 검색할 문장

metadatas
    → 사용자 ID, 기억 유형, 중요도 등 부가정보
 

이렇게 하면 나중에 사용자별 검색도 할 수 있습니다.

예를 들어:

 
results = collection.query(
    query_texts=[
        "AI Agent 학습"
    ],
    n_results=5,
    where={
        "user_id": 1
    }
)
 

이것은 다음 의미입니다.

전체 기억 중에서 user_id=1인 사용자의 기억만 대상으로 의미 검색하라.


11. 다만 첫 번째 실습에서 주의할 점

위의 test_chroma.py를 여러 번 실행하면 같은 ID가 이미 존재한다는 오류가 발생할 수 있습니다.

처음 학습할 때는 다음과 같이 처리할 수 있습니다.

방법 1. 컬렉션 삭제 후 다시 생성

 
client.delete_collection(
    name="memories"
)
 

그 후:

 
collection = client.get_or_create_collection(
    name="memories"
)
 

단, 이 방법은 기존 데이터를 모두 삭제합니다.

방법 2. upsert() 사용

반복 실행을 고려하면 다음과 같이 하는 것이 좋습니다.

 
collection.upsert(
    ids=[
        "memory_1",
        "memory_2",
        "memory_3"
    ],
    documents=[
        "사용자는 Python과 AI Agent를 학습하고 있다.",
        "사용자는 PostgreSQL을 Docker로 실행했다.",
        "사용자는 한컴 기업시장 사업을 운영하고 있다."
    ]
)
 

upsert()는 다음과 같은 의미입니다.

ID가 없으면 새로 저장
ID가 있으면 기존 문서 수정
 

실제 Memory 시스템에서는 add()보다 upsert()가 더 편리한 경우가 많습니다.


12. 기존 PostgreSQL과 연결할 때의 올바른 방향

ChromaDB 기본 실습이 끝나면 vector_store.py를 만듭니다.

처음에는 다음 세 함수만 만들면 됩니다.

 
def get_chroma_collection():
    pass


def add_memory_to_vector_store(
    memory_id,
    user_id,
    content,
    memory_type,
    importance
):
    pass


def search_similar_memories(
    user_id,
    query,
    limit=5
):
    pass
 

각 함수의 역할은 다음과 같습니다.

get_chroma_collection()
    → ChromaDB 컬렉션 준비

add_memory_to_vector_store()
    → 기억을 ChromaDB에 저장

search_similar_memories()
    → 질문과 의미가 비슷한 기억 검색
 

13. PostgreSQL과 ChromaDB를 연결하는 핵심 설계

기억 하나가 저장될 때:

PostgreSQL
    memory_id = 15
    user_id = 1
    content = "사용자는 Python을 공부하고 있다."
 

ChromaDB에는 다음과 같이 저장합니다.

id = "memory_15"

document =
"사용자는 Python을 공부하고 있다."

metadata =
{
    "memory_id": 15,
    "user_id": 1,
    "memory_type": "goal",
    "importance": 8
}
 

즉, PostgreSQL의 memory_id와 ChromaDB의 id를 연결합니다.

PostgreSQL memory_id
        ↕
ChromaDB document id
 

이 연결이 매우 중요합니다.

왜냐하면 ChromaDB는 관련 문서를 찾아주는 역할이고, 실제 상세 데이터는 PostgreSQL에서 다시 가져올 수 있기 때문입니다.


14. 향후 실제 검색 흐름

예를 들어 사용자가 다음 질문을 합니다.

내가 AI Agent 공부를 어디까지 했지?
 

ChromaDB가 다음 기억을 찾습니다.

memory_id = 15
memory_id = 22
memory_id = 31
 

그다음 PostgreSQL에서 해당 ID를 조회합니다.

 
SELECT
    memory_id,
    memory_type,
    content,
    importance,
    created_at
FROM memories
WHERE memory_id IN (15, 22, 31);
 

그 결과를 LLM에 전달합니다.

[관련 장기기억]
- 사용자는 Python과 AI Agent를 학습하고 있다.
- 사용자는 PostgreSQL 기반 장기기억을 구현했다.
- 사용자는 Agent Loop와 Tool Calling을 학습했다.
 

이렇게 하면 단순히 최근 기억을 나열하는 것이 아니라, 현재 질문과 관련된 기억만 선택해서 전달할 수 있습니다.


15. 앞으로의 학습 로드맵

제가 권하는 전체 순서는 다음과 같습니다.

1단계 — ChromaDB 기본

설치
    ↓
PersistentClient
    ↓
Collection
    ↓
Document 저장
    ↓
Query 검색
 

2단계 — 메타데이터 필터

user_id별 검색
memory_type별 검색
importance 조건 검색
 

3단계 — PostgreSQL과 연결

PostgreSQL memory_id
        ↕
ChromaDB document id
 

4단계 — 기존 Memory Layer에 연결

extract_memory()
    ↓
PostgreSQL 저장
    ↓
ChromaDB 저장
 

5단계 — Agent 검색 기능 교체

기존:

 
memories = search_memory(
    conn,
    user_id,
    limit=10
)
 

변경 후:

 
memories = search_memory(
    conn,
    user_id,
    query=user_message,
    limit=5
)
 

내부적으로는:

search_memory()
    ↓
ChromaDB 의미 검색
    ↓
memory_id 확인
    ↓
PostgreSQL 상세 조회
    ↓
기억 반환
 

6단계 — 성능과 품질 개선

나중에는 다음을 추가할 수 있습니다.

중복 기억 제거
기억 중요도 반영
최근성 반영
사용자별 필터
기억 유형별 필터
검색 결과 재정렬
임베딩 모델 변경
PostgreSQL pgvector 검토
 

16. ChromaDB를 배운 뒤에도 남는 중요한 한계

ChromaDB를 추가한다고 Agent가 자동으로 똑똑해지는 것은 아닙니다.

다음 문제는 여전히 직접 해결해야 합니다.

① 무엇을 기억할 것인가?

모든 대화를 저장하면 안 됩니다.

오늘 점심으로 김치찌개를 먹었다.
 

이런 정보는 대부분 장기기억으로 저장할 필요가 없습니다.

반면:

사용자는 Python과 AI Agent를 장기적으로 학습하고 있다.
 

이 정보는 저장할 가치가 있습니다.


② 검색된 기억이 정말 관련 있는가?

벡터 검색은 의미가 비슷한 문장을 찾지만, 항상 정답을 찾는 것은 아닙니다.

따라서 나중에는 다음을 고려해야 합니다.

의미 유사도
+
사용자 ID
+
기억 중요도
+
최근성
 

③ 기억이 중복될 수 있다

다음과 같은 기억이 반복 저장될 수 있습니다.

사용자는 Python을 공부하고 있다.
 
사용자는 파이썬 학습을 진행 중이다.
 

따라서 나중에는 중복 검사나 기억 병합이 필요합니다.


결론

지금 ChromaDB를 배우는 것은 적절합니다. 다만 학습 방향은 다음과 같이 잡는 것이 가장 좋습니다.

기존 PostgreSQL Memory Layer
        ↓
ChromaDB 기본 개념 학습
        ↓
문서 저장·검색 실습
        ↓
PostgreSQL 기억을 ChromaDB에 연결
        ↓
의미 기반 기억 검색
        ↓
Agent의 search_memory() 교체
 

특히 지금은 기존 네 개 파일을 바로 크게 수정하지 말고, 먼저 다음 두 파일을 추가하는 방식이 좋습니다.

test_chroma.py
vector_store.py
 

첫 번째 수업은 다음 주제로 시작하면 됩니다.

ChromaDB 설치 → PersistentClient → Collection → 문서 저장 → 의미 검색 → 검색 결과 해설

이 단계를 먼저 완성한 뒤, 제가 test_chroma.py의 각 줄을 현재 학습 방식대로 아주 친절하게 설명하고, 그다음 vector_store.py를 PostgreSQL과 연결하는 방향으로 진행하면 됩니다.

반응형