File size: 30,659 Bytes
cb92864
f0b1337
08fb91a
 
 
cb92864
 
 
 
08fb91a
cb92864
 
 
 
64ad66f
f0b1337
cb92864
f0b1337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb92864
 
 
 
 
 
 
f0b1337
64ad66f
 
 
c64138a
 
 
cb92864
 
f0b1337
64ad66f
cb92864
fd7f235
 
 
d0cc9ab
 
 
 
fd7f235
 
 
 
 
c64138a
 
 
 
 
fd7f235
 
 
 
 
cb92864
 
 
 
 
 
 
 
 
 
 
64ad66f
cb92864
64ad66f
cb92864
 
 
ff0395c
 
cb92864
ff0395c
 
cb92864
ff0395c
 
 
 
 
 
cb92864
ff0395c
 
 
 
 
 
cb92864
 
7f57ffc
78a2a73
ea86e27
 
cb92864
 
 
 
64ad66f
 
 
 
ff0395c
64ad66f
6fecdf0
 
 
 
 
 
d8c8177
79ef842
 
 
 
d8c8177
 
6fecdf0
64ad66f
 
 
 
 
 
e1ae1af
 
8d44475
 
 
 
 
 
 
e1ae1af
 
b458005
 
 
 
 
 
 
 
d8c8177
 
 
 
 
 
 
c64138a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72c2a85
 
 
 
 
 
 
 
 
71c5f81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aff570a
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
###### μ°Έκ³ : https://wikidocs.net/340866
###### 참고자료(GraphRAG ToolsRetriever): https://github.com/gongwon-nayeon/graphrag-tools-retriever
###### ν•˜λ„€μŠ€ μ—”μ§€λ‹ˆμ–΄λ§: Globalμ§€μΉ¨, Skills와 Workflowλ₯Ό λͺ¨λ‘ ν¬ν•¨ν•˜λŠ” μ§€μΉ¨
###### 개발 μ‹œμž‘λΆ€ν„° λ°°ν¬κΉŒμ§€ λͺ¨λ“  것은 AGENTS.md에 κΈ°λ‘ν•œλ‹€.
###### 예λ₯Όλ“€μ–΄ 개발 λ‹¨κ³„μ—μ„œ 체크리슀트λ₯Ό λ§Œλ“€μ–΄μ„œ κ°œλ°œμ„ ν•  λ•Œλ§ˆλ‹€ ν•˜λ‚˜μ”© μ²΄ν¬ν•˜λ„λ‘ μ§€μ‹œν•œλ‹€.

# AGENTS.md

## ν”„λ‘œμ νŠΈ κ°œμš”
- λͺ©μ : AI 기반 ν•€ν…Œν¬ 기술의 νŠΈλ Œλ“œλ₯Ό νŒŒμ•…ν•˜λ„λ‘ λ•λŠ” 챗봇
- μ–Έμ–΄: Python 3.10
- κΈ°μˆ μŠ€νƒ: GraphRAG, LangChain, LangGraph, Neo4j, HugingFace, Gradio

## 디렉토리 ꡬ쑰
FinGraph/
β”œβ”€β”€ app.py                  # Gradio + LangGraph 챗봇 (HF 배포 μ§„μž…μ  및 Fail-Fast μžκ°€ 진단 μ‹€ν–‰)
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ __init__.py         # νŒ¨ν‚€μ§€ μ΄ˆκΈ°ν™” 파일 (Import-time λΆ€ν•˜ λ°©μ§€μš© 빈 파일)
β”‚   β”œβ”€β”€ utils/              # μœ ν‹Έλ¦¬ν‹° λͺ¨λ“ˆ 폴더
β”‚   β”‚   └── ui_templates.py # Gradio GNB HTML, 프리미엄 μ»€μŠ€ν…€ CSS 및 톡계 μΉ΄λ“œ λ Œλ”λ§μš© λ§ˆν¬μ—… ν…œν”Œλ¦Ώ
β”‚   β”œβ”€β”€ graphBuilder/       # 지식 κ·Έλž˜ν”„ 생성 μ—”μ§„
β”‚   β”‚   β”œβ”€β”€ scrapping/      # λ‰΄μŠ€ 데이터 μˆ˜μ§‘ λ ˆμ΄μ–΄
β”‚   β”‚   β”‚   └── finScrapping.py # Selenium 기반 금육 AI νƒ€κ²Ÿ 넀이버 λ‰΄μŠ€ μ‹€μ‹œκ°„/동적 ꡐ차 크둀러
β”‚   β”‚   └── neo4j/          # κ·Έλž˜ν”„ 데이터 적재 λ ˆμ΄μ–΄
β”‚   β”‚       └── finGraph.py # LLM(gpt-4o) + LangGraph 기반 점진적 μ—”ν‹°ν‹°/관계 μΆ”μΆœ 및 Neo4j AuraDB 적재 νŒŒμ΄ν”„λΌμΈ
β”‚   └── retrieval/          # GraphRAG 검색 λ ˆμ΄μ–΄
β”‚       └── finRetrieval.py # Vector / VectorCypher / Text2Cypher ν•˜μ΄λΈŒλ¦¬λ“œ 검색 기반 GraphRAG μ—”μ§„
β”œβ”€β”€ scripts/                # μœ μ§€λ³΄μˆ˜ 및 데이터 적재 μœ ν‹Έλ¦¬ν‹° 슀크립트
β”‚   β”œβ”€β”€ delete_zero_rel_articles.py  # 관계가 μ—†λŠ” 고립 기사 정리 (둜컬 μ •λ¦¬μš©)
β”‚   β”œβ”€β”€ inject_fintech_gold_data.py  # 포트폴리였용 κ³¨λ“œ 데이터 μ£Όμž… 및 Neo4j μŠ€ν‚€λ§ˆ μ •ν•©μ„± 톡합 슀크립트
β”‚   β”œβ”€β”€ plot_keywords.py             # μˆ˜μ§‘ ν‚€μ›Œλ“œ λΉˆλ„ μ‹œκ°ν™” 및 뢄석
β”‚   β”œβ”€β”€ reset_db.py                  # λ°μ΄ν„°λ² μ΄μŠ€ μ™„μ „ μ΄ˆκΈ°ν™” (μ œμ•½ 쑰건 μž¬μ„€μ • 포함)
β”‚   └── run_pipeline.py              # νŒŒμ΄ν”„λΌμΈ(크둀링+λΉŒλ“œ) 순차 μ‹€ν–‰ μœ ν‹Έλ¦¬ν‹°
β”œβ”€β”€ tests/                  # ν…ŒμŠ€νŠΈ 및 검증 디렉토리
β”‚   β”œβ”€β”€ smoke_test_rag.py   # κ·Έλž˜ν”„ μ—°κ²°μ„±(밀도 3.0 이상) 및 4λŒ€ μ‹œλ‚˜λ¦¬μ˜€ RAG 톡합 검증
β”‚   └── test_retrieval.py   # RAG 핡심 λ™μž‘ 및 ν•˜μ΄λΈŒλ¦¬λ“œ 검색기 λ‹¨μœ„ ν…ŒμŠ€νŠΈ
β”œβ”€β”€ Dockerfile              # Hugging Face Spaces(Gradio ꡬ동 ν™˜κ²½) 배포용 μ»¨ν…Œμ΄λ„ˆ λΉŒλ“œ λͺ…μ„Έ
β”œβ”€β”€ requirements.txt        # ν”„λ‘œλ•μ…˜/Hugging Face λΉŒλ“œ ν¬λž˜μ‹œ λ°©μ§€μš© 핡심 μ˜μ‘΄μ„± λͺ©λ‘
β”œβ”€β”€ .env.example            # API ν‚€ 및 Neo4j 접속 정보 μ„€μ • μ˜ˆμ‹œ ν…œν”Œλ¦Ώ
β”œβ”€β”€ .gitignore              # references/, λ°±μ—… 파일, 크둀링 μ—‘μ…€(Articles_*.xlsx) λ“± λΆˆν•„μš”ν•œ 파일의 μ—…λ‘œλ“œλ₯Ό μ›μ²œ μ°¨λ‹¨ν•˜λŠ” Git ν•„ν„°
β”œβ”€β”€ .pre-commit-config.yaml # ruff, mypy, black λ“± 정적 뢄석을 컀밋 전에 μžλ™ μˆ˜ν–‰ν•˜λŠ” 검증 ν›…
β”œβ”€β”€ pyproject.toml          # ruff 및 mypy λ“±μ˜ 개발 도ꡬ 린트 λ£° ꡬ성 μ„€μ •
β”œβ”€β”€ AGENTS.md               # AI μ—μ΄μ „νŠΈ 개발 μ§€μΉ¨, 디렉토리 ꡬ쑰, 재발 λ°©μ§€ λŒ€μ±… 및 ν”„λ‘œμ νŠΈ νžˆμŠ€ν† λ¦¬ 기둝 (λ³Έ 파일)
β”œβ”€β”€ README.md               # Hugging Face Spaces μ•± κΈ°λ³Έ μ„€μ • 메타데이터 및 전체 ν”„λ‘œμ νŠΈ κ°œμš”μ™€ μ•„ν‚€μ²˜ λͺ…μ„Έ
β”œβ”€β”€ LICENSE                 # μ˜€ν”ˆμ†ŒμŠ€ 배포λ₯Ό μœ„ν•œ MIT 정식 λΌμ΄μ„ μŠ€ 파일
└── .github/
    └── workflows/
        β”œβ”€β”€ ci.yml               # GitHub Actions CI 검증 μ›Œν¬ν”Œλ‘œμš° (Ruff/Mypy/Pytest)
        β”œβ”€β”€ daily_pipeline.yml   # 맀일 μƒˆλ²½ 1μ‹œ νŒŒμ΄ν”„λΌμΈ (λΉ„ν™œμ„±, μΆ”ν›„ μŠ€μΌ€μ€„λŸ¬ μž¬κ°€λ™μš© λͺ…μ„Έ μž₯μ°©)
        └── deploy.yml           # HF Spaces 배포 μ›Œν¬ν”Œλ‘œμš° (Git Push 트리거 동기화)

