Skip to content

Sol-Lite/AI

Repository files navigation

🧠 AI Investment Chatbot Server

자연어로 주가 조회, 포트폴리오 분석, 매매 주문, 시황 요약을 처리하는 모의 투자 서비스의 AI 챗봇 백엔드입니다.


📌 Overview

이 서버는 FastAPI 기반의 AI 챗봇 엔드포인트를 제공하며, 사용자의 자연어 입력을 분석해 적절한 투자 데이터를 응답합니다.

대부분의 요청은 규칙 기반 의도 감지(Intent Detection) 로 빠르게 처리되고, 포트폴리오 분석·거래내역 조회처럼 복잡한 질문은 LLM Tool Calling Agent 로 위임합니다. 시황 요약에 필요한 뉴스는 자체 크롤러가 30분마다 자동 수집하고 KoBART 모델로 요약해 MongoDB에 저장합니다.

Spring Boot 메인 백엔드, React 프론트엔드와 연동되어 주문 실행 및 계좌 데이터를 실시간으로 처리합니다.


🚀 Key Features

  • 하이브리드 의도 처리: 14개 Intent를 정규식 기반으로 분류하고, 복잡한 분석은 LLM에 위임
  • LLM Tool Calling Agent: 포트폴리오 분석, 거래내역 조회 등에서 최대 5턴의 도구 반복 호출
  • 문맥 인식 대화: MongoDB 대화 기록을 로드해 이전 맥락 기반의 follow-up 질문 처리
  • Shortcut 메커니즘: 종목명만 입력해도 직전 조회 유형 자동 재실행 (LLM 호출 없음)
  • 자동 뉴스 크롤링: KOSPI200 + NASDAQ100 종목 뉴스를 30분마다 멀티스레드로 자동 수집
  • 뉴스 자동 요약: KoBART(EbanLee/kobart-summary-v3) 모델로 뉴스 본문 요약
  • 위젯 드래그 연동: 프론트엔드 위젯에서 종목코드 힌트를 직접 주입해 CSV 미등록 종목도 처리
  • 매매·환전 주문 연동: Spring 백엔드와 REST API로 실제 주문 실행
  • 입력 검증: 미완성 한글 자모, 지원 범위 외 질문에 대한 안내 응답 자동 반환
  • 다중 배포 환경: Ollama(로컬) / AWS SageMaker(운영) LLM 전환 지원

🏗️ Architecture

[사용자]
   │  자연어 입력 (예: "삼성전자 시세")
   ▼
[React Frontend]
   │  POST /chat  { message, stock_code_hint?, stock_name_hint? }
   ▼
[AI Server - FastAPI]  ← 이 서버
   ├─ 입력 검증 (미완성 자모, 너무 짧은 입력)
   ├─ try_shortcut()  → 직전 조회 재실행 (LLM 없이)
   ├─ detect()        → 규칙 기반 Intent + 파라미터 추출
   ├─ pre_dispatch()  → 문맥 기반 Intent 보정
   ├─ dispatch()      → Intent별 Handler 실행
   │       ├─ 규칙 기반 Handler (시세/지수/환율/순위/잔고/시황/주문)
   │       └─ LLM Agent (포트폴리오/거래내역/unknown)
   │              └─ Tool Calling Loop (최대 5턴)
   │                     ├─ get_stock_price
   │                     ├─ get_stock_news
   │                     ├─ get_portfolio_info
   │                     ├─ get_market_summary
   │                     └─ get_trade_history
   └─ 대화 기록 저장 (MongoDB)

[외부 연동]
   ├─ Spring Backend  → 시장 데이터, 계좌/잔고/주문/거래내역 REST API
   ├─ MongoDB         → 대화 기록, 뉴스 저장
   ├─ Oracle DB       → 종목 마스터 데이터
   └─ LLM             → Ollama (llama3.1:8b) / AWS SageMaker

[크롤러 - 백그라운드 스레드]
   30분마다 KOSPI200 + NASDAQ100 뉴스 수집
   → KoBART 요약 → MongoDB stock_news 컬렉션 저장

이 AI 서버의 역할: Spring 메인 백엔드와 프론트엔드 사이에서 자연어 해석·데이터 가공·AI 추론을 전담합니다. 주문 실행 등 트랜잭션 처리는 Spring에 위임합니다.


⚙️ 기술 스택

