Skip to content

[Feat] W4 Layer A: Text2Cypher 모듈 + 평가 셋 8/8 PASS (#18)#48

Merged
TaskerJang merged 4 commits into
devfrom
feat/18-text2cypher
May 16, 2026
Merged

[Feat] W4 Layer A: Text2Cypher 모듈 + 평가 셋 8/8 PASS (#18)#48
TaskerJang merged 4 commits into
devfrom
feat/18-text2cypher

Conversation

@TaskerJang

Copy link
Copy Markdown
Owner

🎯 Closes #18

W4 첫 retrieval 진입점 — 자연어 질의 → Cypher → 결과 → 자연어 답변.

5/16 박제된 평가 셋 (Q1Q5 + F1F3) 정성 검증 8/8 PASS 🎉🎉🎉🎉

변경 사항

신규 파일

  • retrieval/__init__.py — 공개 인터페이스 (text2cypher, Text2CypherResult, Text2CypherError)
  • retrieval/text2cypher.py (320 lines) — 2단계 LLM 흐름 + 안전장치
  • retrieval/prompts/text2cypher_system_v1.md — 스키마 + few-shot 5개 + fallback 1개
  • retrieval/prompts/text2cypher_answer_v1.md — 답변 가이드 (라벨 한계 정직 보고)
  • retrieval/eval_set.md — 평가 셋 정답 Cypher (Q1Q5 + F1F3 + 작업 흐름)
  • scripts/run_w4_eval.py (550 lines) — 자동 정성 검증 + markdown 표 + JSON 박제
  • docs/weekly-log/2026-05-17-text2cypher.md — 8/8 PASS 결과 박제

Commits (4개)

  • 142ff00 docs(eval): 평가 셋 사이퍼 쿼리 5종 박제
  • ff03ba9 feat(retrieval): Text2Cypher 모듈 구현
  • 98490c0 feat(scripts): W4 평가 셋 정성 검증 스크립트
  • f26f70b docs(weekly-log): 🎉🎉🎉🎉 W4 Text2Cypher 정성 검증 8/8 PASS!

🏗️ 아키텍처

자연어 질의
   │
   ▼ _generate_cypher (LLM 1단계, JSON 강제, temp=0.1)
Cypher (또는 null — 스키마 외)
   │
   ▼ _enforce_safety (read-only + LIMIT 100 강제)
   ▼ Neo4jClient.read
결과 (list[dict])
   │
   ▼ _generate_answer (LLM 2단계, plain text, temp=0.3)
자연어 답변

안전장치 (#18 DoD)

  1. read-only 강제: CREATE/DELETE/SET/MERGE/REMOVE/DROP/CALL/LOAD word boundary 정규식 검사
  2. LIMIT 100 강제: 미명시 시 자동 부착 (Neo4jClient 도 강제하지만 본 모듈에서 사전 검증)
  3. 2중 방어: LLM 자체 거부 (cypher=null) + 코드 차단 (Text2CypherError)
  4. graceful fallback: 모든 경우 answer 필드 채워짐 (cypher=null / 실행 실패 / 빈 결과 / 보안 위반)

LLM 패턴 (kg/extractor.py 와 일치)

  • JSON 응답 강제 (response_format={"type": "json_object"})
  • tenacity 재시도 (3회, exponential backoff)
  • JSON 파싱 방어 (코드펜스 strip + 따옴표 escape)
  • 프롬프트 hot-reload (캐시 안 함)
  • Opik @track 데코레이터

🎯 정성 검증 결과 (5/17 20:52, 44.8s)

ID category verdict kw% limit safe rows elapsed
Q1 traversal PASS 100% 68 8.1s
Q2 factual PASS 100% 1 4.3s
Q3 filter PASS 100% 100 9.0s
Q4 semantic PASS 100% 0 6.2s
Q5 topN PASS 100% 5 6.4s
F1 fallback PASS 100% 0 5.1s
F2 fallback PASS 0% 0 3.5s
F3 fallback PASS 0% 0 2.3s

총 8/8 PASS · 평균 5.6초/케이스

⭐⭐⭐ Q5 진짜 발표 보석 정량 확정

5/16 발견된 Entity 라벨 품질 challenge 가 LLM 답변까지 정확히 보고:

Company 라벨 top 5 = 100% 금융 metric, 진짜 회사명 0개
1. 공모발행액 23조 7,050억원 (31 mentions)
2. 일반회사채 발행 규모 전월 대비 감소 (19)
3. 기업공개 발행액 524억원 (17)
4. 유상증자 발행액 415억원 (17)
5. AA등급 이상 회사채 발행 비중 73.0% (17)

LLM 자연어 답변:

"...'Company' 라벨임에도 실제 회사명이 아닌 금융 metric 으로 추출되어 있어,
entity 추출 단계의 라벨 품질을 production 환경에서 보강할 필요가 있습니다."

→ 발표 슬라이드 10 의 정직한 한계 분석 그대로.

✅ DoD 100% 충족

  • retrieval/text2cypher.py — LLM 기반 자연어 → Cypher
  • 스키마 프롬프트 (LLM 주입)
  • read-only 강제 + LIMIT 100 강제
  • 결과 → 자연어 답변 (LLM 2단계)
  • 평가 셋 factual/numerical 5개 정성 검증
  • "X 문서의 Y 섹션에 무엇이 있나" 답변 (Q1, Q3)
  • graceful fallback (F1 / F2 / F3)

🚧 발견된 production 개선 포인트 (#18+ 후속)

  1. LLM 이 가정한 property 가 스키마에 없는 경우 (F1: c.page 경고)
    → system prompt 에 actual property 목록 추가
  2. 유용 property 활용 가이드 (Q5: e.member_count 미사용)
    → 답변 풍부도 개선
  3. answer_hints 매칭률 낮음 (정상)
    → heuristic 한계, 박제만

의존 / 후속

발표 슬라이드 (5/23) 연결

  • 슬라이드 10: Entity 라벨 품질 challenge (5/16 + 5/17 Q5 정량 정밀)
  • 슬라이드 11: VectorRAG ↔ GraphRAG 보완 (Q4 0 rows 증거)
  • 슬라이드 12: Text2Cypher 데모 (NEW! Q2 5.7초 데모)

5/16 8문서 적재 (610노드/2451관계/1189MENTIONS) 위에서 검증된
정답 Cypher 5종 + graceful fallback 3종.

질문 5종:
Q1. DS투자증권 entity 들 (라벨 혼재 검증)
Q2. 미래에셋 4Q Table 수 (factual)
Q3. disclosure 문서 entity (doc_type 필터)
Q4. 두산밥캣 리스크 (FACES_RISK + co-mention)
Q5. Company top 5 (라벨 품질 challenge 정량 검증) ⭐

각 질문별:
- 정답 Cypher
- 예상 결과 (5/16 적재 기반)
- LLM 답변 가이드
- 발표 슬라이드 연결

graceful fallback 3종:
- 존재하지 않는 문서
- 잘못된 라벨
- write 쿼리 (보안)

Text2Cypher 모듈 (retrieval/text2cypher.py) 작업 흐름 박제:
- 스키마 프롬프트
- Few-shot examples
- read-only + LIMIT 100 안전장치
- 결과 → 자연어 답변 (LLM 2단계)
- graceful fallback

발표 슬라이드 연결:
- Q5 = 슬라이드 10 (Entity 라벨 품질 challenge) ⭐ 정직한 보고
W4 첫 retrieval 진입점. 자연어 → Cypher → 결과 → 자연어 답변 2단계 LLM 흐름.

핵심 구현:
- retrieval/text2cypher.py: text2cypher() 공개 함수 + Text2CypherResult dataclass
- retrieval/prompts/text2cypher_system_v1.md: 스키마 + few-shot 5개 + fallback 예시
- retrieval/prompts/text2cypher_answer_v1.md: 결과 → 자연어 변환 가이드
- retrieval/__init__.py: 공개 인터페이스 노출

안전장치 (이슈 #18 DoD):
- read-only 강제: FORBIDDEN_KEYWORDS (CREATE/DELETE/SET/MERGE/REMOVE/DROP/CALL/LOAD)
  word boundary 정규식으로 false positive 회피
- LIMIT 100 강제 (Neo4jClient 도 강제하지만 본 모듈에서 사전 검증)
- graceful fallback: cypher=null / 실행 실패 / 빈 결과 모두 친절한 자연어 안내
- Text2CypherError 예외 (보안 위반 시만 raise, 나머지는 result.error 에 박제)

LLM 흐름 (kg/extractor.py 패턴 차용):
- 1단계 (_generate_cypher): JSON 응답 강제, temperature=0.1
- 2단계 (_generate_answer): plain text, temperature=0.3, top 30 row 만 전달
- tenacity 재시도 (3회, exponential backoff)
- JSON 파싱 방어 (코드펜스 strip + 따옴표 escape 보정)

프롬프트 박제:
- 스키마: doc-ontology.md §3-§4 그대로 (5 노드 + 9 관계)
- Few-shot 5개: 5/16 박제된 평가 셋 Q1~Q5 정답 Cypher
- Entity 라벨 품질 challenge: 'Company 라벨에 metric 섞임' 명시
- Answer 가이드: 빈 결과 친절 안내, Entity 라벨 한계 정직하게 언급

#24 Opik:
- 공개 진입점 text2cypher() 에 @track
- 환경 미설정 시 자동 no-op

레퍼런스: kg/extractor.py (LLM 호출 / JSON 파싱 / 프롬프트 로드 / 재시도 패턴)
의존: kg/neo4j_client.py (read() 한 줄로 안전장치 호출)

DoD:
- 'X 문서의 Y 섹션에 무엇이 있나' 류 답변 생성 ✅
- 잘못된 Cypher graceful fallback ✅
- read-only + LIMIT 100 강제 ✅
`scripts/run_w4_eval.py` — 5/16 박제된 평가 셋 Q1~Q5 + F1~F3 fallback 8 케이스
순회하며 `retrieval.text2cypher` 호출 → 정답 Cypher 비교 → 답변 품질 검증.

평가 셋 8 케이스 박제 (EvalCase dataclass):
- Q1 [traversal]: DS투자증권 entity 들 (라벨 혼재 검증)
- Q2 [factual]:   미래에셋 4Q Table 수 (numerical, 정답 6)
- Q3 [filter]:    disclosure 문서 entity (doc_type 필터)
- Q4 [semantic]:  두산밥캣 리스크 (FACES_RISK + co-mention)
- Q5 [topN]:      Company top 5 (라벨 품질 challenge 정량 검증) ⭐
- F1 [fallback]:  존재하지 않는 문서 (빈 결과 graceful)
- F2 [fallback]:  Person entity (스키마 외 라벨, cypher=null 기대)
- F3 [fallback]:  모든 노드 삭제 (write 시도, _enforce_safety 차단 기대)

휴리스틱 평가 (CaseEvalResult):
- keyword_coverage: required_keywords 커버리지 (%)
- has_limit: Cypher 에 LIMIT 있는지
- is_safe: 보안 위반 없는지 (정규식 검사)
- answer_hint_hits: 답변 자연어 힌트 매칭
- verdict: PASS (70%+ kw + LIMIT + answer) / PARTIAL (40%+) / FAIL

출력:
- 케이스별 상세 (생성 Cypher vs 정답, answer, heuristics)
- 마크다운 summary 표 (weekly-log 박제용)
- --json 옵션으로 raw 결과 박제 (eval_w4.json)

CLI:
- --case Q1: 한 케이스만
- --only-eval: F* fallback 제외 (빠른 정성 확인)
- --json out.json: raw 결과 박제
- --no-detail: summary 표만

#18 DoD 자동 검증:
- 'X 문서의 Y 섹션에 뭐 있나' 류 답변 (Q1, Q3)
- factual/numerical 5개 (Q1~Q5)
- graceful fallback (F1~F3)
- read-only + LIMIT 100 강제 (F3 + has_limit/is_safe)

레퍼런스: scripts/run_w3_batch.py (dataclass + markdown 표 + CLI 패턴)
의존: retrieval/text2cypher.py + retrieval/eval_set.md 평가 셋
5/17 20:52 평가 셋 8 케이스 실행 결과:
- Q1 [traversal]: PASS (68 rows, 라벨 혼재 정직 보고)
- Q2 [factual]:   PASS (1 row, "6개")
- Q3 [filter]:    PASS (100 rows, DISTINCT 자발 사용)
- Q4 [semantic]:  PASS (0 rows, graceful + 대안 제안)
- Q5 [topN]:      PASS (5 rows) ⭐ Entity 라벨 품질 정량 확정!
- F1 [fallback]:  PASS (존재하지 않는 문서)
- F2 [fallback]:  PASS (cypher=null, 스키마 외)
- F3 [fallback]:  PASS (cypher=null, 보안 거부)

총 8/8 PASS, 평균 5.6초/케이스, 전체 44.8초

⭐⭐⭐ Q5 진짜 발표 보석 (5/16 발견 + 5/17 정량 확정):
Company 라벨 top 5 = 100% 금융 metric, 진짜 회사명 0개
1. 공모발행액 23조 7,050억원 (31 mentions)
2. 일반회사채 발행 규모 전월 대비 감소 (19)
3. 기업공개 발행액 524억원 (18)
4. 유상증자 발행액 415억원 (17)
5. AA등급 이상 회사채 발행 비중 73.0% (17)

LLM 자연어 답변이 한계 정직 보고:
"...'Company' 라벨임에도 실제 회사명이 아닌 금융 metric 으로 추출되어
있어, entity 추출 단계의 라벨 품질을 production 환경에서 보강할
필요가 있습니다."

#18 DoD 100% 충족:
- "X 문서의 Y 섹션에 뭐 있나" 답변 (Q1, Q3) ✅
- factual/numerical 5개 (Q1~Q5) ✅
- graceful fallback (F1, F2, F3) ✅
- read-only + LIMIT 100 강제 ✅

발견된 production 개선 포인트 (#18+):
- LLM 이 가정한 property (c.page) 가 스키마에 없는 경우
- e.member_count 같은 유용 property 활용 가이드 필요
- system prompt 에 actual property 목록 추가

발표 슬라이드 (5/23) 갱신:
- 슬라이드 10: 5/16 진단 + 5/17 Q5 정량 정밀
- 슬라이드 11: VectorRAG ↔ GraphRAG 보완 (Q4 0 rows 증거)
- 슬라이드 12: Text2Cypher 데모 (NEW!)

다음 단계: PR #18 머지 → #21 Routing Agent
@TaskerJang TaskerJang merged commit 7b8cb5e into dev May 16, 2026
TaskerJang added a commit that referenced this pull request May 16, 2026
#22 W4 점검 결정:
- 시나리오 A 정상 진행 (Layer A/B/C + Hybrid + 5행 표)
- 빠듯해도 완주 도전 (옵션 B 는 future work)
- #4 Hybrid 설계 → 옵션 A Score Fusion 채택 (close)

이슈 정리 (5/17):
- #4 close — Hybrid Score Fusion 결정 박제
- #19/#20/#21/#22/#23/#25/#26/#27 진행 시점 박제
- #1/#3/#35 W5 묶음 작업으로 박제
- #32 ADR 트래킹 갱신 (0005/0006/0007 일정)

일정 박제 (5/17~5/23):
- 5/17 (일): PR #48 머지 + Layer B (#19)
- 5/18 (월): Layer C (#20) + Routing (#21)
- 5/19 (화): Hybrid Score Fusion (#23)
- 5/20 (수): LLM 어댑터 + 외부 측정 (#25, #3, #35)
- 5/21~22 (목~금): 5행 표 측정 (#26) + 발표 자료 (#27)
- 5/23 (토): 발표일 (#28)

진척률:
- W1~W3: 100% (PR #29~45)
- W4: 25% (PR #48 = Layer A 완료)
- W5: 0%
- 발표 D-day 6일 남음

발표 자료:
- 5/16~17 박제 12장 + W4/W5 추가 7장 = 총 19장
- 위험 대응: 최악 fallback 도 Layer A + 8/8 PASS 로 강력

ADR-0008 후보 박제:
- Entity 추출 라벨 품질 보강 정책 (#46 OpenAI 마이그 후 결정)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] W4 Layer A: Text2Cypher

1 participant