> [!IMPORTANT]
> **λΆˆν•„μš”ν•œ μž„μ‹œ 파일, μ—‘μ…€ 데이터 파일(Articles_*.xlsx), 둜컬 λΆ„μ„μš© λ…ΈνŠΈλΆ(references/), 패치용 슀크립트 등은 μ ˆλŒ€λ‘œ κΉƒν—ˆλΈŒ(GitHub) μ €μž₯μ†Œμ— μ—…λ‘œλ“œλ˜κ±°λ‚˜ λ°°ν¬λ˜μ§€ μ•Šλ„λ‘ `.gitignore` νŒŒμΌμ— μ™„λ²½ν•˜κ²Œ λ“±λ‘ν•˜μ—¬ μ €μž₯μ†Œλ₯Ό 항상 κΉ¨λ—ν•˜κ²Œ μœ μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.**

## μ½”λ“œ κ·œμΉ™
- ν•¨μˆ˜λͺ…: snake_case
- 클래슀λͺ…: PascalCase
- λ³€μˆ˜λͺ…: camelCase
- ν•œ ν•¨μˆ˜λŠ” ν•˜λ‚˜μ˜ μ—­ν• λ§Œ μˆ˜ν–‰ν•œλ‹€
- νƒ€μž… 힌트 ν•„μˆ˜
- **λͺ¨λ“  파일 μ΅œμƒλ‹¨μ—λŠ” 파일의 상세 μ—­ν• , μž‘μ„±μž, λΌμ΄μ„ μŠ€ 등을 ν•œκΈ€ μ£Όμ„μœΌλ‘œ μ™„λ²½ν•˜κ²Œ λͺ…μ‹œν•˜κ³ , 각 λͺ¨λ“ˆ, 클래슀, ν•¨μˆ˜μ—λ„ μƒμ„Έν•œ ν•œκΈ€ λ…μŠ€νŠΈλ§(Docstring) 및 인라인 주석을 달아야 ν•œλ‹€.**

- **지식 κ·Έλž˜ν”„ 적재 κ·œμΉ™ (Incremental Load)**: κΈ°μ‘΄ 데이터λ₯Ό 전체 μ‚­μ œ(DETACH DELETE)ν•˜μ§€ μ•Šκ³ , 이미 적재된 기사(`article_id`) 및 청킹이 μ™„λ£Œλœ `Content` λ…Έλ“œλŠ” OpenAI API(Chat/Embeddings) 호좜 낭비와 속도 μ €ν•˜λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ **λ°˜λ“œμ‹œ μ΄ˆκ³ μ† μŠ€ν‚΅(Skip)**ν•˜λ„λ‘ κ΅¬ν˜„ν•œλ‹€.
- **Neo4j 인증 ν¬λ ˆλ΄μ…œ κ·œμΉ™**: AuraDB λ“±μ˜ ν΄λΌμš°λ“œ ν™˜κ²½ 접속 μ‹œ 인증(Unauthorized) 였λ₯˜λ₯Ό μ™„λ²½νžˆ λ°©μ§€ν•˜κΈ° μœ„ν•΄, λ“œλΌμ΄λ²„ μ—°κ²° μ‹œ `NEO4J_USERNAME`κ³Ό `NEO4J_PASSWORD` ν™˜κ²½ λ³€μˆ˜λ§Œ λ‹¨λ…μœΌλ‘œ ν•˜λ“œμ½”λ”©ν•˜κ±°λ‚˜ μ˜μ‘΄ν•˜λŠ” 것을 **μ—„κ²©νžˆ κΈˆμ§€**ν•œλ‹€. λ°˜λ“œμ‹œ `NEO4J_CLIENT_ID`와 `NEO4J_CLIENT_SECRET`을 μš°μ„  κ°μ§€ν•˜μ—¬ μžλ™ λ§΅ν•‘(Fallback)ν•˜λŠ” μœ μ—°ν•œ 인증 μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό ν•œλ‹€.
- **κ·Έλž˜ν”„ 관계 μ—°κ²° κ·œμΉ™ (Graph Connectivity)**: μ—”ν‹°ν‹° κ°„ 직접 관계(DEVELOPS, APPLIES, USED_IN λ“±)κ°€ λ°˜λ“œμ‹œ μ μž¬λ˜μ–΄μ•Ό ν•œλ‹€. `extract_relations` λ…Έλ“œμ—μ„œ LLM이 λ°˜ν™˜ν•œ source/target 이름이 μ‹€μ œ `extract_entities`μ—μ„œ μΆ”μΆœλœ 이름과 **μ •ν™•νžˆ 일치**ν•˜λŠ”μ§€ κ²€μ¦ν•œ ν›„μ—λ§Œ Neo4j에 μ μž¬ν•œλ‹€. μ—”ν‹°ν‹°κ°€ 2개 이상 μΆ”μΆœλ˜μ—ˆμŒμ—λ„ 관계가 0개인 경우 **μ΅œλŒ€ 2회 μžκΈ°λ°˜μ„±(Self-Reflection) λ£¨ν”„λ‘œ μž¬μΆ”μΆœ**을 κ°•μ œν•œλ‹€.
- **κ·Έλž˜ν”„ 관계 밀도 κΈ°μ€€ (Coverage)**: `smoke_test_rag.py`의 사전 점검 λ‹¨κ³„μ—μ„œ **기사당 평균 μ—”ν‹°ν‹° κ°„ 직접 관계 3.0개 이상**을 μ΅œμ†Œ κΈ°μ€€μœΌλ‘œ κ²€μ¦ν•œλ‹€. 이 기쀀을 λ―Έλ‹¬ν•˜λ©΄ νŒŒμ΄ν”„λΌμΈ μž¬μ‹€ν–‰μ΄ ν•„μš”ν•˜λ‹€.
- **LLM λͺ¨λΈ κ·œμΉ™ (Model Governance)**: μ—”ν‹°ν‹°/관계 μΆ”μΆœ(`finGraph.py`)μ—λŠ” **λ°˜λ“œμ‹œ `gpt-4o`** λ₯Ό μ‚¬μš©ν•˜μ—¬ κ·Έλž˜ν”„ ν’ˆμ§ˆμ„ μ΅œλŒ€ν™”ν•œλ‹€. RAG 검색 및 λ‹΅λ³€ 생성(`finRetrieval.py`), μž„λ² λ”©μ—λŠ” `gpt-4o-mini`와 `text-embedding-3-small`을 μ‚¬μš©ν•œλ‹€. λΉ„μš© μ ˆκ°μ„ 이유둜 μ—”ν‹°ν‹°/관계 μΆ”μΆœ λͺ¨λΈμ„ `gpt-4o-mini`둜 λ‹€μš΄κ·Έλ ˆμ΄λ“œν•˜λŠ” 것을 **μ—„κ²©νžˆ κΈˆμ§€**ν•œλ‹€.

## μ ˆλŒ€ κΈˆμ§€
- 'references/' 파일 μˆ˜μ • κΈˆμ§€ (참고자료, 둜컬 μ „μš©)
- Neo4j λ“œλΌμ΄λ²„ μ—°κ²° μ‹œ `NEO4J_USERNAME`, `NEO4J_PASSWORD`λ§Œμ„ μš”κ΅¬ν•˜κ±°λ‚˜ μ‚¬μš©ν•˜λŠ” λ°©μ‹μ˜ μ˜›λ‚  μ½”λ“œ μž‘μ„± μ ˆλŒ€ κΈˆμ§€ (Connection Client Credentials 병행 λ§€ν•‘ ν•„μˆ˜)

## 🚨 재발 λ°©μ§€ 및 치λͺ…적 μ•ˆν‹° νŒ¨ν„΄ κΈˆμ§€ (Recurring Issues Prevention)
이 ν”„λ‘œμ νŠΈμ—μ„œ 3회 이상 반볡적으둜 λ°œμƒν•˜μ—¬ 전체 νŒŒμ΄ν”„λΌμΈ(둜컬, CI, ν”„λ‘œλ•μ…˜)을 λΆ•κ΄΄μ‹œμΌ°λ˜ 핡심 μž₯애듀을 영ꡬ적으둜 μ°¨λ‹¨ν•˜κΈ° μœ„ν•œ ν•„μˆ˜ κ·œμΉ™ 및 λ°©μ–΄ ν…ŒμŠ€νŠΈμž…λ‹ˆλ‹€.

