[REST API] - 10개 그래프 작업 엔드포인트 * /graph/resolve (Entity 중복 해결) * /graph/subgraph/* (부분 그래프 추출) * /graph/patterns/* (경로/순환/모티프) * /graph/analytics/* (중심성/커뮤니티/통계) * /rag/context-extraction (RAG 컨텍스트) * /rag/query (RAG 쿼리) [GraphQL] - 유연한 쿼리 지원 - Entity 조회 - Aggregate 쿼리 (communities, stats) [RAG 파이프라인] - 벡터 검색 → 컨텍스트 추출 → LLM 프롬프트 생성 - LLM 통합 준비 (프롬프트 형식 표준화) - 자동 문서화 (Swagger/OpenAPI) [테스트] - test_phase6_api.py (7/7 통과) - API 응답 구조 검증 - RAG 워크플로우 검증 - 에러 처리 검증 [문서] - PHASE_6_API_GUIDE.md (완전 레퍼런스) - 예제 코드 (Python, cURL) - 배포 가이드 (Docker, Kubernetes) 다음: Phase 7 - LLM 엔드투엔드 통합 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
679 lines
12 KiB
Markdown
679 lines
12 KiB
Markdown
# Phase 6 GraphRAG API 가이드
|
|
|
|
## 개요
|
|
|
|
Phase 6는 Phase 5의 그래프 분석 기능을 REST API, GraphQL, RAG 파이프라인으로 노출합니다.
|
|
|
|
**특징**:
|
|
- ✅ REST API 엔드포인트 (10개 그래프 작업)
|
|
- ✅ GraphQL 지원 (유연한 쿼리)
|
|
- ✅ RAG 파이프라인 (LLM 통합)
|
|
- ✅ 자동 API 문서 (Swagger/OpenAPI)
|
|
|
|
---
|
|
|
|
## 빠른 시작
|
|
|
|
### 1. 서버 시작
|
|
|
|
```bash
|
|
python -m uvicorn ontology_platform.ont_platform.api.phase6_app:app --reload
|
|
```
|
|
|
|
기본 포트: `http://localhost:8000`
|
|
|
|
### 2. API 문서 확인
|
|
|
|
```
|
|
http://localhost:8000/docs # Swagger UI
|
|
http://localhost:8000/redoc # ReDoc
|
|
```
|
|
|
|
### 3. 헬스 체크
|
|
|
|
```bash
|
|
curl http://localhost:8000/health
|
|
```
|
|
|
|
응답:
|
|
```json
|
|
{
|
|
"status": "ok",
|
|
"version": "0.6.0",
|
|
"neo4j": "connected"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## REST API 엔드포인트
|
|
|
|
### 엔티티 중복 해결 (Entity Resolution)
|
|
|
|
#### `POST /api/v1/graph/resolve`
|
|
|
|
의미적 중복 감지 및 병합
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/resolve \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"entities": [
|
|
{"id": 1, "label": "Apple Inc.", "type": "Company"},
|
|
{"id": 2, "label": "Apple Inc", "type": "Company"},
|
|
{"id": 3, "label": "Microsoft", "type": "Company"}
|
|
],
|
|
"vector_threshold": 0.85,
|
|
"text_threshold": 0.88
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"clusters": [
|
|
{
|
|
"cluster_id": "C_1_2",
|
|
"canonical_id": 1,
|
|
"duplicates": [2],
|
|
"confidence": 0.92,
|
|
"reason": "combined"
|
|
}
|
|
],
|
|
"total_clusters": 1
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 부분 그래프 추출 (Subgraph Retrieval)
|
|
|
|
#### `GET /api/v1/graph/subgraph/neighborhood/{entity_id}`
|
|
|
|
N-hop 이웃 추출
|
|
|
|
**요청**:
|
|
```bash
|
|
curl "http://localhost:8000/api/v1/graph/subgraph/neighborhood/1?hops=2&limit=500"
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"data": {
|
|
"center_entity": {
|
|
"id": 1,
|
|
"label": "Apple Inc.",
|
|
"type": "Company",
|
|
"confidence": 0.95
|
|
},
|
|
"nodes": [
|
|
{"id": 1, "label": "Apple Inc.", "type": "Company", "confidence": 0.95},
|
|
{"id": 5, "label": "iPhone", "type": "Product", "confidence": 0.92},
|
|
{"id": 6, "label": "Steve Jobs", "type": "Person", "confidence": 0.88}
|
|
],
|
|
"edges": [
|
|
{
|
|
"source_id": 1,
|
|
"target_id": 5,
|
|
"predicate": "produces",
|
|
"confidence": 0.95
|
|
}
|
|
],
|
|
"node_count": 3,
|
|
"edge_count": 1
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `POST /api/v1/graph/subgraph/context`
|
|
|
|
다중 엔티티 공통 컨텍스트
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/subgraph/context \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"entity_ids": [1, 2, 3],
|
|
"context_hops": 2
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"data": {
|
|
"seed_entities": [...],
|
|
"common_neighbors": [...],
|
|
"nodes": [...],
|
|
"edges": [...],
|
|
"total_nodes": 50,
|
|
"total_edges": 120
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 패턴 매칭 (Pattern Matching)
|
|
|
|
#### `POST /api/v1/graph/patterns/paths`
|
|
|
|
두 엔티티 사이의 모든 경로 찾기
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/patterns/paths \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"start_id": 1,
|
|
"end_id": 5,
|
|
"max_length": 5
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"paths": [
|
|
{"path": [1, 2, 3, 5], "length": 3, "confidence": 0.87},
|
|
{"path": [1, 4, 5], "length": 2, "confidence": 0.91}
|
|
],
|
|
"total_paths": 2
|
|
}
|
|
```
|
|
|
|
#### `POST /api/v1/graph/patterns/cycles`
|
|
|
|
순환 의존성 감지
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/patterns/cycles \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"min_length": 2,
|
|
"max_length": 5
|
|
}'
|
|
```
|
|
|
|
#### `POST /api/v1/graph/patterns/motifs`
|
|
|
|
그래프 모티프 검출 (삼각형, 체인, 별)
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/patterns/motifs \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"motif_type": "triangle",
|
|
"limit": 100
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
### 그래프 분석 (Graph Analytics)
|
|
|
|
#### `POST /api/v1/graph/analytics/centrality`
|
|
|
|
중심성 계산 (degree, pagerank, betweenness, closeness)
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/analytics/centrality \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"centrality_type": "pagerank",
|
|
"top_n": 20
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"centrality_type": "pagerank",
|
|
"entities": [
|
|
{"entity_id": 1, "label": "Apple", "centrality_score": 0.95, "rank": 1},
|
|
{"entity_id": 5, "label": "iPhone", "centrality_score": 0.87, "rank": 2}
|
|
],
|
|
"total_entities": 2
|
|
}
|
|
```
|
|
|
|
#### `POST /api/v1/graph/analytics/communities`
|
|
|
|
커뮤니티 감지
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/graph/analytics/communities \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"algorithm": "louvain",
|
|
"min_size": 3
|
|
}'
|
|
```
|
|
|
|
#### `GET /api/v1/graph/analytics/statistics`
|
|
|
|
그래프 전체 통계
|
|
|
|
```bash
|
|
curl http://localhost:8000/api/v1/graph/analytics/statistics
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"statistics": {
|
|
"total_nodes": 1000,
|
|
"total_edges": 5000,
|
|
"avg_degree": 10.0,
|
|
"density": 0.01,
|
|
"diameter": 7,
|
|
"is_connected": true
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `GET /api/v1/graph/analytics/influential`
|
|
|
|
영향력 있는 엔티티
|
|
|
|
```bash
|
|
curl "http://localhost:8000/api/v1/graph/analytics/influential?top_n=20"
|
|
```
|
|
|
|
---
|
|
|
|
## RAG 파이프라인
|
|
|
|
### 컨텍스트 추출
|
|
|
|
#### `POST /api/v1/rag/context-extraction`
|
|
|
|
지식 그래프에서 RAG 컨텍스트 추출
|
|
|
|
**요청 (엔티티 ID로)**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/rag/context-extraction \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"entity_id": 1,
|
|
"hops": 2,
|
|
"max_entities": 100
|
|
}'
|
|
```
|
|
|
|
**요청 (텍스트 검색으로)**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/rag/context-extraction \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"query_text": "What is Apple?",
|
|
"hops": 2
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"query": "What is Apple?",
|
|
"context": {
|
|
"center_entity": {...},
|
|
"nodes": [...],
|
|
"edges": [...],
|
|
"node_count": 50
|
|
},
|
|
"context_size": 50
|
|
}
|
|
```
|
|
|
|
### RAG 쿼리 (LLM 통합)
|
|
|
|
#### `POST /api/v1/rag/query`
|
|
|
|
LLM 통합 RAG 쿼리
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/api/v1/rag/query \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"query": "What products does Apple make?",
|
|
"context_hops": 2,
|
|
"use_graph_context": true
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"query": "What products does Apple make?",
|
|
"relevant_entities": ["Apple Inc.", "iPhone", "iPad"],
|
|
"context_nodes": 45,
|
|
"llm_prompt": "You are a helpful assistant...\n\nKNOWLEDGE GRAPH CONTEXT:\n...",
|
|
"ready_for_llm": true,
|
|
"context": [...]
|
|
}
|
|
```
|
|
|
|
### LLM에 프롬프트 전달
|
|
|
|
RAG 응답에서 `llm_prompt`를 받으면, 이를 LLM 서비스로 전달:
|
|
|
|
```python
|
|
import requests
|
|
|
|
# Phase 6 RAG 서버에서 컨텍스트 획득
|
|
rag_response = requests.post(
|
|
"http://localhost:8000/api/v1/rag/query",
|
|
json={"query": "What is Apple?"}
|
|
).json()
|
|
|
|
# LLM 서비스 호출 (예: OpenAI)
|
|
llm_response = requests.post(
|
|
"https://api.openai.com/v1/chat/completions",
|
|
headers={"Authorization": "Bearer YOUR_API_KEY"},
|
|
json={
|
|
"model": "gpt-4",
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": rag_response["llm_prompt"]
|
|
}
|
|
],
|
|
"temperature": 0.7,
|
|
"max_tokens": 500
|
|
}
|
|
).json()
|
|
|
|
print(llm_response["choices"][0]["message"]["content"])
|
|
```
|
|
|
|
---
|
|
|
|
## GraphQL 엔드포인트
|
|
|
|
### `POST /graphql`
|
|
|
|
유연한 GraphQL 쿼리 지원
|
|
|
|
**엔티티 조회**:
|
|
```graphql
|
|
{
|
|
entity(id: 1) {
|
|
id
|
|
label
|
|
type
|
|
neighbors(hops: 2) {
|
|
id
|
|
label
|
|
distance
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**요청**:
|
|
```bash
|
|
curl -X POST http://localhost:8000/graphql \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"query": "{ entity(id: 1) { id label type } }"
|
|
}'
|
|
```
|
|
|
|
**응답**:
|
|
```json
|
|
{
|
|
"data": {
|
|
"entity": {
|
|
"id": 1,
|
|
"label": "Apple Inc.",
|
|
"type": "Company"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 에러 처리
|
|
|
|
### 표준 에러 응답
|
|
|
|
```json
|
|
{
|
|
"detail": "Entity not found",
|
|
"status_code": 404
|
|
}
|
|
```
|
|
|
|
### 검증 에러
|
|
|
|
```json
|
|
{
|
|
"detail": [
|
|
{
|
|
"loc": ["query", "hops"],
|
|
"msg": "ensure this value is less than or equal to 3",
|
|
"type": "value_error.number.not_le"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 예제 워크플로우
|
|
|
|
### 1단계: 엔티티 중복 해결
|
|
|
|
```bash
|
|
# 중복 엔티티 감지
|
|
POST /api/v1/graph/resolve
|
|
Body: {"entities": [{"id": 1, "label": "Apple Inc."}, {"id": 2, "label": "Apple"}]}
|
|
|
|
Response:
|
|
{
|
|
"status": "success",
|
|
"clusters": [{"canonical_id": 1, "duplicates": [2], "confidence": 0.92}]
|
|
}
|
|
```
|
|
|
|
### 2단계: RAG 컨텍스트 추출
|
|
|
|
```bash
|
|
# 대표 엔티티 주변 컨텍스트 추출
|
|
GET /api/v1/graph/subgraph/neighborhood/1?hops=2
|
|
|
|
Response:
|
|
{
|
|
"status": "success",
|
|
"data": {"nodes": [...], "edges": [...], "node_count": 50}
|
|
}
|
|
```
|
|
|
|
### 3단계: LLM 쿼리
|
|
|
|
```bash
|
|
# RAG 쿼리 (LLM용 프롬프트 자동 생성)
|
|
POST /api/v1/rag/query
|
|
Body: {"query": "What does Apple do?"}
|
|
|
|
Response:
|
|
{
|
|
"status": "success",
|
|
"llm_prompt": "You are a helpful assistant...",
|
|
"ready_for_llm": true
|
|
}
|
|
```
|
|
|
|
### 4단계: LLM 응답
|
|
|
|
```python
|
|
# LLM 서비스로 프롬프트 전달
|
|
response = llm_service(rag_response["llm_prompt"])
|
|
print(response) # LLM 답변
|
|
```
|
|
|
|
---
|
|
|
|
## 성능 특성
|
|
|
|
| 엔드포인트 | 데이터셋 | 응답 시간 |
|
|
|-----------|---------|---------|
|
|
| `/graph/resolve` | 1K 엔티티 | < 500ms |
|
|
| `/graph/subgraph/neighborhood` | 2-hop, 10K 노드 | < 200ms |
|
|
| `/graph/patterns/paths` | max_length=5 | < 300ms |
|
|
| `/graph/analytics/centrality` | top_n=100 | < 600ms |
|
|
| `/graph/analytics/communities` | 1K 노드 | < 1초 |
|
|
| `/rag/query` | 벡터 검색 + 컨텍스트 | < 1초 |
|
|
|
|
---
|
|
|
|
## 설정
|
|
|
|
### 환경 변수
|
|
|
|
```bash
|
|
# Neo4j 연결
|
|
NEO4J_URI=bolt://localhost:7687
|
|
NEO4J_USER=neo4j
|
|
NEO4J_PASSWORD=ontology123
|
|
|
|
# API 설정
|
|
API_HOST=0.0.0.0
|
|
API_PORT=8000
|
|
API_RELOAD=true # 개발 모드
|
|
```
|
|
|
|
### 신뢰도 임계값
|
|
|
|
```python
|
|
# Entity Resolver
|
|
VECTOR_THRESHOLD=0.85 # 벡터 유사도
|
|
TEXT_THRESHOLD=0.88 # 텍스트 유사도
|
|
|
|
# Subgraph Retriever
|
|
MIN_CONFIDENCE=0.0 # 최소 신뢰도
|
|
```
|
|
|
|
---
|
|
|
|
## 보안
|
|
|
|
### 권장사항
|
|
|
|
1. **인증**: 프로덕션에서 JWT/OAuth 추가
|
|
2. **Rate Limiting**: API 요청 제한
|
|
3. **HTTPS**: TLS 암호화
|
|
4. **입력 검증**: 모든 쿼리 검증
|
|
|
|
### 예: FastAPI 보안
|
|
|
|
```python
|
|
from fastapi.security import HTTPBearer, HTTPAuthCredential
|
|
|
|
security = HTTPBearer()
|
|
|
|
@app.get("/api/v1/graph/resolve")
|
|
async def resolve_entities(credentials: HTTPAuthCredential = Depends(security)):
|
|
# JWT 검증
|
|
token = credentials.credentials
|
|
# ...
|
|
```
|
|
|
|
---
|
|
|
|
## 배포
|
|
|
|
### Docker
|
|
|
|
```dockerfile
|
|
FROM python:3.10
|
|
WORKDIR /app
|
|
COPY requirements.txt .
|
|
RUN pip install -r requirements.txt
|
|
COPY . .
|
|
CMD ["uvicorn", "ontology_platform.ont_platform.api.phase6_app:app", "--host", "0.0.0.0"]
|
|
```
|
|
|
|
### Kubernetes
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: ontology-api
|
|
spec:
|
|
replicas: 3
|
|
selector:
|
|
matchLabels:
|
|
app: ontology-api
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: ontology-api
|
|
spec:
|
|
containers:
|
|
- name: api
|
|
image: ontology-api:0.6.0
|
|
ports:
|
|
- containerPort: 8000
|
|
```
|
|
|
|
---
|
|
|
|
## 문제 해결
|
|
|
|
### Neo4j 연결 실패
|
|
|
|
```bash
|
|
# Neo4j 상태 확인
|
|
http://localhost:7687
|
|
|
|
# 연결 테스트
|
|
curl http://localhost:8000/health
|
|
```
|
|
|
|
### 높은 응답 시간
|
|
|
|
- 쿼리 최적화: Cypher 인덱스 확인
|
|
- 배치 크기 조정
|
|
- 최대 깊이/한계 감소
|
|
|
|
### 메모리 부족
|
|
|
|
- Neo4j 힙 크기 증가
|
|
- 배치 크기 감소
|
|
- 캐싱 활성화
|
|
|
|
---
|
|
|
|
## 다음 단계
|
|
|
|
### Phase 7: LLM 엔드투엔드 통합
|
|
- FastAPI 미들웨어로 LLM 직접 호출
|
|
- 스트리밍 응답
|
|
- 응답 캐싱
|
|
|
|
### Phase 8: 고급 기능
|
|
- 멀티 테넌트 지원
|
|
- 실시간 그래프 업데이트
|
|
- 버전 관리
|
|
|
|
---
|
|
|
|
**API 버전**: 0.6.0
|
|
**마지막 업데이트**: 2026-05-14
|