Skip to content

figure-2/Financial-Agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Financial-agent


๋ชฉ์ฐจ

  1. Agent ๊ตฌ์กฐ๋„
  2. Agent ํ‰๊ฐ€ ๋ฐฉ๋ฒ•
  3. ์ฃผ์š” ๊ธฐ๋Šฅ ๋ฐ API ํ˜ธ์ถœ ์˜ˆ์‹œ
  4. Local ํ™˜๊ฒฝ ์‹คํ–‰ ๊ฐ€์ด๋“œ

Agent ํ‰๊ฐ€ ๋ฐฉ๋ฒ•

๋ฐฐํฌ๋œ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด Agent์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์„ฑ๋Šฅ์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API Endpoint

http://211.188.58.134:8000/agent

API ํ˜ธ์ถœ ์˜ˆ์‹œ

์ฐธ๊ณ  ํŒŒ์ผ : 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': '์• ๋“œ๋ฐ”์ด์˜คํ…, ์•„๋‚œํ‹ฐ, ์— ์—ํ”„์— ์ฝ”๋ฆฌ์•„ ์ž…๋‹ˆ๋‹ค.'}

์ฃผ์š” ๊ธฐ๋Šฅ ๋ฐ API ํ˜ธ์ถœ ์˜ˆ์‹œ

Agent๊ฐ€ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” 5๊ฐ€์ง€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ๊ณผ ์‹ค์ œ API ํ˜ธ์ถœ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

Task 1: ๋‹จ์ˆœ ์กฐํšŒ

์ฃผ์‹ ์‹œ์žฅ์˜ ๋‹ค์–‘ํ•œ ๊ธˆ์œต ์ •๋ณด๋ฅผ ์ž์—ฐ์–ด ์งˆ๋ฌธ์œผ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
๋ณต์žกํ•œ SQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š” ์—†์ด ์ผ์ƒ์ ์ธ ์–ธ์–ด๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์ž์—ฐ์–ด ํŒŒ์‹ฑ ํ”„๋กœ์„ธ์Šค

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_PROPORTION

2. ์‹คํ–‰ ๊ณ„ํš ์ฒ˜๋ฆฌ

def execute_plan(state: AgentState) -> Dict[str, Any]:
    # ๋ถ„์„๋œ JSON ๊ณ„ํš์„ SQL ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ์‹คํ–‰
    # ๊ฑฐ๋ž˜์ผ ๊ฒ€์ฆ, CTE๋ฅผ ํ™œ์šฉํ•œ ๋“ฑ๋ฝ๋ฅ  ๊ณ„์‚ฐ, ๊ฒฐ๊ณผ ํฌ๋งทํŒ… ์ˆ˜ํ–‰
    # ๊ฐ task_type๋ณ„ ์ฟผ๋ฆฌ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰ ๋กœ์ง ์ฒ˜๋ฆฌ

3. ์ง€์›๋˜๋Š” ์ž‘์—… ์œ ํ˜• ๋ฐ ์‚ฌ์šฉ ์˜ˆ์ œ

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์ฃผ)"

4. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์—๋Ÿฌ ์ƒํ™ฉ ๊ฒ€์ฆ ๋ฐฉ๋ฒ• ๋ฐ˜ํ™˜ ๋ฉ”์‹œ์ง€
์ฃผ๋ง/๊ณตํœด์ผ dt.weekday() >= 5 "ํ† ์š”์ผ (๋ฐ์ดํ„ฐ ์—†์Œ)"
๋ฐ์ดํ„ฐ ์—†์Œ DB ์กฐํšŒ ๊ฒฐ๊ณผ NULL "ํ•ด๋‹น ๋‚ ์งœ ๋ฐ์ดํ„ฐ ์—†์Œ"
LLM ํŒŒ์‹ฑ ์‹คํŒจ JSON ๋””์ฝ”๋”ฉ ์˜ˆ์™ธ "LLM ๋ถ„์„์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค"
์ข…๋ชฉ ๋ฏธ์กด์žฌ DB ์กฐํšŒ ๊ฒฐ๊ณผ ์—†์Œ "ํ•ด๋‹น ์ข…๋ชฉ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"
์•Œ ์ˆ˜ ์—†๋Š” ์ž‘์—… task_type ๋ถˆ์ผ์น˜ "์•Œ ์ˆ˜ ์—†๋Š” ์ž‘์—… ์œ ํ˜•์ž…๋‹ˆ๋‹ค"

5. ๋ฐ์ดํ„ฐ ํฌ๋งทํŒ…

def format_value(value: Any, metric: str) -> Optional[str]:
    # ๊ฐ€๊ฒฉ: "15,400์›"
    # ๊ฑฐ๋ž˜๋Ÿ‰: "1,234,567์ฃผ"
    # ๋“ฑ๋ฝ๋ฅ : "+3.45%"
    # ์ง€์ˆ˜: "2,456.78"

Task 2: ์กฐ๊ฑด ๊ฒ€์ƒ‰

๋ณต์žกํ•œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ข…๋ชฉ๋“ค์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ AND ์—ฐ์‚ฐ์œผ๋กœ ๊ฒฐํ•ฉํ•˜์—ฌ ์ •๊ตํ•œ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋“ฑ๋ฝ๋ฅ ๊ณผ ๊ฑฐ๋ž˜๋Ÿ‰ ๋ณ€๋™๋ฅ  ๊ฐ™์€ ๋™์  ์ง€ํ‘œ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

1. ์ž์—ฐ์–ด ํŒŒ์‹ฑ ํ”„๋กœ์„ธ์Šค

def parse_question_with_llm(state: AgentState) -> Dict[str, Any]:
    # LLM์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž์—ฐ์–ด ์งˆ๋ฌธ์„ ๊ตฌ์กฐํ™”๋œ JSON ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
    # ๋‚ ์งœ, ์‹œ์žฅ, ์กฐ๊ฑด์„ ์ถ”์ถœํ•˜์—ฌ JSON ํ˜•์‹์œผ๋กœ ๊ตฌ์กฐํ™”
    # ์ˆ˜์น˜๋Š” ๋‹จ์œ„๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ˆซ์ž๋งŒ ์ถ”์ถœ (300% โ†’ 300, 10๋งŒ์› โ†’ 100000)

2. ์‹คํ–‰ ๊ณ„ํš ์ฒ˜๋ฆฌ

def execute_plan(state: AgentState) -> Dict[str, Any]:
    # LLM์ด ์ƒ์„ฑํ•œ ๊ตฌ์กฐํ™”๋œ ๊ณ„ํš์— ๋”ฐ๋ผ SQL ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰
    # CTE๋ฅผ ํ™œ์šฉํ•œ ์ „์ผ ๋Œ€๋น„ ๊ณ„์‚ฐ, ๋™์  WHERE ์ ˆ ์ƒ์„ฑ
    # ๊ฒฐ๊ณผ๊ฐ€ 20๊ฐœ๋ฅผ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ "๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค" ์ถ”๊ฐ€

3. ์ง€์›๋˜๋Š” ์ž‘์—… ์œ ํ˜• ๋ฐ ์‚ฌ์šฉ ์˜ˆ์ œ

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์—๋„ˆ์ง€์†”๋ฃจ์…˜, ์‚ผ์„ฑ๋ฐ”์ด์˜ค๋กœ์ง์Šค"

4. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์—๋Ÿฌ ์ƒํ™ฉ ๊ฒ€์ฆ ๋ฐฉ๋ฒ• ๋ฐ˜ํ™˜ ๋ฉ”์‹œ์ง€
๋‚ ์งœ ๋ˆ„๋ฝ plan.get('date') is None "ํ•„์ˆ˜ ์ •๋ณด(๋‚ ์งœ)๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
๋‚ ์งœ ํ˜•์‹ ์˜ค๋ฅ˜ datetime.strptime()์˜ˆ์™ธ "๋‚ ์งœ ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค: 'YYYY-MM-DD' ํ˜•์‹ ํ•„์š”"
์กฐ๊ฑด ๋ˆ„๋ฝ plan.get('conditions') is None "์œ ํšจํ•œ ์‹คํ–‰ ๊ณ„ํš์ด ์—†์Šต๋‹ˆ๋‹ค"
DB ์—ฐ๊ฒฐ ์‹คํŒจ engine is None "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
์ฟผ๋ฆฌ ์‹คํ–‰ ์˜ค๋ฅ˜ Exception๋ฐœ์ƒ "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค"
๊ฒฐ๊ณผ ์—†์Œ results == [] "์กฐ๊ฑด์— ๋งž๋Š” ์ข…๋ชฉ ์—†์Œ"

Task 3: ์‹œ๊ทธ๋„ ๊ฐ์ง€

๊ธฐ์ˆ ์  ๋ถ„์„ ์ง€ํ‘œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋งค๋งค ์‹œ์ ์„ ํฌ์ฐฉํ•˜๋Š” ๊ณ ๊ธ‰ ๋ถ„์„ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
RSI, ์ด๋™ํ‰๊ท ์„ , ๋ณผ๋ฆฐ์ € ๋ฐด๋“œ, ๊ณจ๋“ ํฌ๋กœ์Šค/๋ฐ๋“œํฌ๋กœ์Šค ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ ์  ์ง€ํ‘œ๋ฅผ ์ข…ํ•ฉ์ ์œผ๋กœ ํ™œ์šฉํ•˜์—ฌ ํˆฌ์ž ์‹œ๊ทธ๋„์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

1. ์ž์—ฐ์–ด ํŒŒ์‹ฑ ํ”„๋กœ์„ธ์Šค

def parse_question_with_llm(state: AgentState) -> Dict[str, Any]:
    # LLM์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์ˆ ์  ๋ถ„์„ ์งˆ๋ฌธ์„ ๊ตฌ์กฐํ™”๋œ JSON ๊ณ„ํš์œผ๋กœ ๋ณ€ํ™˜
    # ์ž‘์—… ์œ ํ˜•, ๋‚ ์งœ/๊ธฐ๊ฐ„, ์‹œ๊ทธ๋„ ์ข…๋ฅ˜, ์ž„๊ณ„๊ฐ’์„ ์ถ”์ถœ
    # ๊ธฐ๋ณธ๊ฐ’๊ณผ ๋‹ค๋ฅธ ๊ธฐ์ค€์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ thresholds ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •

2. ์‹คํ–‰ ๊ณ„ํš ์ฒ˜๋ฆฌ

def execute_plan(state: AgentState) -> Dict[str, Any]:
    # ๋ถ„์„๋œ ๊ณ„ํš์— ๋”ฐ๋ผ DB๋ฅผ ์กฐํšŒํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ฑ
    # ๊ฐ ์‹œ๊ทธ๋„ ์œ ํ˜•๋ณ„ SQL ์ฟผ๋ฆฌ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
    # ๋‹ต๋ณ€์ด 20๊ฐœ๋ฅผ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ LIMIT ์ ์šฉ ๋ฐ "๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค" ์ถ”๊ฐ€

3. ์ง€์›๋˜๋Š” ์ž‘์—… ์œ ํ˜• ๋ฐ ์‚ฌ์šฉ ์˜ˆ์ œ

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๋ฒˆ"

4. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์—๋Ÿฌ ์ƒํ™ฉ ๊ฒ€์ฆ ๋ฐฉ๋ฒ• ๋ฐ˜ํ™˜ ๋ฉ”์‹œ์ง€
๋‚ ์งœ ๋ˆ„๋ฝ 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 == [] "์กฐ๊ฑด์— ๋งž๋Š” ์ข…๋ชฉ ์—†์Œ" ๋˜๋Š” "ํ•ด๋‹น ๊ธฐ๊ฐ„์— ์กฐ๊ฑด์— ๋งž๋Š” ์ข…๋ชฉ ์—†์Œ"

Task 4: ๋ชจํ˜ธํ•œ ์˜๋ฏธ ํ•ด์„

๋ถˆ์™„์ „ํ•˜๊ฑฐ๋‚˜ ๋ชจํ˜ธํ•œ ์ฃผ์‹ ๊ด€๋ จ ์งˆ๋ฌธ์„ ์ž๋™์œผ๋กœ ํ•ด์„ํ•˜๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š” AI ์—์ด์ „ํŠธ์ž…๋‹ˆ๋‹ค.
์ถ•์•ฝ์–ด, ์€์–ด, ๋ถˆ๋ช…ํ™•ํ•œ ํ‘œํ˜„์„ ์ธ์‹ํ•˜์—ฌ ์ ์ ˆํžˆ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

  • ํ•ต์‹ฌ ์„ค๊ณ„ ์ฒ ํ•™

    • LLM์ด ๋ชจํ˜ธ์„ฑ์„ ํŒ๋‹จํ•˜๋Š” ๋ช…ํ™•ํ•œ ๊ธฐ์ค€์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ชจํ˜ธ์„ฑ์„ ์œ ํ˜•ํ™”ํ•˜๊ณ , Checklist์ฒ˜๋Ÿผ Yes/No๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„
  • ๊ธˆ์œต ๋„๋ฉ”์ธ์—์„œ์˜ ๋ชจํ˜ธ์„ฑ ์œ ํ˜•ํ™”

    • ์ฆ๊ถŒ ํ˜„์—… ์ „๋ฌธ๊ฐ€ ์ž๋ฌธ์„ ํ†ตํ•ด ๊ธˆ์œต ๋„๋ฉ”์ธ์˜ ๋ชจํ˜ธ์„ฑ์„ 4๊ฐ€์ง€๋กœ ์œ ํ˜•ํ™”
      • ์œ ํ˜•1) ์ถ•์•ฝ์–ด: "์‚ผ์ „" โ†’ "์‚ผ์„ฑ์ „์ž"
      • ์œ ํ˜•2) ์ „๋ฌธ์šฉ์–ด: "๋–ก์ƒ", "๋ˆŒ๋ฆผ๋ชฉ"
      • ์œ ํ˜•3) ์ค‘์˜์  ํ‘œํ˜„: ์—ฌ๋Ÿฌ ์˜๋ฏธ๋กœ ํ•ด์„ ๊ฐ€๋Šฅํ•œ ํ‘œํ˜„(์˜ˆ. ์ตœ๊ทผ, ๊ณผ๊ฑฐ ๋“ฑ)
      • ์œ ํ˜•4) ์กฐ๊ฑด ๋ˆ„๋ฝ: ๋‚ ์งœ, ์ข…๋ชฉ๋ช… ๋“ฑ ํ•„์ˆ˜ ์ •๋ณด ๋ˆ„๋ฝ
      • ์ฐธ๊ณ  ๋…ผ๋ฌธ: FinDER, PACIFIC
  • ๋ชจํ˜ธ์„ฑ ์ •๋Ÿ‰ํ™” ๋ฐฉ๋ฒ•๋ก 

    • ๊ธฐ์กด ์—ฐ๊ตฌ์˜ ํ•œ๊ณ„์ 
      • ์ „๋ฌธ๊ฐ€ ์ง์ ‘ Annotation โ†’ ๋†’์€ ๋น„์šฉ
      • LLM ๋‹ค์ค‘ ๋‹ต๋ณ€ ํ›„ ๋‹ค์–‘๋„ ์ธก์ • โ†’ ๋น„ํšจ์œจ์ 
  • ํ•ด๊ฒฐ์ฑ…: CheckEval ๋ฐฉ์‹ ์ ์šฉ

    • Checklist ํ˜•ํƒœ์˜ Yes/No ์กฐ๊ฑด๋ฌธ์œผ๋กœ ๋ชจํ˜ธ์„ฑ ํŒ๋‹จ
    • ์‚ฌ๋žŒ์˜ ์ผ๊ด€์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ
    • ๋ช…ํ™•ํ•œ ์ ์ˆ˜ ๊ธฐ์ค€ ์ œ๊ณต
    • ์ฐธ๊ณ  ๋…ผ๋ฌธ: CheckEval
  • ๋ชจํ˜ธ์„ฑ ํŒ๋‹จ ํ›„ ์ฒ˜๋ฆฌ ๋ฐฉ์•ˆ

    • Query Rewriting: DB/์ง€์‹๊ทธ๋ž˜ํ”„ ํ™œ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก ์งˆ๋ฌธ ์žฌ์ž‘์„ฑ
    • Clarification: ๋ˆ„๋ฝ๋œ ์ •๋ณด์— ๋Œ€ํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์—ญ์งˆ๋ฌธ
    • ์ฐธ๊ณ  ๋…ผ๋ฌธ: APA, Query Clarification

1. ๋ชจํ˜ธ์„ฑ ๊ฐ์ง€ ํ”„๋กœ์„ธ์Šค

def task4_router_node(state: AgentState) -> dict:
    # ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์˜ ๋ชจํ˜ธ์„ฑ ์œ ํ˜•์„ ํŒ๋‹จํ•˜์—ฌ ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ ๊ฒฐ์ •
    # 4๊ฐ€์ง€ ์กฐ๊ฑด์„ ์ฒดํฌํ•˜์—ฌ Rewriting ๋˜๋Š” Clarify ๊ฒฝ๋กœ ์„ ํƒ
    # ์กฐ๊ฑด 1,2๋Š” Rewriting, ์กฐ๊ฑด 3,4๋Š” Clarify๋กœ ๋ผ์šฐํŒ…

2. ์งˆ๋ฌธ ์žฌ์ž‘์„ฑ ๋ฐ ๋ช…ํ™•ํ™”

def rewrite_query_node(state: AgentState) -> dict:
    # ์ข…๋ชฉ ์ถ•์•ฝ์–ด์™€ ์ฃผ์‹ ์€์–ด๋ฅผ ์ •์‹ ์šฉ์–ด๋กœ ์ž๋™ ๋ณ€ํ™˜
    # ๋ณ€ํ™˜๋œ ์งˆ๋ฌธ์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ฉ”์ธ ๊ทธ๋ž˜ํ”„๊ฐ€ ์žฌ์ฒ˜๋ฆฌ
    # turn_count๋ฅผ ํ†ตํ•ด ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€
def clarify_question_node(state: AgentState) -> dict:
    # ๋ชจํ˜ธํ•œ ๊ธฐ๊ฐ„์ด๋‚˜ ๋ˆ„๋ฝ๋œ ์ •๋ณด์— ๋Œ€ํ•œ ์—ญ์งˆ๋ฌธ ์ƒ์„ฑ
    # ์‚ฌ์šฉ์ž ์นœํ™”์ ์ด๊ณ  ์„ ํƒ์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ˜•ํƒœ์˜ ์งˆ๋ฌธ
    # ๊ฐ„๋‹จํ•˜๊ณ  ๋ช…๋ฃŒํ•œ ํ•œ ๋ฌธ์žฅ ํ˜•ํƒœ๋กœ ์ƒ์„ฑ

3. ๋ชจํ˜ธ์„ฑ ์œ ํ˜• ๋ฐ ์ฒ˜๋ฆฌ ์˜ˆ์ œ

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๊ฐœ ์ข…๋ชฉ์„ ์•Œ๋ ค๋“œ๋ฆด๊นŒ์š”?"

4. ์ฒ˜๋ฆฌ ํ”Œ๋กœ์šฐ

์‚ฌ์šฉ์ž ์ž…๋ ฅ โ†’ ๋ชจํ˜ธ์„ฑ ๋ถ„์„ โ†’ ๊ฒฝ๋กœ ๊ฒฐ์ •
              โ”œโ”€ ์กฐ๊ฑด 1,2 โ†’ Rewriting โ†’ ๋ณ€ํ™˜๋œ ์งˆ๋ฌธ โ†’ ์žฌ์ฒ˜๋ฆฌ
              โ””โ”€ ์กฐ๊ฑด 3,4 โ†’ Clarifying โ†’ ์—ญ์งˆ๋ฌธ ์ƒ์„ฑ

Task 5: ์ง‘์ค‘ ํˆฌ์ž ์œ„ํ—˜ ์•Œ๋ฆผ

๊ฐœ์ธ ํˆฌ์ž์ž์˜ ๋งค๋งค ํŒจํ„ด์„ ๋ถ„์„ํ•˜์—ฌ ๊ณผ๋„ํ•œ ์ง‘์ค‘ ํˆฌ์ž ์œ„ํ—˜์„ ๊ฐ์ง€ํ•˜๊ณ , ๋‰ด์Šค ๋ถ„์„์„ ํ†ตํ•ด ๋งค๋งค ๋™๊ธฐ๋ฅผ ํŒŒ์•…ํ•˜์—ฌ ๋งž์ถคํ˜• ๊ฒฝ๊ณ ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์Šคํฌ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
ํˆฌ์ž์ž์˜ ๋‚˜์ด, ํˆฌ์ž์„ฑํ–ฅ, ์ž์‚ฐ๊ทœ๋ชจ๋ฅผ ๊ณ ๋ คํ•œ ๊ฐœ์ธํ™”๋œ ์ง‘์ค‘ ํˆฌ์ž ์œ„ํ—˜ ๋ถ„์„(PTPRA: Personalized Trading Pattern Risk Alert)์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
์ตœ์ข…์ ์œผ๋กœ ์œ„ํ—˜ ๋ถ„์„์„ ํ†ตํ•ด, ์ ํ•ฉํ•œ ๋‰ด์Šค๋ฅผ ํ•˜์ด๋ผ์ดํŒ…ํ•˜์—ฌ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
LLM์˜ ์ƒ์„ฑ๋ฌธ ๋ ๋ถ€๋ถ„์—์„œ 'ํ•˜์ด๋ผ์ดํŒ…ํ•œ ๋‰ด์Šค ๊ธฐ์‚ฌ ์ด๋ฏธ์ง€: http://~~~' ๋ถ€๋ถ„์— -> image ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด image๊ฐ€ url ํ˜•ํƒœ๋กœ ๋„์›Œ์ง‘๋‹ˆ๋‹ค.

1. ๋ฐ์ดํ„ฐ ์ถ”์ถœ ํ”„๋กœ์„ธ์Šค

def extract_transactions(state: AgentState) -> Dict[str, Any]:
    # ์‚ฌ์šฉ์ž ์ž…๋ ฅ์—์„œ JSON ํ˜•ํƒœ์˜ ๋งค๋งค ๊ธฐ๋ก ์ถ”์ถœ
    # ๋‚ ์งœ, ๊ฑฐ๋ž˜์œ ํ˜•, ์ข…๋ชฉ, ๊ฐ€๊ฒฉ, ์ˆ˜๋Ÿ‰ ์ •๋ณด๋ฅผ ํŒŒ์‹ฑ
    # ์‹œ๊ฐ„์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ํฌํŠธํด๋ฆฌ์˜ค ๋ณ€ํ™” ์ถ”์ 

def extract_mydata(state: AgentState) -> Dict[str, Any]:
    # LLM์„ ํ™œ์šฉํ•˜์—ฌ ๋งˆ์ด๋ฐ์ดํ„ฐ ์ •๋ณด ์ถ”์ถœ
    # ํˆฌ์ž์„ฑํ–ฅ์„ 5๊ฐ€์ง€ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋ถ„๋ฅ˜
    # ๋‚˜์ด์™€ ์ด ๊ธˆ์œต์ž์‚ฐ ์ •๋ณด ๊ตฌ์กฐํ™”

2. ์œ„ํ—˜ ๋ถ„์„ ๋ฐ ๊ฒ€์ฆ

def analyze_risk_patterns(state: AgentState) -> Dict[str, Any]:
    # ๋งค๋งค ๊ธฐ๋ก์œผ๋กœ๋ถ€ํ„ฐ ํ˜„์žฌ ํฌํŠธํด๋ฆฌ์˜ค ๊ณ„์‚ฐ
    # ๊ฐœ์ธํ™”๋œ ์œ„ํ—˜ ์ž„๊ณ„์น˜ = ํˆฌ์ž์„ฑํ–ฅ ํ•œ๋„ ร— ์ƒ์• ์ฃผ๊ธฐ ๊ณ„์ˆ˜
    # ๋‹จ์ผ ์ข…๋ชฉ ์ง‘์ค‘๋„๊ฐ€ ์ž„๊ณ„์น˜๋ฅผ ์ดˆ๊ณผํ•˜๋Š”์ง€ ๊ฒ€์‚ฌ

def verify_analysis_results(state: AgentState) -> Dict[str, Any]:
    # ๊ณ„์‚ฐ๋œ ์œ„ํ—˜ ํŒจํ„ด์˜ ์ •ํ™•์„ฑ ๊ฒ€์ฆ
    # ๋ณด๊ณ ๋œ ๋น„์ค‘๊ณผ ์‹ค์ œ ๊ณ„์‚ฐ ๋น„์ค‘์˜ ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ
    # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋…ธ๋“œ๋กœ ๋ผ์šฐํŒ…

3. ์ง€์›๋˜๋Š” ์ž…๋ ฅ ํ˜•์‹ ๋ฐ ์‚ฌ์šฉ ์˜ˆ์ œ

  • ์ž…๋ ฅ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
๋ฐ์ดํ„ฐ ์œ ํ˜• ํ•„์ˆ˜ ํ•ญ๋ชฉ ํ˜•์‹
ํ…์ŠคํŠธ 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": "ํŠน์ • ์ข…๋ชฉ์— ๋Œ€ํ•œ ๊ณผ๋„ํ•œ ํˆฌ์ž๋Š” ํ•ด๋‹น ์ข…๋ชฉ์˜ ๊ฐ€๊ฒฉ ๋ณ€๋™์— ํฌํŠธํด๋ฆฌ์˜ค ์ „์ฒด๊ฐ€ ํฌ๊ฒŒ ํ”๋“ค๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ„์‚ฐ ํˆฌ์ž๋ฅผ ํ†ตํ•ด ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”."
}

4. ๋‰ด์Šค ๋ถ„์„ ํ”„๋กœ์„ธ์Šค

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 ๋ Œ๋”๋ง
    # ํ•˜์ด๋ผ์ดํŠธ ์˜์—ญ ์ค‘์‹ฌ์œผ๋กœ ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜

5. ์ตœ์ข… ๋ฆฌํฌํŠธ ๊ตฌ์„ฑ

  1. ๋งค๋งค ์ธ์‹: ๊ฑฐ๋ž˜ ๋‚ด์—ญ ํ™•์ธ
  2. ์›์ธ ์ถ”์ •: ๋‰ด์Šค ๊ธฐ๋ฐ˜ ๋งค๋งค ๋™๊ธฐ ๋ถ„์„
  3. ์œ„ํ—˜ ๊ฒฝ๊ณ : ๊ฐœ์ธํ™”๋œ ์œ„ํ—˜ ์ˆ˜์ค€ ์•Œ๋ฆผ
  4. ๊ถŒ๊ณ  ์‚ฌํ•ญ: ๋ถ„์‚ฐ ํˆฌ์ž ์ œ์•ˆ
  5. ์‹œ๊ฐ์  ๊ทผ๊ฑฐ: ํ•˜์ด๋ผ์ดํŒ…๋œ ๋‰ด์Šค ์ด๋ฏธ์ง€
์ตœ๊ทผ **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/...

6. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์—๋Ÿฌ ์ƒํ™ฉ ๊ฒ€์ฆ ๋ฐฉ๋ฒ• ๋ฐ˜ํ™˜ ๋ฉ”์‹œ์ง€
๋งค๋งค๊ธฐ๋ก ํŒŒ์‹ฑ ์‹คํŒจ JSON ๋””์ฝ”๋”ฉ ์˜ˆ์™ธ "๋งค๋งค ๊ธฐ๋ก JSON ํŒŒ์‹ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ"
๋งˆ์ด๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ ํ•„์ˆ˜ ํ•„๋“œ null "๋งˆ์ด๋ฐ์ดํ„ฐ(ํˆฌ์ž์„ฑํ–ฅ, ๋‚˜์ด, ์ด๊ธˆ์œต์ž์‚ฐ) ์ •๋ณด๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค"
ํฌํŠธํด๋ฆฌ์˜ค ์—†์Œ ์ด ๊ฐ€์น˜ = 0 "๋ชจ๋“  ์ข…๋ชฉ์ด ๋งค๋„๋˜์–ด ๋ถ„์„ํ•  ํ˜„์žฌ ๋ณด์œ  ํฌํŠธํด๋ฆฌ์˜ค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"
๋‰ด์Šค ๊ฒ€์ƒ‰ ์‹คํŒจ API ์‘๋‹ต ์—†์Œ "๊ด€๋ จ ๋‰ด์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
์Šคํฌ๋ž˜ํ•‘ ์‹คํŒจ Selenium ํƒ€์ž„์•„์›ƒ "๋‰ด์Šค ์›๋ฌธ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค"
๊ฒ€์ฆ ์‹คํŒจ ๋น„์ค‘ ๋ถˆ์ผ์น˜ "๋ณด๊ณ ๋œ ๋น„์ค‘๊ณผ ์‹ค์ œ ๊ณ„์‚ฐ๋œ ๋น„์ค‘์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค"

๐Ÿš€ Local ํ™˜๊ฒฝ์—์„œ Agent ์‹คํ–‰ํ•˜๊ธฐ

01. Docker Image ์ƒ์„ฑ

ํ”„๋กœ์ ํŠธ Root Directory์—์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ Agent ์‹คํ–‰์— ํ•„์š”ํ•œ ๋ชจ๋“  ํ™˜๊ฒฝ์ด ํฌํ•จ๋œ Docker Image๋ฅผ Buildํ•ฉ๋‹ˆ๋‹ค.

# Docker ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™
cd docker/

# Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ
docker build -t financial-agent .

02. Docker ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰

๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก Note: make_container.sh ๋‚ด๋ถ€์—๋Š” docker run ๋ช…๋ น์–ด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ, Agent ์„œ๋ฒ„๊ฐ€ ์™ธ๋ถ€ ์š”์ฒญ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํŠน์ • ํฌํŠธ(์˜ˆ: 8000๋ฒˆ)๋ฅผ ์ง€์ •ํ•˜์—ฌ ํ˜ธ์ŠคํŠธ์™€ ์—ฐ๊ฒฐํ•˜๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

# ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
bash make_container.sh

03. Chrome Driver ์„ค์น˜

Task 5๋ฅผ ์œ„ํ•œ Chrome Driver๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก Note: make_container.sh๋ฅผ ์‹คํ–‰ํ•˜์˜€๋˜ /docker ์œ„์น˜์—์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Chrome Driver๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

# ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
bash chrome_driver.sh

04. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์ถ•

Agent๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ธˆ์œต ๋ฐ์ดํ„ฐ(์˜ˆ: ๋‚ ์งœ๋ณ„ ์ข…๊ฐ€, ์‹œ๊ฐ€ ๋“ฑ)๋ฅผ yfinance๋กœ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.
index_kospi_kosdaq, stocks_kospi_kosdaq, technical_signals ์ด 3๊ฐ€์ง€ ํ…Œ์ด๋ธ”์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.

Workflow Diagram

  • index_kospi_kosdaq: KOSPI/KOSDAQ ์ง€์ˆ˜ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
  • stocks_kospi_kosdaq: KOSPI/KOSDAQ ์ „ ์ข…๋ชฉ์˜ ๊ฐœ๋ณ„ ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
  • technical_signals: ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ๊ธฐ์ˆ ์  ์ง€ํ‘œ ๊ณ„์‚ฐ
    • RSI, ์ด๋™ํ‰๊ท ์„ (5~240์ผ), ๋ณผ๋ฆฐ์ €๋ฐด๋“œ, ๊ณจ๋“ /๋ฐ๋“œํฌ๋กœ์Šค ๋“ฑ
# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ๋ฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ
bash make_db.sh

05. Agent ์‹คํ–‰

๋ชจ๋“  ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ Agent๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

# ์‹คํ–‰
python endpoint_final.py

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages