Summary

Text2Cypher의 한계를 극복한 Self-Correcting Agent 구현. 초기 Cypher 쿼리 생성 후 실행 결과를 검증하고, 실패하면 오류 피드백을 LLM에 전달하여 자동으로 쿼리를 재작성하는 Reactive Loop 패턴. LangGraph를 활용한 실전 Agentic GraphRAG 사례.

Key Claims

  • Text2Cypher의 한계 극복 — 초기 쿼리 생성은 완벽하지 않음 (문법 오류, 잘못된 관계명 등)
  • 결과 기반 자동 수정 — 빈 결과셋이나 오류 메시지를 피드백으로 활용
  • 다중 재시도 — 최대 N회까지 자동으로 쿼리 개선
  • 사용자 개입 최소화 — “쿼리가 맞는지 확인해줘” 같은 수정 요청 없음
  • 신뢰도 향상 — 각 재시도마다 정확도 증가

동작 원리 (Reactive Loop)

사용자 질문
  ↓
1️⃣ Text2Cypher: 자연어 → Cypher 생성
  ↓
2️⃣ 쿼리 실행 & 결과 수집
  ↓
3️⃣ 결과 검증
  ├─ 성공 (유의미 결과) → 답변 생성 & 종료
  └─ 실패 (빈 결과 또는 오류) → 4️⃣로
  ↓
4️⃣ 오류 원인 분석
  ├─ "관계명이 존재하지 않음"
  ├─ "노드 속성명이 틀림"
  ├─ "WHERE 절 문법 오류"
  └─ ... 등
  ↓
5️⃣ LLM에게 피드백 전달
  "다음 쿼리에 오류가 있었습니다: {error message}
   가능한 관계명: [WORKS_AT, MANAGES, OWNS, ...]
   다시 작성해주세요."
  ↓
6️⃣ 개선된 Cypher 재생성 → 2️⃣로 (반복)
  ↓
최대 재시도 횟수 도달
  └─ 최종 답변 또는 오류 보고

Key Concepts Introduced

1. Text2Cypher 기초

from langchain.chains import GraphCypherQAChain
from langchain.graphs import Neo4jGraph
 
graph = Neo4jGraph(
    url="bolt://localhost:7687",
    username="neo4j",
    password="password"
)
 
qa_chain = GraphCypherQAChain.from_llm(
    llm=ChatOpenAI(model="gpt-4"),
    graph=graph,
    verbose=True
)
 
result = qa_chain.run("Alice의 매니저는?")

2. Self-Correcting Logic

def validate_result(result):
    """결과가 유의미한지 검증"""
    if not result or len(result) == 0:
        return False, "Empty result"
    if "error" in result.lower():
        return False, result
    return True, None
 
# Agent 루프에서 사용
 
> [!caution] 검토 필요
> 
> 원본 자료 재방문 권장 (최초 수집: 2025-02-27)
is_valid, error_msg = validate_result(query_result)
if not is_valid:
    feedback = f"쿼리 오류: {error_msg}. 다시 시도해주세요."
    # LLM에 피드백 전달

3. Error Categorization

오류 타입원인해결 방법
Empty Result관계명 오류 / WHERE 조건 불일치사용 가능한 관계명 제시
Syntax ErrorCypher 문법 오류정정된 문법 제시
Type Mismatch속성 타입 불일치 (문자열 vs 숫자)타입 변환 방법 제시
Missing Property노드 속성 존재 안 함실제 속성명 제시

4. Retrying Strategy

max_retries = 3
retry_count = 0
 
while retry_count < max_retries:
    cypher = llm.generate_cypher(user_query)
    result = execute_query(cypher)
    
    if validate_result(result):
        return result  # 성공
    
    # 실패 → 피드백과 함께 재시도
    feedback = analyze_error(cypher, result)
    retry_count += 1
    user_query = f"{user_query}\n[피드백: {feedback}]"
 
# 모든 재시도 실패 → 오류 보고
return f"최대 {max_retries}회 재시도 후에도 실패했습니다: {feedback}"

실전 예시: 단계별 실행

사용자 질문

"2025년 이후 입사한 개발 팀원들의 평균 급여는?"

1차 시도 (실패)

생성된 Cypher:

MATCH (p:Person)-[:EMPLOYED_SINCE]->(d:Date)
WHERE d.year >= 2025
RETURN AVG(p.salary) AS avg_salary

오류:

Neo4jError: Property 'EMPLOYED_SINCE' not found in relationships

피드백: “EMPLOYED_SINCE 관계가 없습니다. 사용 가능한 관계: [WORKS_AT, MANAGES, REPORTS_TO]“

2차 시도 (실패)

개선된 Cypher:

MATCH (p:Person)-[r:WORKS_AT]->(c:Company)
WHERE c.name = "Development"
  AND r.since >= 2025
RETURN AVG(p.salary) AS avg_salary

오류:

Empty result set (no rows)

원인 분석:

  • 관계는 맞지만 WHERE 조건이 잘못됨
  • Company 노드의 속성이 “name”이 아닐 수 있음
  • 또는 2025년 이후 입사자가 없음

피드백: “조건을 재검토해주세요. 가능한 Company 속성: [id, name, industry, size]. 또는 더 넓은 연도 범위를 시도해보세요.”

3차 시도 (성공)

최종 Cypher:

MATCH (p:Person)-[r:WORKS_AT]->(c:Company)
WHERE p.department = "Development"
  AND r.start_date >= "2025-01-01"
RETURN AVG(p.salary) AS avg_salary, COUNT(p) AS num_employees

결과:

avg_salary: 75000000
num_employees: 5

최종 답변:

"2025년 이후 입사한 개발 팀원은 5명이며, 평균 급여는 7,500만 원입니다."

장점과 활용

장점

장점설명
정확도 향상다중 재시도로 쿼리 정확도 증가
사용 편의성사용자가 쿼리 문법을 몰라도 됨
신뢰성자동 오류 복구
학습 효과Agent가 도메인 특성 자동 학습

활용 분야

  • BI/분석 자동화 — 경영진이 자연어로 쿼리
  • 고객 지원 — FAQ 자동 응답
  • 데이터 검색 — 사용자 친화적 DB 조회

Reactive Loop vs Proactive Loop

구분ReactiveProactive
시작오류 발생 후 반응미리 예방
구현간단함복잡함 (선제 검증)
성능느림 (재시도)빠름 (첫 시도 성공)
활용처대부분 GraphRAG고 신뢰도 필요 시스템

관련 개념


관련 소스: data-to-kg (전체 파이프라인), automated-code-generation-agent (자동 수정 패턴), text2cypher-retriever (기초 Text2Cypher)

인정: YouTube 공원나연 채널 (2025-02-27)