hee_!J
feat: Hybrid RAG(BM25+FAISS+RRF) + Cross-encoder Rerank + RAGAS eval
744e87d
Raw
History Blame Contribute Delete
5.08 kB
"""D2: ๊ฒ€์ƒ‰ ๋ฐฑ์—”๋“œ ๋น„๊ต - keyword vs FAISS vs Hybrid vs Hybrid+Rerank
๊ฐ™์€ ์ฟผ๋ฆฌ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด 4๊ฐ€์ง€ retrieval ๋ฐฉ์‹ ๋น„๊ต
- keyword: ๋‹จ์ˆœ ๋‹จ์–ด ๋นˆ๋„ ๋งค์นญ
- FAISS: sentence-transformer ์ž„๋ฒ ๋”ฉ + ์ฝ”์‚ฌ์ธ ์œ ์‚ฌ๋„
- Hybrid: BM25 + FAISS + Reciprocal Rank Fusion (production ํ‘œ์ค€)
- Hybrid+Rerank: hybrid top-K ํ›„๋ณด๋ฅผ cross-encoder๋กœ ์žฌ์ •๋ ฌ (production ์ •๋ฐ€)
์‹คํ–‰: python -m experiments.retrieval_compare.benchmark
๊ฒฐ๊ณผ: results.md
"""
import statistics
import time
from pathlib import Path
from agents.rag.faiss_store import faiss_search
from agents.rag.hybrid_store import hybrid_search
from agents.rag.rerank import rerank
from agents.rag.store import keyword_search
OUT_DIR = Path(__file__).parent
QUERIES = [
("CD ์‚ฐํฌ ์ง์ ‘", "Photo Step CD-X ์‚ฐํฌ ์›์ธ ๋ Œ์ฆˆ ๋…ธ๊ด‘"),
("CMP ์ง์ ‘", "CMP ์Šฌ๋Ÿฌ๋ฆฌ ์œ ๋Ÿ‰ ์ด์ƒ SLURRY_FLOW"),
("Etch ์ง์ ‘", "Etch ํŠธ๋ Œ์น˜ ๊นŠ์ด ๋ถ€์กฑ ์‹๊ฐ ๊ฐ€์Šค"),
("์˜๋ฏธ ์šฐํšŒ 1", "๋…ธ๊ด‘ ์žฅ๋น„ ํ‘œ๋ฉด ์˜ค์—ผ ์ฒญ์†Œ"),
("์˜๋ฏธ ์šฐํšŒ 2", "ํ›„๊ณต์ • ์ˆ˜์œจ ์†์‹ค ์ •๋Ÿ‰ ์˜ํ–ฅ"),
("์˜๋ฏธ ์šฐํšŒ 3", "์ •๋น„ ์ฃผ๊ธฐ ํ‘œ์ค€ ๊ฐ€์ด๋“œ"),
]
def hybrid_rerank_search(query, top_k=3):
candidates = hybrid_search(query, top_k=10)
return rerank(query, candidates, top_k)
BACKENDS = [
("keyword", keyword_search),
("faiss", faiss_search),
("hybrid", hybrid_search),
("hybrid+rerank", hybrid_rerank_search),
]
def main():
# ์›Œ๋ฐ์—…
print("=== ์›Œ๋ฐ์—… (๋ชจ๋ธ ๋กœ๋“œ) ===")
for name, fn in BACKENDS:
t0 = time.time()
fn("warmup query", top_k=1)
print(f" {name}: {time.time()-t0:.2f}s")
print()
rows = []
for label, q in QUERIES:
record = {"label": label, "query": q, "results": {}, "latency_ms": {}}
print(f"[{label}] '{q}'")
for name, fn in BACKENDS:
t0 = time.time()
res = fn(q, top_k=3)
ms = (time.time() - t0) * 1000
record["results"][name] = res
record["latency_ms"][name] = ms
print(f" {name:14s} ({ms:7.2f}ms): {res}")
rows.append(record)
print()
write_results(rows)
print(f"--- ์ €์žฅ: {OUT_DIR / 'results.md'} ---")
def write_results(rows):
avg_lat = {
name: statistics.mean(r["latency_ms"][name] for r in rows)
for name, _ in BACKENDS
}
lines = [
"# D2. Retrieval ๋ฐฑ์—”๋“œ ๋น„๊ต (production-grade)",
"",
"์ฟผ๋ฆฌ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด 4๊ฐ€์ง€ ๋ฐฑ์—”๋“œ์˜ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผยทlatency๋ฅผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.",
"ํ˜„์žฌ knowledge ์ฝ”ํผ์Šค ์•ฝ 10๊ฐœ ํ•œ๊ตญ์–ด ๋„๋ฉ”์ธ ๋ฌธ์„œ.",
"",
"## ์‹คํ—˜ ์„ค์ •",
"",
"- ์ฝ”ํผ์Šค: `agents/rag/knowledge/*.md`",
"- ๋ฐฑ์—”๋“œ 4์ข…:",
" - **keyword**: ๋‹จ์–ด ๋นˆ๋„ ๋งค์นญ (baseline)",
" - **FAISS**: sentence-transformer ์ž„๋ฒ ๋”ฉ + ์ฝ”์‚ฌ์ธ (dense vector)",
" - **Hybrid**: BM25 + FAISS + RRF (production ํ‘œ์ค€)",
" - **Hybrid+Rerank**: hybrid top-10 ํ›„๋ณด๋ฅผ BAAI/bge-reranker-base๋กœ ์žฌ์ •๋ ฌ (production ์ •๋ฐ€)",
f"- ์ฟผ๋ฆฌ {len(rows)}๊ฑด (์˜๋ฏธ ์šฐํšŒ 3๊ฑด ํฌํ•จ)",
"",
"## ์ฟผ๋ฆฌ๋ณ„ ๊ฒฐ๊ณผ",
"",
]
for r in rows:
lines.append(f"### {r['label']} - `{r['query']}`")
lines.append("")
lines.append("| ๋ฐฑ์—”๋“œ | ๊ฒฐ๊ณผ | latency |")
lines.append("|---|---|---|")
for name, _ in BACKENDS:
res = ", ".join(r["results"][name]) or "(empty)"
lines.append(f"| {name} | {res} | {r['latency_ms'][name]:.2f}ms |")
lines.append("")
lines += [
"## ์ง‘๊ณ„ - ํ‰๊ท  Latency",
"",
"| ๋ฐฑ์—”๋“œ | ํ‰๊ท  latency |",
"|---|---|",
]
for name, _ in BACKENDS:
lines.append(f"| {name} | {avg_lat[name]:.2f} ms |")
lines += [
"",
"## ํŠธ๋ ˆ์ด๋“œ์˜คํ”„",
"",
"| ์ธก๋ฉด | keyword | FAISS | Hybrid | Hybrid+Rerank |",
"|---|---|---|---|---|",
"| ๋„๋ฉ”์ธ ์šฉ์–ด ์ •ํ™• ๋งค์นญ | โœ… | โŒ | โœ… | โœ… |",
"| ์˜๋ฏธยท๋™์˜์–ด ๋งค์นญ | โŒ | โœ… | โœ… | โœ… |",
"| ์ •๋ฐ€ํ•œ ๊ด€๋ จ์„ฑ ํ‰๊ฐ€ | โŒ | โŒ | โŒ | โœ… |",
"| Latency | ๋งค์šฐ ๋น ๋ฆ„ | ๋ณดํ†ต | ๋ณดํ†ต | ๋А๋ฆผ (CrossEncoder) |",
"| ๋ชจ๋ธ ์˜์กด์„ฑ | ์—†์Œ | ST(~120MB) | ST(~120MB) | ST + Reranker(~280MB) |",
"| ์ฝ”ํผ์Šค ํ™•์žฅ(100+) ๊ฒฌ๊ณ ์„ฑ | ์•ฝํ•จ | ๊ฐ•ํ•จ | **๋งค์šฐ ๊ฐ•ํ•จ** | **๋งค์šฐ ๊ฐ•ํ•จ** |",
"",
"## ์ฑ„ํƒ",
"",
"**๊ธฐ๋ณธ backend = `hybrid_rerank`**. production ํ‘œ์ค€ ํŒจํ„ด. ํ™˜๊ฒฝ๋ณ€์ˆ˜ `RAG_BACKEND`๋กœ 4๊ฐ€์ง€ ๋ชจ๋‘ ์ „ํ™˜ ๊ฐ€๋Šฅ.",
"",
"- ์ฝ”ํผ์Šค 100๋ฌธ์„œ ์ด์ƒ: hybrid_rerank์˜ ์ •๋ฐ€๋„ ์šฐ์œ„ ๊ฒฐ์ •์ ",
"- MVPยท์‹œ์—ฐ ํ™˜๊ฒฝ: hybrid๋„ ์ถฉ๋ถ„ (rerank ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ์‹œ๊ฐ„ ์ ˆ์•ฝ)",
"- ๋‹จ์ˆœ ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰๋งŒ ํ•„์š”: keyword (์˜์กด์„ฑ ์—†์Œ)",
"",
]
(OUT_DIR / "results.md").write_text("\n".join(lines), encoding="utf-8")
if __name__ == "__main__":
main()