분류 기술
Language Python 3.11+
Framework FastAPI 0.111+, Uvicorn
LLM Ollama (llama3.1:8b) / AWS SageMaker (TGI)
요약 모델 HuggingFace Transformers — EbanLee/kobart-summary-v3 (KoBART)
Database MongoDB 4.6+ (대화 기록, 뉴스) / Oracle DB 2.0+ (종목 마스터)
HTTP Client httpx, requests
크롤링 BeautifulSoup4, 멀티스레드 (ThreadPoolExecutor)
설정 관리 python-dotenv, pydantic-settings
인증 JWT (HTTPBearer, HS384)
AWS boto3 (SageMaker Runtime)
기타 json-repair (LLM 응답 JSON 복구), certifi

📂 폴더 구조

AI/
├── app/
│   ├── main.py                        # FastAPI 진입점, POST /chat 엔드포인트
│   │                                  # 입력 검증 + 맥락 기반 intent 재분류
│   ├── stock_ref.py                   # 공유 CSV 종목 참조 (이름↔코드, 동의어 650개+)
│   │
│   ├── core/
│   │   ├── auth.py                    # JWT 검증, user_context 추출
│   │   ├── config.py                  # 환경변수 로드 (.env)
│   │   └── llm.py                     # LLM 호출 추상화 (Ollama / SageMaker)
│   │
│   ├── db/
│   │   ├── oracle.py                  # Oracle 연결 (fetch_one / fetch_all / resolve_stock_code)
│   │   └── mongo.py                   # MongoDB 연결 + 채팅 기록 턴 기반 저장
│   │
│   ├── chatbot/                       # 규칙 기반 라우터
│   │   ├── router.py                  # 키워드 기반 의도 감지 + follow-up 대명사 감지
│   │   ├── dispatcher.py              # 라우팅 상수, dispatch / try_shortcut / pre_dispatch
│   │   ├── handlers.py                # intent별 핸들러 함수 (_handle_*)
│   │   └── session.py                 # 세션 히스토리 분석 헬퍼
│   │
│   ├── agent/                         # 단일 LLM 에이전트
│   │   └── llm_agent.py               # Tool Calling 에이전트 (5개 도구, 대화 기록, 맥락 보강)
│   │
│   ├── data/                          # 외부 데이터 조회
│   │   ├── market.py                  # 시세·차트·순위·지수·환율 (Spring API)
│   │   ├── news.py                    # 한국/미국 시황, 종목 뉴스 (MongoDB)
│   │   ├── account.py                 # 잔고 조회 (Spring API)
│   │   ├── portfolio.py               # 포트폴리오 분석 (Oracle DB + Spring API)
│   │   └── trades.py                  # 거래내역 조회 (Oracle DB)
│   │
│   ├── crawlers/                      # 백그라운드 뉴스 크롤러
│   │   ├── __init__.py                # start() / stop() 인터페이스
│   │   ├── scheduled_crawler.py       # KOSPI200 + NASDAQ100 통합 스케줄러 (30분)
│   │   ├── kosdaq_crawler.py          # KOSDAQ 뉴스 크롤러
│   │   ├── nasdaq_crawler.py          # NASDAQ 뉴스 크롤러
│   │   └── summarizer.py              # KoBART 기반 뉴스 요약
│   │
│   └── templates/                     # 응답 포맷 함수 모음
│       ├── index.py                   # format_index()
│       ├── exchange_rate.py           # format_exchange_rate()
│       ├── ranking.py                 # format_ranking()
│       ├── chart_price.py             # format_chart_price()
│       ├── account.py                 # format_balance(balance_type)
│       ├── stock_news.py              # format_korea_summary() / format_us_summary() / format_stock_news()
│       ├── trades.py                  # format_trades() / format_trades_by_date()
│       ├── portfolio.py               # format_portfolio() / format_portfolio_analysis(metric_type)
│       └── guide.py                   # _GUIDE_MESSAGE / _FALLBACK_MESSAGE / _INVEST_ADVICE_MESSAGE
│
├── kospi200_targets.csv               # KOSPI200 종목 목록 (한글명 ↔ 코드)
├── NASDAQ100.csv                      # NASDAQ100 종목 목록 (한글명 ↔ 티커)
├── test_scenarios.md                  # 챗봇 기능별 테스트 시나리오
├── .env
├── .gitignore
├── requirements.txt
└── README.md

🔄 전체 처리 흐름

