Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 0 additions & 103 deletions pg_experiment/RESULTS.md

This file was deleted.

108 changes: 108 additions & 0 deletions pg_experiment/docs/AB_BD_EXECUTION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# A vs B, B vs D 실행 계획

`LLM_QUERY_GENERATION.md`에서 설계한 두 비교(§6)를 **실제로 어떻게 돌릴지**만
다루는 문서. 프로덕션 코드/배포/가드레일은 다루지 않는다(그건
`DYNAMIC_QUERY_IMPLEMENTATION.md`) — 여기는 순수하게 오프라인 실험 스크립트를
어떤 순서로, 뭘 재사용해서, 어디에 만들지에 대한 계획이다.

| | 고정 쿼리 | LLM 가변 쿼리 |
|---|---|---|
| **GraphDB** | **A** (기존 쿼리) | **B** (신규) |
| **RDB** | ~~C~~ (안 씀) | **D** (신규, 조건부) |

## 1. 이미 있어서 그대로 재사용하는 것

- **gold label**: `eval/gold_labels.py`의 `PRODUCTION_CONCERN_EFFECT_MAP`
(26개 concern → effect 정답 매핑) + 그래프에서 gold 성분/제품을 뽑는 함수.
- **채점 로직**: `eval/graphrag_ranking_eval.py`의 Precision@K / NDCG@K 계산.
`eval/RESULTS.md`에 쓰인 방법론 그대로 재사용.
- **A(고정 쿼리) 실행**: `pg_experiment/queries.py`의 `CYPHER_*` 상수를 그대로
Neo4j에 실행하면 됨 — 새로 만들 게 없음.
- **DB/데이터**: `pg_experiment`에 이미 떠 있는 Neo4j(기존 컨테이너)와
Postgres(`pg_experiment/docker-compose.yml`, 5433) 그대로 사용.

## 2. 새로 만들어야 하는 것

전부 `pg_experiment/llm_eval/` 아래 새 폴더를 만들어서 넣는다(오프라인 실험용
스크립트라 프로덕션 `app/` 디렉터리와 분리).

```
pg_experiment/llm_eval/
├── questions.py # 자연어 질문 세트 (concern별, gold_labels.py와 매칭)
├── prompts/
│ ├── cypher_generation.txt
│ └── sql_generation.txt # B vs D 단계에서만 필요
├── generate.py # LLM 호출 -> Cypher/SQL 생성 (+ EXPLAIN 검증만)
├── run_ab.py # A vs B 실행 + 채점
└── run_bd.py # B vs D 실행 + 채점 (조건부)
```

### 2-1. `questions.py` — 질문 세트

`gold_labels.py`의 concern 코드 26개를 키로 써서, 각 concern당 자연어 질문
1~2개를 매핑한다.

```python
QUESTIONS = {
"ACNE": ["여드름 때문에 고민이에요, 어떤 성분이 좋을까요?"],
"DRY_SKIN": ["피부가 너무 건조해요", ...],
... # 26개 전부
}
```

### 2-2. `prompts/cypher_generation.txt`

- 그래프 스키마 설명 (`README.md`의 구조 그대로)
- 출력 형식: `{"cypher": "...", "params": {...}}` JSON만
- few-shot 예시 2~3개
- **읽기 전용 안내만** (`MATCH만 써라`) — 실제 DB 권한 제한은 안 함, 실험용
스크립트가 직접 실행 전 `EXPLAIN`으로 한 번 더 거르기 때문에 이 단계에서는
텍스트 안내만으로 충분함.

### 2-3. `generate.py`

```python
async def generate_cypher(question: str) -> dict:
... # llm_client.py의 call_llm()과 같은 패턴, get_async_llm_client() 재사용
return {"cypher": ..., "params": ...}

def validate(cypher: str, driver) -> bool:
with driver.session() as s:
s.run(f"EXPLAIN {cypher}") # 문법만 확인, 결과는 안 씀
return True # 실패하면 예외
```

재시도는 최소 구현으로 — 문법 오류 시 1회만 재생성 (프로덕션처럼 여러 겹
가드레일 필요 없음, 실험 스크립트가 로컬에서 도는 것뿐).

### 2-4. `run_ab.py`

1. `questions.py`의 26개 concern 질문을 순회
2. 각 질문마다:
- A: `pg_experiment/queries.py`의 `CYPHER_INGREDIENTS_BY_EFFECTS` 등을
해당 concern의 gold effect로 실행
- B: `generate.py`로 Cypher 생성 → 검증 → 실행
3. A 결과, B 결과 각각 `graphrag_ranking_eval.py`의 Precision@K/NDCG@K로 채점
4. concern별 표 + 평균 요약 출력 (`RESULTS.md`와 비슷한 포맷)

### 2-5. `run_bd.py` (조건부)

`run_ab.py` 결과에서 B가 A보다 품질이 뚜렷이 높을 때만 착수.

