본문으로 건너뛰기

AI Neo4j & VectorDB 스키마 · ERD · 쿼리 가이드

Mind-Log v5.0 | 최종 업데이트: 2026-04-17 11:00


1. 듀얼 데이터 스토어 아키텍처 개요

Mind-Log v5.0은 4-Database Stack을 채택하여 각 데이터의 특성에 최적화된 저장소를 사용합니다.

데이터베이스용도담당 에이전트핵심 역할
Neo4j그래프 DBReasoning, Knowledge, Visualization감정 관계 그래프, 마인드 온톨로지, 지식 그래프
PineconeVector DBMemory, Knowledge, Episode Memory의미론적 유사도 검색, 임베딩 저장
MySQL관계형 DB — AI 서버 직접 접근 없음, src/api/backend_client.py 경유Backend 서버 (AI 서버는 간접 호출)메시지 로그, 세션, 메타데이터, 사용자 프로필
Redis캐시Intent Classifier, Safety실시간 세션 캐시, 응답 속도 최적화

왜 Neo4j + VectorDB 듀얼 스토어인가?

Vector DB만으로는 "왜 그런 감정이 생겼는지"(인과관계)를 파악할 수 없고, Graph DB만으로는 "비슷한 경험을 한 사람의 이야기"(의미론적 유사도)를 찾을 수 없습니다. Mind-Log는 두 DB를 결합하여 GraphRAG 패턴을 구현합니다.

[사용자 입력] "팀원들이 변화를 거부해요"

├──→ VectorDB: "변화 저항" 관련 과거 기록/전문 지식 검색 (유사도 기반)

└──→ Neo4j: "변화 저항"LEADS_TO"공감 능력 강화" (인과 관계)

└──→ 결과: 단순 기록이 아닌 "근본 원인 + 해결책" 연결

2. Neo4j 그래프 스키마 설계

2.1 노드 타입 정의

Mind-Log의 Neo4j 그래프는 6가지 핵심 노드 타입으로 구성됩니다.

노드 타입설명주요 속성사용 에이전트
Emotion감정 노드 (GoT 출력)name, intensity, duration, trigger, confidenceReasoning (GoT)
Technique치료/코칭 기법name, domain, description, evidence_levelKnowledge
Condition심리 상태/조건name, category, severityKnowledge
Concept리더십/심리학 개념name, definition, domainKnowledge
Memory사용자 기억 노드id, content, created_at, importanceMemory
Person사용자 관계 인물name, role, relationship_to_userMemory

추가 노드 타입 (마인드 온톨로지 전용):

노드 타입설명주요 속성사용 레이어
LeadershipConcept리더십 개념name, description, framework조직 레이어
PsychologicalPattern심리 패턴name, type, frequency개인 레이어
UserStory익명 사용자 이야기id, themes, anonymized_at팀 레이어
ExpertResource전문 자료title, source, evidence_level조직 레이어

2.2 관계 타입 정의

현재 구현 (코드 기준):

감정 노드 간 관계는 LEADS_TO 단일 타입만 사용합니다. src/agents/podcast/podcast_reasoning.py:701에서 다음 Cypher를 직접 발행합니다.

MERGE (a)-[:LEADS_TO {relationship: $rel}]->(b)

relationship 프로퍼티(문자열)로 의미(amplifies, triggers, conflict_with, exacerbates 등)를 구분합니다. 별도 관계 타입 enum을 사용하지 않습니다.

관계시작 노드끝 노드속성의미
LEADS_TOEmotionEmotionrelationship (str), strength감정 흐름 단일 관계 — podcast_reasoning.py:701 MERGE
TREATSTechniqueConditioneffectiveness, evidence_strength기법이 조건을 치료
RELATED_TOTechniqueTechniquesimilarity_score기법 간 유사 관계
INCLUDESTechniqueConcept기법이 개념을 포함
MENTIONSMemoryPersoncontext기억이 인물을 언급
ASSOCIATED_WITHMemoryEmotionintensity기억에 연관된 감정
HAS_ROOT_CAUSEPsychologicalPatternConceptconfidence심리 패턴의 근본 원인
RELATED_TO_CONCEPTUserStoryLeadershipConceptrelevance사용자 이야기 → 리더십 개념
SIMILAR_TO_PATTERNPsychologicalPatternPsychologicalPatternsimilarity_score유사 심리 패턴 연결
SUPPORTED_BY_RESEARCHLeadershipConceptExpertResourceevidence_level리더십 개념의 연구 근거

