ChromaDB는 내부적으로 선택 가능한 저장 방식(backends) 중 하나로 DuckDB + Parquet를 사용한다.
: Chroma의 GitHub 레포지토리와 공식 문서를 보면, "Chroma는 기본적으로 DuckDB를 내장 엔진으로 사용하고, 데이터를 Parquet 형식으로 저장한다." 라고 되어 있다.
* 요약: ChromaDB는 내부 저장소로 DuckDB + Parquet 사용 가능
구성 요소 역할
- DuckDB : SQL 실행과 데이터 저장 (경량 DB 엔진)
- Parquet : 벡터 임베딩, 메타데이터 등을 저장하는 파일 포맷
- Chroma : 벡터 DB API (임베딩 저장, 검색, 필터링 등)
구체적으로 ChromaDB는 다음 두 가지 저장 백엔드를 지원한다.
1) DuckDB + Parquet (기본값, 로컬 저장)
2) ClickHouse (고급 분산 환경에서 사용)
로컬에서 Chroma.from_documents() 등을 사용하면 기본적으로 DuckDB + Parquet 조합으로 저장된다.
* 저장 구조 예시 (./chroma/ 디렉터리 내부)
chroma/
├── chroma.sqlite ← DuckDB 파일
├── chroma-collections.parquet ← 벡터 컬렉션 정보
├── chroma-embeddings.parquet ← 임베딩 벡터 값들
├── chroma-documents.parquet ← 원본 문서 및 메타데이터
* 확인 방법 ---
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
loader = TextLoader("sample.txt")
documents = loader.load()
splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=10)
docs = splitter.split_documents(documents)
# 임베딩 및 Chroma 생성
db = Chroma.from_documents(docs, OpenAIEmbeddings(), persist_directory="./chroma")
db.persist()
위 코드 실행 후 ./chroma 폴더가 생기며 DuckDB + Parquet 구조로 저장된다.
💖 결론적으로
Chroma가 DuckDB를 사용하는가? 넵! 기본 저장 엔진.
Parquet는 어디에 사용되는가? 벡터, 문서, 메타데이터를 컬럼 지향으로 저장.
DuckDB + Parquet의 장점은? 빠른 검색, SQL 지원, Pandas 등과 호환
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DuckDB란? SQLite처럼 가볍지만 분석(OLAP)에 강한 SQL DB이며, Pandas, Parquet, CSV 등과 잘 통합되며 설치도 간편하다.
LangChain에서 DuckDB를 Tool로 연결해서 LLM이 SQL로 데이터를 조회하게 만들 수 있다.
Parquet + DuckDB + LangChain을 연동하고, 요약(예: 전체 개수)이나 그룹별 평균(예: 도시별 평균 나이)과 같은 SQL도 처리할 수 있도록 할 수 있다.
* 실습코드 : 요약 및 그룹별 통계까지 포함
# 1. 필요한 라이브러리
import pandas as pd
import duckdb
from langchain.tools import Tool
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
# 2. 샘플 데이터 -> Parquet로 저장
df = pd.DataFrame({
'name': ['공기밥', '주먹밥', '김밥', '김치국', '박치기'],
'age': [25, 30, 22, 32, 25],
'city': ['서울', '부산', '인천', '서울', '부산']
})
df.to_parquet("people.parquet")
# 3. DuckDB에 연결 및 Parquet(고성능 컬럼 지향 저장 포맷) 파일을 VIEW로 등록
con = duckdb.connect()
con.execute("CREATE OR REPLACE VIEW people AS SELECT * FROM 'people.parquet'")
# 4. DuckDB Tool 정의 (요약, 그룹화 포함)
def query_duckdb(sql: str) -> str:
try:
df = con.execute(sql).fetchdf()
return df.to_string(index=False)
except Exception as e:
return f"[쿼리 에러] {e}"
duckdb_tool = Tool(
name="DuckDBParquetQuery",
func=query_duckdb,
description=(
"Parquet에서 로딩된 people 테이블에 SQL 쿼리를 실행합니다. "
"예: 'SELECT name FROM people WHERE age > 25', "
"'SELECT COUNT(*) FROM people', "
"'SELECT city, AVG(age) FROM people GROUP BY city'"
)
)
# 5. LangChain Agent 구성
llm = ChatOpenAI(temperature=0)
agent = initialize_agent(
tools=[duckdb_tool],
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # 프롬프트 형식은 "생각 → 행동 → 관찰 → 생각 → ..."의 흐름을 따르며, 사전 훈련된 명령어 없이도 주어진 설명만으로 행동을 결정
verbose=True
)
# 6. 자연어로 실행 예시
examples = [
"30세 이상인 사람의 이름은 누구야?",
"전체 사람 수는 몇 명이야?",
"각 도시별 평균 나이를 알려줘",
"서울에 사는 사람들만 보여줘"
]
for question in examples:
print(f"\n질문: {question}")
answer = agent.run(question)
print(f"답변:\n{answer}")
--- 예시 출력 예상 -------------
질문: 전체 몇 명이야?
답변: 5
질문: 각 도시별 평균 나이를 알려줘
답변:
city avg_age
Busan 27.5
Incheon 22.0
Seoul 27.5
* 이 코드로 가능한 질의 예
자연어 질의 변환되는 SQL
전체 몇 명이야? SELECT COUNT(*) FROM people
도시별 평균 나이 알려줘 SELECT city, AVG(age) FROM people GROUP BY city
25세 이상만 보여줘 SELECT * FROM people WHERE age >= 25
서울 사람만 SELECT * FROM people WHERE city = '서울'
* 이 실습을 통해
- Parquet 파일로 데이터를 저장
- DuckDB에서 SQL VIEW로 불러오기
- LangChain Tool로 SQL 쿼리 수행
- 자연어로 요약, 필터, 그룹화 쿼리 가능
을 알게 됨
참고 : LangChain AgentType 종류 비교표
| AgentType | 이름 | 특징 요약 | 입력 형식 | 대화 스타일 기타 |
| ZERO_SHOT_REACT_DESCRIPTION | 가장 기본 ReAct 기반 에이전트. 도구 설명만 보고 LLM이 도구 선택 및 호출. | 일반 텍스트 | 빠르게 프로토타입 만들 때. 설명 기반 제어 | 추론+행동 반복 (ReAct) |
| CHAT_ZERO_SHOT_REACT_DESCRIPTION | Chat 기반 LLM (예: GPT-4)을 위한 ReAct | Chat 형식 (message 기반) | 멀티턴 상호작용 필요 시 | OpenAI ChatModel 등에 최적화 |
| STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION | Chat 기반 + 구조화된 Tool 사용 (도구와 인자 명확 분리) | Chat 형식 | 툴이 많거나 파라미터 구조가 명확할 때 | JSON 기반 structured tool 호출 |
| REACT_DOCSTORE | 검색용으로 특화된 ReAct | 일반 텍스트 | 문서 검색 후 응답 생성 | 내부적으로 docstore와 연결 |
| OPENAI_FUNCTIONS | OpenAI Function 호출 방식 지원 | Chat 형식 | OpenAI function-calling 모델 사용 시 | tool이 자동 JSON 매핑됨 |
| OPENAI_MULTI_FUNCTIONS | OpenAI Function 여러 개 동시 실행 가능 | Chat 형식 | 병렬 실행이 필요한 상황 | 여러 function-call 최적화 |
| TOOL_CALLING | OpenAI Tool Calling 전용 (function 대체 개념) | Chat 형식 | 최신 OpenAI API 전용 | tool_call_id 등 사용 |
* 상황에 따른 추천 에이전트 타입
| 상황 | 추천 AgentType |
| 빠르게 단일 LLM + 단일 도구 테스트 | ZERO_SHOT_REACT_DESCRIPTION |
| ChatGPT 계열 모델로 대화형 상호작용 | CHAT_ZERO_SHOT_REACT_DESCRIPTION |
| 각 도구에 입력 인자가 많고 구조가 명확할 때 | STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION |
| OpenAI GPT 4-turbo + function-calling API 사용 시 | OPENAI_FUNCTIONS 또는 TOOL_CALLING |
| 병렬 도구 실행을 원할 때 | OPENAI_MULTI_FUNCTIONS |