1. `prompts/sql_generation.txt` 작성 (스키마는 `pg_experiment/schema.sql` DDL)
2. `generate.py`에 `generate_sql()` 추가, Postgres에 `EXPLAIN`으로 검증
3. B(이미 있음)와 D(신규 생성) 결과를 같은 gold label로 채점해 비교

## 3. 실행 순서 (요약)

1. `questions.py` 작성
2. `prompts/cypher_generation.txt` + `generate.py`(Cypher 부분) 작성
3. `run_ab.py` 실행 → A vs B 결과 확인
4. **여기서 판단**: B가 A보다 품질이 안 오르면 종료, 보고서만 작성
5. B가 A보다 품질이 오르면 → `prompts/sql_generation.txt` + `generate.py`(SQL
부분) 추가 작성 → `run_bd.py` 실행 → B vs D 결과 확인
6. 두 결과 종합해서 `pg_experiment/docs/RESULTS_llm_query.md`(가칭)로 정리

## 4. 진행 여부

계획 단계. 실제 스크립트 작성은 착수 확인 후 진행.
47 changes: 47 additions & 0 deletions pg_experiment/docs/DISCUSSION_quality_vs_hop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 논의: 추천 품질 비교 실험 + "자유도가 늘면 그래프DB 이유가 명확해지는가"

`LLM_QUERY_GENERATION.md` §5의 근거가 된 대화 기록. 실험 결과가 아니라 논의/가설
단계라는 걸 분명히 하려고 별도 문서로 남긴다.

---

**Q1. 쿼리 실행 속도 측면에서 지금 RDB랑 GraphDB 비교하는 실험을 한 거잖아. 그럼
추천 품질 측면에서 지금 고정 쿼리랑 LLM이 가변적으로 쿼리를 만들었을 때의 품질을
비교해보는 실험을 해볼 수도 있는 건가?**

A. 네, 완전히 다른 축이라 해볼 가치 있다. 지금 고정 템플릿은 엔지니어가 미리
정해둔 단일 순회 경로(Product→Ingredient→Effect, 항상 이 모양)인 반면, LLM에게
스키마를 자유롭게 주면 질문마다 다른 경로를 스스로 짤 수 있다 — 어떤 질문엔
브랜드 필터를 추가하거나, 어떤 질문엔 성분 간 관계를 더 타고 들어갈 수도 있다.
이게 고정 템플릿보다 나을 수도, LLM이 스키마를 잘못 이해해서 더 나쁠 수도 있는데
— 이건 실행 성공 여부(문법)가 아니라 **결과의 관련성**을 재는 문제라,
`eval/RESULTS.md`에 이미 있는 Precision/Recall/NDCG 평가 방식을 LLM 생성 쿼리
결과에도 그대로 적용해서 고정 쿼리와 비교하면 된다.

**Q2. 근데 가변으로 바꿨을 때 hop 수가 늘어날 수 있으니까 그래프DB를 사용하는
이유가 더 명확해지려나?**

A. 이 연결고리가 꽤 날카로운 지적이다. 맞다 — 지금 고정 템플릿이 hop을 1~2개로
낮게 묶어둔 건 **엔지니어가 미리 성능을 고려해서 억지로 제한한 것**이지, 원래 이
도메인 질문들이 다 1~2-hop이라서가 아니다. LLM이 자유롭게 쿼리를 짜면 질문의
의미에 맞춰 자연스럽게 hop이 늘어나거나(3~4-hop), 심지어
`LLM_QUERY_GENERATION.md`에서 짚었던 가변 길이 탐색(`*1..N`)까지 필요해질
가능성이 있다. 그리고 이번 벤치마크(`RESULTS.md`)가 보여준 게 정확히 "hop이
늘수록 그래프DB 우위가 커진다"는 거였으니, **자유도를 주는 방향으로 갈수록
그래프DB를 쓸 이유가 강해지는 쪽으로 결과가 수렴할 가능성이 높다.**

다만 이건 아직 가설이다 — "LLM이 실제로 더 긴/복잡한 경로를 짜는지"와 "그게
실제로 품질을 높이는지"는 둘 다 직접 재보기 전엔 모른다. 그냥 "자유도 주면
그래프DB가 유리해질 것"이라고 가정하고 결론 내리면 안 되고, 실제 실험(질문 세트
→ 생성 → gold reference 비교)에서 hop 수 분포까지 같이 기록해야 이 가설을
검증할 수 있다.

---

## 결론 (이 논의에서 확정된 것)

- 품질 비교 실험은 별도로 할 가치가 있고, 기존 `eval/RESULTS.md` 평가 방법론을
재사용하면 됨 (새로 만들 필요 없음).
- "자유도 증가 → hop 증가 → 그래프DB 유리"는 **논리적으로 그럴듯한 가설**이지,
아직 검증된 사실이 아님. `LLM_QUERY_GENERATION.md` §5에 이 가설을 검증하기 위한
구체적 측정 항목(hop 분포 기록 + 품질 지표 동시 관찰)을 추가해둠.
Loading