Skip to content

Commit 068b70d

Browse files
authored
Merge pull request #211 from CausalInferenceLab/feat/vector-retriever
Feat/vector retriever
2 parents 5f4a009 + dd723b8 commit 068b70d

29 files changed

Lines changed: 4014 additions & 18 deletions

docs/tutorials/v2-complete-tutorial.md

Lines changed: 993 additions & 0 deletions
Large diffs are not rendered by default.

docs/tutorials/v2-usage-guide.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# lang2sql v2 Usage Guide
2+
3+
이 문서는 `src/lang2sql` 기준의 새로운 v2 API만 다룹니다.
4+
기존 `engine/`, `interface/`, `utils/llm/` 경로는 범위에서 제외합니다.
5+
6+
자세한 단계별 실습은 [v2-complete-tutorial.md](./v2-complete-tutorial.md) 를 참고하세요.
7+
8+
## 0) Why lang2sql
9+
10+
- **운영 친화적인 기본 경로**: `Retriever -> Generator -> Executor`가 단순하고 디버깅 포인트가 명확합니다.
11+
- **명시적 인덱싱 파이프라인**: `chunker.split(docs)``VectorRetriever.from_chunks(chunks)` 패턴으로 split/embed/store 각 단계가 코드에 보입니다.
12+
- **프레임워크 락인 최소화**: 코어가 Protocol(`EmbeddingPort`, `VectorStorePort`, `DocumentChunkerPort`) 기반이라 구현체를 교체하기 쉽습니다.
13+
- **관측성 내장**: Hook(`TraceHook`, `MemoryHook`)으로 컴포넌트 단위 실행 이벤트를 바로 수집할 수 있습니다.
14+
15+
## 0-1) 튜토리얼 데이터 자동 준비
16+
17+
```bash
18+
python scripts/setup_sample_db.py
19+
python scripts/setup_sample_docs.py
20+
```
21+
22+
문서 생성 후 `docs/business` 아래 파일을 로더 예제에서 그대로 사용합니다.
23+
24+
## 1) v2에서 실제로 지원되는 기능
25+
26+
### Flows
27+
- `BaselineNL2SQL`: BM25 `KeywordRetriever` 기반 기본 파이프라인
28+
- `HybridNL2SQL`: BM25 + Vector `HybridRetriever` 기반 파이프라인
29+
30+
### Retrievers
31+
- `KeywordRetriever`
32+
- `VectorRetriever`
33+
- `HybridRetriever`
34+
35+
### Vector / Embedding (v2 내장)
36+
- Embedding: `OpenAIEmbedding` (내장 1개)
37+
- Vector store: `InMemoryVectorStore` (내장 1개)
38+
39+
### Chunking / Loading
40+
- Chunkers: `CatalogChunker`, `RecursiveCharacterChunker`, `SemanticChunker`
41+
- 모두 `.split(list)` 메서드 제공 — LangChain 스타일 batch 입력/출력
42+
- Loaders: `MarkdownLoader`, `PlainTextLoader`, `DirectoryLoader`
43+
- `PDFLoader` (optional, `pip install pymupdf`)
44+
45+
### Extensibility (Protocol)
46+
- `EmbeddingPort`, `VectorStorePort`, `DocumentChunkerPort`, `DocumentLoaderPort`
47+
- 즉, 내장 구현 외에도 사용자 어댑터를 연결할 수 있습니다.
48+
49+
## 2) 빠른 선택 가이드
50+
51+
### 가장 쉬운 시작
52+
- 목적: 설치 후 바로 NL2SQL 확인
53+
- 선택: `BaselineNL2SQL`
54+
- 특징: 벡터 인덱싱 없이 즉시 사용
55+
56+
### 검색 품질을 빠르게 올리고 싶을 때
57+
- 목적: 키워드 매칭 한계를 보완
58+
- 선택: `HybridNL2SQL` + `OpenAIEmbedding`
59+
- 특징: BM25 + Vector RRF 결합으로 안정적인 검색 품질
60+
61+
### 고급 제어가 필요할 때
62+
- 목적: 청킹/임베딩/인덱싱/검색 파이프라인 세밀 제어
63+
- 선택: `chunker.split()` + `VectorRetriever.from_chunks()` + 수동 컴포넌트 조합
64+
- 특징: 증분 인덱싱, 커스텀 Chunker/VectorStore/Embedding 연동 가능
65+
66+
## 3) 최소 예제
67+
68+
### A. BaselineNL2SQL (키워드 기반)
69+
```python
70+
from lang2sql import BaselineNL2SQL
71+
from lang2sql.integrations.db import SQLAlchemyDB
72+
from lang2sql.integrations.llm import OpenAILLM
73+
74+
catalog = [
75+
{
76+
"name": "orders",
77+
"description": "order table",
78+
"columns": {"order_id": "pk", "amount": "order amount"},
79+
}
80+
]
81+
82+
pipeline = BaselineNL2SQL(
83+
catalog=catalog,
84+
llm=OpenAILLM(model="gpt-4o-mini"),
85+
db=SQLAlchemyDB("sqlite:///sample.db"),
86+
db_dialect="sqlite",
87+
)
88+
89+
rows = pipeline.run("지난달 주문 건수")
90+
print(rows)
91+
```
92+
93+
### B. HybridNL2SQL (키워드 + 벡터)
94+
```python
95+
from lang2sql import HybridNL2SQL
96+
from lang2sql.integrations.db import SQLAlchemyDB
97+
from lang2sql.integrations.embedding import OpenAIEmbedding
98+
from lang2sql.integrations.llm import OpenAILLM
99+
100+
catalog = [
101+
{
102+
"name": "orders",
103+
"description": "order table",
104+
"columns": {"order_id": "pk", "amount": "order amount"},
105+
}
106+
]
107+
108+
docs = [
109+
{
110+
"id": "biz_rules",
111+
"title": "매출 정의",
112+
"content": "매출은 반품 제외 순매출이다.",
113+
"source": "docs/biz_rules.md",
114+
}
115+
]
116+
117+
pipeline = HybridNL2SQL(
118+
catalog=catalog,
119+
llm=OpenAILLM(model="gpt-4o-mini"),
120+
db=SQLAlchemyDB("sqlite:///sample.db"),
121+
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
122+
documents=docs,
123+
db_dialect="sqlite",
124+
top_n=5,
125+
)
126+
127+
rows = pipeline.run("지난달 순매출")
128+
print(rows)
129+
```
130+
131+
### C. 명시적 파이프라인: split → from_chunks (LangChain 스타일)
132+
```python
133+
from lang2sql import (
134+
CatalogChunker,
135+
DirectoryLoader,
136+
RecursiveCharacterChunker,
137+
VectorRetriever,
138+
)
139+
from lang2sql.integrations.embedding import OpenAIEmbedding
140+
141+
catalog = [
142+
{
143+
"name": "orders",
144+
"description": "order table",
145+
"columns": {"order_id": "pk", "amount": "order amount"},
146+
}
147+
]
148+
149+
# 1) 문서 로딩
150+
docs = DirectoryLoader("docs/business").load()
151+
152+
# 2) 각 소스를 명시적으로 split
153+
catalog_chunks = CatalogChunker().split(catalog)
154+
doc_chunks = RecursiveCharacterChunker(chunk_size=800, chunk_overlap=80).split(docs)
155+
156+
# 3) chunks를 합쳐서 retriever 생성 (embed + store 자동)
157+
retriever = VectorRetriever.from_chunks(
158+
catalog_chunks + doc_chunks,
159+
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
160+
top_n=5,
161+
)
162+
163+
result = retriever.run("순매출 계산 기준")
164+
print("schemas:", [s["name"] for s in result.schemas])
165+
print("context:", result.context[:2])
166+
```
167+
168+
명시적 플로우의 장점:
169+
170+
1. split 단계가 코드에 보임 — `chunker.split(docs)`가 명시적
171+
2. catalog chunks + doc chunks를 Python list로 자유롭게 조합 가능
172+
3. `registry = {}` 같은 내부 상태를 사용자가 직접 관리할 필요 없음
173+
174+
증분 추가 시에는 chunks를 미리 split한 뒤 전달합니다:
175+
176+
```python
177+
new_docs = DirectoryLoader("docs/new").load()
178+
retriever.add(RecursiveCharacterChunker().split(new_docs))
179+
```
180+
181+
### D. DirectoryLoader → HybridNL2SQL 직결
182+
183+
문서를 로드한 뒤 바로 HybridNL2SQL에 전달하는 가장 간결한 패턴입니다.
184+
185+
```python
186+
from lang2sql import DirectoryLoader, HybridNL2SQL
187+
from lang2sql.integrations.db import SQLAlchemyDB
188+
from lang2sql.integrations.embedding import OpenAIEmbedding
189+
from lang2sql.integrations.llm import OpenAILLM
190+
191+
docs = DirectoryLoader("docs/business").load()
192+
193+
pipeline = HybridNL2SQL(
194+
catalog=catalog,
195+
llm=OpenAILLM(model="gpt-4o-mini"),
196+
db=SQLAlchemyDB("sqlite:///sample.db"),
197+
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
198+
documents=docs,
199+
db_dialect="sqlite",
200+
)
201+
202+
rows = pipeline.run("지난달 순매출")
203+
print(rows)
204+
```
205+
206+
### E. PDFLoader — PDF 파일 인덱싱
207+
208+
PDF 파일은 `PDFLoader`로 로드합니다 (`pip install pymupdf` 필요).
209+
210+
```python
211+
from lang2sql import DirectoryLoader, MarkdownLoader, VectorRetriever
212+
from lang2sql.integrations.embedding import OpenAIEmbedding
213+
from lang2sql.integrations.loaders import PDFLoader
214+
215+
# PDFLoader를 DirectoryLoader에 등록
216+
docs = DirectoryLoader(
217+
"docs/",
218+
loaders={
219+
".md": MarkdownLoader(),
220+
".pdf": PDFLoader(),
221+
},
222+
).load()
223+
224+
# 이후 from_chunks 패턴으로 인덱싱
225+
from lang2sql import CatalogChunker, RecursiveCharacterChunker
226+
227+
chunks = (
228+
CatalogChunker().split(catalog) +
229+
RecursiveCharacterChunker().split(docs)
230+
)
231+
retriever = VectorRetriever.from_chunks(
232+
chunks,
233+
embedding=OpenAIEmbedding(model="text-embedding-3-small"),
234+
)
235+
```
236+
237+
PDF는 페이지 단위로 `TextDocument`를 생성합니다:
238+
- `id`: `"{filename}__p{page_number}"`
239+
- `title`: `"{filename} page {page_number}"`
240+
241+
## 4) 중요한 현재 제약
242+
243+
- v2 내장 VectorStore는 현재 `InMemoryVectorStore`만 공식 제공됩니다.
244+
- `BaselineNL2SQL`은 현재 `retriever` 주입 파라미터를 받지 않습니다.
245+
- 벡터 기반 파이프라인은 `HybridNL2SQL` 또는 수동 조합을 사용하세요.
246+
- `VectorRetriever` 결과의 `context`는 현재 `list[str]`입니다.
247+
- 문서 출처 구조화가 필요하면 `metadata`를 별도 조회하거나 커스텀 래퍼를 두세요.
248+
- `retriever.add()`**`list[IndexedChunk]`만 받습니다**`TextDocument` 직접 전달 불가.
249+
- 추가 전 반드시 `chunker.split(docs)`로 split한 결과를 전달하세요:
250+
```python
251+
# ❌ 동작 안 함
252+
retriever.add(docs)
253+
254+
# ✅ 올바른 방법
255+
retriever.add(RecursiveCharacterChunker().split(docs))
256+
```
257+
258+
## 5) 추천 실습 순서
259+
260+
1. [v2-complete-tutorial.md](./v2-complete-tutorial.md) 1~4단계로 로컬 스모크 테스트
261+
2. 동일 문서 5~8단계로 실제 DB/LLM 연결
262+
3. 동일 문서 9~13단계로 벡터 인덱싱/문서 파싱/청킹 튜닝
263+
4. 동일 문서 14~18단계로 고급 조합과 커스텀 어댑터 테스트

0 commit comments

Comments
 (0)