2.3 Neo4j ERD (Entity-Relationship Diagram)

                  ┌──────────────────────────────────────────────┐
Mind-Log Neo4j ERD (v5.0)
└──────────────────────────────────────────────┘

┌─────────┐ LEADS_TO {relationship: "amplifies"} ┌─────────┐
Emotion │────────────────────────────────────────►│ Emotion
│ │ LEADS_TO {relationship: "triggers"} │ │
│ name │ LEADS_TO {relationship: "conflict_with"}│ name │
│intensity│ LEADS_TO {relationship: "exacerbates"} │intensity│
│duration (단일 타입, relationship 프로퍼티로 구분) │trigger │
└────┬────┘ └─────────┘
ASSOCIATED_WITH

┌─────────┐ MENTIONS ┌─────────┐
Memory │────────────►│ Person
│ │ │ │
│ id │ │ name │
│ content │ │ role │
│importance│ └─────────┘
└─────────┘

┌───────────┐ TREATS ┌───────────┐
Technique │───────────►│ Condition
│ │ │ │
│ name │ INCLUDES │ name │
│ domain │─────┐ │ category │
│ evidence │ │ └───────────┘
└─────┬─────┘ ▼
│ ┌─────────┐
RELATED_TOConcept
│ │ │
▼ │ name │
┌───────────┐│definition│
Technique ││ │
└───────────┘└────┬────┘
HAS_ROOT_CAUSE

┌────────────────────┐ SIMILAR_TO_PATTERN ┌────────────────────┐
│PsychologicalPattern│◄────────────────────►│PsychologicalPattern│
│ name │ │ name │
│ type │ └────────────────────┘
└────────┬───────────┘

┌────────┴───────────┐ RELATED_TO_CONCEPT ┌──────────────────┐
UserStory │────────────────────►│LeadershipConcept │
│ id, themes │ │ name, framework │
└────────────────────┘ └────────┬─────────┘
SUPPORTED_BY_RESEARCH

┌──────────────────┐
ExpertResource
│ title, source │
└──────────────────┘

GoT → Neo4j 자동 연동 메커니즘

핵심 원칙: 노드를 미리 하나하나 만들어놓는 것이 아닙니다. 미리 정의하는 것은 **"틀"(스키마)**뿐이고, 구체적인 감정 노드와 관계는 LLM(Reasoning Agent)이 사용자 입력을 분석하여 매 세션마다 자동으로 생성합니다.

① 미리 정의하는 것 vs 자동 생성되는 것

구분미리 정의 (시스템 설계 시)자동 생성 (매 세션 실행 시)
노드라벨(타입)만 정의: :Emotion, :Technique, :Condition구체적 인스턴스: {name: '고립감', intensity: 0.8} — LLM이 매번 새로 생성
관계관계 타입 LEADS_TO 정의, relationship 프로퍼티 값 목록 정의구체적 연결: 고립감 -[:LEADS_TO {relationship: "amplifies"}]-> 책임감
속성값속성 필드명만 정의: intensity, confidence, strength구체적 수치: intensity: 0.8, strength: 0.7 — LLM이 문맥 기반으로 자동 산출

② 전체 파이프라인: 사용자 입력 → Neo4j 저장까지

[1] 사용자 입력 (그린룸 입장권)
│ 상황: "팀 회의에서 내 의견이 무시됨"
│ 생각: "리더로서 존중받지 못하고 있다"

[2] Intent Classifier (TIER 0)
│ complexity_score = 0.72GoT 활성화 결정

[3] Reasoning Agent (TIER 1)LLMGoT 수행
LLM에게 전달하는 프롬프트:
│ "사용자의 감정을 분석하여 다음 JSON Schema에 맞게
│ 감정 노드와 관계를 생성하세요."

LLM Structured Output (JSON Schema):
{
"emotion_nodes": [
{"label": "고립감", ...},
{"label": "책임감", ...},
{"label": "자기의심", ...}
],
"edges": [
{"from": "자기의심",
"to": "고립감",
"relation": "triggers"},
...
]
}
│ ★ LLM이 이 구조를 자동으로 채워서 반환!