사용자 메시지
      │
      ▼
POST /chat  (main.py)
      │  Authorization: Bearer <JWT>
      │  → get_user_context() : user_id, account_id 추출
      │  → try_shortcut(): 종목명만 입력 + 직전 tool 재사용 → LLM 없이 즉시 반환
      ▼
router.detect(message)
      │  키워드 패턴 매칭 → intent, params 반환
      │  대명사("그 종목", "아까" 등) 감지 시 → unknown 반환
      ▼
dispatcher.pre_dispatch(intent, params, message)
      │  문맥 기반 intent 보정
      │  · 손익 키워드 + chart_price → unknown
      │  · 보유 종목 비교 → unknown (agent)
      │  · 명시적 종목 2개+ 비교 → chart_price (multi_stock: True → 단일 종목 안내)
      │  · 매수/매도 추천 질문 → invest_advice (안내문구)
      │  · 종목 미지정 + 명시적 토큰 있음 → stock_not_found
      │  · 종목 미지정 + follow-up → 히스토리에서 마지막 종목 추출
      ▼
dispatcher.dispatch(intent, params, user_context, message)
      │  → handlers.py의 _handle_* 함수 호출
      │
      ├── greeting        → _GUIDE_MESSAGE (서비스 안내)
      ├── index           → get_market_data("index")           → format_index()
      ├── exchange_rate   → get_market_data("exchange")        → format_exchange_rate()
      ├── ranking         → get_market_data("ranking")         → format_ranking()
      ├── chart_price     → get_market_data("price")           → format_chart_price()
      ├── balance         → get_db_data("balance")             → format_balance(balance_type)
      ├── buy_intent      → type: "order", stock_code 반환
      ├── sell_intent     → type: "order", stock_code 반환
      ├── invest_advice   → _INVEST_ADVICE_MESSAGE (매수/매도 추천 불가 안내)
      ├── exchange_order  → type: "exchange"
      ├── korea_summary   → get_market_summary("korea")        → format_korea_summary()
      ├── us_summary      → get_market_summary("us")           → format_us_summary()
      ├── market_summary  → korea + us 동시 조회 → 통합 응답
      ├── stock_news      → 섹터 키워드 감지 시 안내 / get_market_summary("stock_news")
      ├── trades          → 단순 조회 → format_trades()
      │                     날짜 조회 → format_trades_by_date()
      │                     매수/매도 비교 → 직접 계산 (환각 방지)
      │                     세부 질문 → LLM 에이전트
      ├── portfolio       → 단순 조회 → format_portfolio()
      │                     보유 종목 뉴스 → 전용 핸들러 (rule-based)
      │                     지표별 질문 → format_portfolio_analysis(metric_type)
      │                     크로스도메인 질문 → LLM 에이전트
      └── unknown         → 섹터 키워드 감지 시 안내 / LLM 에이전트
      │
      ▼
MongoDB에 턴 단위 저장
  { messages: [user, tool_calls, tool_result, assistant] }
  (템플릿 응답은 "조회한 데이터를 보여드렸어요."로 단축 저장 — LLM 템플릿 모방 방지)
      │
      ▼
ChatResponse { type, reply, stock_code }

🧩 처리 흐름 예시

흐름 1 — 규칙 기반 처리 (빠른 경로)

사용자: "삼성전자 시세 알려줘"

1. POST /chat  { "message": "삼성전자 시세 알려줘" }
2. 미완성 자모 없음 → 통과
3. try_shortcut() — 의도 키워드(시세) 존재 → shortcut 미적용
4. detect()  [router.py]
   - "시세" 패턴 → intent = "chart_price"
   - 종목 추출: "삼성전자" → CSV 검색 → "005930"
5. dispatch() → _handle_chart_price()  [handlers.py]
   - Spring API 호출: GET /api/market/stocks/005930/price
   - templates/chart_price.py로 마크다운 포맷팅
6. MongoDB에 대화 턴 저장
7. 응답: { "type": "stock_price", "reply": "...", "stock_code": "005930" }

흐름 2 — LLM Agent 처리 (복잡한 질문)

사용자: "내 포트폴리오에서 수익률 가장 높은 종목은?"