- **1. Import-Time DB Connection 및 API Client 객체 생성 μ ˆλŒ€ κΈˆμ§€ (CI ν¬λž˜μ‹œ λ°©μ§€)**
  - **원인**: λͺ¨λ“ˆ μ „μ—­ λ²”μœ„(Global Scope)μ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ¦‰μ‹œ μ—°κ²°(`driver = get_neo4j_driver()`)ν•˜κ±°λ‚˜ OpenAI API ν‚€κ°€ ν•„μš”ν•œ ν΄λΌμ΄μ–ΈνŠΈ 객체(`OpenAILLM`, `OpenAIEmbeddings`)λ₯Ό μ„ μ–Έν•˜μ—¬, GitHub Actions(CI)λ‚˜ `pytest`κ°€ ν…ŒμŠ€νŠΈλ₯Ό μˆ˜μ§‘(`import`)ν•˜κΈ°λ§Œ 해도 접속 λΆˆκ°€ μ—λŸ¬(`Connection refused`)λ‚˜ API Key λˆ„λ½ μ—λŸ¬(`OpenAIError`)둜 λ»—μ–΄λ²„λ¦¬λŠ” 문제 지속 λ°œμƒ.
  - **κ·œμΉ™**: λͺ¨λ“ˆ μž„ν¬νŠΈ μ‹œμ μ—λŠ” μ ˆλŒ€ μ™ΈλΆ€ DBλ‚˜ API ν΄λΌμ΄μ–ΈνŠΈμ™€ 톡신/μ΄ˆκΈ°ν™”ν•˜μ§€ 말 것. DB λ“œλΌμ΄λ²„, LLM, Embeddings μΈμŠ€ν„΄μŠ€λŠ” λ°˜λ“œμ‹œ `LazyGraphRAG` ν”„λ‘μ‹œ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ μ‹€μ œ 쿼리(`search`)λ‚˜ μžκ°€ 진단(`_init_once()`) 호좜 μ‹œμ μ— 단 1회 μ§€μ—° μ΄ˆκΈ°ν™”(`Lazy Initialization`) λ˜λ„λ‘ 섀계해야 함. `finGraph.py` μ—­μ‹œ 전역이 μ•„λ‹Œ `main()` λ‚΄λΆ€μ—μ„œ λ“œλΌμ΄λ²„λ₯Ό λŸ°νƒ€μž„ μ΄ˆκΈ°ν™”ν•  것.
  - **λ°©μ–΄ ν…ŒμŠ€νŠΈ**: `env -i .venv/bin/python3 -c "import src.retrieval.finRetrieval"` 및 `env -i .venv/bin/python3 -c "import src.graphBuilder.neo4j.finGraph"` λͺ…령을 μ‹€ν–‰ν–ˆμ„ λ•Œ, μ™ΈλΆ€ 접속 및 API ν‚€ 검증 없이 즉각 0.2초 λ§Œμ— 정상 μ’…λ£Œλ˜λŠ”μ§€ 점검 ν›„ 컀밋할 것.

- **2. ν”„λ‘œλ•μ…˜ Fail-Fast μžκ°€ 진단 ν•„μˆ˜ (침묡의 λŸ°νƒ€μž„ μ—λŸ¬ λ°©μ§€)**
  - **원인**: ν—ˆκΉ…νŽ˜μ΄μŠ€(HF Spaces) 배포 μ‹œ DB μ—°κ²° ν™˜κ²½ λ³€μˆ˜κ°€ λˆ„λ½λ˜μ—ˆμŒμ—λ„ λΆˆκ΅¬ν•˜κ³  μ›Ή 앱은 μ •μƒμ μœΌλ‘œ μΌœμ§„ μ²™(Running) ν•˜λ‹€κ°€, μ‚¬μš©μžκ°€ 처음 μ§ˆλ¬Έμ„ λ˜μ§„ μˆœκ°„ 500 λ‚΄λΆ€ μ—λŸ¬λ₯Ό 뿜으며 λ»—μ–΄λ²„λ¦¬λŠ” μ‹¬κ°ν•œ 운영 μž₯μ•  λ°œμƒ.
  - **κ·œμΉ™**: 배포 μ§„μž…μ (`app.py`) ꡬ동 μ‹œμ μ—λŠ” μ§€μ—° μ΄ˆκΈ°ν™”λ₯Ό λ¬΄μ‹œν•˜κ³  κ°•μ œλ‘œ μ¦‰μ‹œ μ—°κ²°(`graphrag._init_once()`)을 μ‹œλ„ν•˜μ—¬, μ‹€νŒ¨ μ‹œ μ•± ꡬ동 자체λ₯Ό μ‹€νŒ¨μ‹œν‚€λŠ” `Fail-Fast` μžκ°€ 진단 μ½”λ“œλ₯Ό `app.py` 상단에 λ°˜λ“œμ‹œ μœ μ§€ν•  것.

- **4. κ·Έλž˜ν”„ 관계 μ—°κ²° λˆ„λ½ (Graph Isolation Prevention)**
  - **원인**: `extract_relations` ν”„λ‘¬ν”„νŠΈμ˜ JSON μ§€μ‹œλ¬Έ μ˜€νƒ€(`곡으둜만:` λ“±)둜 인해 LLM이 JSON을 정상 μƒμ„±ν•˜μ§€ λͺ»ν•˜κ±°λ‚˜, LLM이 λ°˜ν™˜ν•œ source/target 이름이 `extract_entities`μ—μ„œ 뽑은 이름과 λ―Έμ„Έν•˜κ²Œ 달라(`AI` vs `인곡지λŠ₯`) 관계 ν•„ν„°μ—μ„œ μ „λŸ‰ μ œκ±°λ˜λŠ” λ¬Έμ œκ°€ 반볡 λ°œμƒ. 결과적으둜 μ—”ν‹°ν‹° λ…Έλ“œλŠ” 수백 개인데 관계선(DEVELOPS λ“±)은 κ·Ήμ†Œμˆ˜μ΄κ±°λ‚˜ μ™„μ „νžˆ λˆ„λ½λ˜μ–΄ κ·Έλž˜ν”„κ°€ 사싀상 λ¬΄μ˜λ―Έν•΄μ§€λŠ” μ‹¬κ°ν•œ ν’ˆμ§ˆ μ €ν•˜ λ°œμƒ.
  - **κ·œμΉ™**: β‘ ν”„λ‘¬ν”„νŠΈμ—μ„œ μ—”ν‹°ν‹° 이름 λͺ©λ‘μ„ λͺ…μ‹œμ μœΌλ‘œ μ „λ‹¬ν•˜μ—¬ LLM이 동일 이름을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜λ„λ‘ κ°•μ œ. ⑑관계 μΆ”μΆœ ν›„ source/target 이름을 μ—”ν‹°ν‹° μ§‘ν•©κ³Ό λŒ€μ‘°ν•˜μ—¬ 뢈일치 μ‹œ Self-Reflection ν”Όλ“œλ°±μœΌλ‘œ μž¬μΆ”μΆœ(μ΅œλŒ€ 2회). β‘’μ—”ν‹°ν‹°κ°€ 2개 이상인데 관계가 0개이면 κ²½κ³  둜그λ₯Ό 남기며, `smoke_test_rag.py`μ—μ„œ **기사당 평균 3.0개 μ΄μƒμ˜ μ—”ν‹°ν‹° 관계** 기쀀을 μžλ™ 점검.
  - **λ°©μ–΄ ν…ŒμŠ€νŠΈ**: `python tests/smoke_test_rag.py` μ‹€ν–‰ μ‹œ `[μ—”ν‹°ν‹° κ°„ 직접 관계 μ—°κ²°μ„± 점검]` μ„Ήμ…˜μ—μ„œ λͺ¨λ“  관계 μœ ν˜•(DEVELOPS/INVESTS_IN/PARTNERS_WITH/APPLIES/USED_IN/RELATED_TO)의 μˆ˜μ™€ 고립 λ…Έλ“œ λΉ„μœ¨, 기사당 평균 관계 μˆ˜κ°€ 좜λ ₯되며 μž„κ³„κ°’(3.0) μ΄μƒμž„μ„ λ°˜λ“œμ‹œ 확인 ν›„ 컀밋.