[4] GoTCypher 직접 발행 (podcast_reasoning.py:701)
│ 별도 graph_transformer 없음
MERGE (a:Emotion {name: '고립감', user_id: $user_id})
MERGE (b:Emotion {name: '책임감', user_id: $user_id})
MERGE (a)-[:LEADS_TO {relationship: 'amplifies', strength: 0.7}]->(b)

[5] Neo4j 저장
│ 그래프가 누적되어 사용자의 감정 변화 추적 가능

[6] 다음 세션에서 활용
과거 그래프 조회 → 감정 패턴 변화 감지
"지난달에는 고립감 0.8 → 이번 달 0.5로 개선"

③ LLM에게 전달하는 Structured Output Schema

Reasoning Agent가 LLM에게 전달하는 JSON Schema입니다. LLM은 이 틀 안에서 자유롭게 감정과 관계를 생성합니다.

GOT_OUTPUT_SCHEMA = {
"type": "object",
"properties": {
"core_pattern": {
"type": "string",
"description": "감정 구조의 핵심 패턴명 (예: Authority Paradox)"
},
"emotion_nodes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "string"},
"label": {"type": "string", "description": "감정 이름 (한국어)"},
"intensity": {"type": "number", "minimum": 0, "maximum": 1},
"duration": {"type": "string", "enum": ["단기", "중기", "장기"]},
"trigger": {"type": "string", "description": "감정의 촉발 요인"},
"confidence": {"type": "number", "minimum": 0, "maximum": 1}
},
"required": ["id", "label", "intensity", "confidence"]
}
},
"edges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"from": {"type": "string"},
"to": {"type": "string"},
"relation": {
"type": "string",
"description": "관계 의미 (amplifies / triggers / conflict_with / exacerbates 등) — LEADS_TO 관계의 relationship 프로퍼티 값으로 저장됨"
},
"strength": {"type": "number", "minimum": 0, "maximum": 1}
},
"required": ["from", "to", "relation"]
}
}
},
"required": ["core_pattern", "emotion_nodes", "edges"]
}

④ GoT → Cypher 직접 발행 코드 (실제 구현)

별도 graph_transformer 유틸은 없습니다. src/agents/podcast/podcast_reasoning.py:701 내부에서 LEADS_TO Cypher를 직접 발행합니다.

# podcast_reasoning.py:701 핵심 MERGE 패턴
async def _save_got_to_neo4j(
self, got_output: dict, user_id: str, session_id: str
) -> None:
"""GoT 감정 그래프를 Neo4j에 저장한다.

관계 타입은 LEADS_TO 단일로 고정하며,
relationship 프로퍼티(문자열)로 의미를 구분한다.
"""
for node in got_output["emotion_nodes"]:
await self.neo4j.run(
"""
MERGE (e:Emotion {name: $name, user_id: $user_id})
ON CREATE SET
e.intensity = $intensity,
e.confidence = $confidence,
e.created_at = datetime(),
e.session_id = $session_id
ON MATCH SET
e.intensity = $intensity,
e.confidence = $confidence,
e.last_updated = datetime()
""",
name=node["label"], user_id=user_id,
intensity=node["intensity"], confidence=node.get("confidence", 0.8),
session_id=session_id,
)

for edge in got_output["edges"]:
await self.neo4j.run(
"""
MATCH (a:Emotion {name: $from_name, user_id: $user_id})
MATCH (b:Emotion {name: $to_name, user_id: $user_id})
MERGE (a)-[:LEADS_TO {relationship: $rel}]->(b)
SET r.strength = $strength, r.session_id = $session_id
""",
from_name=edge["from"], to_name=edge["to"],
user_id=user_id, rel=edge["relation"],
strength=edge.get("strength", 0.5), session_id=session_id,
)

⑤ 축적과 활용: 시간이 지나면 그래프가 성장한다

세션 1 (1):
고립감(0.8) -[:LEADS_TO {relationship:"amplifies"}]책임감(0.9)

세션 2 (2):
고립감(0.7) -[:LEADS_TO {relationship:"amplifies"}]책임감(0.85)
억울함(0.7) -[:LEADS_TO {relationship:"conflict_with"}]책임감(0.85) ← 새 노드 추가

세션 3 (3):
고립감(0.5) -[:LEADS_TO {relationship:"amplifies"}]책임감(0.8) ← 고립감 감소 감지
자기효능감(0.6) -[:LEADS_TO {relationship:"triggers"}]신뢰감(0.5) ← 긍정 패턴 등장

Reasoning Agent: "3개월간 고립감이 0.80.5로 개선되었습니다.
자기효능감이라는 새로운 긍정 패턴이 나타나고 있습니다."

Knowledge Agent의 지식 그래프와의 차이점:

구분GoT 감정 그래프 (Reasoning Agent)지식 그래프 (Knowledge Agent)
생성 시점매 세션마다 LLM이 동적 생성시스템 구축 시 전문가가 사전 구축 · 주기적 업데이트
노드 내용사용자의 구체적 감정 (고립감, 책임감 등)심리학 개념, 치료 기법 (CBT, DBT 등)
데이터 성격개인화 데이터 (user_id로 격리)공용 지식 베이스 (모든 사용자 공유)
변화 빈도매 세션마다 노드 추가/수치 업데이트새 논문/기법 추가 시에만 업데이트
주요 관계LEADS_TO (relationship 프로퍼티로 의미 구분)TREATS, RELATED_TO, INCLUDES, SUPPORTED_BY_RESEARCH

3. Neo4j Cypher 쿼리 예제

3.1 스키마 생성 (Constraints & Indexes)

-- 노드 고유 제약조건
CREATE CONSTRAINT emotion_name_unique FOR (e:Emotion) REQUIRE e.name IS UNIQUE;
CREATE CONSTRAINT technique_name_unique FOR (t:Technique) REQUIRE t.name IS UNIQUE;
CREATE CONSTRAINT condition_name_unique FOR (c:Condition) REQUIRE c.name IS UNIQUE;
CREATE CONSTRAINT memory_id_unique FOR (m:Memory) REQUIRE m.id IS UNIQUE;
CREATE CONSTRAINT person_name_unique FOR (p:Person) REQUIRE p.name IS UNIQUE;

-- 인덱스 생성 (검색 성능 최적화)
CREATE INDEX emotion_intensity FOR (e:Emotion) ON (e.intensity);
CREATE INDEX technique_domain FOR (t:Technique) ON (t.domain);
CREATE INDEX memory_importance FOR (m:Memory) ON (m.importance);
CREATE INDEX memory_created FOR (m:Memory) ON (m.created_at);

3.2 GoT 감정 그래프 생성 (Reasoning Agent — LEADS_TO 단일 타입)

-- CEO의 복합 감정 구조 생성
MERGE (isolation:Emotion {name: '고립감', user_id: $user_id})
SET isolation.intensity = 0.8, isolation.duration = '장기', isolation.confidence = 0.92

MERGE (injustice:Emotion {name: '억울함', user_id: $user_id})
SET injustice.intensity = 0.7, injustice.trigger = '팀원_이해_부족', injustice.confidence = 0.85

MERGE (responsibility:Emotion {name: '책임감', user_id: $user_id})
SET responsibility.intensity = 0.9, responsibility.confidence = 0.95

MERGE (decision_anxiety:Emotion {name: '결정불안감', user_id: $user_id})
SET decision_anxiety.intensity = 0.6, decision_anxiety.confidence = 0.78

-- 감정 간 관계 (LEADS_TO 단일 타입, relationship 프로퍼티로 의미 구분)
MERGE (isolation)-[r1:LEADS_TO {relationship: 'amplifies'}]->(responsibility)
SET r1.strength = 0.7

MERGE (injustice)-[r2:LEADS_TO {relationship: 'conflict_with'}]->(responsibility)
SET r2.strength = 0.8

MERGE (responsibility)-[r3:LEADS_TO {relationship: 'triggers'}]->(decision_anxiety)
SET r3.strength = 0.65

MERGE (isolation)-[r4:LEADS_TO {relationship: 'exacerbates'}]->(injustice)
SET r4.strength = 0.6

3.3 Knowledge Agent 지식 그래프 쿼리

-- 특정 조건에 효과적인 치료 기법 검색
MATCH (t:Technique)-[r:TREATS]->(c:Condition {name: '불안'})
WHERE r.effectiveness_level >= 0.7
RETURN t.name, t.domain, r.effectiveness_level, r.evidence_strength
ORDER BY r.effectiveness_level DESC
LIMIT 5;

-- 관련 기법 네트워크 탐색 (2-hop)
MATCH path = (t1:Technique {name: 'CBT 인지재구성'})
-[:RELATED_TO*1..2]-(t2:Technique)
RETURN t1.name, t2.name,
[r IN relationships(path) | r.similarity_score] AS scores;

-- 기법 → 개념 → 근본원인 경로 탐색
MATCH path = (t:Technique)-[:INCLUDES]->(c:Concept)
<-[:HAS_ROOT_CAUSE]-(p:PsychologicalPattern)
WHERE t.domain = 'cbt'
RETURN t.name AS technique, c.name AS concept,
p.name AS pattern, p.type AS pattern_type;

3.4 Memory Agent 기억 관계 쿼리

-- 특정 사용자의 기억에 등장하는 인물 관계 검색
MATCH (m:Memory)-[:MENTIONS]->(p:Person)
WHERE m.user_id = $user_id
RETURN p.name, COUNT(m) AS mention_count,
AVG(m.importance) AS avg_importance
ORDER BY mention_count DESC;

-- 감정-기억 연관 패턴 분석
MATCH (m:Memory)-[r:ASSOCIATED_WITH]->(e:Emotion)
WHERE m.user_id = $user_id AND e.intensity >= 0.7
RETURN e.name, COUNT(m) AS memory_count,
AVG(r.intensity) AS avg_emotion_intensity,
COLLECT(DISTINCT m.id) AS memory_ids
ORDER BY avg_emotion_intensity DESC;

3.5 마인드 온톨로지 시각화 쿼리 (Visualization Agent)

-- 조직 전체의 감정 그래프 추출 (B2B 대시보드용)
MATCH (e1:Emotion)-[r:LEADS_TO]->(e2:Emotion)
WHERE e1.intensity >= 0.5 OR e2.intensity >= 0.5
RETURN e1.name AS source, r.relationship AS relationship,
e2.name AS target, e1.intensity AS source_intensity,
e2.intensity AS target_intensity
ORDER BY e1.intensity DESC;

-- 3-레이어 온톨로지 네트워크 추출
MATCH (lc:LeadershipConcept)-[:SUPPORTED_BY_RESEARCH]->(er:ExpertResource)
OPTIONAL MATCH (us:UserStory)-[:RELATED_TO_CONCEPT]->(lc)
OPTIONAL MATCH (pp:PsychologicalPattern)-[:SIMILAR_TO_PATTERN]-(pp2:PsychologicalPattern)
RETURN lc.name AS concept, er.title AS research,
COLLECT(DISTINCT us.id) AS related_stories,
COLLECT(DISTINCT pp.name) AS related_patterns;

4. Pinecone VectorDB 스키마 설계

4.1 인덱스 구조 (현재 운영)

Pinecone은 단일 통합 인덱스 rag-suite-knowledge 하나만 운영합니다 (PR #153). user_id 네임스페이스 필터로 개인 데이터와 공용 전문 지식을 격리합니다.

  • 인덱스명: rag-suite-knowledge
  • 임베딩 모델: KT Cloud RAG Suite Embedding (PASSAGE)
  • 쿼리 유사도 임계값: 0.25 (PR #155, KT Cloud Query↔Passage 실측 score 0.20~0.35 반영)
  • 격리 방식: user_id 네임스페이스 필터
  • 담당 에이전트: Knowledge, Memory, Episode Memory 공통
  • 관련 코드: src/services/knowledge/pinecone_client.py, src/agents/podcast/knowledge_agent.py

4.2 메타데이터 스키마

개인 기억 데이터 (Memory Agent)

메타데이터 필드타입설명필터 용도
user_idstring사용자 식별자개인 데이터 격리 (필수)
memory_typestring"short" / "medium" / "long"기억 유형 필터
importancefloat중요도 점수 (0.0–1.0)중요도 임계값 필터
created_atstring (ISO)기억 생성 시각시간 범위 필터
topic_tagstring주제 태그주제 기반 필터
sentimentstring감정 톤감정 기반 리랭킹

전문 지식 데이터 (Knowledge Agent)

메타데이터 필드타입설명필터 용도
domainstring"cbt" / "dbt" / "psychodynamic" / "crisis_intervention" / "general_psychology"도메인 필터
sourcestring학술 출처출처 필터
evidence_levelstring"high" / "moderate" / "low"근거 수준 필터
is_sharedboolean공유 허락 여부공유 데이터 필터
is_anonymizedboolean익명화 여부프라이버시 필터

에피소드 아카이브 데이터 (Episode Memory)

메타데이터 필드타입설명필터 용도
user_idstring사용자 식별자개인 에피소드 필터
themeslist[string]에피소드 주제주제 필터
created_atstring (ISO)생성 시각시간 필터
episode_idstring에피소드 식별자에피소드 특정 조회

4.3 에피소드 레벨 청킹 전략

데이터 타입청킹 단위토큰 범위메타데이터
개인 그린룸 데이터1회 세션 = 1 청크300~800 토큰user_id, session_date, topic, emotion, risk_level
전문 지식/공유 이야기1 사례/개념 = 1 청크400~1000 토큰source_type, domain, themes, difficulty_level
팟캐스트 에피소드전체 에피소드 = 1 청크2000~3000 토큰episode_id, user_id, themes, publication_date

5. Pinecone Python 쿼리 예제

실제 구현 기준입니다. src/services/knowledge/pinecone_client.pysrc/agents/podcast/knowledge_agent.py 참조.

5.1 Knowledge Agent — 전문 지식 검색

from src.services.knowledge.pinecone_client import PineconeClient

client = PineconeClient()

# rag-suite-knowledge 단일 인덱스, user_id 네임스페이스 필터
results = await client.search(
query="팀 갈등 해소 코칭 기법",
top_k=5,
score_threshold=0.25, # PR #155 임계값
filter={"domain": {"$in": ["cbt", "crisis_intervention"]}},
namespace=user_id, # user_id 네임스페이스
)

for match in results:
if match["score"] >= 0.25:
print(f"ID: {match['id']}, Score: {match['score']:.4f}")
print(f" Metadata: {match['metadata']}")

5.2 Memory Agent — 기억 저장

# KT Cloud RAG Suite Embedding (PASSAGE)으로 임베딩 생성 후 upsert
await client.upsert(
vectors=[{
"id": "mem_abc123",
"values": embedding, # KT Cloud PASSAGE 임베딩
"metadata": {
"user_id": user_id,
"memory_type": "long",
"importance": 0.85,
"created_at": datetime.now().isoformat(),
"topic_tag": "team_communication",
"sentiment": "negative",
}
}],
namespace=user_id,
)

5.3 Memory Agent — 기억 검색

results = await client.search(
query="팀원과의 갈등 경험",
top_k=10,
score_threshold=0.25,
filter={
"memory_type": {"$in": ["long", "medium"]},
"importance": {"$gte": 0.5},
"created_at": {"$gte": "2025-01-01"},
},
namespace=user_id, # user_id 네임스페이스로 개인 데이터 격리
)

5.4 Episode Memory — 유사 에피소드 검색 (Podcast)

results = await client.search(
query="리더십 역할 전환 신뢰 구축",
top_k=3,
score_threshold=0.25,
filter={"episode_id": {"$exists": True}},
namespace=user_id,
)

for match in results:
if match["score"] > 0.85:
continuity = "build_on" # 이전 테마 확장
elif match["score"] > 0.7:
continuity = "contrast_with" # 대비 관점 제시
else:
continuity = "none" # 새로운 주제
print(f"Episode: {match['id']}, Score: {match['score']:.4f}, Action: {continuity}")

6. 듀얼 스토어 연동 패턴 (GraphRAG)

6.1 GraphRAG 워크플로우

VectorDB의 유사도 검색과 Neo4j의 관계 탐색을 결합하여 인과 관계가 포함된 검색 결과를 생성합니다.

async def graph_rag_search(user_input: str, user_id: str) -> dict:
"""
GraphRAG 통합 검색 파이프라인
1. VectorDB에서 유사 문서 검색
2. Neo4j에서 관계 그래프 탐색
3. 결과 통합
"""
# Step 1: VectorDB 검색 (의미론적 유사도)
# rag-suite-knowledge 단일 인덱스, user_id 네임스페이스
personal_docs = await pinecone_client.search(
query=user_input,
score_threshold=0.25,
filter={"memory_type": {"$exists": True}},
namespace=user_id,
top_k=5,
)

expert_docs = await pinecone_client.search(
query=user_input,
score_threshold=0.25,
filter={"is_shared": True, "is_anonymized": True},
namespace="shared",
top_k=5,
)

# Step 2: Neo4j 그래프 탐색 (LEADS_TO 관계)
graph_results = await neo4j.run(
"""
MATCH (e:Emotion {user_id: $user_id})-[r:LEADS_TO]->(e2:Emotion)
RETURN e.name, r.relationship, e2.name, r.strength
ORDER BY r.strength DESC LIMIT 10
""",
user_id=user_id,
)

return {
"personal_context": personal_docs,
"expert_context": expert_docs,
"graph_context": graph_results,
}

6.2 감정 기반 리랭킹

def rerank_by_emotion(documents: list, emotion_vector: dict, user_id: str) -> list:
"""
감정 기반 문서 재순위화
Final Score = (Vector Similarity × 0.4)
+ (Emotion Match × 0.3)
+ (Recency Weight × 0.2)
- (Trauma Risk × 0.1)
"""
reranked = []
for doc in documents:
vector_score = doc['similarity_score']
emotion_score = cosine_similarity(doc['emotion_vector'], emotion_vector)
recency_weight = calculate_recency(doc['timestamp'])
trauma_risk = safety_agent.check_trauma_trigger(doc, user_id)

final_score = (
vector_score * 0.4 +
emotion_score * 0.3 +
recency_weight * 0.2 -
trauma_risk * 0.1
)
reranked.append((doc, final_score))

return sorted(reranked, key=lambda x: x[1], reverse=True)

7. 에이전트별 DB 활용 매트릭스

에이전트TIERNeo4jPinecone (rag-suite-knowledge)MySQL (Backend Client 경유)주요 작업
MemoryTIER 1Memory↔Person 관계user_id 네임스페이스 검색/저장memories 테이블 (Backend 합의 필요)개인 기억 R/W
KnowledgeTIER 1Technique→Condition 탐색shared 네임스페이스 검색knowledge_base 테이블 (Backend 합의 필요)전문 지식 검색
ReasoningTIER 1GoT 감정 그래프 생성/탐색 (LEADS_TO)복합 감정 구조화
Episode MemoryTIER 1 (팟캐스트)user_id 네임스페이스 검색/저장podcast_episodes 테이블 (Backend 합의 필요)에피소드 연속성
Visualization비동기온톨로지 네트워크 추출B2B 대시보드 시각화
Learning비동기user_profiles, learning_log사용자 선호도 학습

8. 실전 활용 시나리오

시나리오 1: 그린룸 입장권 분석

사용자 입력: "팀장으로서 팀과의 거리감이 느껴집니다"

① Memory Agent (Pinecone rag-suite-knowledge, user_id 네임스페이스):
→ "팀 거리감" 관련 과거 기록 검색 (임계값 0.25)
→ 결과: 3개월 전 유사 고민 기록 발견

② Knowledge Agent (Pinecone rag-suite-knowledge, shared 네임스페이스):
→ "리더십 거리감" 전문 자료 검색 (임계값 0.25)
→ Neo4j: Technique(서번트 리더십) -[:TREATS]→ Condition(팀 소외감)

③ Reasoning Agent (Neo4j):
→ GoT 감정 그래프 생성:
고립감 -[:LEADS_TO {relationship:"amplifies"}]→ 책임감
책임감 -[:LEADS_TO {relationship:"triggers"}]→ 결정불안감

④ 결과:
→ 개인 경험 + 전문 지식 + 감정 구조 = 맞춤형 코칭 응답

시나리오 2: 팟캐스트 콘텐츠 기획

사용자 테마: "변화의 시대에 팀을 이끌어야 해요"

① Episode Memory (Pinecone rag-suite-knowledge, user_id 네임스페이스):
→ 유사 에피소드 검색 (임계값 0.25)
→ score=0.82 → continuity="contrast_with" (대비 관점)

② Memory Agent (Pinecone):
→ 사용자의 과거 그린룸 데이터에서 변화 관련 경험 추출

③ Knowledge Agent (Neo4j):
→ 변화관리 이론 -[:SUPPORTED_BY_RESEARCH]→ Kotter 8단계
→ PsychologicalPattern(변화저항) -[:SIMILAR_TO_PATTERN]→ 불확실성 회피

④ Podcast Reasoning:
→ 개인 경험 + 전문 지식 + 관계 구조 = 에피소드 스크립트

9. 프라이버시 및 보안 고려사항

Pinecone 데이터 격리는 단일 rag-suite-knowledge 인덱스 + user_id 네임스페이스 필터 기반입니다 (PR #153).

원칙Neo4j 적용Pinecone 적용
데이터 격리user_id 기반 서브그래프 분리모든 쿼리에 user_id 네임스페이스 필수
익명화UserStory 노드 식별정보 제거is_anonymized=true 메타데이터 필터
동의 관리공유 노드에 consent 속성 부여is_shared=true 필터로 동의 데이터만 검색
삭제 권리DETACH DELETE로 노드+관계 삭제delete API로 벡터 즉시 삭제
감사 로그쿼리 로그 MySQL에 Backend Client 경유 기록검색 로그 MySQL에 Backend Client 경유 기록
-- Neo4j: 사용자 데이터 삭제 (잊혀질 권리)
MATCH (m:Memory {user_id: $user_id})
OPTIONAL MATCH (m)-[r]-()
DETACH DELETE m;

-- Neo4j: 공유 이야기 익명화
MATCH (us:UserStory {id: $story_id})
SET us.anonymized_at = datetime(),
us.original_user_id = null;
# Pinecone: 사용자 벡터 삭제
await pinecone_client.delete(
filter={"user_id": {"$eq": user_id}},
namespace=user_id,
)

참고 문서 및 연계


향후 계획

Neo4j 관계 타입 확장 (GoT 정교화)

현재 LEADS_TO 단일 관계 + relationship 프로퍼티로 구분하는 구조를 향후 GoT 추론 정교화 시 전용 관계 타입으로 분리할 수 있습니다.

관계의미추가 속성
AMPLIFIES감정 A가 감정 B를 증폭strength
TRIGGERS감정 A가 감정 B를 유발probability
CONFLICT_WITH감정 간 충돌 관계tension_level
EXACERBATES감정 A가 감정 B를 악화severity

관계 타입 분리 시 GoT Structured Output Schema의 edges.relation enum도 함께 업데이트 필요.

Pinecone BM25 하이브리드 검색

현재 Dense(의미론적) 검색만 운영 중입니다. 향후 BM25(키워드) 검색을 결합하여 치료 기법명 등 고유명사 정확 매칭 성능을 개선할 수 있습니다.

BM25(키워드) 30% + Dense(의미론적) 70% 하이브리드 검색

Elasticsearch 또는 Pinecone 자체 하이브리드 검색 기능 활용 검토 필요.

Pinecone user_id 필터 경로 재검증

user_id 필터가 네임스페이스 방식과 filter={user_id: ...} 메타데이터 방식 중 어느 경로로 적용되는지 실제 코드 재확인 필요.

  • 확인 대상: src/services/knowledge/pinecone_client.py
  • Episode Memory 쿼리 경로: src/agents/podcast/episode_memory.py

MySQL 테이블 스키마 Backend 합의

아래 테이블은 Backend 팀과 스키마 합의 후 TYPE_* 상수(src/api/backend_resources.py)로 포맷화 예정입니다.

  • memories 테이블 (Memory Agent)
  • knowledge_base 테이블 (Knowledge Agent)
  • podcast_episodes 테이블 (Episode Memory)

변경 이력

날짜변경 내용
2026-04-17Neo4j 감정 관계 LEADS_TO 단일 타입으로 직접 반영 (AMPLIFIES 등 4종 → 향후 계획 이동), Pinecone rag-suite-knowledge 단일 인덱스 직접 기재 (3개 분리 인덱스 설계 제거), MySQL + Backend Client 경유 명시 (PostgreSQL 제거), GoT → Cypher graph_transformer 없음 명시 (LEADS_TO 직접 발행 코드로 교체), 경고 박스 전면 제거