1. detect() → intent = "portfolio"
2. pre_dispatch() — 크로스도메인 패턴 감지 → LLM Agent 위임
3. _handle_portfolio() → llm_agent.ask_general()
4. LLM Agent 실행
   ├─ MongoDB에서 최근 6턴 대화 기록 로드
   ├─ system + history + 사용자 질문 → LLM 전송
   ├─ LLM: tool_call { get_portfolio_info(info_type="returns") }
   ├─ 도구 실행 → Spring API에서 수익률 데이터 조회
   ├─ 결과를 LLM에 재전송 (최대 5턴 반복)
   └─ LLM 최종 응답 생성
5. 응답: { "type": "text", "reply": "삼성전자가 +18.3%로 ..." }

흐름 3 — Shortcut (직전 조회 재실행)

사용자: (삼성전자 시세 조회 직후) "현대차"

1. try_shortcut()
   - 의도 키워드 없음 확인
   - "현대차" → CSV 검색 → "005380"
   - MongoDB에서 직전 tool 확인 → "get_stock_price"
   - 바로 Spring API 호출 → 응답 반환 (LLM 호출 없음)

흐름 4 — 뉴스 크롤링 (백그라운드)

서버 시작 시 crawler.start() 실행
└─ scheduled_crawler.py
    ├─ 30분마다 반복 실행
    ├─ KOSPI200 + NASDAQ100 종목 목록 순회
    ├─ 네이버 모바일 증권 뉴스 API 호출
    ├─ BeautifulSoup4로 본문 파싱 (50자 미만 기사 제외)
    ├─ summarizer.py → KoBART 요약
    └─ MongoDB stock_news 컬렉션에 upsert (중복 방지)

🧪 API 명세 및 예시

POST /chat

Request:

POST /chat
Authorization: Bearer <JWT>
Content-Type: application/json

{
  "message": "삼성전자 현재가"
}

Response 타입별 구조:

type 프론트엔드 동작
text 마크다운 텍스트 응답
stock_price 주가 카드 렌더링
stock_news 뉴스 카드 렌더링
order 주문 폼 활성화 (stock_code 자동 입력)
exchange 환전 화면 활성화
ranking 순위 카드 렌더링
index 지수 카드 렌더링
balance 잔고 카드 렌더링
market_overview 시황 카드 렌더링

주가 조회 응답 예시:

{
  "type": "stock_price",
  "reply": "## 삼성전자 (005930)\n**현재가**: 71,800원\n**등락률**: +1.27%\n**거래량**: 12,345,678주",
  "stock_code": "005930",
  "stock_name": null,
  "data": { "current_price": 71800, "change_rate": 1.27 }
}

매수 주문 연동 응답 예시:

{
  "type": "order",
  "reply": "**삼성전자** 주문 정보를 입력하세요:",
  "stock_code": "005930"
}

환전 응답 예시:

{
  "type": "exchange",
  "reply": "환전 정보를 입력하세요:",
  "stock_code": null
}

⚙️ Setup & Run

1. 환경변수 설정 (.env)

# 실행 환경
APP_ENV=local                        # local | prod
ENABLE_CRAWLERS=true                 # 뉴스 크롤러 활성화 여부

# LLM
LLM_PROVIDER=ollama                  # ollama | sagemaker
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.1:8b
# SageMaker 사용 시
SAGEMAKER_ENDPOINT_NAME=<endpoint>
SAGEMAKER_RUNTIME_MODE=tgi           # tgi | ollama_proxy
AWS_REGION=ap-northeast-2

# Database
MONGO_URI=mongodb://localhost:27017
MONGO_DB=mockstock
ORACLE_DSN=localhost:1521/XEPDB1
ORACLE_USER=mockstock
ORACLE_PASSWORD=<password>

# Spring 백엔드
SPRING_BASE_URL=http://localhost:8080

# 인증
JWT_SECRET_KEY=<secret>

SageMaker 커스텀 컨테이너(ollama-proxy) 배포 방법은 sagemaker/ollama-proxy/README.md를 참고하세요.

2. 의존성 설치

pip install -r requirements.txt

KoBART 요약 모델 사용 시 PyTorch + Transformers 설치 필요 (torch>=2.2).
GPU 없이도 동작하지만 CPU 추론은 속도가 느릴 수 있습니다.

3. LLM 준비 (Ollama 로컬 실행 시)

ollama pull llama3.1:8b
ollama serve

4. 서버 실행

# 개발
uvicorn app.main:app --reload --port 8000

# 운영
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4

5. 헬스 체크

