자연어로 주가 조회, 포트폴리오 분석, 매매 주문, 시황 요약을 처리하는 모의 투자 서비스의 AI 챗봇 백엔드입니다.
이 서버는 FastAPI 기반의 AI 챗봇 엔드포인트를 제공하며, 사용자의 자연어 입력을 분석해 적절한 투자 데이터를 응답합니다.
대부분의 요청은 규칙 기반 의도 감지(Intent Detection) 로 빠르게 처리되고, 포트폴리오 분석·거래내역 조회처럼 복잡한 질문은 LLM Tool Calling Agent 로 위임합니다. 시황 요약에 필요한 뉴스는 자체 크롤러가 30분마다 자동 수집하고 KoBART 모델로 요약해 MongoDB에 저장합니다.
Spring Boot 메인 백엔드, React 프론트엔드와 연동되어 주문 실행 및 계좌 데이터를 실시간으로 처리합니다.
- 하이브리드 의도 처리: 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 전환 지원
[사용자]
│ 자연어 입력 (예: "삼성전자 시세")
▼
[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. 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" }
사용자: "내 포트폴리오에서 수익률 가장 높은 종목은?"
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%로 ..." }
사용자: (삼성전자 시세 조회 직후) "현대차"
1. try_shortcut()
- 의도 키워드 없음 확인
- "현대차" → CSV 검색 → "005380"
- MongoDB에서 직전 tool 확인 → "get_stock_price"
- 바로 Spring API 호출 → 응답 반환 (LLM 호출 없음)
서버 시작 시 crawler.start() 실행
└─ scheduled_crawler.py
├─ 30분마다 반복 실행
├─ KOSPI200 + NASDAQ100 종목 목록 순회
├─ 네이버 모바일 증권 뉴스 API 호출
├─ BeautifulSoup4로 본문 파싱 (50자 미만 기사 제외)
├─ summarizer.py → KoBART 요약
└─ MongoDB stock_news 컬렉션에 upsert (중복 방지)
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
}# 실행 환경
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를 참고하세요.
pip install -r requirements.txtKoBART 요약 모델 사용 시 PyTorch + Transformers 설치 필요 (
torch>=2.2).
GPU 없이도 동작하지만 CPU 추론은 속도가 느릴 수 있습니다.
ollama pull llama3.1:8b
ollama serve# 개발
uvicorn app.main:app --reload --port 8000
# 운영
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4curl http://localhost:8000/health
# {"status":"ok","app_env":"local"}# 지수 조회
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": "내 포트폴리오에서 수익률 가장 좋은 종목은?"}'- 스트리밍 응답:
/chat엔드포인트에 SSE(Server-Sent Events) 적용으로 LLM 응답 실시간 출력 - LLM 교체 유연성: OpenAI / Claude API 등 프로바이더 추상화 레이어 확장
- Intent 분류 고도화: 정규식 기반 → 경량 분류 모델(DistilBERT 등) 적용으로 엣지 케이스 대응
- 크롤러 안정화: IP 차단 대응을 위한 프록시 로테이션, 재시도 로직 강화
- Unknown 폴백 최적화: LLM 호출 전 1차 필터로 지원 범위 외 질문 조기 차단
- Redis 캐싱: 자주 조회되는 시세/지수 데이터 응답 속도 개선
- 모니터링: Prometheus + Grafana로 Intent별 호출 빈도, LLM 응답 시간 추적
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 안녕, hi, hello, 뭐 할 수 있어, 도움말, 기능 |
| 응답 | 서비스 목록 안내 (_GUIDE_MESSAGE) |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 지수, 코스피, 코스닥, 나스닥, S&P, 다우, 닛케이, 항셍 |
| 데이터 소스 | Spring API GET /api/market/indices |
| 응답 포맷 | format_index() |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 환율, 달러, 원달러, 유로, 엔화, 파운드 |
| 데이터 소스 | Spring API GET /api/market/exchange |
| 파라미터 | currency_pair: USDKRW / EURKRW / JPYKRW / GBPKRW |
| 응답 포맷 | format_exchange_rate() — 통화별 이모지, 1000원 역환율, 엔화는 100엔 기준 |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 순위, 랭킹, 상승주, 하락주, 많이 오른, 많이 내린, 거래량 순 |
| 데이터 소스 | 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 |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 차트, 시세, 주가, 현재가, 얼마야 + (종목명/코드) |
| 데이터 소스 | Spring API GET /api/market/stocks/{code}/price |
| 파라미터 | stock_code (6자리 → 국내, 영문 대문자 → 미국, 한글 → CSV 종목명 매칭) |
| 응답 포맷 | format_chart_price() |
| 종목 미지정 시 | intent = unknown → LLM 에이전트 (이전 맥락에서 종목 추출) |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 잔고, 잔액, 예수금, 총 자산, 현금, 내 계좌 |
| 데이터 소스 | Spring API GET /api/balance/summary |
| 파라미터 | balance_type: total_assets / cash / summary |
| 응답 포맷 | format_balance(balance_type) |
| 항목 | 내용 |
|---|---|
| 매수 트리거 | 매수, 사고 싶어, 살게, 구매 (단, "매수가/매수는" 등 조사 붙은 경우 제외) |
| 매도 트리거 | 매도, 팔고 싶어, 팔게, 판매 (단, "매도가/매도는" 등 조사 붙은 경우 제외) |
| 동작 | type: "order" + stock_code 로 프론트엔드에 전달 |
응답 예시:
{
"type": "order",
"reply": "**삼성전자** 주문 정보를 입력하세요:",
"stock_code": "005930"
}| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 환전해줘, 달러로 바꿔, 원화로 바꿔 |
| 동작 | type: "exchange" 로 프론트엔드 환전 화면 활성화 |
| 의도 | 트리거 예시 | 응답 |
|---|---|---|
korea_summary |
한국 시황, 국내 시황, 국장 | 한국 시황만 |
us_summary |
미국 시황, 미장, 나스닥 시황 | 미국 시황만 |
market_summary |
시황, 오늘 장 어때, 장 상황 | 한국 + 미국 통합 |
범용 키워드("시황", "장 어때")는
market_summary로 처리되어 두 시장 모두 표시됩니다.
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 뉴스, 기사, 소식 + 종목명 |
| 데이터 소스 | MongoDB sollite.stock_news |
| 섹터 감지 | 바이오/반도체 등 섹터 키워드 → 안내 메시지 반환 |
| 종목 미지정 시 | intent = unknown → LLM 에이전트 (이전 맥락에서 종목 추출) |
섹터 키워드 (SECTOR_KEYWORDS, handlers.py): 바이오, 반도체, 제약, 화학, 자동차, IT, 금융, 에너지, 헬스케어, 게임, 엔터, 식품, 건설, 철강, 전기차, 배터리, 2차전지, 항공, 조선, 보험, 은행, 유통, 통신, 방산
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 거래내역, 매매 내역, 체결 내역, 최근 거래 |
| 단순 조회 | format_trades() / format_trades_by_date() 직접 반환 |
| 매수/매도 비교 | 직접 계산 후 반환 (LLM 비교 환각 방지) |
| 세부 질문 | LLM 에이전트 → get_trade_history 도구 호출 |
| 항목 | 내용 |
|---|---|
| 트리거 키워드 | 포트폴리오, 포폴, 수익률, 손익, 보유 종목 |
| 단순 조회 | 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 |
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 |
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()가 메시지를 보강합니다.
| 입력 패턴 | 변환 결과 | 처리 방식 |
|---|---|---|
"그 종목 현재가" |
"현대차 현재가" |
대명사 → 직전 종목명 치환 |
"현재가 어때" (종목 없음) |
"현대차 현재가 어때" |
직전 언급 종목 주입 |
"하닉은?" |
"하닉 뉴스" 또는 "하닉 현재가" |
직전 도구 타입 주입 |
직전 종목 추출 우선순위:
- 최근
get_stock_price/get_stock_newstool_call 인자 - 최근
get_trade_history(by_stock)tool_call 인자 - 최근 assistant 텍스트 첫 줄에서 종목명 파싱
MongoDB 히스토리는 최근 6턴만 로드해 이전 세션 오염을 방지합니다.
한 턴의 모든 메시지를 단일 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하이닉스 등 종목명 검색 → {종목명} 주가 |
| 종목별 뉴스 (종목명 자동) | 위와 동일 → {종목명} 뉴스 |
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으로 직행합니다.
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 |
환전 화면 활성화 |