# LLM이 추측하지 않는 주식 분석기 — 4개 전문 AI가 병렬로 리포트를 쓰는 멀티에이전트 대시보드
> 종목코드 하나를 넣으면 재무·뉴스·업종 전문 AI가 각자 분석하고, 전략가 AI가 하나의 투자 리포트로 종합한다. Claude Code로 만들어 [dataso.biz](https://www.dataso.biz)에 올린 기록.
---
## 한 줄 요약
LLM에게 "삼성전자 분석해줘"라고 하면 그럴듯하게 지어낸다. 이 프로젝트는 그 반대로 설계했다 — 스크립트가 DART·시세·뉴스를 먼저 결정적으로 수집해 JSON으로 정리하고, 그 위에서만 AI가 해 석한다. 재무·뉴스·업종 3개 에이전트가 병렬로 분석한 뒤, 종합 전략가 에이전트가 요약본만 받아 최종 시나리오를 낸다. 환각을 구조로 막은 것이 핵심.
- 결과물: [dataso.biz](https://www.dataso.biz) — Railway 배포, 액세스 코드 게이트로 운영
- 스택: Streamlit · Python 하네스 · Anthropic Claude · DART · yfinance · 네이버금융 · Google News · Firecrawl
- 저장소: stock-app (Streamlit 단일 앱, app.py 698줄 + 하네스 7모듈)
- 성격: 교육·실습용 예시 (실제 투자 권유 아님)
---
## 1. 왜 만들었나 — "LLM은 추측한다"는 문제
주식 분석에 LLM을 쓸 때 진짜 위험은 거짓을 사실처럼 말하는 것이다. 매출·부채비율·PER을 물으면 학습 데이터의 흐릿한 기억으로 그럴듯한 숫자를 뱉는다. 투자 판단에 그대로 쓰면 위험하다.
그래서 이 앱의 1번 원칙은 이렇게 못박혀 있다:
> "LLM이 추측하지 않습니다. 스크립트가 DART·시세·뉴스를 먼저 수집해 JSON으로 정리합니다."
이게 랜딩 페이지 첫 화면 에 대문짝만하게 적힌 이유다. 결정적 하네스(deterministic harness) — 데이터는 코드가 확정하고, AI는 확정된 데이터만 해석한다.
---
## 2. 구조 — 4개 에이전트 + 결정적 하네스
### 데이터 수집 하네스 (AI 개입 없음)
| 모듈 | 소스 | 역할 |
|---|---|---|
| dart_metrics.py | DART OpenAPI | 정식 재무제표 → 매출·영업이익·부채비율·ROE |
| fin_metrics.py | yfinance + 네이버금융 | 시세·PER·PBR |
| news_fetch.py | Google News RSS | 뉴스 수집 |
| firecrawl_news.py | Firecrawl | 뉴스가 부실할 때 웹 검색으로 보강(백업) |
| sector_scan.py | yfinance + RSS | 업종 피어 비교 |
| text_insight.py | — | 토픽·워드클라우드·감성분포 |
뉴스는 Google News를 먼저 긁고, 결과가 빈약하면 Firecrawl로 자동 보강한다(`source: "google+firecrawl"`). 이 폴백 하나가 "데이터 없어서 못 씀"을 "그래도 채워옴"으로 바꾼다.
### 4개 전문 에이전트 (각자 독립 컨텍스트)
```
재무 분석가 ─┐
뉴스 감성 분석가 ─┼─(요약만 전달)→ 종합 전략가(4옵션)
산업 연구원 ─┘
```
- 💰 재무 분석가 — DART 재무제표로 수익성/안정성 판정
- 📰 뉴스 감성 분석가 — 뉴스 톤을 0~100 점수로 정량화, 호재·악재·중립 분류
- 🏭 산업 연구원 — 업종 동향 + 글로벌 피어 PER·PBR·성장률로 상대 위치 파악
- 🎯 종합 전략가 — 공격적🎯·보수적🛡️·균형⚖️·퀀트📐 4성향 중 선택해 최종 시나리오
핵심 설계: 각 에이전트는 독립된 컨텍스트에서 분석하고, 종합 단계로는 "요약만" 전달한다. 전체 원본을 한 프롬프트에 욱여넣지 않아 컨텍스트 오염과 환각을 줄인다. 멀티에이전트를 쓰는 진짜 이유가 여기 있다.
---
## 3. 점수 엔진 — 재현 가능한 룰 기반 종합
AI의 정성 판단과 별개로, 종합점수는 결정적 공식으로 계산한다(app.py):
```
종합점수 = 0.40 × 재무점수 + 0.25 × 뉴스감성 + 0.35 × 업종점수
```
이 종합점수(0~100)가 4대 전략 시나리오의 목표가·손절가·제안비중을 만든다.
| 전략 | 판단(예: 점수 70+) | 제안비중 | 손절 |
|---|---|---|---|
| 공격적 🎯 | 적극 매수 | 8~10% | −15% |
| 균형 ⚖️ | 비중확대 | 4~5% | −12% |
| 보수적 🛡️ | 관망·소량 | 1~2% | −8% |
| 퀀트 📐 | 점수 기반 시그널 | 모델 | −12% |
같은 입력이면 같은 점수 — 재현 가능. AI는 해석과 서술을, 룰 엔진은 숫자를 담당하는 역할 분리.
---
## 4. 한 번 검색 → 8개 화면 자동 생성
종목코드(예: 005930, 000660, AAPL) 하나 넣고 실행하면 탭 이 전부 자동으로 뜬다:
- 📄 종합 보고서 — Word·Markdown 다운로드 (python-docx)
- 💬 AI 질문 — 리포트 기반 Q&A + 필요 시 웹 검색 보강 (Claude, 프롬프트 캐싱 사용)
- 🎯 시나리오 — 종합점수 기반 4대 전략의 목표가·손절가·비중
- 🔎 인사이트 — 토픽·워드클라우드·감성분포 + AI 인사이트
- 그 외 재무·밸류·감성·업종 탭
AI 질문 탭은 ANTHROPIC_API_KEY로 동작하고, 수집된 리포트를 1차 근거로 답하되 부족하면 웹 검색으로 보강한다. cache_control: ephemeral로 리포트 컨텍스트를 캐싱해 반복 질문 비용을 줄였다.
---
## 5. 운영까지 — 배포·게이트·비용 통제
Railway 배포. Streamlit 무료 호스팅이 자는 문제를 피해 Railway 컨테이너로 올렸다. railway.json/`Procfile`에 시작 명령을 넣어 $PORT 자동 주입, Nixpacks 자동 빌드.
액세스 코드 게이트. 이 앱은 운영자의 API(Claude·Firecrawl)로 돌아간다 — 즉 호출할 때마다 운영자 돈이 나간다. 그래서 아무나 못 쓰게 ACCESS_CODES 환경변수 기반 접속 게이트를 붙였다. 이용하려면 이메일로 신청 → 승인 후 코드 발급.
캐시로 비용·속도 통제. 데이터 수집은 TTL 캐시(재무·시세 1시간, 뉴스 15분, AI 응답 10분)로 묶어 같은 종목 반복 조회 시 API 재호출을 막는다.
커밋 3개가 전체 스토리 를 요약한다:
- Initial commit: 주식 분석 멀티에이전트 Streamlit 웹앱
- 랜딩페이지 + 접속 게이트(ACCESS_CODES) 추가
- UI 세련화: 기본 개발자 툴바 숨김 + 브랜드 상단바
만들고 → 돈 새는 것 막고 → 얼굴 다듬고. 개인 API로 서비스를 공개할 때 딱 필요한 순서.
---
## 6. 이 프로젝트가 잘한 것 (다른 바이브코더에게)
1. 환각을 프롬프트가 아니라 구조로 막았다. "정확히 답해줘"라고 빌지 않고, 데이터를 코드가 먼저 확정한 뒤 AI에게 넘긴다. 이게 신뢰의 뿌리.
2. 멀티에이전트를 이유 있게 썼다. 4개로 쪼갠 건 멋져 보여서가 아니라, 독립 컨텍스트 + 요약 전달로 오염을 줄이려는 설계다.
3. AI와 룰을 분리했다. 서술은 AI, 점수는 결정적 공식. 재현 가능성과 설명력을 둘 다 챙겼다.
4. 공개 = 비용을 잊지 않았다. 개인 API로 서비스를 열면 반드시 접속 게이트와 캐시가 필요하다. 이걸 처음부터 넣었다.
5. 폴백을 깔았다. Google News가 비면 Firecrawl로 채운다. "데이터 없음"으로 끝나지 않는다.
---
## 링크·구성
- 서비스: [dataso.biz](https://www.dataso.biz) (액세스 코드 필요 — [email protected] 신청)
- 저장소: stock-app (Streamlit)
- 데이터: 📊 DART · 💹 네이버금융 · 🌐 yfinance · 📰 Google News · 🔥 Firecrawl
- AI: 🤖 Claude (Anthropic)
- 배포: Railway (Nixpacks)
---
이 글은 GitHub 저장소(stock-app)의 코드·커밋 이력과 라이브 사이트(dataso.biz)를 근거로 작성했습니다. 종합점수 공식·캐시 TTL·에이전트 구성·배포 방식은 실제 app.py와 README에서 확인한 사실입니다. 본 서비스는 교육·실습용 예시이며 투자 권유가 아닙니다.