simonlin1212/Vibe-Research · 라이선스 MIT · 백엔드 Python 3.10+ / FastAPI(:8900) · 프런트 React 19 + TypeScript + Vite + Tailwind(:5899) · 저장소 파일 97개 · 태그 v0.3.0(2026-07-31) · 저자 Simon(@linsizhen) · 공식 사이트 viberesearch.wiki · TrendShift Daily #16)
Vibe-Research는 "A주·미국·홍콩 주식의 객관적 공개 데이터(시세·밸류에이션·자금흐름·재무·공시·뉴스)를 로컬 대시보드로 모아 주되, 매수·매도·목표가 같은 결론은 절대 내지 않고 그 판단을 사용자 자신의 AI에게 넘기는 개인 투자 리서치 시스템"이다. 한마디로 "결론을 아웃소싱한 투자 리서치 대시보드"다. 스스로 판단하는 트레이딩 봇이 아니라, 판단에 필요한 재료만 완벽히 손질해 주는 주방이다.
좋은 레스토랑 주방에는 재료 손질 담당(prep cook)이 있다. 그는 채소를 씻고 다듬고, 고기를 손질하고, 계량까지 끝내 깨끗한 접시에 재료를 가지런히 올려 준다. 하지만 그는 "이 요리를 하세요"라고 절대 말하지 않는다. 어떤 요리를 할지, 간을 어떻게 볼지는 주방장(head chef)의 몫이다.
Vibe-Research가 정확히 이 손질 담당이다. 시세·재무·자금흐름·공시·뉴스라는 재료를 정확히 씻고 다듬어(수집·정제·토큰 절약용 압축) 대시보드에 올려 주지만, "이 종목을 사라"는 요리(결론)는 하지 않는다. 그 판단은 주방장 = 사용자 자신의 AI(구독 CLI·API·MCP)가 내린다. "재료만 손질하고 요리는 안 한다"는 이 의도적 절제가 이 프로젝트를 TradingAgents·ai-hedge-fund 같은 "봇이 결론까지 내는" 부류와 갈라놓는 핵심이다.
사용자가 "600519(구이저우 마오타이) 좀 봐줘"라고 하면 Vibe-Research 안에서 벌어지는 일을 압축하면 이렇다:
여기서 핵심은 ③까지는 AI가 전혀 없다는 점이다. 대시보드에 뜨는 숫자는 전부 검증 가능한 공개 데이터이지 LLM이 지어낸 값이 아니다. AI는 ④에서 사용자가 원할 때만 등장하고, 그마저도 "결론을 강요받지 않는" 재료 위에서 스스로 판단한다. 이 "데이터와 AI의 분리"가 Vibe-Research 설계 철학의 뼈대다.
첫째, "봇이 결론까지 내는" 피로감을 정면으로 때린다. TradingAgents, ai-hedge-fund 같은 화제작들은 "AI가 알아서 매수/매도 신호를 준다"를 판다. 그런데 실제로 돈을 걸어 본 사람은 안다 — 근거 없이 "사라"고만 하는 AI는 못 믿는다. Vibe-Research는 신뢰의 방향을 뒤집었다. "결론은 못 믿어도 데이터는 믿을 수 있게" 만들고, 결론은 사용자(와 그의 AI)에게 돌려준다. 이 절제가 오히려 실무 투자자의 공감을 산다.
점집 vs 종합병원 영상의학과. 점집은 "이 주식 사"라고 결론만 주지만 근거를 검증할 수 없다. 반면 영상의학과는 CT·MRI·혈액검사 수치와 영상(객관 데이터)을 정확히 뽑아 주되, "이 병이다·이 약을 먹어라"라는 처방(결론)은 주치의에게 넘긴다. Vibe-Research는 후자를 택했다 — 정확한 검사 결과를 주고, 진단은 네(네 AI)가 내리게 한다. 검사 수치가 정확하면, 오진의 책임은 판독이 아니라 진단에 있다.
둘째, 규제 준수(合规)를 마케팅이 아니라 아키텍처로 박았다. 중국·한국을 포함해 많은 나라에서 "특정 종목 매수 추천"은 투자자문업 인허가 영역이라 함부로 하면 위법 소지가 있다. Vibe-Research는 이 위험을 설계 단계에서 회피한다 — 앱이 절대 매수/매도/목표가/등급을 출력하지 않고, 오직 공개 데이터·공개 순위만 보여 준다. 뉴스 수집기(newsradar)조차 도박·예측시장·코인·성인물 같은 레드라인 키워드를 필터링한다. "합법적으로 배포 가능한 투자 도구"라는 점이 개발자·창작자에게 매력적이다.
셋째, "내 AI를 그대로 꽂는다"는 개방성이다. 대부분의 투자 SaaS는 자기네 모델·자기네 요금제에 가둔다. Vibe-Research는 구독 CLI(Claude Code·Qwen·DeepSeek·Codex) · OpenAI 호환 API · MCP 세 가지 경로를 모두 열어, 사용자가 이미 내는 AI 구독료 안에서 리서치를 돌릴 수 있게 한다. 앱 자체는 API 키 없이도 데이터가 즉시 나오고, AI는 원할 때만 붙인다. 벤더 종속(lock-in)이 없다는 점이 오픈소스 사용자에게 크게 어필한다.
넷째, 중화권 개인투자자(散户)라는 거대한 미충족 수요다. A주 시장은 개인 비중이 압도적으로 높지만, 흩어진 데이터(동방재부·텐센트·용호방·자금흐름·공시)를 한 화면에 모아 주는 무료·로컬·프라이버시 우선 도구는 드물다. Vibe-Research는 이 틈을 정확히 노렸고, 그래서 A주 데이터에 특화된 기능(연판/봉판율/용호방/북향자금 등)이 유독 촘촘하다. 미국·홍콩·한국 종목도 지원하지만, 무게중심은 A주 개인투자자다.
대부분의 투자 AI는 데이터 수집과 결론 생성을 한 덩어리로 묶어, 어디까지가 사실이고 어디부터가 AI의 상상인지 흐릿하다. Vibe-Research는 이 둘을 물리적으로 다른 계층으로 갈랐다 — 백엔드(FastAPI)는 검증 가능한 공개 데이터만 다루는 무상태(stateless) 계층이고, AI는 그 위에 선택적으로 얹히는 별도 출구다. "숫자는 데이터층에서, 해석은 AI층에서"라는 이 분리가 신뢰성과 규제 준수를 동시에 잡는다.
이름에 "Trading"이 들어가고 금융 카테고리라 자동매매·시그널 봇으로 오해하기 쉽지만, Vibe-Research는 주문을 넣지 않고, 매수/매도 신호도 주지 않는다. 증권사 계좌에 연결되지도 않는다. 어디까지나 "데이터를 모아 보여 주는 리서치 대시보드"이며, 매매 판단과 실행은 전적으로 사용자 책임이다. 이 문서 역시 특정 종목·투자 전략을 권하지 않으며, 오직 소프트웨어 아키텍처 학습 관점에서 이 레포를 해부한다.
Vibe-Research는 전형적인 2-티어 웹앱이다: FastAPI 백엔드(데이터)와 React 프런트(화면). 그런데 스택 선택 곳곳에 "무겁지 않게, 필요할 때만 로드"라는 실용주의가 배어 있다. GPU도, 데이터베이스도, Docker도 없다. LLM 추론은 사용자 몫이라 서버는 데이터 심부름과 조율만 한다.
| 구성요소 | 무엇 | 왜 이걸 썼나 |
|---|---|---|
fastapi ≥0.110uvicorn[standard] ≥0.29 | 웹 프레임워크 + ASGI 서버 | 44개 /api/* REST 엔드포인트를 얇게 노출. 무상태·읽기전용이라 가벼움 |
requests | HTTP 클라이언트(핵심 의존) | 이것만 있으면 시세·공시가 즉시 동작. 무거운 데이터 라이브러리 없이도 기본 기능이 뜬다 |
akshare ≥1.10mootdx ≥0.10pandas | 보조 데이터 소스(게으른 의존) | lazy-import — 필요한 엔드포인트에서만 로드. 없으면 그 엔드포인트만 501 + 설치안내를 주고 서비스는 안 죽는다 |
텐센트 qt.gtimg.cn | A주 시세 1차 소스(표준 urllib, GBK) | v3.0부터 akshare 의존을 걷어내고 텐센트로 이동 — "IP를 밴하지 않는다"가 선택 이유 |
| 동방재부(东财, Eastmoney) | 재무·자금흐름·신용융자·공시 | em_get 쓰로틀러로 감싸 밴 방지. 자금흐름·용호방·락업 같은 깊은 데이터의 출처 |
pytest · httpx | 테스트(dev 의존) | 마커로 오프라인(-m "not live") / 네트워크 형태검증(-m live) 분리 |
| 구성요소 | 버전 | 역할 |
|---|---|---|
| React + TypeScript | 19 · 5.7 | SPA 본체. 12개 라우트(每日复盘·个股·板块·多空辩论·持仓…) |
| Vite | 6.4 | 번들러/개발서버. /api를 localhost:8900으로 프록시해 CORS 회피 |
| Tailwind CSS | 3.4 | 글래스(반투명) 웜오렌지 테마. 유틸리티 클래스 기반 스타일 |
zustand | 5 | 가벼운 전역 상태관리(Redux 대안). 자선주·설정 등 |
echarts | 6 | 차트(캔들·자금흐름·밸류에이션 히스토리 등) |
react-markdown + remark-gfm | 9 | AI 응답을 마크다운으로 렌더(v0.3.0 신규) |
react-router-dom · lucide-react · sonner | 7 · — · — | 라우팅 · 아이콘 · 토스트 알림 |
가장 독특한 스택 결정이다. Vibe-Research는 모델 비종속(model-agnostic)이라, 자기가 OpenAI/Claude를 호출하는 코드가 없다. 대신 사용자가 frontend/src/lib/ai-models.ts에서 자기 AI를 골라 꽂는다. 세 가지 경로가 있다:
cli-*, API 키 불필요) — 로컬에 이미 로그인된 CLI를 서브프로세스로 실행: Claude Code · Qwen Code · DeepSeek CLI · Codex(OpenCode·Cursor·Kimi는 예정). 사용자의 월 구독 할당량으로 돈다(추가 API 과금 0)./chat/completions 규격이면 무엇이든: DeepSeek · SiliconFlow · OpenAI · MiniMax · OpenRouter · Groq · Together · 도우바오(豆包) 등. baseURL 자동 채움.requirements.txt에는 여전히 akshare가 적혀 있어 "akshare 기반이구나"로 오해하기 쉽다. 하지만 README v3.0 이후 A주 시세 엔진은 akshare 의존을 걷어냈고, 1차 시세는 텐센트(qt.gtimg.cn, 표준 urllib), 재무·자금흐름은 동방재부다. akshare·mootdx는 이제 보조·폴백일 뿐이며 게으르게 로드된다. 스택을 파악할 땐 requirements.txt보다 astock.py의 실제 호출부를 진실로 삼아야 한다.
구조를 한 문장으로 하면 "공개 데이터 소스 → 무상태 백엔드(23개 객관 도구) → 프런트 대시보드, 그리고 원할 때만 AI 출구 3종"이다.
핵심 성질 셋. (1) 백엔드는 무상태·읽기전용이다 — 6자리 종목코드를 받아 공개 데이터를 돌려줄 뿐, 사용자 데이터를 서버에 쌓지 않는다. (2) 데이터베이스가 없다 — 보유종목·자선주·LLM 키는 브라우저 localStorage에, 업로드한 리포트·보유 내역은 로컬 디스크 ~/.vibe-research/에 저장되고 서버로 올라가지 않는다. (3) AI는 필수가 아니다 — 대시보드 데이터는 AI 없이도 다 나오고, AI 출구 3종은 사용자가 원할 때만 붙는다.
중화권 금융 사이트(동방재부 등)를 자주 긁으면 IP가 차단된다. Vibe-Research의 astock.py는 이걸 타임스탬프 기반 쓰로틀러로 막는다. 이 작은 함수가 뒤에 나올 "토론(debate)" 설계까지 좌우하는 숨은 주춧돌이다:
# astock.py — 동방재부 요청 사이 최소 간격 강제
_EM_MIN_INTERVAL = 1.0 # 두 요청 사이 최소 1초
def em_get(url, params=None, headers=None, timeout=15):
wait = _EM_MIN_INTERVAL - (time.time() - _em_last_call[0])
if wait > 0:
time.sleep(wait + random.uniform(0.1, 0.5)) # 락(lock)이 아니라 시각 기반!
# 자동 전략: 먼저 직결(direct) 시도 → VPN/Clash 프록시가
# 본토 금융사이트를 깨는 걸 우회 → 안 되면 시스템 프록시로
# 폴백하고, 성공한 모드를 이후 고정(pin)
또 하나 실전적인 디테일: "직결 먼저, 프록시는 폴백"이다. 중국 본토 사용자는 VPN/Clash를 켜 두는 경우가 많은데, 그 프록시가 오히려 본토 금융 사이트 접속을 깬다. 그래서 em_get은 시스템 프록시를 무시하고 직결을 먼저 시도한 뒤, 실패하면 프록시로 폴백하고 성공한 모드를 고정한다. 사용자의 네트워크 환경 차이를 코드가 흡수하는 영리한 처리다.
AI 출구 3종(채팅·MCP·토론)은 모두 같은 23개 도구 세트를 공유한다. 각 도구는 종목코드를 받아 한 종류의 객관 데이터를 돌려주는데, 설계에 두 가지 규율이 있다:
raise하지 않고 {"error": ...}를 반환한다. 그래야 AI가 "이 도구는 실패했으니 다른 도구를 써 보자"라고 스스로 복구할 수 있다.도구 목록 일부: query_quote(시세) · query_valuation_percentile(밸류에이션 히스토리 백분위) · query_fund_flow(자금흐름) · query_dragon_tiger(용호방) · query_lockup(락업 해제) · query_market(지수·글로벌·시장심리·거래대금·개요) · query_news_radar(뉴스) · query_global_stock(미·홍콩·한국) · query_hk_cashflow(홍콩 현금흐름표).
이 프로젝트의 정체성을 가장 잘 보여 주는 모듈이다. 일반적인 "AI 애널리스트"는 하나의 AI가 결론을 낸다. Vibe-Research의 多空辩论(다공 = 롱/숏 토론)은 여러 역할로 나누되, 일부러 "트레이더(결정자)" 역할을 두지 않는다. 흐름은 이렇다:
포인트는 ④의 사회자가 승자를 가리지 않는다는 것이다. "강세가 맞다"가 아니라 "둘의 진짜 이견은 무엇이고, 그걸 판가름하려면 어떤 데이터를 더 봐야 하는가"를 정리해 준다. 결론을 내는 대신 "무엇을 더 확인해야 하는지"를 돌려주는 것 — 이것이 "추천 안 함" 철학이 코드로 구현된 모습이다.
parallel 플래그가 붙어, 동방재부(em_get) 기반 항목(밸류에이션·자금흐름·신용융자·주주·락업·개념)은 강제로 직렬(serial) 실행해 쓰로틀러를 지키고, 텐센트·직결 엔드포인트만 4-워커 스레드풀로 병렬 처리한다. "데이터를 먼저 확정하고, 그 위에서만 토론한다"는 순서를 강제하는 장치.여기에 환각 방지 장치가 하나 더 있다. _payload_empty() 헬퍼는 응답에서 메타키를 벗겨 내 "껍데기뿐(빈 값)"인지 판별한다. 정말 비었지만 유효한 항목에는 "기록 없음 — 진짜 없을 수도, 소스가 죽었을 수도 있으니 넘겨짚지 마라"는 명시 주석을 붙인다. AI가 빈 데이터를 보고 지어내지 못하게 데이터의 "없음"까지 정직하게 전달하는 것이다. (비용도 문서화돼 있다: 1라운드 ≈ 모델 호출 3회·약 100~120초, 2라운드 ≈ 5회·약 3분.)
API 키 없이 월 구독만으로 AI를 쓰게 해 주는 다리다. 로컬에 이미 로그인된 CLI를 subprocess로 띄워 답을 받는다. CLI마다 전달 방식이 달라 세심하게 분기한다:
-p --output-format text --system-prompt-file로 넘기고, 모든 도구를 비활성화(--disallowedTools Read Write Edit … Bash) — 데이터는 이미 프롬프트에 있으니 CLI가 파일·셸을 건드릴 필요가 없다.encoding="utf-8" 강제(중문 윈도우의 GBK 크래시 방지), 300초 타임아웃, stderr를 스레드로 빨아들여 진짜 에러 메시지를 노출.단, CLI들은 함수호출(function-calling)을 못 하므로 이 모드는 "데이터가 이미 프롬프트에 다 담긴" 시나리오에만 맞는다. 그래서 도구를 여러 번 오가야 하는 심층 분석은 API 모드나 MCP 모드를 쓴다.
로컬 전용이면 문제없지만, 누군가 이 앱을 공개 서버에 올리면 위험이 생긴다. chat.py는 사용자가 baseURL을 넣을 수 있게 열어 두는데, 악의적 baseURL이 클라우드 메타데이터(169.254.x.x)나 내부망을 가리키면 서버가 대신 접속해 버리는 SSRF가 된다. 그래서 _check_base_url가 이를 차단하고, 공개 배포 시(VR_API_KEY 설정 시)엔 DNS를 재해석해 우회까지 막는다. 또 VR_API_KEY가 설정되면 /api/health를 뺀 모든 엔드포인트가 Authorization: Bearer를 요구한다.
169.254.169.254, 여기에 접속하면 서버의 자격증명이 샐 수 있다)를 가리키는 경우. Vibe-Research는 사용자 입력 baseURL을 허용 대상인지 검사하고 사설/메타데이터 대역을 막아 이를 방어한다. 사용자 입력을 서버가 대신 fetch하는 모든 앱이 신경 써야 할 고전 위협이다.구조가 알려주는 힌트 셋. 첫째, 가장 큰 두 파일이 astock.py(816줄)와 app.py(678줄)다 — 이 앱의 무게중심이 "AI"가 아니라 "A주 데이터를 정확히·안전하게 긁어오는 일"에 실려 있음을 그대로 보여 준다. 둘째, chat.py·debate.py·mcp_server.py·cli_runtime.py가 모두 tools.py를 공유한다 — "객관 도구 23개"라는 한 우물을 세 AI 출구가 함께 쓰는 DRY(중복 제거) 설계다. 셋째, newsradar.py·gstock.py 주석에 "investment-news에서 이식", "global-stock-data 이식"이 적혀 있다 — 저자의 기존 소형 레포들을 흡수해 만든 통합작임을 코드가 증언한다.
백엔드를 보면 시세(astock)·해외(gstock)·뉴스(newsradar)·시장(market)·도구(tools)·채팅(chat)·토론(debate)·감사(reflection)·CLI(cli_runtime)·MCP(mcp_server)가 각각 한 파일로 딱 갈려 있다. 파일 이름만 봐도 어디를 열어야 할지 안다. 초·중급 개발자가 "기능을 어떻게 파일로 나눌까"를 배우기에 이보다 깔끔한 표본이 드물다 — 거대 프레임워크의 미로가 아니라, 읽으면 바로 이해되는 크기의 모듈들이다.
앱이 LLM을 내장하지 않고 세 경로(구독 CLI · OpenAI 호환 API · MCP)로 사용자의 AI를 꽂게 한 설계. "우리 모델에 가두지 않는다"는 방향은 오픈소스 도구의 채택률을 크게 높인다. 특히 OpenAI 호환 규격 하나로 10여 개 공급자를 다 받는 방식, 그리고 함수호출 되는 API와 함수호출 안 되는 CLI를 용도별로 나눠 쓰는 판단이 배울 점이다. 어떤 앱이든 "AI를 붙이되 특정 벤더에 종속되지 않는 법"의 표본.
무거운 라이브러리를 함수 안에서만 import하고, 없으면 그 기능만 501 + 설치안내로 응답해 서비스는 살려 두는 패턴. "핵심은 requests만으로 즉시 동작, 부가는 필요할 때만"이라는 티어드 디펜던시는 배포·온보딩 마찰을 줄인다. 사용자가 전체 의존성을 깔지 않아도 앱이 뜨는 경험을 만드는 실전 기법이다.
외부 API를 자주 긁을 때 IP 차단을 피하는 현실적 기술 — 요청 간 최소 간격 강제, 랜덤 지터(random.uniform) 추가, 직결 먼저·프록시 폴백의 자동 전환. 그리고 "시각 기반 쓰로틀러는 락이 아니라서 동시성에 뚫린다"는 한계를 알고, 그 위험한 호출을 직렬로 묶어 보완하는 사고방식. 스크래핑·데이터 파이프라인을 짜는 누구에게나 직접 쓸모 있다.
도구가 원본을 그대로 뱉지 않고 "최근 N행 + 핵심 필드 + 요약통계"로 압축해 맥락을 아끼는 것, 도구가 절대 예외를 던지지 않고 {"error":...}를 돌려줘 AI가 스스로 복구하게 하는 것, 그리고 _payload_empty()로 "빈 데이터"를 명시해 AI가 없는 데이터를 지어내지 못하게 막는 것. 함수호출 에이전트를 만들 때 두고두고 참고할 세 가지 규율이다.
강세/약세 리서처를 두되 승자를 가리는 트레이더 역할을 일부러 뺀 구조. 사회자가 결론 대신 "진짜 이견 + 검증 체크리스트 + 데이터 공백"을 돌려준다. "AI에게 결론을 시키지 말고, 무엇을 더 확인해야 하는지를 시켜라"는 발상은, 신뢰성이 중요한 어떤 분석 도구에도 이식할 수 있는 값진 패턴이다.
DB 없이 localStorage + ~/.vibe-research/로 민감정보를 로컬에만 두는 설계, 그리고 그럼에도 누군가 공개 배포할 때를 대비한 SSRF 가드 · VR_API_KEY 인증 · DNS 재해석. "로컬에선 편하게, 공개 땐 안전하게"를 환경변수 하나로 전환하는 방식은 실무 배포 감각을 길러 준다.
이 레포의 핵심은 A주 데이터가 아니라 "AI에게 결론을 강요하지 않는 프롬프트 설계"다. 아무 주제(예: 새 노트북 살까)로, 자기 AI에게 두 번 물어보라 — (a) "사는 게 나아?"라고 결론을 요구, (b) "강세 논거·약세 논거·더 확인할 것만 정리하고 결론은 내지 마"라고 요구. 후자가 얼마나 더 검증 가능한 사고를 끌어내는지 곧바로 체감된다. Vibe-Research의 debate.py가 정확히 이걸 코드로 강제한 것이다.
| 항목 | 필요한 것 |
|---|---|
| 런타임 | Python 3.10+(백엔드) · Node.js(프런트). 그게 전부 — GPU·데이터베이스·Docker 불필요 |
| LLM | 추론은 사용자의 AI가 한다 — 로컬 CLI 구독(Claude Code 등) 또는 OpenAI 호환 API 키 또는 MCP. 앱 자체엔 모델이 없다 |
| 네트워크 | 텐센트·동방재부·108개 RSS에 접속 가능해야. 본토 밖이면 일부 소스가 느리거나 막힐 수 있음(직결/프록시 자동전환이 흡수 시도) |
| 백엔드 기동 | cd backend && python3 -m venv .venv && .venv/bin/pip install -r requirements.txt → .venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8900 |
| 프런트 기동 | cd frontend && npm install && npm run dev → 브라우저에서 http://localhost:5899 |
| AI 연결 | 웹 UI의 "接入 AI(AI 연결)" 페이지에서 구독/API/MCP 중 선택. API 키는 브라우저 localStorage에만 저장(서버 업로드 없음) |
| 변수 | 의미 |
|---|---|
VR_API_KEY | 설정 시 /api/health를 뺀 모든 API가 Authorization: Bearer 요구 — 공개 배포엔 필수(openssl rand -hex 32로 생성) |
VR_ALLOW_ORIGINS | CORS 화이트리스트(기본 *) |
VR_DATA_DIR · VR_REPORTS_DIR | 로컬 저장 루트(기본 ~/.vibe-research/) — 보유내역·업로드 리포트가 여기 저장, 서버로 안 올라감 |
VR_DATA_PROXY=1 | 시스템 프록시 강제(기본은 직결 우선 — 본토 VPN 우회용) |
IWENCAI_API_KEY | 선택 — iwencai(问财) 시맨틱 리포트 검색에만 사용 |
git clone --depth 1 후 백엔드만 기동한다(프런트 없이). 브라우저나 curl로 http://localhost:8900/api/health, 그리고 시세 엔드포인트를 종목코드와 함께 호출해 JSON 응답을 눈으로 본다. "프런트 없이도 데이터 계층이 독립적으로 동작한다"를 체감하는 게 목표. app.py의 44개 라우트를 훑어 어떤 데이터가 있는지 지도부터 그리자.
tools.py의 23개 도구 중 query_fund_flow처럼 큰 표를 다루는 걸 골라, 원본을 어떻게 잘라 내는지(최근 N행·핵심 필드·요약통계)를 코드에서 짚는다. 그리고 도구가 실패할 때 raise 대신 {"error": ...}를 반환하는 부분을 찾아, "왜 예외를 안 던지는가(=AI가 복구하게)"를 한 줄로 설명해 보자.
astock.py의 em_get(최소 간격 + 랜덤 지터 + 직결/프록시 전환)을 참고해, 아무 공개 API를 밴당하지 않게 긁는 작은 파이썬 스크래퍼를 짜 본다. 일부러 간격을 0으로 줄여 보고(밴 위험), 다시 1초로 늘려 안정화되는지 관찰하라. "시각 기반 쓰로틀러는 동시 호출에 뚫린다"를 스레드 2개로 재현해 보는 게 하이라이트.
강세/약세 리서처 + 중립 사회자(승자 판정 없음) 구조를 투자가 아닌 주제(예: "이 기술 스택을 도입할까")로 옮겨 프롬프트를 짜 본다. 핵심 제약 두 개를 지켜라 — (1) 각 논거는 구체적 근거를 인용, (2) 사회자는 결론 대신 "진짜 이견 + 더 확인할 것"만 출력. 결론을 요구했을 때와 결과가 어떻게 달라지는지 비교하라.
106줄짜리 mcp_server.py(표준 라이브러리만, JSON-RPC over stdio)를 정독해, OpenAI 도구 스키마를 MCP {name, description, inputSchema}로 변환하는 부분을 이해한다. 그다음, 자기만의 작은 데이터(예: 로컬 CSV 조회)를 MCP 도구 하나로 감싸 Claude Code에 붙여 본다. claude mcp add ...로 등록해 실제로 호출되는지 확인하는 게 최종 목표. "내 도구를 에이전트에 꽂는" 감각을 완성한다.
| 주차 | 주제 | 구체적으로 |
|---|---|---|
| 1주 | FastAPI로 데이터 API 짓기 | 라우팅·의존성 주입·비동기·CORS·프록시. app.py의 44개 라우트를 표본으로, 작은 "시세 조회 API"를 직접 만들어 본다 |
| 2주 | 안전한 웹 스크래핑 / 데이터 수집 | 쓰로틀링·랜덤 지터·재시도·직결/프록시 전환·인코딩(GBK/UTF-8). astock.py의 em_get 정독 + 내 스크래퍼 이식 |
| 3주 | LLM 함수호출(툴 유스) 에이전트 | OpenAI 호환 /chat/completions 함수호출 루프, 도구 스키마, 토큰 절약 압축, 에러를 값으로 반환. chat.py·tools.py |
| 4주 | 멀티에이전트 & MCP | 역할 분리(강세/약세/사회자), "결정자 없는" 토론, MCP 서버(JSON-RPC over stdio)로 도구 노출. debate.py·mcp_server.py |
| 5주 | React 19 데이터 대시보드 | Vite 프록시·zustand 상태·echarts 차트·localStorage 프라이버시·NDJSON 스트리밍 소비. frontend/src/lib·pages |
| 6주(선택) | 배포 보안 | SSRF 가드·Bearer 인증·환경변수로 로컬/공개 전환·DNS 재해석. chat.py의 _check_base_url + VR_API_KEY 경로 |
README.md(철학·기능) → backend/app.py(라우트 지도) → backend/tools.py(도구 23개) → backend/astock.py(em_get·데이터층) → backend/debate.py(결정자 없는 토론) → backend/chat.py·mcp_server.py·cli_runtime.py(AI 출구 3종)simonlin1212/investment-news(뉴스 레이더 원류) · a-stock-data(A주 툴킷) · global-stock-data(미·홍콩 툴킷) — 각각 newsradar.py·a-stock-data/·gstock.py로 이식됨