- **3. νŒ¨ν‚€μ§€ μ˜μ‘΄μ„± 및 νƒ€μž… 엄격 검증 (Hugging Face λΉŒλ“œ ν¬λž˜μ‹œ λ°©μ§€)**
  - **원인**: λ‘œμ»¬μ—μ„œλŠ” 잘 λŒμ•„κ°€λŠ”λ°, ν—ˆκΉ…νŽ˜μ΄μŠ€ ν”„λ‘œλ•μ…˜ ν™˜κ²½μ—μ„œ `audioop`, `huggingface_hub` λ“± λͺ¨λ“ˆ λˆ„λ½μ΄λ‚˜ MyPy νƒ€μž… μ—λŸ¬(`Format Error`)둜 λŸ°νƒ€μž„ ν¬λž˜μ‹œκ°€ 3회 이상 λ°œμƒ.
  - **κ·œμΉ™**: μƒˆλ‘œμš΄ λΌμ΄λΈŒλŸ¬λ¦¬λ‚˜ κΈ°λŠ₯ μΆ”κ°€ μ‹œ 무쑰건 `requirements.txt`에 λͺ…μ‹œν•  것. 컀밋 직전 `mypy src tests --ignore-missing-imports` 및 `ruff check .`λ₯Ό 돌렀 단 1개의 경고도 남기지 말 것.
  - **λ°©μ–΄ ν…ŒμŠ€νŠΈ**: 컀밋 μ „ 무쑰건 ν„°λ―Έλ„μ—μ„œ `python -c "import app"`을 μ‹€ν–‰ν•˜μ—¬ Gradio λΉŒλ“œ 단계 및 μ˜μ‘΄μ„± μ—λŸ¬κ°€ μ—†λŠ”μ§€ ν˜„μž₯ 점검 ν›„ ν‘Έμ‹œν•  것.

## COMMIT κ·œμΉ™
- 컀밋 λ©”μ‹œμ§€: 'feat:', 'fix:', 'refactor:' 접두사 μ‚¬μš©
- push ν•˜λ‚˜μ— ν•˜λ‚˜μ˜ λ³€κ²½λ§Œ
- ν…ŒμŠ€νŠΈ μ—†λŠ” pushλŠ” μ˜¬λ¦¬μ§€ μ•ŠλŠ”λ‹€

## ν…ŒμŠ€νŠΈ
- ν…ŒμŠ€νŠΈ 파일 μœ„μΉ˜: 'tests/' 디렉토리
- μ‹€ν–‰ λͺ…λ Ή: 'pytest tests/'
- λ°˜λ“œμ‹œ μ˜ˆμ‹œ μž…λ ₯으둜 ν…ŒμŠ€νŠΈν•œλ‹€

### ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ‘œ κΈ°λŒ€ λ™μž‘ λͺ…μ‹œ
이 ν”„λ‘œμ νŠΈλŠ” κΈ°λŠ₯의 μ•ˆμ •μ„±μ„ μœ„ν•΄ RAG μ‹œλ‚˜λ¦¬μ˜€ ν…ŒμŠ€νŠΈ μ½”λ“œκ°€ ν•„μˆ˜μ μœΌλ‘œ 톡과해야 ν•©λ‹ˆλ‹€.

#### RAG μ‹œλ‚˜λ¦¬μ˜€ ν…ŒμŠ€νŠΈ (Integration Test) - μ˜ˆμ‹œ: `GraphRAG`
μ‹€μ œ λ‰΄μŠ€ 지식 κ·Έλž˜ν”„κ°€ λΉŒλ“œλœ ν›„, μž„μ˜μ˜ μ΅œμ‹  데이터λ₯Ό λ™μ μœΌλ‘œ νƒμƒ‰ν•˜μ—¬ 포트폴리였 μˆ˜μ€€μ˜ 완성도 높은 닡변을 λ„μΆœν•˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

```python
# tests/test_retrieval.py (λ˜λŠ” smoke_test_rag.py)
def test_4_core_scenarios():
    """
    [포트폴리였 핡심 4λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€]
    Gradio 앱에 λ“±λ‘λœ 4κ°€μ§€ λŒ€ν‘œ 예제 μ§ˆμ˜κ°€ μ™„λ²½νžˆ 응닡을 λ°˜ν™˜ν•˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.
    """
    scenarios = [
        "μ‚Όμ„±μ „μžμ˜ 졜근 AI 기술 νŠΈλ Œλ“œλŠ”?",
        "μΉ΄μΉ΄μ˜€κ°€ 개발 쀑인 AI μ„œλΉ„μŠ€ λͺ©λ‘μ„ μ•Œλ €μ€˜",
        "μ–΄λ–€ 기업이 LLM κΈ°μˆ μ„ κ°œλ°œν•˜λ‚˜μš”?",
        "졜근 AI κ΄€λ ¨ λ‰΄μŠ€ 기사λ₯Ό μš”μ•½ν•΄μ€˜"
    ]
    
    for query in scenarios:
        response = graphrag.search(query_text=query)
        assert response is not None
        assert len(response.answer.strip()) > 0
        # 좜처(기사 λ“±)κ°€ λ°˜λ“œμ‹œ ν¬ν•¨λ˜μ–΄μ•Ό 함
        assert any(indicator in response.answer for indicator in ["기사", "좜처", "λ‰΄μŠ€", "보도"])
```

## μžλ™ 검사 및 λŸ°νƒ€μž„ μ—λŸ¬ λ°©μ§€
- 둜컬 개발 ν™˜κ²½μ—μ„œ μ»€λ°‹ν•˜κΈ° μ „, λ°˜λ“œμ‹œ 터미널에 `ruff check .` 및 `mypy src tests --ignore-missing-imports` λͺ…λ Ήμ–΄λ₯Ό 직접 μ‹€ν–‰ν•˜μ—¬ 린트 및 μ—„κ²©ν•œ νƒ€μž… 였λ₯˜λ₯Ό ν™•μ‹€ν•˜κ²Œ ν™•μΈν•˜κ³  λͺ¨λ‘ κ³ μΉ  것 (였λ₯˜κ°€ λ‚¨μ•„μžˆλŠ” μƒνƒœλ‘œ 컀밋 κΈˆμ§€).
- **λ°±μ—”λ“œ(RAG) λŸ°νƒ€μž„ μ—λŸ¬ λ°©μ§€**: 린트/νƒ€μž… 검사 ν›„ λ°˜λ“œμ‹œ `python tests/smoke_test_rag.py`λ₯Ό λ‘œμ»¬μ—μ„œ μ‹€ν–‰ν•˜μ—¬ `neo4j.exceptions.AuthError` λ“±μ˜ λŸ°νƒ€μž„ μ—λŸ¬κ°€ ν„°μ§€μ§€ μ•Šκ³  μ™„λ²½νžˆ RAG κ²°κ³Όκ°€ 좜λ ₯λ˜λŠ”μ§€ ν˜„μž₯ 점검(Smoke Test) ν›„ ν‘Έμ‹œν•  것.
- **ν”„λ‘ νŠΈμ—”λ“œ(Gradio) λŸ°νƒ€μž„ μ—λŸ¬ λ°©μ§€**: `python -c "import app"` λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜μ—¬ Gradio λΉŒλ“œ(`gr.ChatInterface` λ“±) μ΄ˆκΈ°ν™” κ³Όμ •μ—μ„œ νƒ€μž… 뢈일치(Format Error)λ‚˜ μž„ν¬νŠΈ μ—λŸ¬κ°€ ν„°μ§€μ§€ μ•ŠλŠ”μ§€ 확인 ν›„ 컀밋할 것.
- 컀밋 μ „ `pre-commit` μžλ™ μ‹€ν–‰
- `ruff`, `mypy` 검사 톡과 ν•„μˆ˜
- 검사 μ‹€νŒ¨ μ‹œ 컀밋 λΆˆκ°€

## 개발 체크리슀트 (데이터 ν™•μΆ© 및 RAG ν’ˆμ§ˆ κ°œμ„  단계)
- [x] **1. 기사 데이터 λŒ€λŸ‰ μˆ˜μ§‘**: `finScrapping.py`의 μˆ˜μ§‘λŸ‰/λΆ„μ•Όλ₯Ό μ‘°μ ˆν•˜μ—¬ μ΅œμ†Œ 100건 μ΄μƒμ˜ ν’λΆ€ν•œ λ‰΄μŠ€ 데이터 ν’€(Pool) 확보. (총 74건의 κ³ ν’ˆμ§ˆ μ‹€λ¬Ό λ‰΄μŠ€ 데이터 μˆ˜μ§‘ μ™„λ£Œ)
- [x] **2. 지식 κ·Έλž˜ν”„ 밀도 ν–₯상**: ν™•λ³΄λœ 데이터λ₯Ό `finGraph.py`λ₯Ό 톡해 Neo4j에 μ μž¬ν•˜μ—¬ Company, Technology λ“±μ˜ λ…Έλ“œμ™€ 관계선(Edge) λŒ€ν­ ν™•μž₯. (총 296개의 λ…Έλ“œ 및 346개의 κ΄€κ³„μ„ μœΌλ‘œ μ΄ˆκ³ λ°€λ„ μ€ν•˜μˆ˜ μŠ€μΌ€μΌ κ·Έλž˜ν”„ ꡬ좕 μ™„λ£Œ)
- [x] **3. ν™˜κ°(Hallucination) λ°©μ§€ ν”„λ‘¬ν”„νŠΈ κ°•ν™”**: `finRetrieval.py`의 ν”„λ‘¬ν”„νŠΈμ— "λ°˜λ“œμ‹œ 제곡된 검색 κ²°κ³Ό 기반으둜만 λ‹΅λ³€ν•˜κ³ , μ—†λŠ” κΈ°μ—…μ΄λ‚˜ κ°€μ§œ URL(example.com λ“±)은 μ ˆλŒ€ μ§€μ–΄λ‚΄μ§€ 말 것"을 λͺ…μ‹œ. (μ² λ²½ ν”„λ‘¬ν”„νŠΈ κ°€λ“œλ ˆμΌ 섀계 μ™„λ£Œ)
- [x] **4. 4λŒ€ 핡심 μ‹œλ‚˜λ¦¬μ˜€ μ΅œμ’… 톡과**: `tests/smoke_test_rag.py`λ₯Ό μž¬μ‹€ν–‰ν•˜μ—¬ κ°€μ§œ λ§ν¬λ‚˜ μ™ΈλΆ€ 지식 κ°œμž… 없이, μˆ˜μ§‘λœ κ΅­λ‚΄ λ‰΄μŠ€ 기반으둜 μ™„λ²½νžˆ λ‹΅λ³€ν•˜λŠ”μ§€ 검증. (ν•˜μ΄λΈŒλ¦¬λ“œ μ˜ˆλΉ„ 검색기 및 Text2Cypher κ²°ν•©μœΌλ‘œ 4λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€ μ™„μ „ PASS 검증 성곡)

## 개발 체크리슀트 (UI/UX μ‹œκ°μ  κ°œμ„  단계)
- [x] **1. λŒ€μ‹œλ³΄λ“œ 톡계 쑰회 κ΅¬ν˜„**: Neo4j μ—°λ™ν•˜μ—¬ λ…Έλ“œ 카운트, κΈ°μ—…/기술 뱃지 및 μ΅œμ‹  λ‰΄μŠ€ ν”Όλ“œ 쑰회 κΈ°λŠ₯ κ΅¬ν˜„
- [x] **2. 2컬럼 Blocks λ ˆμ΄μ•„μ›ƒ 개편**: μ™Όμͺ½ μ»¬λŸΌμ— HTML/CSS λŒ€μ‹œλ³΄λ“œ μ‚½μž… 및 였λ₯Έμͺ½ μ»¬λŸΌμ— 챗봇 μ»΄ν¬λ„ŒνŠΈ 이식
- [x] **3. μ»€μŠ€ν…€ CSS 및 λ²„νŠΌ κ³ λŒ€λΉ„ν™”**: 흰색 λ°°κ²½μ—μ„œ λ²„νŠΌμ΄ μ™„λ²½ν•˜κ²Œ 보이도둝 κ³ λŒ€λΉ„ Indigo/Blue 색상 및 프리미엄 μŠ€νƒ€μΌ μ§€μ •
- [x] **4. 정적/동적 λ°©μ–΄ ν…ŒμŠ€νŠΈ**: Ruff/Mypy 톡과, `python -c "import app"` 정상 λΉŒλ“œ, `smoke_test_rag.py` 성곡 검증

## 개발 체크리슀트 (Gradio UI/UX λ””ν…ŒμΌ κ°œμ„  단계)
- [x] **1. ν™”λ©΄ λ„ˆλΉ„ λŒ€ν­ ν™•λŒ€**: `.gradio-container` 및 블둝 λ ˆμ΄μ•„μ›ƒμ˜ max-widthλ₯Ό λŒ€ν­ ν™•μž₯ν•˜μ—¬ λŒ€ν™”λ©΄ 지원
- [x] **2. μ˜ˆμ‹œ 질문 μ΅œμƒλ‹¨(챗봇 μœ„) 이동**: CSS Flexbox order λ˜λŠ” Blocks ꡬ쑰 κ°œνŽΈμ„ 톡해 μ˜ˆμ‹œ μ§ˆλ¬Έμ„ ν™”λ©΄ 맨 μœ„λ‘œ κ³ μ •
- [x] **3. λ²„νŠΌ ν…Œλ‘λ¦¬ μ–‡κ²Œ κ°œμ„ **: μ˜ˆμ‹œ 질문 λ²„νŠΌμ˜ 포인트 보더 λ‘κ»˜λ₯Ό μΆ•μ†Œν•˜κ³  μ–‡κ³  κΉ”λ”ν•˜κ²Œ λ―Έλ‹ˆλ©€λ¦¬μ¦˜ λ””μžμΈ 적용
- [x] **4. 정적/동적 검증**: Ruff/Mypy 톡과 및 `browser_subagent`λ₯Ό ν†΅ν•œ μ‹€μ œ λ Œλ”λ§ 무결성 μŠ€ν¬λ¦°μƒ· 검증



## 배포 및 μžλ™ν™” νŒŒμ΄ν”„λΌμΈ (Pipeline Automation)
- [x] **맀일 μƒˆλ²½ 1μ‹œ(KST) μ΅œμ‹ ν™” νŒŒμ΄ν”„λΌμΈ ꡬ좕**: 크둀링(`finScrapping.py`) ➑️ 지식 κ·Έλž˜ν”„ 적재(`finGraph.py`)둜 μ΄μ–΄μ§€λŠ” μ—”λ“œνˆ¬μ—”λ“œ(End-to-End) μžλ™ν™”.
  - **ν˜„μž¬ μƒνƒœ: λΉ„ν™œμ„±ν™” (Temporarily Disabled)**
  - **λΉ„ν™œμ„±ν™” μ‚¬μœ **: 무인 μžλ™ μŠ€μΌ€μ€„ μ‹€ν–‰ μ‹œ λ°œμƒν•˜λŠ” OpenAI API 토큰 λΉ„μš©μ„ μ„Έμ΄λΈŒν•˜κ³ , ν–₯ν›„ μ˜ˆμ •λœ Neo4j ν΄λΌμš°λ“œ μΈμŠ€ν„΄μŠ€ λ³€κ²½ 및 이전(Migration) μž‘μ—…μ— μœ μ—°ν•˜κ²Œ λŒ€μ²˜ν•˜κΈ° μœ„ν•΄ μž„μ‹œ λΉ„ν™œμ„±ν™” μ²˜λ¦¬ν•΄ λ‘μ—ˆμŠ΅λ‹ˆλ‹€.
  - **κ΅¬ν˜„ μ™„λ£Œ λ‚΄μ—­**: `.github/workflows/daily_pipeline.yml` μ›Œν¬ν”Œλ‘œμš° λͺ…μ„Έ 및 연쇄 배포(HF Spaces) 동기화 μ²΄κ³„λŠ” 100% μ™„μ „ν•˜κ²Œ 섀계/κ΅¬ν˜„λ˜μ–΄ μž₯μ°©λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν˜„μž¬λŠ” μŠ€μΌ€μ€„ 크둠(`schedule cron`) λΆ€λΆ„λ§Œ μ£Όμ„μœΌλ‘œ 막아둔 μ•ˆμ „ μƒνƒœμ΄λ©°, ν–₯ν›„ μΈμŠ€ν„΄μŠ€ 이전이 μ™„λ£Œλ˜λ©΄ μ£Όμ„λ§Œ ν’€μ–΄ μ¦‰μ‹œ 가동할 수 μžˆμŠ΅λ‹ˆλ‹€.

## πŸ› οΈ 졜근 이슈 ν•΄κ²° λ‚΄μ—­ (2026-05-19)
- [x] **Hugging Face Spaces λŸ°νƒ€μž„ μ—λŸ¬(ValueError 및 Internal Server Error) ν•΄κ²°**:
  - **ν˜„μƒ**: Hugging Face Spaces ν™˜κ²½μ—μ„œ λΉŒλ“œλŠ” μ„±κ³΅ν•˜μ˜€μœΌλ‚˜ ꡬ동 μ‹œ ν˜Ήμ€ 첫 질의 μ‹œ λŸ°νƒ€μž„ μ—λŸ¬(ValueError) ν˜Ήμ€ 500 Internal Server Error(TypeError: unhashable type: 'dict') λ°œμƒ.
  - **원인**:
    1. `demo.launch()`에 ν˜ΈμŠ€νŠΈμ™€ 포트(`server_name="0.0.0.0"`, `server_port=7860`)λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ£Όμ§€ μ•Šμ•„ localhost 바인딩 μ‹œ μ™ΈλΆ€ 접근이 μ°¨λ‹¨λ˜λ©΄μ„œ `ValueError: When localhost is not accessible, a shareable link must be created.` μ—λŸ¬ λ°œμƒ.
    2. ꡬ버전 Gradio 4.44.0 ν™˜κ²½μ—μ„œ Jinja2/Starlette ν…œν”Œλ¦Ώ 직렬화 캐싱 도쀑 ν…Œλ§ˆ μ„€μ • λ§€ν•‘ 데이터가 `dict` ν‚€λ‘œ μΊμ‹œ 맀핑에 λ“€μ–΄κ°€λ©΄μ„œ `TypeError: unhashable type: 'dict'` ν¬λž˜μ‹œ λ°œμƒ.
  - **쑰치**:
    1. `app.py`의 `launch_kwargs`에 `server_name="0.0.0.0"`κ³Ό `server_port=7860`을 μƒμ‹œλ‘œ μ£Όμž…ν•˜λ„λ‘ μˆ˜μ • μ™„λ£Œ.
    2. `README.md`의 `sdk_version`을 둜컬 검증 사양인 `6.14.0`으둜 전격 상ν–₯ μ‘°μ •ν•˜κ³ , `requirements.txt`μ—μ„œλ„ `gradio>=6.0.0` 및 `huggingface_hub>=0.20.0`으둜 μ—…κ·Έλ ˆμ΄λ“œν•˜μ—¬ 둜컬-ν”„λ‘œλ•μ…˜ κ°„ ν™˜κ²½ 및 ν…Œλ§ˆ λ Œλ”λ§ 무결성을 100% μΌμΉ˜μ‹œν‚΄.
  - **검증**: `ruff`, `mypy` 검사λ₯Ό 단 1개의 였λ₯˜λ„ 없이 ν†΅κ³Όν•˜κ³  `pytest tests/` 및 3λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€ `smoke_test_rag.py`λ₯Ό 100% μ™„μ „ ν†΅κ³Όν•˜μ—¬ 완벽성을 보μž₯함.

- [x] **RAG 검색 κ³Όμ • μ‹€μ‹œκ°„ 진행상황 ν‘œμ‹œ 및 μ˜ˆμ‹œ 질문 응닡 λˆ„λ½ ν•΄κ²° (κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€ 4/4 100% 톡과)**:
  - **ν˜„μƒ**: RAG 검색 μ‹œ 닀단계 μ²˜λ¦¬κ°€ λ°œμƒν•˜μ—¬ 화면이 멈좰 μ‚¬μš©μžκ°€ λ‹΅λ‹΅ν•΄ν•˜λŠ” ν˜„μƒ λ°œμƒ. λ˜ν•œ, 크둀러의 동적 크둀링 νŠΉμ„±μœΌλ‘œ 인해 DB 내에 μ‚Όμ„±μ „μž/카카였 κ΄€λ ¨ μ‹€λ¬Ό 정보가 μΆ©λΆ„μΉ˜ μ•Šμ•„, 예제 질문 클릭 μ‹œ guardrail에 λ§‰ν˜€ "κ΄€λ ¨ 정보가 μ—†λ‹€"λŠ” 빈 닡변을 λ±‰λŠ” 문제 λ°œμƒ.
  - **쑰치**:
    1. `app.py`의 `chat()` ν•¨μˆ˜λ₯Ό 동적 Generator(`yield`) 기반으둜 μ „λ©΄ λ¦¬νŒ©ν† λ§ν•˜κ³  LangGraph의 `chat_graph.stream(state)`λ₯Ό μ—°λ™ν•˜μ—¬ `"πŸ” 검색 μ§„ν–‰ 쀑..."`, `"πŸ’‘ λ‹΅λ³€ 생성 쀑..."` 과정을 μ‹€μ‹œκ°„μœΌλ‘œ 화면에 λ…ΈμΆœν•˜λ„λ‘ UX λŒ€ν­ κ°•ν™”.
    2. μ‚Όμ„±μ „μž(Gauss 2, Galaxy AI, HBM3E, NPU) 및 카카였(Kanana, KoGPT 2.0, μΉ΄λ‚˜λ‚˜ μ›Œν¬)의 μ‹€μ œ κ³ ν’ˆμ§ˆ μ‹€λ¬Ό λ‰΄μŠ€ 아티클 및 μ—”ν‹°ν‹°/관계 ꡬ쑰, 벑터 μž„λ² λ”©μ„ AuraDB에 μ μž¬ν•˜λŠ” μ „μš© 슀크립트(`inject_gold_data.py`)λ₯Ό 개발 및 λ‘œλ“œ μ™„λ£Œ.
    3. `finRetrieval.py` λ‚΄μ˜ `Text2Cypher` μ˜ˆμ œλ“€κ³Ό RAG μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈλ₯Ό μ „λ©΄ κ°œνŽΈν•˜μ—¬ ꡬ쑰적 Cypher 검색 μ‹œμ—λ„ μ‹€μ œ κΈ°μ‚¬μ˜ 제λͺ© 및 URL([좜처 링크])을 μžλ™ λ§€ν•‘ν•˜μ—¬ λ‹΅λ³€ν•˜λ„λ‘ 좜처 신뒰성을 λŒ€ν­ κ°•ν™”.
  - **검증**: `ruff`, `mypy` λ¦°νŠΈμ™€ νƒ€μž… 검사λ₯Ό 무결점 ν†΅κ³Όν•˜μ˜€κ³ , 4λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€λ₯Ό κ²€μ¦ν•˜λŠ” `smoke_test_rag.py`μ—μ„œ **4/4 μ‹œλ‚˜λ¦¬μ˜€ 전원 μ΄ˆκ³ μ† μ™„μ „ 합격(PASS)**ν•˜μ—¬ 졜고의 완성도λ₯Ό μž…μ¦ν•¨.

- [x] **메인 μ§„μž…μ (app.py) ν”„λ ˆμ  ν…Œμ΄μ…˜ μžμ› λͺ¨λ“ˆν™” 및 클린 μ½”λ“œ 개편**:
  - **ν˜„μƒ**: 450쀄이 λ„˜λŠ” λ°©λŒ€ν•œ 정적 CSS μŠ€νƒ€μΌμ‹œνŠΈμ™€ HTML λ¬Έμžμ—΄ ν…œν”Œλ¦Ώ(GNB, 2x3 μƒνƒœ λŒ€μ‹œλ³΄λ“œ ν…œν”Œλ¦Ώ λ“±)이 메인 μ§„μž…μ μΈ `app.py` 내에 인라인으둜 μ„žμ—¬ μžˆμ–΄, 개발 μœ μ§€ 보수 νš¨μœ¨μ„±κ³Ό μ½”λ“œ 가독성이 ν˜„μ €νžˆ μ €ν•΄λ˜λŠ” 문제 확인.
  - **쑰치**:
    1. λͺ¨λ“  정적/동적 ν”„λ ˆμ  ν…Œμ΄μ…˜ μš”μ†Œ(`CUSTOM_CSS`, `GNB_HTML`, `build_stats_html`)λ₯Ό μ‹ κ·œ μœ ν‹Έλ¦¬ν‹° λͺ¨λ“ˆμΈ `src/utils/ui_templates.py`둜 μ™„λ²½ν•˜κ²Œ μ΄μ „ν•˜μ—¬ μ½”λ“œλ₯Ό 물리적으둜 μ™„μ „ 뢄리.
    2. `app.py`μ—μ„œλŠ” κ°„λ‹¨νžˆ `from src.utils.ui_templates import CUSTOM_CSS, build_stats_html`둜 μ°Έμ‘°ν•˜λ„λ‘ λ³€κ²½ν•¨μœΌλ‘œμ¨, 메인 μ§„μž…μ  μ½”λ“œκ°€ λ³Έμ—°μ˜ λŸ°νƒ€μž„ μ œμ–΄ 및 Gradio μ»΄ν¬λ„ŒνŠΈ μ„ μ–Έμ—λ§Œ μˆœμˆ˜ν•˜κ²Œ 집쀑할 수 μžˆλ„λ‘ μ΄ˆκ²½λŸ‰ 개편 μ™„λ£Œ.
  - **검증**: `ruff` 정적 린트 및 `mypy` νƒ€μž… 검사λ₯Ό 100% 무결점으둜 ν†΅κ³Όν•˜μ˜€μœΌλ©°, `python -c "import app"` 및 `tests/smoke_test_rag.py` ν•˜μ΄λΈŒλ¦¬λ“œ RAG ν…ŒμŠ€νŠΈλ„ 전원 μ™„λ²½ν•˜κ²Œ 합격(PASS)함.

- [x] **κ·Έλž˜ν”„ 관계 μ—°κ²° λˆ„λ½ κ·Όλ³Έ ν•΄κ²° 및 관계 검증 μžλ™ν™” (2026-05-20)**:
  - **ν˜„μƒ**: Neo4j κ·Έλž˜ν”„ μ‹œκ°ν™” μ‹œ μ—”ν‹°ν‹° λ…Έλ“œ 수백 κ°œμ— λΉ„ν•΄ μ—”ν‹°ν‹° κ°„ 직접 관계선(DEVELOPS, APPLIES λ“±)이 4개 μˆ˜μ€€μœΌλ‘œ κ·Ήμ†Œμˆ˜μ—¬μ„œ κ·Έλž˜ν”„ 기반 뢄석이 사싀상 λΆˆκ°€λŠ₯ν•œ μƒνƒœ 발견.
  - **원인**:
    1. `extract_relations` ν”„λ‘¬ν”„νŠΈμ˜ JSON μ§€μ‹œλ¬Έ μ˜€νƒ€(`'곡으둜만:{...}'`)둜 인해 LLM이 μ˜¬λ°”λ₯Έ JSON을 μƒμ„±ν•˜μ§€ λͺ»ν•΄ 관계 νŒŒμ‹± μ „λŸ‰ μ‹€νŒ¨.
    2. LLM이 λ°˜ν™˜ν•œ source/target 이름이 `extract_entities` μΆ”μΆœ 이름과 λ―Έμ„Έν•˜κ²Œ 달라 관계 ν•„ν„°μ—μ„œ μ „λŸ‰ 제거.
    3. 관계 μΆ”μΆœ ν›„ ν’ˆμ§ˆ 검증 및 μžκΈ°λ°˜μ„±(Self-Reflection) 루프가 μ—†μ–΄ 0개 관계λ₯Ό κ·ΈλŒ€λ‘œ 적재.
    4. `gpt-4o-mini`의 λ³΅μž‘ν•œ 관계 μΆ”λ‘  λŠ₯λ ₯ ν•œκ³„.
  - **쑰치**:
    1. **`gpt-4o` μ—…κ·Έλ ˆμ΄λ“œ**: μ—”ν‹°ν‹°/관계 μΆ”μΆœ μ „μš© λͺ¨λΈμ„ `gpt-4o`둜 승격. RAG 검색 및 μž„λ² λ”©μ€ `gpt-4o-mini` μœ μ§€.
    2. **`extract_relations` ν”„λ‘¬ν”„νŠΈ μ „λ©΄ μž¬μ„€κ³„**: μ—”ν‹°ν‹° 이름 λͺ©λ‘μ„ λͺ…μ‹œ μ „λ‹¬ν•˜μ—¬ LLM이 동일 이름을 μ‚¬μš©ν•˜λ„λ‘ κ°•μ œ. JSON μ§€μ‹œλ¬Έ μ˜€νƒ€ μˆ˜μ •.
    3. **`ArticleState`에 `relation_retry_count`, `relation_feedback` ν•„λ“œ μΆ”κ°€**: 관계 μΆ”μΆœ μž¬μ‹œλ„ μΉ΄μš΄ν„°μ™€ ν”Όλ“œλ°±μ„ μƒνƒœλ‘œ 좔적.
    4. **`validate_relations` λ…Έλ“œ μ‹ μ„€ 및 LangGraph νŒŒμ΄ν”„λΌμΈ μ—°κ²°**: μ—”ν‹°ν‹° 2개 이상인데 관계 0개이면 μ΅œλŒ€ 2회 μžλ™ μž¬μΆ”μΆœ 루프 μ‹€ν–‰.
    5. **적재 λ‘œκ·Έμ— 관계 수 및 κ²½κ³  ν‘œμ‹œ**: 기사당 μ—”ν‹°ν‹° 수/관계 수λ₯Ό λͺ…μ‹œ 좜λ ₯, 관계 0개인 경우 ⚠️ κ²½κ³  λ…ΈμΆœ.
    6. **`smoke_test_rag.py` 관계 μ—°κ²°μ„± 심측 검증 μΆ”κ°€**: 6μ’… 관계 μœ ν˜•λ³„ 카운트, 고립 λ…Έλ“œ λΉ„μœ¨, 기사당 평균 관계 수 μžλ™ 점검 및 μž„κ³„κ°’(3.0개) νŒμ •.
  - **검증**: `ruff`, `mypy` 무결점 톡과. ν˜„μž¬ κ·Έλž˜ν”„ μƒνƒœ: DEVELOPS 69개/APPLIES 102개/전체 μ—”ν‹°ν‹° 관계 401개(기사당 5.6개). 관계 재적재 νŒŒμ΄ν”„λΌμΈ μž¬μ‹€ν–‰ μ˜ˆμ •.

- [x] **무결성, λ³΄μ•ˆ 및 μ €μž‘κΆŒ 심측 검사 톡과 및 Git 원격 배포 μ™„λ£Œ (2026-05-20)**:
  - **ν˜„μƒ**: 원격 배포 μ „ μ½”λ“œμ˜ μ „λ°˜μ μΈ ꡬ동 μ•ˆμ •μ„±(무결성), μ‹œν¬λ¦Ώ λ…ΈμΆœ μœ„ν—˜(λ³΄μ•ˆ), λΌμ΄μ„ μŠ€ 좩돌 및 ꢌ리 주체(μ €μž‘κΆŒ)에 λŒ€ν•œ 곡인 검증 μˆ˜ν–‰ ν•„μš”.
  - **쑰치**:
    1. **무결성 검사(Integrity)**: `ruff check`와 `mypy` 정적 νƒ€μž… 검사λ₯Ό μ‹€ν–‰ν•˜μ—¬ μ‹ κ·œ 슀크립트 μŠ€νƒ€μΌ 였λ₯˜ 및 κ²½κ³  0건으둜 톡과함. `pytest tests/` λ‹¨μœ„ ν…ŒμŠ€νŠΈ(2/2 Passed) 및 `tests/smoke_test_rag.py` 4λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€ 톡합 ν…ŒμŠ€νŠΈ(4/4 Passed)λ₯Ό μ™„μ „ ν†΅κ³Όν•¨μœΌλ‘œμ¨ RAG 쿼리 μ •ν™•μ„±κ³Ό κ·Έλž˜ν”„ 밀도λ₯Ό μ™„λ²½νžˆ 검증함. `python -c "import app"`으둜 Gradio λΉŒλ“œ 및 μžκ°€ 진단 톡과 확인.
    2. **λ³΄μ•ˆ 검사(Security)**: `bandit` λ³΄μ•ˆ 취약점 뢄석기λ₯Ό μ΄μš©ν•΄ μ†ŒμŠ€ μ½”λ“œ μ „λ°˜μ˜ λ³΄μ•ˆ μœ„ν˜‘μ„ νƒμƒ‰ν•˜μ—¬ High/Medium λ“±κΈ‰ 취약점 0건 검증 μ™„λ£Œ. `.gitignore`에 `.env`, `Articles_*.xlsx`λ₯Ό μ™„μ „ μ°¨λ‹¨ν•˜μ—¬ μ‹œν¬λ¦Ώ ν‚€ 및 기사 데이터 유좜 κ°€λŠ₯성을 μ›μ²œ μ œκ±°ν•¨.
    3. **μ €μž‘κΆŒ 검사(Copyright)**: μ˜μ‘΄μ„± νŒ¨ν‚€μ§€λ“€μ˜ λΌμ΄μ„ μŠ€λ₯Ό μ „μˆ˜ λΆ„μ„ν•˜μ—¬ λͺ¨λ‘ Apache 2.0, MIT, BSD λ“± ν—ˆμš©μ  λΌμ΄μ„ μŠ€μž„μ„ ν™•μΈν•˜μ—¬ 법적 μœ„ν—˜ 0% 보μž₯. λ£¨νŠΈμ— MIT `LICENSE` νŒŒμΌμ„ 정식 λ°°ν¬ν•˜κ³ , `delete_zero_rel_articles.py`, `plot_keywords.py` λ“± μ‹ κ·œ μœ ν‹Έλ¦¬ν‹° νŒŒμΌμ— ν•œκΈ€ μ„€λͺ… 주석 및 μ €μž‘κΆŒ λͺ…μ‹œ 헀더λ₯Ό μ™„λ²½ μ μš©ν•¨.
    4. **Git μ—…λ‘œλ“œ**: λͺ¨λ“  μš”κ±΄μ„ κ°–μΆ˜ μ½”λ“œλ₯Ό μ΅œμ’… μŠ€ν…Œμ΄μ§•ν•˜κ³  μ˜μ–΄ 짧은 컀밋 λ©”μ‹œμ§€ κ·œμΉ™ μ€€μˆ˜ ν›„ `origin/main`으둜 μ΅œμ’… Push μ™„λ£Œ.
    5. **UI λ””μžμΈ ν”Όλ“œλ°± 반영**: μ™ΈλΆ€λ‘œ λŒμΆœλ˜μ–΄ μ±„νŒ… μ°½ μ˜μ—­μ„ μΉ¨λ²”ν•˜λ˜ 우츑 μ„€λͺ… HTML을 μ œκ±°ν•˜κ³  챗봇 λ‚΄λΆ€μ˜ placeholder μ˜μ—­μœΌλ‘œ μ›λ³΅ν•˜μ˜€μœΌλ©°, λŒ€μ‹œλ³΄λ“œμ™€ μ±„νŒ…μ°½μ˜ 골든 ν™”λ©΄ λΉ„μœ¨(3:7 split)을 μ™„λ²½ν•˜κ²Œ 볡ꡬ함.

- [x] **Gradio κΈ°λ³Έ μ˜ˆμ‹œ 질문 100% GraphRAG λ™μž‘ 보μž₯ 개편 (2026-05-20)**:
  - **ν˜„μƒ**: 메인 ν™”λ©΄μ˜ κΈ°λ³Έ 4개 μ˜ˆμ‹œ 질문 쀑 일뢀(LLM 개발 κΈ°μ—…, 기사 μš”μ•½ λ“±)κ°€ λ‹€μ†Œ μΌλ°˜μ μ΄κ±°λ‚˜ DB μ •λ³΄μ˜ λͺ¨ν˜Έν•¨μœΌλ‘œ 인해 GraphRAG 기반 λͺ¨λ“œκ°€ μ•„λ‹Œ GPT-4o-mini 일반(general) 지식 λͺ¨λ“œλ‘œ μš°νšŒλ˜λŠ” ν˜„μƒ 확인.
  - **쑰치**:
    1. Neo4j AuraDB μ‹€λ¬Ό 기사 및 μ—”ν‹°ν‹° 적재 데이터(μ‚Όμ„± κ°€μš°μŠ€ 2, 카카였 μΉ΄λ‚˜λ‚˜, AWS 피지컬 AI, ꡬ글 I/O μ œλ―Έλ‚˜μ΄ λ“±)λ₯Ό μ² μ €νžˆ ν”„λ‘œνŒŒμΌλ§ν•˜μ—¬ 100% λ¦¬νŠΈλ¦¬λ²„λ₯Ό νŠΈλ¦¬κ±°ν•  수 μžˆλŠ” μ΄ˆκ³ ν’ˆμ§ˆ 질문 4개둜 μ˜ˆμ‹œ μ§ˆλ¬Έμ„ 전격 개편.
    2. `app.py`와 톡합 검증 슀크립트인 `tests/smoke_test_rag.py`에 적용된 ν…ŒμŠ€νŠΈ μ‹œλ‚˜λ¦¬μ˜€ 질문 ν…μŠ€νŠΈ 및 κΈ°λŒ€ ν‚€μ›Œλ“œλ₯Ό μ™„μ „νžˆ μΌμΉ˜ν•˜λ„λ‘ 동기화 μˆ˜μ • μ™„λ£Œ.
  - **검증**: `ruff` 정적 린트 및 `mypy` νƒ€μž… 검사λ₯Ό 무결점 ν†΅κ³Όν•˜μ˜€μœΌλ©°, `tests/smoke_test_rag.py` 톡합 4λŒ€ κ³¨λ“œ μ‹œλ‚˜λ¦¬μ˜€ μ‹€ν–‰ μ‹œ μ „ ν•­λͺ© `βœ… PASS` 및 **100% GraphRAG (graph mode) 기반 응닡과 원본 URL [좜처 링크] λ…ΈμΆœ**을 μ™„λ²½ν•˜κ²Œ 검증 및 μž…μ¦ μ™„λ£Œ.

- [x] **μ±„νŒ… μ˜μ—­ λ„ˆλΉ„ 70% μΆ•μ†Œ 및 μƒν•˜ 간격(μ—¬λ°±) μ΅œμ ν™” κ°œμ„  (2026-05-20)**:
  - **ν˜„μƒ**: 메인 ν™”λ©΄ 였λ₯Έμͺ½ μ»¬λŸΌμ—μ„œ 챗봇 μΈν„°νŽ˜μ΄μŠ€μ™€ κ°œλ³„ μ»΄ν¬λ„ŒνŠΈ(μ†Œκ°œ λ³΄λ“œ, μ˜ˆμ‹œ 질문 λ²„νŠΌ, λ©”μ‹œμ§€ 버블, μž…λ ₯μ°½)κ°€ 화면을 100% 꽉 μ±„μ›Œ λ‹€μ†Œ μ‹œκ°μ μœΌλ‘œ 퍼져 보이고 가독성이 μ €ν•˜λ˜λŠ” 문제 λ°œμƒ. λ˜ν•œ 상단 GNB와 챗봇 μ‚¬μ΄μ˜ 수직 μ—¬λ°± 및 챗봇 λ‚΄ μ»΄ν¬λ„ŒνŠΈ κ°„ 간격이 λ„ˆλ¬΄ μ»€μ„œ 곡간 λ‚­λΉ„ λ°œμƒ.
  - **쑰치**:
    1. **우츑 Column ID μ§€μ •**: `app.py`μ—μ„œ 우츑 챗봇 μ»΄ν¬λ„ŒνŠΈλ₯Ό λ‹΄λŠ” Column에 `elem_id="chat-column"`을 κ³ μœ ν•˜κ²Œ μ§€μ •.
    2. **μ»¨ν…Œμ΄λ„ˆ 기반 70% λ„ˆλΉ„ ν†΅μ œ**: `src/utils/ui_templates.py`의 `CUSTOM_CSS`μ—μ„œ `#chat-column > div`λ₯Ό μ§€μ •ν•˜μ—¬ 챗봇 μ΅œμ™Έκ³½ ν”„λ ˆμž„ 전체λ₯Ό `70%` λ„ˆλΉ„λ‘œ μ œν•œν•˜κ³  `margin: 0 auto`둜 쀑앙 정렬을 κ°•μ œ. 이에 맞좰 λ‚΄λΆ€ μžμ‹ μš”μ†Œλ“€(`.placeholder`, `.examples-container`, `.message-wrap`, `.input-container`)은 `width: 100%`둜 λΆ€λͺ¨ μ»¨ν…Œμ΄λ„ˆμ— λ”± λ“€μ–΄λ§žκ²Œ μ •λ ¬ν•˜μ—¬ λ ˆμ΄μ•„μ›ƒ 어긋남 μ›μ²œ 제거.
    3. **수직 μ—¬λ°± λŒ€ν­ κΈ΄λ°€ν™”**: GNB μ•„λž˜μ˜ λ°”ν…€ λ§ˆμ§„μ„ `20px`μ—μ„œ `6px`둜 쀄이고 νŒ¨λ”©μ„ μ••μΆ•. 챗봇 λ‚΄λΆ€μ˜ μ»΄ν¬λ„ŒνŠΈ κ°„ 간격(`gap` 및 `margin`)κ³Ό κ°œλ³„ λ³΄λ“œμ˜ μ•ˆμͺ½ νŒ¨λ”©(`padding`)을 μ „μ²΄μ μœΌλ‘œ 쀄여(예: μ†Œκ°œκΈ€ νŒ¨λ”© `10px 14px`, λ§ˆμ§„ `4px auto 6px auto` λ“±) ν™”λ©΄ 내에 ν•œλˆˆμ— 쏙 λ“€μ–΄μ˜€λ„λ‘ μ΅œμ ν™”.
    4. **λ°˜μ‘ν˜• λͺ¨λ°”일 λ―Έλ””μ–΄ 쿼리 κ°±μ‹ **: κ°€λ‘œ 800px μ΄ν•˜ λͺ¨λ°”일 ν™”λ©΄μ—μ„œλŠ” μžλ™μœΌλ‘œ 100% 꽉 차도둝 κ°±μ‹ ν•˜μ—¬ 프리미엄 UXλ₯Ό μ™„λ²½ν•˜κ²Œ μœ μ§€.
  - **검증**: `ruff`와 `mypy` 검사λ₯Ό 무였λ₯˜ 톡과함. `python -c "import app"`으둜 Gradio μ›Ήμ•± λΉŒλ“œ 무결성을 μ΅œμ’… 확보함.

- [x] **GraphRAG 검색 기사 더미 URL μ›μ²œ ꡐ체 및 DB 반영 (2026-05-21)**:
  - **ν˜„μƒ**: GraphRAG 검색 κ²°κ³Όμ—μ„œ μ œκ³΅λ˜λŠ” 'κ·Όκ±° λ‰΄μŠ€ 좜처' 링크(URL)κ°€ μ ‘κ·Όν•  수 μ—†λŠ” κ°€μ§œ 넀이버 λ‰΄μŠ€ URL ν˜•μ‹(`news.naver.com/main/read.naver?...&oid=001&aid=11111111` λ“±)으둜 ν•˜λ“œμ½”λ”© λ˜μ–΄ μžˆμ–΄ 좜처 확인 λΆˆκ°€λŠ₯.
  - **쑰치**:
    1. **μ‹ ν•œ/카카였/ν† μŠ€/λ„€μ΄λ²„νŽ˜μ΄ 4λŒ€ 핡심 주제**의 μ‹€μ‘΄ν•˜λŠ” 곡신λ ₯ μžˆλŠ” 언둠사 기사(ν•œκ΅­κ²½μ œλ§€κ±°μ§„, λ‰΄μ‹œμŠ€, λ””μ§€ν„Ένƒ€μž„μŠ€, 더밸λ₯˜λ‰΄μŠ€)의 μ‹€μ œ URL μ£Όμ†Œλ₯Ό 검증.
    2. `inject_fintech_gold_data.py` 파일 λ‚΄ ν•˜λ“œμ½”λ”©λœ 더미 URL 4건을 μ‹€μ œ URL둜 ꡐ체.
    3. Neo4j AuraDB에 μ ‘μ†ν•˜μ—¬ κΈ°μ‘΄ 적재된 `Article` λ…Έλ“œλ“€μ˜ 더미 URL을 μ‹€μ œ URL둜 직접 `MERGE` μ—…λ°μ΄νŠΈν•˜λŠ” μž„μ‹œ 슀크립트λ₯Ό μž‘μ„±ν•˜μ—¬ DB μ—…λ°μ΄νŠΈ μ™„λ£Œ.
    4. μ½”λ“œ 무결성(ruff, mypy) 및 λ³΄μ•ˆ 무결성(bandit High/Medium 취약점 0건) 검증 ν›„ 원격 배포(Git push).
  - **검증**: Git pre-push hookμ—μ„œ `pytest` 및 `smoke_test_rag.py` 100% PASS 확인 μ™„λ£Œ.