FinGraph / AGENTS.md
dev-yuje's picture
refactor: AGENTS.md 및 README.md 디렉토리 ꡬ쑰 μ΅œμ‹ ν™” 및 참고자료 ν¬λ ˆλ”§ μΆ”κ°€
f0b1337

A newer version of the Gradio SDK is available: 6.15.2

Upgrade
μ°Έκ³ : 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 트리거 동기화)

λΆˆν•„μš”ν•œ μž„μ‹œ 파일, μ—‘μ…€ 데이터 파일(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

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

# 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 ν’ˆμ§ˆ κ°œμ„  단계)

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

개발 체크리슀트 (UI/UX μ‹œκ°μ  κ°œμ„  단계)

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

개발 체크리슀트 (Gradio UI/UX λ””ν…ŒμΌ κ°œμ„  단계)

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

배포 및 μžλ™ν™” νŒŒμ΄ν”„λΌμΈ (Pipeline Automation)

  • 맀일 μƒˆλ²½ 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)

  • 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% μ™„μ „ ν†΅κ³Όν•˜μ—¬ 완벽성을 보μž₯함.
  • 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)**ν•˜μ—¬ 졜고의 완성도λ₯Ό μž…μ¦ν•¨.
  • 메인 μ§„μž…μ (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)함.
  • κ·Έλž˜ν”„ 관계 μ—°κ²° λˆ„λ½ κ·Όλ³Έ ν•΄κ²° 및 관계 검증 μžλ™ν™” (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개). 관계 재적재 νŒŒμ΄ν”„λΌμΈ μž¬μ‹€ν–‰ μ˜ˆμ •.
  • 무결성, λ³΄μ•ˆ 및 μ €μž‘κΆŒ 심측 검사 톡과 및 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)을 μ™„λ²½ν•˜κ²Œ 볡ꡬ함.
  • 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 [좜처 링크] λ…ΈμΆœμ„ μ™„λ²½ν•˜κ²Œ 검증 및 μž…μ¦ μ™„λ£Œ.
  • μ±„νŒ… μ˜μ—­ λ„ˆλΉ„ 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 μ›Ήμ•± λΉŒλ“œ 무결성을 μ΅œμ’… 확보함.
  • 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 확인 μ™„λ£Œ.