- Agent ๊ตฌ์กฐ๋
- Agent ํ๊ฐ ๋ฐฉ๋ฒ
- ์ฃผ์ ๊ธฐ๋ฅ ๋ฐ API ํธ์ถ ์์
- Local ํ๊ฒฝ ์คํ ๊ฐ์ด๋
๋ฐฐํฌ๋ API ์๋ํฌ์ธํธ๋ฅผ ํตํด Agent์ ๊ธฐ๋ฅ์ ํ ์คํธํ๊ณ ์ฑ๋ฅ์ ํ๊ฐํ ์ ์์ต๋๋ค.
http://211.188.58.134:8000/agent
์ฐธ๊ณ ํ์ผ : eval.py
import requests
# API ์ค์
URL = 'http://211.188.58.134:8000/agent'
API_KEY = 'nv-89f...' # ์ค์ ํ๊ฐ์ ๋ฏธ๋์์
์ฆ๊ถ ํ๊ฐ์ฉ API KEY ์ฌ์ฉ
REQUEST_ID = '23ef...' # ์์ฒญ๋ณ ๊ณ ์ ID
# ํค๋ ์ค์
headers = {
'Authorization': f'Bearer {API_KEY}',
'X-NCP-CLOVASTUDIO-REQUEST-ID': f'{REQUEST_ID}'
}
# ์์ฒญ ํ๋ผ๋ฏธํฐ
params = {
'question': '๊ฑฐ๋๋์ด ์ ๋ ๋๋น 15% ์ด์ ์ค๋ฅธ ์ข
๋ชฉ์ ๋ชจ๋ ๋ณด์ฌ์ค'
}
# API ํธ์ถ
response = requests.get(URL, headers=headers, params=params)
print(response.json()) # {'answer': '์ ๋๋ฐ์ด์คํ
, ์๋ํฐ, ์ ์ํ์ ์ฝ๋ฆฌ์ ์
๋๋ค.'}Agent๊ฐ ์ํํ ์ ์๋ 5๊ฐ์ง ํต์ฌ ๊ธฐ๋ฅ๊ณผ ์ค์ API ํธ์ถ ์์์ ๋๋ค.
์ฃผ์ ์์ฅ์ ๋ค์ํ ๊ธ์ต ์ ๋ณด๋ฅผ ์์ฐ์ด ์ง๋ฌธ์ผ๋ก ์กฐํํ ์ ์๋ ๊ธฐ๋ฅ์
๋๋ค.
๋ณต์กํ SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ํ์ ์์ด ์ผ์์ ์ธ ์ธ์ด๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ณด์ ์ ๊ทผํ ์ ์์ต๋๋ค.
def parse_question_with_llm(state: AgentState) -> Dict[str, Any]:
# LLM์ ํตํด ์์ฐ์ด๋ฅผ ๊ตฌ์กฐํ๋ JSON์ผ๋ก ๋ณํ
# ์ง์ task_type: PRICE_INQUIRY, MARKET_STATISTICS, RANKING,
# COMPARISON, SPECIFIC_RANKING, COMPARE_TO_AVERAGE,
# MARKET_PROPORTIONdef execute_plan(state: AgentState) -> Dict[str, Any]:
# ๋ถ์๋ JSON ๊ณํ์ SQL ์ฟผ๋ฆฌ๋ก ๋ณํํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ ์คํ
# ๊ฑฐ๋์ผ ๊ฒ์ฆ, CTE๋ฅผ ํ์ฉํ ๋ฑ๋ฝ๋ฅ ๊ณ์ฐ, ๊ฒฐ๊ณผ ํฌ๋งทํ
์ํ
# ๊ฐ task_type๋ณ ์ฟผ๋ฆฌ ์์ฑ ๋ฐ ์คํ ๋ก์ง ์ฒ๋ฆฌ| Task Type | ์ค๋ช | ์ฌ์ฉ ์ฌ๋ก |
|---|---|---|
PRICE_INQUIRY |
ํน์ ์ข ๋ชฉ์ ๊ฐ๊ฒฉ ์ ๋ณด ์กฐํ | ์๊ฐ, ๊ณ ๊ฐ, ์ ๊ฐ, ์ข ๊ฐ, ๋ฑ๋ฝ๋ฅ |
MARKET_STATISTICS |
์์ฅ ์ ์ฒด ํต๊ณ ์ ๋ณด | ์์น/ํ๋ฝ ์ข ๋ชฉ ์, ์ง์ ๊ฐ๊ฒฉ |
RANKING |
์กฐ๊ฑด๋ณ ์ข ๋ชฉ ์์ | ์์น๋ฅ /ํ๋ฝ๋ฅ /๊ฑฐ๋๋ ์์ ์ข ๋ชฉ |
COMPARISON |
๋ณต์ ์ข ๋ชฉ ๊ฐ ๋น๊ต | ๊ฐ๊ฒฉ, ์๊ฐ์ด์ก ๋น๊ต |
SPECIFIC_RANKING |
ํน์ ์ข ๋ชฉ์ ์์ ํ์ธ | ์ ์ฒด ์์ฅ ๋ด ์์ |
COMPARE_TO_AVERAGE |
์์ฅ ํ๊ท ๋๋น ๋น๊ต | ๊ฐ๋ณ ์ข ๋ชฉ vs ์์ฅ ํ๊ท |
MARKET_PROPORTION |
์์ฅ ๋ด ๋น์ค ๊ณ์ฐ | ์ ์ฒด ์์ฅ ๋๋น ๋น์จ |
- ๊ฐ๊ฒฉ ์กฐํ (
PRICE_INQUIRY)
์ง๋ฌธ: "๋๋ถ๊ฑด์ค์ฐ์ 2024-11-06 ์๊ฐ๋?"
JSON ๋ณํ: {
"task_type": "PRICE_INQUIRY",
"parameters": {
"date": "2024-11-06",
"stock_name": "๋๋ถ๊ฑด์ค์ฐ",
"metric": "open"
}
}
๋ต๋ณ: "15,400์"- ์์ฅ ํต๊ณ (
MARKET_STATISTICS)
์ง๋ฌธ: "2025-03-15์ KOSDAQ์์ ์์นํ ์ข
๋ชฉ์ ๋ช ๊ฐ?"
JSON ๋ณํ: {
"task_type": "MARKET_STATISTICS",
"parameters": {
"date": "2025-03-15",
"market": "KOSDAQ",
"statistic": "rising_stocks"
}
}
๋ต๋ณ: "487๊ฐ"- ์์ ์กฐํ (
RANKING)
์ง๋ฌธ: "2025-01-20์์ KOSPI์์ ์์น๋ฅ ๋์ ์ข
๋ชฉ 5๊ฐ๋?"
JSON ๋ณํ: {
"task_type": "RANKING",
"parameters": {
"date": "2025-01-20",
"market": "KOSPI",
"rank_by": "price_increase",
"top_n": 5
}
}
๋ต๋ณ: "์ผ์ฑ์ ์, SKํ์ด๋์ค, ํ๋์ฐจ, POSCOํ๋ฉ์ค, LGํํ"- ์๊ฐ์ด์ก ๋น๊ต (
COMPARISON)
์ง๋ฌธ: "2025-04-07์ ์นด์นด์ค์ ํ๋์ฐจ ์ค ์๊ฐ์ด์ก์ด ๋ ํฐ ์ข
๋ชฉ์?"
JSON ๋ณํ: {
"task_type": "COMPARISON",
"parameters": {
"date": "2025-04-07",
"stock_names": ["์นด์นด์ค", "ํ๋์ฐจ"],
"metric": "market_cap"
}
}
๋ต๋ณ: "ํ๋์ฐจ (45,234,567,890,123์)"- ํน์ ์ข
๋ชฉ ์์ (
SPECIFIC_RANKING)
์ง๋ฌธ: "2025-02-03์ ์
ํธ๋ฆฌ์จ์ ๊ฑฐ๋๋ ์์๋?"
JSON ๋ณํ: {
"task_type": "SPECIFIC_RANKING",
"parameters": {
"date": "2025-02-03",
"stock_name": "์
ํธ๋ฆฌ์จ",
"rank_by": "volume"
}
}
๋ต๋ณ: "12์"- ์์ฅ ํ๊ท ๋๋น ๋น๊ต (
COMPARE_TO_AVERAGE)
์ง๋ฌธ: "2024-11-18์ ์นด์นด์ค์ ๋ฑ๋ฝ๋ฅ ์ด ์์ฅ ํ๊ท ๋ณด๋ค ๋์๊ฐ?"
JSON ๋ณํ: {
"task_type": "COMPARE_TO_AVERAGE",
"parameters": {
"date": "2024-11-18",
"stock_name": "์นด์นด์ค",
"metric": "change_rate"
}
}
๋ต๋ณ: "์ (+3.45% > ์์ฅํ๊ท +1.23%)"- ์์ฅ ๋น์ค (
MARKET_PROPORTION)
์ง๋ฌธ: "2024-07-22์ NAVER์ ๊ฑฐ๋๋์ด ์ ์ฒด ์์ฅ ๊ฑฐ๋๋์ ๋ช %์ธ๊ฐ?"
JSON ๋ณํ: {
"task_type": "MARKET_PROPORTION",
"parameters": {
"date": "2024-07-22",
"stock_name": "NAVER",
"metric": "volume"
}
}
๋ต๋ณ: "2.34% (1,234,567์ฃผ / 52,768,900์ฃผ)"| ์๋ฌ ์ํฉ | ๊ฒ์ฆ ๋ฐฉ๋ฒ | ๋ฐํ ๋ฉ์์ง |
|---|---|---|
์ฃผ๋ง/๊ณตํด์ผ |
dt.weekday() >= 5 |
"ํ ์์ผ (๋ฐ์ดํฐ ์์)" |
๋ฐ์ดํฐ ์์ |
DB ์กฐํ ๊ฒฐ๊ณผ NULL | "ํด๋น ๋ ์ง ๋ฐ์ดํฐ ์์" |
LLM ํ์ฑ ์คํจ |
JSON ๋์ฝ๋ฉ ์์ธ | "LLM ๋ถ์์ ์คํจํ์์ต๋๋ค" |
์ข
๋ชฉ ๋ฏธ์กด์ฌ |
DB ์กฐํ ๊ฒฐ๊ณผ ์์ | "ํด๋น ์ข ๋ชฉ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค" |
์ ์ ์๋ ์์
|
task_type ๋ถ์ผ์น | "์ ์ ์๋ ์์ ์ ํ์ ๋๋ค" |
def format_value(value: Any, metric: str) -> Optional[str]:
# ๊ฐ๊ฒฉ: "15,400์"
# ๊ฑฐ๋๋: "1,234,567์ฃผ"
# ๋ฑ๋ฝ๋ฅ : "+3.45%"
# ์ง์: "2,456.78"๋ณต์กํ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ข
๋ชฉ๋ค์ ํํฐ๋งํ์ฌ ๊ฒ์ํ๋ ๊ณ ๊ธ ๊ธฐ๋ฅ์
๋๋ค.
์ฌ๋ฌ ์กฐ๊ฑด์ AND ์ฐ์ฐ์ผ๋ก ๊ฒฐํฉํ์ฌ ์ ๊ตํ ๊ฒ์์ด ๊ฐ๋ฅํ๋ฉฐ, ๋ฑ๋ฝ๋ฅ ๊ณผ ๊ฑฐ๋๋ ๋ณ๋๋ฅ ๊ฐ์ ๋์ ์งํ๋ ์ง์ํฉ๋๋ค.
def parse_question_with_llm(state: AgentState) -> Dict[str, Any]:
# LLM์ ์ฌ์ฉํ์ฌ ์์ฐ์ด ์ง๋ฌธ์ ๊ตฌ์กฐํ๋ JSON ๊ฐ์ฒด๋ก ๋ณํ
# ๋ ์ง, ์์ฅ, ์กฐ๊ฑด์ ์ถ์ถํ์ฌ JSON ํ์์ผ๋ก ๊ตฌ์กฐํ
# ์์น๋ ๋จ์๋ฅผ ์ ๊ฑฐํ๊ณ ์ซ์๋ง ์ถ์ถ (300% โ 300, 10๋ง์ โ 100000)def execute_plan(state: AgentState) -> Dict[str, Any]:
# LLM์ด ์์ฑํ ๊ตฌ์กฐํ๋ ๊ณํ์ ๋ฐ๋ผ SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์คํ
# CTE๋ฅผ ํ์ฉํ ์ ์ผ ๋๋น ๊ณ์ฐ, ๋์ WHERE ์ ์์ฑ
# ๊ฒฐ๊ณผ๊ฐ 20๊ฐ๋ฅผ ์ด๊ณผํ ๊ฒฝ์ฐ "๋ฑ์ด ์์ต๋๋ค" ์ถ๊ฐ| Task Type | ์ค๋ช | ์ฌ์ฉ ์ฌ๋ก |
|---|---|---|
price_change |
์ ์ผ ๋๋น ๋ฑ๋ฝ๋ฅ (%) | (ํ์ฌ๊ฐ - ์ ์ผ๊ฐ) / ์ ์ผ๊ฐ ร 100 |
volume_change |
์ ์ผ ๋๋น ๊ฑฐ๋๋ ๋ณ๋๋ฅ (%) | (ํ์ฌ๊ฑฐ๋๋ - ์ ์ผ๊ฑฐ๋๋) / ์ ์ผ๊ฑฐ๋๋ ร 100 |
close_price |
์ข ๊ฐ (์) | ์์ ์ข ๊ฐ ์ง์ ๋น๊ต |
volume |
๊ฑฐ๋๋ (์ฃผ) | ๊ฑฐ๋๋ ์ง์ ๋น๊ต |
- ์์น/ํ๋ฝ ์ข
๋ชฉ ๊ฒ์ (
price_change)
์ง๋ฌธ: "2025-07-21์ ๋ฑ๋ฝ๋ฅ ์ด -5% ์ดํ์ธ KOSDAQ ์ข
๋ชฉ๋ค์ ์ฐพ์์ค"
JSON ๋ณํ: {
"date": "2025-07-21",
"market": "KOSDAQ",
"conditions": [{
"type": "price_change",
"op": "<=",
"value": -5
}]
}
๋ต๋ณ: "์์ฝํ๋ก๋น์ , ํ์ด๋น์ค, ์นด์นด์ค๊ฒ์์ฆ, ์ํ
์ค์ , ์จ์ "- ๊ฑฐ๋๋ ๊ธ์ฆ ๊ฒ์ (
volume_change)
์ง๋ฌธ: "2025-05-14์ ๊ฑฐ๋๋์ด ์ ๋ ๋๋น 300% ์ด์ ์ฆ๊ฐํ ์ข
๋ชฉ์ ๋ชจ๋ ๋ณด์ฌ์ค"
JSON ๋ณํ: {
"date": "2025-05-14",
"market": "all",
"conditions": [{
"type": "volume_change",
"op": ">=",
"value": 300
}]
}
๋ต๋ณ: "์ผ์ฑ๋ฐ์ด์ค๋ก์ง์ค, ์
ํธ๋ฆฌ์จ, ์์ฝํ๋ก๋น์ , ํ์ด๋น์ค, ์นด์นด์ค๊ฒ์์ฆ ๋ฑ์ด ์์ต๋๋ค."- ์ข
๊ฐ ๊ฒ์ (
close_price)
์ง๋ฌธ: "2025-06-10์ KOSPI์์ ์ข
๊ฐ๊ฐ 5๋ง์ ์ดํ์ธ ์ข
๋ชฉ์?"
JSON ๋ณํ: {
"date": "2025-06-10",
"market": "KOSPI",
"conditions": [{
"type": "close_price",
"op": "<=",
"value": 50000
}]
}
# SQL: WHERE "adj_close" <= 50000 AND "market" = 'KOSPI'
๋ต๋ณ: "ํํ์๋ฃจ์
, ํ๋๊ฑด์ค, ๋์ฐ์๋๋น๋ฆฌํฐ, SK์ด๋
ธ๋ฒ ์ด์
, ๋ํํญ๊ณต"- ๋ณตํฉ ์กฐ๊ฑด ๊ฒ์ (
close_price,volume)
์ง๋ฌธ: "2025-09-05์ KOSPI์์ ์ข
๊ฐ๊ฐ 10๋ง์ ์ด์์ด๊ณ ๊ฑฐ๋๋์ด 50๋ง์ฃผ ์ด์์ธ ์ข
๋ชฉ ์๋ ค์ค"
JSON ๋ณํ: {
"date": "2025-09-05",
"market": "KOSPI",
"conditions": [
{"type": "close_price", "op": ">=", "value": 100000},
{"type": "volume", "op": ">=", "value": 500000}
]
}
๋ต๋ณ: "์ผ์ฑ์ ์, SKํ์ด๋์ค, LG์๋์ง์๋ฃจ์
, ์ผ์ฑ๋ฐ์ด์ค๋ก์ง์ค"| ์๋ฌ ์ํฉ | ๊ฒ์ฆ ๋ฐฉ๋ฒ | ๋ฐํ ๋ฉ์์ง |
|---|---|---|
๋ ์ง ๋๋ฝ |
plan.get('date') is None |
"ํ์ ์ ๋ณด(๋ ์ง)๊ฐ ๋๋ฝ๋์์ต๋๋ค" |
๋ ์ง ํ์ ์ค๋ฅ |
datetime.strptime()์์ธ |
"๋ ์ง ํ์์ด ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค: 'YYYY-MM-DD' ํ์ ํ์" |
์กฐ๊ฑด ๋๋ฝ |
plan.get('conditions') is None |
"์ ํจํ ์คํ ๊ณํ์ด ์์ต๋๋ค" |
DB ์ฐ๊ฒฐ ์คํจ |
engine is None |
"๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค" |
์ฟผ๋ฆฌ ์คํ ์ค๋ฅ |
Exception๋ฐ์ |
"๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์คํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค" |
๊ฒฐ๊ณผ ์์ |
results == [] |
"์กฐ๊ฑด์ ๋ง๋ ์ข ๋ชฉ ์์" |
๊ธฐ์ ์ ๋ถ์ ์งํ๋ฅผ ํ์ฉํ์ฌ ๋งค๋งค ์์ ์ ํฌ์ฐฉํ๋ ๊ณ ๊ธ ๋ถ์ ๊ธฐ๋ฅ์
๋๋ค.
RSI, ์ด๋ํ๊ท ์ , ๋ณผ๋ฆฐ์ ๋ฐด๋, ๊ณจ๋ ํฌ๋ก์ค/๋ฐ๋ํฌ๋ก์ค ๋ฑ ๋ค์ํ ๊ธฐ์ ์ ์งํ๋ฅผ ์ข
ํฉ์ ์ผ๋ก ํ์ฉํ์ฌ ํฌ์ ์๊ทธ๋์ ๊ฐ์งํฉ๋๋ค.
def parse_question_with_llm(state: AgentState) -> Dict[str, Any]:
# LLM์ ์ฌ์ฉํ์ฌ ๊ธฐ์ ์ ๋ถ์ ์ง๋ฌธ์ ๊ตฌ์กฐํ๋ JSON ๊ณํ์ผ๋ก ๋ณํ
# ์์
์ ํ, ๋ ์ง/๊ธฐ๊ฐ, ์๊ทธ๋ ์ข
๋ฅ, ์๊ณ๊ฐ์ ์ถ์ถ
# ๊ธฐ๋ณธ๊ฐ๊ณผ ๋ค๋ฅธ ๊ธฐ์ค์ ์ฌ์ฉํ ๊ฒฝ์ฐ thresholds ํ๋ผ๋ฏธํฐ๋ก ์ง์ def execute_plan(state: AgentState) -> Dict[str, Any]:
# ๋ถ์๋ ๊ณํ์ ๋ฐ๋ผ DB๋ฅผ ์กฐํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ฑ
# ๊ฐ ์๊ทธ๋ ์ ํ๋ณ SQL ์ฟผ๋ฆฌ ์์ฑ ๋ฐ ์คํ
# ๋ต๋ณ์ด 20๊ฐ๋ฅผ ์ด๊ณผํ ๊ฒฝ์ฐ LIMIT ์ ์ฉ ๋ฐ "๋ฑ์ด ์์ต๋๋ค" ์ถ๊ฐ| Task Type | ์ค๋ช | ์ฌ์ฉ ์ฌ๋ก |
|---|---|---|
DETECT_SIGNAL |
ํน์ ์ผ์ ์๊ทธ๋ ๊ฐ์ง | RSI ๊ณผ๋งค์/๊ณผ๋งค๋, ๊ฑฐ๋๋ ๊ธ์ฆ ๋ฑ |
FIND_SIGNAL_PERIOD |
๊ธฐ๊ฐ ๋ด ์๊ทธ๋ ๋ฐ์ ์ข ๋ชฉ ๊ฒ์ | ๊ณจ๋ ํฌ๋ก์ค/๋ฐ๋ํฌ๋ก์ค ๋ฐ์ ์ข ๋ชฉ |
COUNT_SIGNAL_PERIOD |
ํน์ ์ข ๋ชฉ์ ๊ธฐ๊ฐ ๋ด ์๊ทธ๋ ํ์ | ๊ฐ๋ณ ์ข ๋ชฉ์ ์๊ทธ๋ ๋ฐ์ ๋น๋ |
| Signal Type | ์ค๋ช | ๊ธฐ๋ณธ ์๊ณ๊ฐ |
|---|---|---|
RSI_OVERBOUGHT |
RSI ๊ณผ๋งค์ | 70 ์ด์ |
RSI_OVERSOLD |
RSI ๊ณผ๋งค๋ | 30 ์ดํ |
VOLUME_SURGE |
๊ฑฐ๋๋ ๊ธ์ฆ | 20์ผ ํ๊ท ๋๋น 200% |
MA_BREAKOUT |
์ด๋ํ๊ท ์ ๋ํ | MA20 ๊ธฐ์ค |
BBAND_UPPER_TOUCH |
๋ณผ๋ฆฐ์ ๋ฐด๋ ์๋จ ์ ์ด | - |
BBAND_LOWER_TOUCH |
๋ณผ๋ฆฐ์ ๋ฐด๋ ํ๋จ ์ ์ด | - |
GOLDEN_CROSS |
๊ณจ๋ ํฌ๋ก์ค (5์ผ์ > 20์ผ์ ) | - |
DEAD_CROSS |
๋ฐ๋ํฌ๋ก์ค (5์ผ์ < 20์ผ์ ) | - |
- RSI ๊ณผ๋งค์ ์ข
๋ชฉ ๊ฒ์ (
DETECT_SIGNAL: RSI_OVERBOUGHT)
์ง๋ฌธ: "2025-01-20์ RSI ๊ณผ๋งค์ ์ข
๋ชฉ์ ์๋ ค์ค"
JSON ๋ณํ: {
"task_type": "DETECT_SIGNAL",
"parameters": {
"date": "2025-01-20",
"signal_type": "RSI_OVERBOUGHT",
"thresholds": {"rsi": 70}
}
}
# SQL: SELECT company, rsi WHERE date = :date AND rsi >= 70
๋ต๋ณ: "์์ฝํ๋ก(RSI:85.3), ํ์ด๋น์ค(RSI:82.1), ์นด์นด์ค๊ฒ์์ฆ(RSI:78.5)"- RSI ๊ณผ๋งค๋ ์ข
๋ชฉ ๊ฒ์ (
DETECT_SIGNAL: RSI_OVERSOLD)
์ง๋ฌธ: "2025-03-06์ RSI๊ฐ 20 ์ดํ์ธ ๊ณผ๋งค๋ ์ข
๋ชฉ์ ์๋ ค์ค"
JSON ๋ณํ: {
"task_type": "DETECT_SIGNAL",
"parameters": {
"date": "2025-03-06",
"signal_type": "RSI_OVERSOLD",
"thresholds": {"rsi": 20}
}
}
๋ต๋ณ: "ํํ์๋ฃจ์
(RSI:15.3), ๋์ฐ์๋๋น๋ฆฌํฐ(RSI:18.7), ํ๋๊ฑด์ค(RSI:19.5)"- ๊ฑฐ๋๋ ๊ธ์ฆ ์ข
๋ชฉ ๊ฒ์ (
DETECT_SIGNAL: VOLUME_SURGE)
์ง๋ฌธ: "2025-02-17์ ๊ฑฐ๋๋์ด 20์ผ ํ๊ท ๋๋น 300% ์ด์ ๊ธ์ฆํ ์ข
๋ชฉ์ ์๋ ค์ค"
JSON ๋ณํ: {
"task_type": "DETECT_SIGNAL",
"parameters": {
"date": "2025-02-17",
"signal_type": "VOLUME_SURGE",
"thresholds": {"volume_surge_percent": 300}
}
}
๋ต๋ณ: "์์ฝํ๋ก๋น์ (450%), ํ์ด๋น์ค(380%), ์นด์นด์ค๊ฒ์์ฆ(325%)"- ์ด๋ํ๊ท ์ ๋ํ ์ข
๋ชฉ ๊ฒ์ (
DETECT_SIGNAL: MA_BREAKOUT)
์ง๋ฌธ: "2024-07-05์ ์ข
๊ฐ๊ฐ 60์ผ ์ด๋ํ๊ท ๋ณด๋ค 3% ์ด์ ๋์ ์ข
๋ชฉ์ ์๋ ค์ค"
JSON ๋ณํ: {
"task_type": "DETECT_SIGNAL",
"parameters": {
"date": "2024-07-05",
"signal_type": "MA_BREAKOUT",
"thresholds": {"ma_period": 60, "ma_breakout_percent": 3}
}
}
๋ต๋ณ: "์ผ์ฑ์ ์(5.23%), SKํ์ด๋์ค(4.87%), ํ๋์ฐจ(3.65%)"- ๊ธฐ๊ฐ ๋ด ๋ฐ๋ํฌ๋ก์ค ๋ฐ์ ์ข
๋ชฉ ๊ฒ์ (
FIND_SIGNAL_PERIOD: DEAD_CROSS)
์ง๋ฌธ: "2024-09-11๋ถํฐ 2024-10-11๊น์ง ๋ฐ๋ํฌ๋ก์ค๊ฐ ๋ฐ์ํ ์ข
๋ชฉ์ ์๋ ค์ค"
JSON ๋ณํ: {
"task_type": "FIND_SIGNAL_PERIOD",
"parameters": {
"start_date": "2024-09-11",
"end_date": "2024-10-11",
"signal_type": "DEAD_CROSS"
}
}
๋ต๋ณ: "2024-10-08 ๊ธฐ์ค: ์นด์นด์ค, ๋ค์ด๋ฒ, ์
ํธ๋ฆฌ์จ, ํ๋์ฐจ, SKํ์ด๋์ค"- ํน์ ์ข
๋ชฉ์ ์๊ทธ๋ ๋ฐ์ ํ์ ๊ณ์ฐ (
COUNT_SIGNAL_PERIOD: GOLDEN_CROSS)
์ง๋ฌธ: "ํ๋๋ฐฑํ์ ์์ 2024-06-01๋ถํฐ 2025-06-30๊น์ง ๊ณจ๋ ํฌ๋ก์ค๊ฐ ๋ช๋ฒ ๋ฐ์ํ์ด?"
JSON ๋ณํ: {
"task_type": "COUNT_SIGNAL_PERIOD",
"parameters": {
"stock_name": "ํ๋๋ฐฑํ์ ",
"start_date": "2024-06-01",
"end_date": "2025-06-30",
"signal_type": "GOLDEN_CROSS"
}
}
๋ต๋ณ: "3๋ฒ"- ํตํฉ ํฌ๋ก์ค ๊ฒ์ (
COUNT_SIGNAL_PERIOD: CROSS_INTEGRATED)
์ง๋ฌธ: "ํจ์
ํ๋ซํผ์ด 2024-06-01๋ถํฐ 2025-06-30๊น์ง ๋ฐ๋ํฌ๋ก์ค ๋๋ ๊ณจ๋ ํฌ๋ก์ค๊ฐ ๋ช๋ฒ ๋ฐ์ํ์ด?"
JSON ๋ณํ: {
"task_type": "COUNT_SIGNAL_PERIOD",
"parameters": {
"stock_name": "ํจ์
ํ๋ซํผ",
"start_date": "2024-06-01",
"end_date": "2025-06-30",
"signal_type": "CROSS_INTEGRATED"
}
}
๋ต๋ณ: "๋ฐ๋ํฌ๋ก์ค 2๋ฒ, ๊ณจ๋ ํฌ๋ก์ค 3๋ฒ"| ์๋ฌ ์ํฉ | ๊ฒ์ฆ ๋ฐฉ๋ฒ | ๋ฐํ ๋ฉ์์ง |
|---|---|---|
๋ ์ง ๋๋ฝ |
params.get('date') is None |
"๋ ์ง ์ ๋ณด๊ฐ ์์ต๋๋ค" |
๋ ์ง ํ์ ์ค๋ฅ |
datetime.strptime()์์ธ |
"๋ ์ง ํ์์ด ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค: 'YYYY-MM-DD' ํ์ ํ์" |
๊ธฐ๊ฐ ์ ๋ณด ๋๋ฝ |
start_date or end_date is None |
"๊ธฐ๊ฐ ์ ๋ณด๊ฐ ์์ต๋๋ค" |
์กฐํ ๊ธฐ๊ฐ ์ด๊ณผ |
(end_date - start_date).days > 30 |
"์กฐํ ๊ธฐ๊ฐ์ด 30์ผ์ ์ด๊ณผํ์ฌ ๋๋ฌด ๊น๋๋ค" |
์ข
๋ชฉ/๊ธฐ๊ฐ ๋๋ฝ |
not all([stock_name, dates]) |
"์ข ๋ชฉ ๋๋ ๊ธฐ๊ฐ ์ ๋ณด๊ฐ ๋ถ์กฑํฉ๋๋ค" |
DB ํ์์์ |
OperationalError |
"๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ ์๊ฐ์ด ์ด๊ณผ๋์์ต๋๋ค" |
์ ์ ์๋ ์๊ทธ๋ |
signal not in valid_signals |
"์ ์ ์๋ ์ ํธ ์ ํ์ ๋๋ค" |
๊ฒฐ๊ณผ ์์ |
results == [] |
"์กฐ๊ฑด์ ๋ง๋ ์ข ๋ชฉ ์์" ๋๋ "ํด๋น ๊ธฐ๊ฐ์ ์กฐ๊ฑด์ ๋ง๋ ์ข ๋ชฉ ์์" |
๋ถ์์ ํ๊ฑฐ๋ ๋ชจํธํ ์ฃผ์ ๊ด๋ จ ์ง๋ฌธ์ ์๋์ผ๋ก ํด์ํ๊ณ ๋ช
ํํ๊ฒ ๋ณํํ๋ AI ์์ด์ ํธ์
๋๋ค.
์ถ์ฝ์ด, ์์ด, ๋ถ๋ช
ํํ ํํ์ ์ธ์ํ์ฌ ์ ์ ํ ๋ณํํ๊ฑฐ๋ ์ฌ์ฉ์์๊ฒ ์ถ๊ฐ ์ ๋ณด๋ฅผ ์์ฒญํฉ๋๋ค.
-
ํต์ฌ ์ค๊ณ ์ฒ ํ
- LLM์ด ๋ชจํธ์ฑ์ ํ๋จํ๋ ๋ช ํํ ๊ธฐ์ค์ ์ ์ฉํ๊ธฐ ์ํด ๋ชจํธ์ฑ์ ์ ํํํ๊ณ , Checklist์ฒ๋ผ Yes/No๋ก ์ฒ๋ฆฌํ๋๋ก ์ค๊ณ
-
๊ธ์ต ๋๋ฉ์ธ์์์ ๋ชจํธ์ฑ ์ ํํ
- ์ฆ๊ถ ํ์ ์ ๋ฌธ๊ฐ ์๋ฌธ์ ํตํด ๊ธ์ต ๋๋ฉ์ธ์ ๋ชจํธ์ฑ์ 4๊ฐ์ง๋ก ์ ํํ
-
๋ชจํธ์ฑ ์ ๋ํ ๋ฐฉ๋ฒ๋ก
- ๊ธฐ์กด ์ฐ๊ตฌ์ ํ๊ณ์
- ์ ๋ฌธ๊ฐ ์ง์ Annotation โ ๋์ ๋น์ฉ
- LLM ๋ค์ค ๋ต๋ณ ํ ๋ค์๋ ์ธก์ โ ๋นํจ์จ์
- ๊ธฐ์กด ์ฐ๊ตฌ์ ํ๊ณ์
-
ํด๊ฒฐ์ฑ : CheckEval ๋ฐฉ์ ์ ์ฉ
- Checklist ํํ์ Yes/No ์กฐ๊ฑด๋ฌธ์ผ๋ก ๋ชจํธ์ฑ ํ๋จ
- ์ฌ๋์ ์ผ๊ด์ฑ ๋ฌธ์ ํด๊ฒฐ
- ๋ช ํํ ์ ์ ๊ธฐ์ค ์ ๊ณต
- ์ฐธ๊ณ ๋ ผ๋ฌธ: CheckEval
-
๋ชจํธ์ฑ ํ๋จ ํ ์ฒ๋ฆฌ ๋ฐฉ์
- Query Rewriting: DB/์ง์๊ทธ๋ํ ํ์ฉ ๊ฐ๋ฅํ๋๋ก ์ง๋ฌธ ์ฌ์์ฑ
- Clarification: ๋๋ฝ๋ ์ ๋ณด์ ๋ํด ์ฌ์ฉ์์๊ฒ ์ญ์ง๋ฌธ
- ์ฐธ๊ณ ๋ ผ๋ฌธ: APA, Query Clarification
def task4_router_node(state: AgentState) -> dict:
# ์ฌ์ฉ์ ์ง๋ฌธ์ ๋ชจํธ์ฑ ์ ํ์ ํ๋จํ์ฌ ์ฒ๋ฆฌ ๊ฒฝ๋ก ๊ฒฐ์
# 4๊ฐ์ง ์กฐ๊ฑด์ ์ฒดํฌํ์ฌ Rewriting ๋๋ Clarify ๊ฒฝ๋ก ์ ํ
# ์กฐ๊ฑด 1,2๋ Rewriting, ์กฐ๊ฑด 3,4๋ Clarify๋ก ๋ผ์ฐํ
def rewrite_query_node(state: AgentState) -> dict:
# ์ข
๋ชฉ ์ถ์ฝ์ด์ ์ฃผ์ ์์ด๋ฅผ ์ ์ ์ฉ์ด๋ก ์๋ ๋ณํ
# ๋ณํ๋ ์ง๋ฌธ์ ๋ฐํํ์ฌ ๋ฉ์ธ ๊ทธ๋ํ๊ฐ ์ฌ์ฒ๋ฆฌ
# turn_count๋ฅผ ํตํด ๋ฌดํ ๋ฃจํ ๋ฐฉ์งdef clarify_question_node(state: AgentState) -> dict:
# ๋ชจํธํ ๊ธฐ๊ฐ์ด๋ ๋๋ฝ๋ ์ ๋ณด์ ๋ํ ์ญ์ง๋ฌธ ์์ฑ
# ์ฌ์ฉ์ ์นํ์ ์ด๊ณ ์ ํ์ง๋ฅผ ์ ๊ณตํ๋ ํํ์ ์ง๋ฌธ
# ๊ฐ๋จํ๊ณ ๋ช
๋ฃํ ํ ๋ฌธ์ฅ ํํ๋ก ์์ฑ| Task Type | ๋ชจํธ์ฑ ์ ํ | ์ฒ๋ฆฌ ๋ฐฉ์ | ์์ |
|---|---|---|---|
์ ํ1 |
์ข ๋ชฉ ์ถ์ฝ์ด ํฌํจ | Rewriting | ์ผ์ โ ์ผ์ฑ์ ์ |
์ ํ2 |
์ฃผ์ ์์ด ํฌํจ | Rewriting | ๋ก์ โ ํฐ ํญ ์์น |
์ ํ3 |
๋ชจํธํ ๊ธฐ๊ฐ ํฌํจ | Clarifying | ์ต๊ทผ โ ๋ฉฐ์น ๊ธฐ์ค? |
์ ํ4 |
์ ๋ณด ๋๋ฝ | Clarifying | ์ข ๋ชฉ๋ช /๋ ์ง ์์ฒญ |
- ์ข
๋ชฉ ์ถ์ฝ์ด ๋ณํ (
์ ํ1)
# ์ฃผ์ ์ถ์ฝ์ด ๋ณํ ์์
์ผ์ โ ์ผ์ฑ์ ์
ํ๋ โ SKํ์ด๋์ค
์นด๋ฑ
โ ์นด์นด์ค๋ฑ
ํฌ
ํ์ฐจ/ํ๊ธฐ์ฐจ โ ํ๋์ฐจ
๋ํ์ โ NICEํ๊ฐ์ ๋ณด
์์ โ LG์๋์ง์๋ฃจ์
SKT โ SKํ
๋ ์ฝค
ํ์ โ ํ๊ตญ์ ๋ ฅ
์ง๋ฌธ: "์ผ์ ์ฃผ๊ฐ ์๋ ค์ค"
๋ณํ: "์ผ์ฑ์ ์ ์ฃผ๊ฐ ์๋ ค์ค"
๊ฒฐ๊ณผ: "__REWRITE_SUCCESS__" ๋ฐํ โ ๋ฉ์ธ ๊ทธ๋ํ ์ฌ์คํ- ์ฃผ์ ์์ด ๋ณํ (
์ ํ2)
# ์ฃผ์ ์์ด ๋ณํ ์ฌ์
๋ก์ โ ์ ์ผ ๋๋น ํฐ ํญ์ผ๋ก ์์นํ
๋ฐ์ โ ๊ณต๋ชจ๊ฐ์ 2๋ฐฐ๋ก ์์ด๊ฐ ํ์ฑ ํ ์ํ๊ฐ ๋๋ฌํ
ํ๋จ โ ํ๊ท ๋งค์ ๋จ๊ฐ
๋ฌผํ๊ธฐ โ ์ถ๊ฐ ๋งค์๋ก ํ๊ท ๋จ๊ฐ๋ฅผ ๋ฎ์ถ
๋๋ฆผ๋ชฉ โ ์ผ์ ๊ธฐ๊ฐ ํ๋ฝ ์กฐ์ ์ด ์์๋
๋จํ์กฑ โ ๋จ๊ธฐ ๋งค๋งค ํฌ์์
์ง๋ฌธ: "์ค๋ ๋ก์ํ ์ข
๋ชฉ ๋ณด์ฌ์ค"
๋ณํ: "์ค๋ ์ ์ผ ๋๋น ํฐ ํญ์ผ๋ก ์์นํ ์ข
๋ชฉ ๋ณด์ฌ์ค"- ๋ชจํธํ ๊ธฐ๊ฐ ์ญ์ง๋ฌธ (
์ ํ3)
์ง๋ฌธ: "์ผ์ฑ์ ์์ ์ต๊ทผ ์ข
๊ฐ๋?"
์ญ์ง๋ฌธ: "์ผ์ฑ์ ์์ ์ต๊ทผ ๋ฉฐ์น ์ ์ข
๊ฐ๋ฅผ ์๋ ค๋๋ฆด๊น์? ์ - 1์ผ, 3์ผ, 5์ผ"
์ง๋ฌธ: "์์ฆ ์์นํ ์ข
๋ชฉ๋ค ์๋ ค์ค"
์ญ์ง๋ฌธ: "์ต๊ทผ ๋ฉฐ์น ๋์์ ์์น ์ข
๋ชฉ์ ํ์ธํ๊ณ ์ถ์ผ์ ๊ฐ์?"- ๋๋ฝ ์ ๋ณด ์ญ์ง๋ฌธ (
์ ํ4)
์ง๋ฌธ: "2025-07-24์ ์ข
๊ฐ๋ฅผ ์๋ ค์ค"
์ญ์ง๋ฌธ: "์ด๋ค ์ข
๋ชฉ์ 2025-07-24 ์ข
๊ฐ๋ฅผ ํ์ธํ๊ณ ์ถ์ผ์ ๊ฐ์?"
์ง๋ฌธ: "์์น๋ฅ 1์ ์ข
๋ชฉ์?"
์ญ์ง๋ฌธ: "์ด๋ ๋ ์ง์ ์์น๋ฅ 1์ ์ข
๋ชฉ์ ํ์ธํ๊ณ ์ถ์ผ์ ๊ฐ์?"
์ง๋ฌธ: "๊ฑฐ๋๋ ๋ง์ ์ข
๋ชฉ 5๊ฐ"
์ญ์ง๋ฌธ: "์ด๋ ๋ ์ง์ ๊ฑฐ๋๋ ์์ 5๊ฐ ์ข
๋ชฉ์ ์๋ ค๋๋ฆด๊น์?"์ฌ์ฉ์ ์
๋ ฅ โ ๋ชจํธ์ฑ ๋ถ์ โ ๊ฒฝ๋ก ๊ฒฐ์
โโ ์กฐ๊ฑด 1,2 โ Rewriting โ ๋ณํ๋ ์ง๋ฌธ โ ์ฌ์ฒ๋ฆฌ
โโ ์กฐ๊ฑด 3,4 โ Clarifying โ ์ญ์ง๋ฌธ ์์ฑ๊ฐ์ธ ํฌ์์์ ๋งค๋งค ํจํด์ ๋ถ์ํ์ฌ ๊ณผ๋ํ ์ง์ค ํฌ์ ์ํ์ ๊ฐ์งํ๊ณ , ๋ด์ค ๋ถ์์ ํตํด ๋งค๋งค ๋๊ธฐ๋ฅผ ํ์
ํ์ฌ ๋ง์ถคํ ๊ฒฝ๊ณ ๋ฅผ ์ ๊ณตํ๋ ๋ฆฌ์คํฌ ๊ด๋ฆฌ ๊ธฐ๋ฅ์
๋๋ค.
ํฌ์์์ ๋์ด, ํฌ์์ฑํฅ, ์์ฐ๊ท๋ชจ๋ฅผ ๊ณ ๋ คํ ๊ฐ์ธํ๋ ์ง์ค ํฌ์ ์ํ ๋ถ์(PTPRA: Personalized Trading Pattern Risk Alert)์ ์ํํฉ๋๋ค.
์ต์ข
์ ์ผ๋ก ์ํ ๋ถ์์ ํตํด, ์ ํฉํ ๋ด์ค๋ฅผ ํ์ด๋ผ์ดํ
ํ์ฌ ๋ณด์ฌ์ค๋๋ค.
LLM์ ์์ฑ๋ฌธ ๋ ๋ถ๋ถ์์ 'ํ์ด๋ผ์ดํ
ํ ๋ด์ค ๊ธฐ์ฌ ์ด๋ฏธ์ง: http://~~~' ๋ถ๋ถ์ -> image ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด image๊ฐ url ํํ๋ก ๋์์ง๋๋ค.
def extract_transactions(state: AgentState) -> Dict[str, Any]:
# ์ฌ์ฉ์ ์
๋ ฅ์์ JSON ํํ์ ๋งค๋งค ๊ธฐ๋ก ์ถ์ถ
# ๋ ์ง, ๊ฑฐ๋์ ํ, ์ข
๋ชฉ, ๊ฐ๊ฒฉ, ์๋ ์ ๋ณด๋ฅผ ํ์ฑ
# ์๊ฐ์์ผ๋ก ์ ๋ ฌํ์ฌ ํฌํธํด๋ฆฌ์ค ๋ณํ ์ถ์
def extract_mydata(state: AgentState) -> Dict[str, Any]:
# LLM์ ํ์ฉํ์ฌ ๋ง์ด๋ฐ์ดํฐ ์ ๋ณด ์ถ์ถ
# ํฌ์์ฑํฅ์ 5๊ฐ์ง ์นดํ
๊ณ ๋ฆฌ๋ก ๋ถ๋ฅ
# ๋์ด์ ์ด ๊ธ์ต์์ฐ ์ ๋ณด ๊ตฌ์กฐํdef analyze_risk_patterns(state: AgentState) -> Dict[str, Any]:
# ๋งค๋งค ๊ธฐ๋ก์ผ๋ก๋ถํฐ ํ์ฌ ํฌํธํด๋ฆฌ์ค ๊ณ์ฐ
# ๊ฐ์ธํ๋ ์ํ ์๊ณ์น = ํฌ์์ฑํฅ ํ๋ ร ์์ ์ฃผ๊ธฐ ๊ณ์
# ๋จ์ผ ์ข
๋ชฉ ์ง์ค๋๊ฐ ์๊ณ์น๋ฅผ ์ด๊ณผํ๋์ง ๊ฒ์ฌ
def verify_analysis_results(state: AgentState) -> Dict[str, Any]:
# ๊ณ์ฐ๋ ์ํ ํจํด์ ์ ํ์ฑ ๊ฒ์ฆ
# ๋ณด๊ณ ๋ ๋น์ค๊ณผ ์ค์ ๊ณ์ฐ ๋น์ค์ ์ผ์น ์ฌ๋ถ ํ์ธ
# ์ค๋ฅ ๋ฐ์ ์ ์๋ฌ ์ฒ๋ฆฌ ๋
ธ๋๋ก ๋ผ์ฐํ
- ์ ๋ ฅ๋๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ
| ๋ฐ์ดํฐ ์ ํ | ํ์ ํญ๋ชฉ | ํ์ |
|---|---|---|
ํ
์คํธ Input |
"์ ๋งค๋งค ํจํด์ ๋ถ์ํด ์ฃผ์ธ์." | ํ ์คํธ |
๋งค๋งค ๊ธฐ๋ก |
๋ ์ง, ๊ฑฐ๋์ ํ, ์ข ๋ชฉ, ๊ฐ๊ฒฉ, ์๋ | JSON ๋ฐฐ์ด |
๋ง์ด๋ฐ์ดํฐ |
ํฌ์์ฑํฅ, ๋์ด, ์ด ๊ธ์ต์์ฐ | JSON ๊ฐ์ฒด |
- ํฌ์์ฑํฅ ๋ถ๋ฅ
| ํฌ์์ฑํฅ | ์ฃผ์ ๋น์ค ํ๋ | ์ํ ์์ค |
|---|---|---|
์์ ํ |
20% | ๋งค์ฐ ๋ฎ์ |
์์ ์ถ๊ตฌํ |
40% | ๋ฎ์ |
์ํ์ค๋ฆฝํ |
60% | ๋ณดํต |
์ ๊ทนํฌ์ํ |
80% | ๋์ |
๊ณต๊ฒฉํฌ์ํ |
90% | ๋งค์ฐ ๋์ |
- ์์ ์ฃผ๊ธฐ ๊ณ์
| ์ฐ๋ น๋ | ๊ณ์ | ์ค๋ช |
|---|---|---|
20-39์ธ |
0.30 | ์ ์ ์ธต, ์ํ ๊ฐ์ ๊ฐ๋ฅ |
40-59์ธ |
0.20 | ์ค๋ ์ธต, ์์ ์ฑ ์ถ๊ตฌ |
60์ธ ์ด์ |
0.10 | ๋ ธ๋ ์ธต, ๋ณด์์ ์ด์ฉ |
- ์ต์ข ์ํ ๊ธฐ์ค = (ํฌ์ ์ฑํฅ๋ณ ์ฃผ์ ํฌ์ ํ๋) ร (์์ ์ฃผ๊ธฐ๋ณ ์์ฐ ์ง์ค๋ ํ๋)
| ์์ ์ฃผ๊ธฐ / ์ง์ค๋ ํ๋ | ์์ ํ(20%) | ์์ ์ถ๊ตฌํ(40%) | ์ํ์ค๋ฆฝํ(60%) | ์ ๊ทนํฌ์ํ(80%) | ๊ณต๊ฒฉํฌ์ํ(90%) |
|---|---|---|---|---|---|
์ด๊ธฐ (30%) |
6.00% | 12.00% | 18.00% | 24.00% | 27.00% |
์ค๊ธฐ (20%) |
4.00% | 8.00% | 12.00% | 16.00% | 18.00% |
ํ๊ธฐ (10%) |
2.00% | 4.00% | 6.00% | 8.00% | 9.00% |
- ๊ธฐ๋ณธ ์ ๋ ฅ ์์
{
"๋งค๋งค๊ธฐ๋ก": [
{
"๋ ์ง": "2025-05-26",
"๊ฑฐ๋์ ํ": "๋งค์",
"์ข
๋ชฉ": "LGํํ",
"๊ฐ๊ฒฉ": 412000,
"์๋": 80
},
{
"๋ ์ง": "2025-05-27",
"๊ฑฐ๋์ ํ": "๋งค์",
"์ข
๋ชฉ": "์นด์นด์ค๋ฑ
ํฌ",
"๊ฐ๊ฒฉ": 23500,
"์๋": 150
}
],
"๋ง์ด๋ฐ์ดํฐ": {
"ํฌ์์ฑํฅ(profile)": "์ํ์ค๋ฆฝํ",
"๋์ด(age)": 25,
"์ด๊ธ์ต์์ฐ(total_financial_assets)": 90000000
}
}- ์ํ ๋ถ์ ๊ฒฐ๊ณผ
# ๊ฐ์ธํ ์๊ณ์น ๊ณ์ฐ
ํฌ์์ฑํฅ ํ๋: 60% (์ํ์ค๋ฆฝํ)
์์ ์ฃผ๊ธฐ ๊ณ์: 0.30 (25์ธ)
์ต์ข
์๊ณ์น: 0.60 ร 0.30 = 18%
# ํฌํธํด๋ฆฌ์ค ๋ถ์
LGํํ ๋น์ค(personalized_threshold): 90% (32,960,000์ / 36,485,000์)
์๊ณ์น(concentration) ์ด๊ณผ: 90% > 18% โ
# ์ํ ํจํด ๊ฐ์ง
{
"risk_category": "์ง์ค ํฌ์ ์ํ",
"stock_name": "LGํํ",
"concentration": 0.18,
"description": f"'{riskiest_stock}' ์ข
๋ชฉ์ ๋น์ค์ด {max_concentration:.2%}๋ก, ๊ณ ๊ฐ๋์ ํ๋กํ(๋์ด: {age}์ธ, ์ฑํฅ: {profile})์ ๋ฐ๋ฅธ ๊ถ์ฅ ํ๋ {personalized_threshold:.2%}๋ฅผ ์ด๊ณผํ ์ํฉ์
๋๋ค.",
"recommendation": "ํน์ ์ข
๋ชฉ์ ๋ํ ๊ณผ๋ํ ํฌ์๋ ํด๋น ์ข
๋ชฉ์ ๊ฐ๊ฒฉ ๋ณ๋์ ํฌํธํด๋ฆฌ์ค ์ ์ฒด๊ฐ ํฌ๊ฒ ํ๋ค๋ฆด ์ ์์ต๋๋ค. ๋ถ์ฐ ํฌ์๋ฅผ ํตํด ์์ ์ฑ์ ๋์ด๋ ๊ฒ์ ๊ณ ๋ คํด๋ณด์ธ์."
}def search_korean_documents(state: AgentState) -> Dict[str, Any]:
# ๋ค์ด๋ฒ ๋ด์ค API๋ฅผ ํตํ ๊ด๋ จ ๋ด์ค ๊ฒ์
# ๋งค์์ผ ๊ธฐ์ค -7์ผ ~ ๋น์ผ ๋ด์ค ์ฐ์ ํํฐ๋ง
# ์ค๋ณต ์ ๊ฑฐ ๋ฐ ์์ ๊ธฐ๋ฐ ์ ๋ ฌ
def analyze_and_extract_from_news(state: AgentState) -> Dict[str, Any]:
# 3๋จ๊ณ ๋ด์ค ๋ถ์ ํ๋ก์ธ์ค
# 1. ๊ฐ์ฅ ๊ด๋ จ์ฑ ๋์ ๋ด์ค URL ์ ์
# 2. Selenium์ผ๋ก ์๋ฌธ ์ ์ฒด ์คํฌ๋ํ
# 3. ํต์ฌ ๋ฌธ๋จ ์ถ์ถ (LLM ํ์ฉ)
def capture_highlighted_image(state: AgentState) -> Dict[str, Any]:
# BeautifulSoup์ผ๋ก HTML ๋ถ์ ๋ฐ <mark> ํ๊ทธ ์ฝ์
# Selenium์ผ๋ก ์์ ๋ HTML ๋ ๋๋ง
# ํ์ด๋ผ์ดํธ ์์ญ ์ค์ฌ์ผ๋ก ์คํฌ๋ฆฐ์ท ์บก์ฒ- ๋งค๋งค ์ธ์: ๊ฑฐ๋ ๋ด์ญ ํ์ธ
- ์์ธ ์ถ์ : ๋ด์ค ๊ธฐ๋ฐ ๋งค๋งค ๋๊ธฐ ๋ถ์
- ์ํ ๊ฒฝ๊ณ : ๊ฐ์ธํ๋ ์ํ ์์ค ์๋ฆผ
- ๊ถ๊ณ ์ฌํญ: ๋ถ์ฐ ํฌ์ ์ ์
- ์๊ฐ์ ๊ทผ๊ฑฐ: ํ์ด๋ผ์ดํ ๋ ๋ด์ค ์ด๋ฏธ์ง
์ต๊ทผ **2025๋
05์ 26์ผ**์ ์งํํ์ **'LGํํ' ๋งค์** ๊ธฐ๋ก์ ํ์ธํ์ต๋๋ค.
๊ณ ๊ฐ๋์ ์ด๋ฌํ ๊ฒฐ์ ์ ์๋ง๋ **'LGํํ์ด ์ผ๋ณธ ๋
ธ๋ฆฌํ์ผ์ ์ ๋ ฅ ๋ฐ๋์ฒด์ฉ ์ค๋ฒ ํ์ด์คํธ ๊ณต๋ ๊ฐ๋ฐ์ ์ฑ๊ณตํจ์ผ๋ก์จ, ์ ๊ธฐ์ฐจ ์์ฅ์์์ ๊ฒฝ์๋ ฅ ๊ฐํ ๋ฐ ์ค์ ๊ฐ์ ๊ธฐ๋๊ฐ ์์น'** ๊ด๋ จ ์์ ๋๋ฌธ์ด์์ ๊ฒ์ผ๋ก ์๊ฐ๋ฉ๋๋ค. ๋น์ ๋ด์ค์์๋ 'LGํํ์ด ์ผ๋ณธ ์ ๋ฐ์ธ๋ผ๋ฏน ์ ๋ฌธ๊ธฐ์
๋
ธ๋ฆฌํ์ผ์ ์์ก๊ณ ์ ๊ธฐ์ฐจ ์ ๋ ฅ ๋ฐ๋์ฒด์ ์ฌ์ฉ๋๋ ๊ณ ์ฑ๋ฅ ์ ์ฐฉ์ ์์ฅ ๊ณต๋ต์ ๋์ ๋ค. ์์ฌ๋ 16์ผ ์ฐจ์ธ๋ ์ฐจ๋์ฉ ์ ๋ ฅ ๋ฐ๋์ฒด์ฉ โ์ค๋ฒ ํ์ด์คํธ(Silver Paste)โ๋ฅผ ๊ณต๋ ๊ฐ๋ฐํ๋ค๊ณ ๋ฐํ๋ค.'๋ผ๋ฉฐ ๊ธ์ ์ ์ธ ์ ๋ง์ ๋ด๋์์ฃ .
ํ์ง๋ง ๊ณ ๊ฐ๋์ ํ๋กํ(๋์ด: 35์ธ, ์ฑํฅ: ์ํ์ค๋ฆฝํ)์ ๊ธฐ์ค์ผ๋ก ๋ถ์ํ ๊ฒฐ๊ณผ, ํ์ฌ **'์ง์ค ํฌ์ ์ํ'** ์ํ์ ํด๋นํฉ๋๋ค. 'LGํํ' ์ข
๋ชฉ์ ๋น์ค์ด 90.34%๋ก, ๊ณ ๊ฐ๋์ ํ๋กํ(๋์ด: 35์ธ, ์ฑํฅ: ์ํ์ค๋ฆฝํ)์ ๋ฐ๋ฅธ ๊ถ์ฅ ํ๋ 18.00%๋ฅผ ์ด๊ณผํ ์ํฉ์
๋๋ค.
์ด๋ฌํ ์ง์ค ํฌ์ ํจํด์ด ์ง์๋ ๊ฒฝ์ฐ, ํด๋น ์ข
๋ชฉ์ ์์ ๋ณ๋์ฑ์๋ ์ ์ฒด ์์ฐ์ด ํฌ๊ฒ ์ํฅ์ ๋ฐ์ ์ ์์ผ๋ฉฐ, ์์์น ๋ชปํ ํ๋ฝ ์ ํฐ ์์ค๋ก ์ด์ด์ง ์ํ์ด ์์ต๋๋ค. ํน์ ์ข
๋ชฉ์ ๋ํ ๊ณผ๋ํ ํฌ์๋ ํด๋น ์ข
๋ชฉ์ ๊ฐ๊ฒฉ ๋ณ๋์ ํฌํธํด๋ฆฌ์ค ์ ์ฒด๊ฐ ํฌ๊ฒ ํ๋ค๋ฆด ์ ์์ต๋๋ค. ๋ถ์ฐ ํฌ์๋ฅผ ํตํด ์์ ์ฑ์ ๋์ด๋ ๊ฒ์ ๊ณ ๋ คํด๋ณด์ธ์.
[ํ์ด๋ผ์ดํ
๋ ๋ด์ค ์ด๋ฏธ์ง]
ํ์ด๋ผ์ดํ
ํ ๋ด์ค ๊ธฐ์ฌ ์ด๋ฏธ์ง: http://147.47.39.102:8000/images/20250728_190233_183488.png
์๋ณธ ์๋ฃ: https://n.news.naver.com/...| ์๋ฌ ์ํฉ | ๊ฒ์ฆ ๋ฐฉ๋ฒ | ๋ฐํ ๋ฉ์์ง |
|---|---|---|
๋งค๋งค๊ธฐ๋ก ํ์ฑ ์คํจ |
JSON ๋์ฝ๋ฉ ์์ธ | "๋งค๋งค ๊ธฐ๋ก JSON ํ์ฑ ์ค ์ค๋ฅ ๋ฐ์" |
๋ง์ด๋ฐ์ดํฐ ๋๋ฝ |
ํ์ ํ๋ null | "๋ง์ด๋ฐ์ดํฐ(ํฌ์์ฑํฅ, ๋์ด, ์ด๊ธ์ต์์ฐ) ์ ๋ณด๊ฐ ๋ถ์กฑํฉ๋๋ค" |
ํฌํธํด๋ฆฌ์ค ์์ |
์ด ๊ฐ์น = 0 | "๋ชจ๋ ์ข ๋ชฉ์ด ๋งค๋๋์ด ๋ถ์ํ ํ์ฌ ๋ณด์ ํฌํธํด๋ฆฌ์ค๊ฐ ์์ต๋๋ค" |
๋ด์ค ๊ฒ์ ์คํจ |
API ์๋ต ์์ | "๊ด๋ จ ๋ด์ค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค" |
์คํฌ๋ํ ์คํจ |
Selenium ํ์์์ | "๋ด์ค ์๋ฌธ์ ๊ฐ์ ธ์ค๋ ๋ฐ ์คํจํ์ต๋๋ค" |
๊ฒ์ฆ ์คํจ |
๋น์ค ๋ถ์ผ์น | "๋ณด๊ณ ๋ ๋น์ค๊ณผ ์ค์ ๊ณ์ฐ๋ ๋น์ค์ด ๋ค๋ฆ ๋๋ค" |
ํ๋ก์ ํธ Root Directory์์ ์๋ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Agent ์คํ์ ํ์ํ ๋ชจ๋ ํ๊ฒฝ์ด ํฌํจ๋ Docker Image๋ฅผ Buildํฉ๋๋ค.
# Docker ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd docker/
# Docker ์ด๋ฏธ์ง ๋น๋
docker build -t financial-agent .๋น๋๋ ์ด๋ฏธ์ง๋ฅผ ์ปจํ ์ด๋๋ก ์คํํฉ๋๋ค.
๐ก Note:
make_container.sh๋ด๋ถ์๋ docker run ๋ช ๋ น์ด๊ฐ ํฌํจ๋์ด ์์ผ๋ฉฐ, Agent ์๋ฒ๊ฐ ์ธ๋ถ ์์ฒญ์ ๋ฐ์ ์ ์๋๋ก ํน์ ํฌํธ(์: 8000๋ฒ)๋ฅผ ์ง์ ํ์ฌ ํธ์คํธ์ ์ฐ๊ฒฐํ๋๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค.
# ์ปจํ
์ด๋ ์์ฑ ๋ฐ ์คํ
bash make_container.shTask 5๋ฅผ ์ํ Chrome Driver๋ฅผ ์ค์นํฉ๋๋ค.
๐ก Note:
make_container.sh๋ฅผ ์คํํ์๋ /docker ์์น์์ ์๋ ๋ช ๋ น์ด๋ฅผ ํตํด Chrome Driver๋ฅผ ์ค์นํฉ๋๋ค.
# ์ปจํ
์ด๋ ์์ฑ ๋ฐ ์คํ
bash chrome_driver.shAgent๊ฐ ์ฌ์ฉํ๋ ๊ธ์ต ๋ฐ์ดํฐ(์: ๋ ์ง๋ณ ์ข
๊ฐ, ์๊ฐ ๋ฑ)๋ฅผ yfinance๋ก ๊ตฌ์ถํฉ๋๋ค.
index_kospi_kosdaq, stocks_kospi_kosdaq, technical_signals ์ด 3๊ฐ์ง ํ
์ด๋ธ์ ๊ตฌ์ถํฉ๋๋ค.
index_kospi_kosdaq: KOSPI/KOSDAQ ์ง์ ๋ฐ์ดํฐ ์์งstocks_kospi_kosdaq: KOSPI/KOSDAQ ์ ์ข ๋ชฉ์ ๊ฐ๋ณ ์ฃผ๊ฐ ๋ฐ์ดํฐ ์์งtechnical_signals: ์ฃผ๊ฐ ๋ฐ์ดํฐ ๊ธฐ๋ฐ ๊ธฐ์ ์ ์งํ ๊ณ์ฐ- RSI, ์ด๋ํ๊ท ์ (5~240์ผ), ๋ณผ๋ฆฐ์ ๋ฐด๋, ๊ณจ๋ /๋ฐ๋ํฌ๋ก์ค ๋ฑ
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ ๋ฐ ๋ฐ์ดํฐ ๋ก๋
bash make_db.sh๋ชจ๋ ์ค์ ์ด ์๋ฃ๋๋ฉด ๋ค์ ๋ช ๋ น์ด๋ก Agent๋ฅผ ์คํํฉ๋๋ค.
# ์คํ
python endpoint_final.py