curl http://localhost:8000/health
# {"status":"ok","app_env":"local"}

6. 호출 예시

# 지수 조회
curl -X POST http://localhost:8000/chat \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"message": "오늘 코스피 어때?"}'

# 종목 시세
curl -X POST http://localhost:8000/chat \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"message": "삼성전자 현재가 알려줘"}'

# 포트폴리오 세부 질문 (LLM 에이전트)
curl -X POST http://localhost:8000/chat \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"message": "내 포트폴리오에서 수익률 가장 좋은 종목은?"}'

📈 Future Improvements

  • 스트리밍 응답: /chat 엔드포인트에 SSE(Server-Sent Events) 적용으로 LLM 응답 실시간 출력
  • LLM 교체 유연성: OpenAI / Claude API 등 프로바이더 추상화 레이어 확장
  • Intent 분류 고도화: 정규식 기반 → 경량 분류 모델(DistilBERT 등) 적용으로 엣지 케이스 대응
  • 크롤러 안정화: IP 차단 대응을 위한 프록시 로테이션, 재시도 로직 강화
  • Unknown 폴백 최적화: LLM 호출 전 1차 필터로 지원 범위 외 질문 조기 차단
  • Redis 캐싱: 자주 조회되는 시세/지수 데이터 응답 속도 개선
  • 모니터링: Prometheus + Grafana로 Intent별 호출 빈도, LLM 응답 시간 추적

📋 기능별 상세 (Intent 참고)

인사 / 도움말

항목 내용
트리거 키워드 안녕, hi, hello, 뭐 할 수 있어, 도움말, 기능
응답 서비스 목록 안내 (_GUIDE_MESSAGE)

(1) 지수 조회

항목 내용
트리거 키워드 지수, 코스피, 코스닥, 나스닥, S&P, 다우, 닛케이, 항셍
데이터 소스 Spring API GET /api/market/indices
응답 포맷 format_index()

(2) 환율 조회

항목 내용
트리거 키워드 환율, 달러, 원달러, 유로, 엔화, 파운드
데이터 소스 Spring API GET /api/market/exchange
파라미터 currency_pair: USDKRW / EURKRW / JPYKRW / GBPKRW
응답 포맷 format_exchange_rate() — 통화별 이모지, 1000원 역환율, 엔화는 100엔 기준

(3) 주식 순위 조회

항목 내용
트리거 키워드 순위, 랭킹, 상승주, 하락주, 많이 오른, 많이 내린, 거래량 순
데이터 소스 Spring API GET /api/market/stocks/ranking
파라미터 ranking_type: trading-value(기본) / trading-volume / rising / falling / market-cap
응답 포맷 format_ranking()

ranking_type 키워드 매핑:

키워드 ranking_type
거래대금 trading-value
거래량 trading-volume
상한가, 상승, 많이 오른, 급등, 올랐 rising
하한가, 하락, 많이 내린, 급락, 떨어졌 falling
시가총액, 시총 market-cap

(4) 차트+시세 조회

항목 내용
트리거 키워드 차트, 시세, 주가, 현재가, 얼마야 + (종목명/코드)
데이터 소스 Spring API GET /api/market/stocks/{code}/price
파라미터 stock_code (6자리 → 국내, 영문 대문자 → 미국, 한글 → CSV 종목명 매칭)
응답 포맷 format_chart_price()
종목 미지정 시 intent = unknown → LLM 에이전트 (이전 맥락에서 종목 추출)

(5) 잔고 조회

항목 내용
트리거 키워드 잔고, 잔액, 예수금, 총 자산, 현금, 내 계좌
데이터 소스 Spring API GET /api/balance/summary
파라미터 balance_type: total_assets / cash / summary
응답 포맷 format_balance(balance_type)

(6) 매수·매도 버튼 연동

항목 내용
매수 트리거 매수, 사고 싶어, 살게, 구매 (단, "매수가/매수는" 등 조사 붙은 경우 제외)
매도 트리거 매도, 팔고 싶어, 팔게, 판매 (단, "매도가/매도는" 등 조사 붙은 경우 제외)
동작 type: "order" + stock_code 로 프론트엔드에 전달

응답 예시:

{
  "type": "order",
  "reply": "**삼성전자** 주문 정보를 입력하세요:",
  "stock_code": "005930"
}

(7) 환전 버튼 연동

항목 내용
트리거 키워드 환전해줘, 달러로 바꿔, 원화로 바꿔
동작 type: "exchange" 로 프론트엔드 환전 화면 활성화

(8) 한국 시황 / (9) 미국 시황 / (8-2) 통합 시황

의도 트리거 예시 응답
korea_summary 한국 시황, 국내 시황, 국장 한국 시황만
us_summary 미국 시황, 미장, 나스닥 시황 미국 시황만
market_summary 시황, 오늘 장 어때, 장 상황 한국 + 미국 통합

범용 키워드("시황", "장 어때")는 market_summary로 처리되어 두 시장 모두 표시됩니다.


(10) 종목별 뉴스 요약

항목 내용
트리거 키워드 뉴스, 기사, 소식 + 종목명
데이터 소스 MongoDB sollite.stock_news
섹터 감지 바이오/반도체 등 섹터 키워드 → 안내 메시지 반환
종목 미지정 시 intent = unknown → LLM 에이전트 (이전 맥락에서 종목 추출)

섹터 키워드 (SECTOR_KEYWORDS, handlers.py): 바이오, 반도체, 제약, 화학, 자동차, IT, 금융, 에너지, 헬스케어, 게임, 엔터, 식품, 건설, 철강, 전기차, 배터리, 2차전지, 항공, 조선, 보험, 은행, 유통, 통신, 방산


(11) 거래내역 조회 — AI agent

항목 내용
트리거 키워드 거래내역, 매매 내역, 체결 내역, 최근 거래
단순 조회 format_trades() / format_trades_by_date() 직접 반환
매수/매도 비교 직접 계산 후 반환 (LLM 비교 환각 방지)
세부 질문 LLM 에이전트 → get_trade_history 도구 호출

(12) 포트폴리오 분석 — AI agent

항목 내용
트리거 키워드 포트폴리오, 포폴, 수익률, 손익, 보유 종목
단순 조회 format_portfolio() 직접 반환
보유 종목 뉴스 전용 rule-based 핸들러 (보유 종목별 뉴스 1건씩)
지표별 질문 format_portfolio_analysis(metric_type) — 수익률/섹터/리스크/거래통계 포커스 뷰
세부/복합 질문 LLM 에이전트 → get_portfolio_info 도구 호출

지표 타입 (metric_type):

키워드 예시 metric_type
수익률, 1개월, 6개월 returns
섹터, 업종, 비중, 국내/해외 sector
리스크, MDD, 변동성, 낙폭 risk
승률, 손익비, 거래 통계 stats
보유 종목, 종목 수 holdings

단일 LLM 에이전트 (llm_agent.py)

unknown / portfolio 세부 질문 / trades 세부 질문이 에이전트로 라우팅됩니다.

도구 목록

도구명 설명 주요 파라미터
get_stock_price 종목 현재가·등락률 조회 stock_code
get_stock_news 종목 최신 뉴스 조회 stock_code
get_market_summary 한국/미국 시황 조회 market: korea / us
get_portfolio_info 포트폴리오 정보 조회 info_type: holdings / sector / returns / risk / stats
get_trade_history 거래내역 조회 query_type: recent / by_stock / by_date

LLM 숫자 환각 방지

tool 결과를 LLM에 전달하기 전에 raw 숫자를 포맷된 문자열로 변환합니다.

도구 변환 내용
get_stock_price current_price: 922000"922,000원" 등 문자열화
get_portfolio_info (holdings) return_rate: -0.003"-0.49%" 등 포맷
get_portfolio_info (returns) 수익률·MDD → "+3.2%" 형식
get_portfolio_info (risk) 손익·MDD → 포맷된 문자열
get_trade_history (by_stock) 거래 목록 → "2026-03-27 매도 1주 @ 179,700원" 형식
get_trade_history (recent) 매수/매도 비교 결과 → "매수(5건)가 매도(1건)보다 많습니다."

대화 맥락 처리 (맥락 보강)

에이전트 호출 전 _enrich_with_context()가 메시지를 보강합니다.

입력 패턴 변환 결과 처리 방식
"그 종목 현재가" "현대차 현재가" 대명사 → 직전 종목명 치환
"현재가 어때" (종목 없음) "현대차 현재가 어때" 직전 언급 종목 주입
"하닉은?" "하닉 뉴스" 또는 "하닉 현재가" 직전 도구 타입 주입

직전 종목 추출 우선순위:

  1. 최근 get_stock_price / get_stock_news tool_call 인자
  2. 최근 get_trade_history(by_stock) tool_call 인자
  3. 최근 assistant 텍스트 첫 줄에서 종목명 파싱

MongoDB 히스토리는 최근 6턴만 로드해 이전 세션 오염을 방지합니다.


MongoDB 채팅 기록 구조

한 턴의 모든 메시지를 단일 document로 저장합니다.

{
  "account_id": "1",
  "timestamp": "2026-03-30T...",
  "messages": [
    {"role": "user",      "content": "삼성전자 뉴스"},
    {"role": "assistant", "tool_calls": [{"function": {"name": "get_stock_news", "arguments": {"stock_code": "005930"}}}]},
    {"role": "tool",      "name": "get_stock_news", "content": "{...}"},
    {"role": "assistant", "content": "삼성전자 최신 뉴스입니다..."}
  ]
}

템플릿 경로(단순 조회) 응답은 "조회한 데이터를 보여드렸어요."로 단축 저장합니다. LLM이 긴 마크다운 템플릿 형식을 모방하는 것을 방지하기 위함입니다.


프론트엔드 위젯 연동 검색어

채팅 입력창에서 아래 키워드를 입력하면 해당 위젯이 활성화됩니다.

위젯 검색어 예시
계좌 잔고 잔고, 계좌, 자산, 평가자산, 내계좌, 예수금, 투자자산, 계좌현황
환율 환율, 달러, 엔, 유로, usd, jpy, eur, 원달러, 환전, 외화, 파운드, 위안
실시간 순위 순위, 랭킹, 거래대금, 상승률, 거래량, 급등, 상한가, 인기종목, 하락률
주요 지수 지수, 코스피, 코스닥, 나스닥, kospi, kosdaq, nasdaq, 다우, s&p, 에센피, 뉴욕, 증시
오늘의 시황 시황, 시장, 헤드라인, 뉴스, 증시뉴스, 장세, 오늘증시, 코스피, 코스닥, 나스닥, 에센피, s&p, nasdaq, kospi, kosdaq, 다우, 뉴욕
포트폴리오 포트폴리오, 포트, 비중, 보유종목, 보유주식, 내주식, 수익률, 손익, 평가손익, 자산배분
관심종목 관심, 관심종목, 즐겨찾기, 찜, 위시
거래내역 거래내역, 매매, 주문내역, 체결, 내역, 거래기록, 주문, 체결내역, 매수내역, 매도내역
주가/차트 (종목명 자동) 삼성전자, SK하이닉스 등 종목명 검색 → {종목명} 주가
종목별 뉴스 (종목명 자동) 위와 동일 → {종목명} 뉴스

의도 감지 우선순위 (router)

1.  greeting       (인사 / 도움말)
2.  buy_intent     (매수 — "매수가/매수는" 등 조사 붙은 경우 제외)
3.  sell_intent    (매도 — "매도가/매도는" 등 조사 붙은 경우 제외)
4.  exchange_order (환전 주문)
5.  balance        (잔고)
6.  portfolio      (포트폴리오 — 보유종목+뉴스 복합 패턴 우선)
7.  stock_news     (종목 뉴스)
8.  korea_summary  (한국 시황)
9.  us_summary     (미국 시황)
10. market_summary (통합 시황)
11. index          (지수)
12. chart_price    (시세·차트)
13. ranking        (주식 순위)
14. exchange_rate  (환율)
15. trades         (거래내역)

follow-up 대명사("그 종목", "아까", "방금" 등) 감지 시 intent = unknown으로 직행합니다.


API 명세

POST /chat

Request:

{
  "message": "삼성전자 시세 알려줘"
}

Headers:

Authorization: Bearer <JWT>
Content-Type: application/json

Response:

{
  "type": "text",
  "reply": "삼성전자 현재가는 75,400원입니다...",
  "stock_code": null
}

주문 응답 (매수/매도 요청 시):

{
  "type": "order",
  "reply": "**삼성전자** 주문 정보를 입력하세요:",
  "stock_code": "005930"
}

환전 응답:

{
  "type": "exchange",
  "reply": "환전 정보를 입력하세요:",
  "stock_code": null
}
type 값 프론트엔드 동작
text 일반 텍스트 응답
order 주문 폼 활성화, stock_code 자동 입력
exchange 환전 화면 활성화

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors