Spaces:
Paused
Paused
| """LangGraph ToolNode์ฉ ๋ถ์ ๋๊ตฌ ํฉํ ๋ฆฌ. | |
| ๊ธฐ์กด IssueDetectorCapability, StatsLookupCapability, | |
| KeywordAnalyzerCapability, DemographicsLookupCapability์ ์์ํ์ฌ | |
| StructuredTool ์ธ์คํด์ค๋ฅผ ๋์ ์์ฑํ๋ค. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from typing import Any, Optional | |
| from langchain_core.tools import StructuredTool | |
| from pydantic import BaseModel, Field | |
| # --------------------------------------------------------------------------- | |
| # Pydantic ์คํค๋ง โ LLM์ด ์์ฑํ๋ JSON ์ธ์ | |
| # --------------------------------------------------------------------------- | |
| class IssueDetectorInput(BaseModel): | |
| """issue_detector ๋๊ตฌ ์ ๋ ฅ ์คํค๋ง.""" | |
| query: str = Field(..., description="์ด์ ํ์ง ๋์ ํค์๋ ๋๋ ์ง์๋ฌธ") | |
| analysis_time: Optional[str] = Field( | |
| None, | |
| description="๋ถ์ ์๊ฐ๋ (YYYYMMDDHH ํ์, 10์๋ฆฌ). ์: '2026040814'", | |
| ) | |
| max_result: int = Field(10, description="๋ฐํํ ์ต๋ ๊ฒฐ๊ณผ ์", ge=1) | |
| class StatsLookupInput(BaseModel): | |
| """stats_lookup ๋๊ตฌ ์ ๋ ฅ ์คํค๋ง.""" | |
| query: str = Field(..., description="ํต๊ณ ์กฐํ ๋์ ํค์๋") | |
| date_from: Optional[str] = Field( | |
| None, description="์กฐํ ์์์ผ (YYYYMMDD ํ์). ์: '20260101'" | |
| ) | |
| date_to: Optional[str] = Field(None, description="์กฐํ ์ข ๋ฃ์ผ (YYYYMMDD ํ์). ์: '20260408'") | |
| period: Optional[str] = Field( | |
| None, description="์ง๊ณ ๊ธฐ๊ฐ ๋จ์ (DAILY, WEEKLY, MONTHLY, YEARLY)" | |
| ) | |
| class KeywordAnalyzerInput(BaseModel): | |
| """keyword_analyzer ๋๊ตฌ ์ ๋ ฅ ์คํค๋ง.""" | |
| query: str = Field(..., description="ํค์๋ ๋ถ์ ๋์ ์ง์๋ฌธ") | |
| date_from: Optional[str] = Field( | |
| None, description="๋ถ์ ์์์ผ (YYYYMMDD ํ์). ์: '20260101'" | |
| ) | |
| date_to: Optional[str] = Field(None, description="๋ถ์ ์ข ๋ฃ์ผ (YYYYMMDD ํ์). ์: '20260408'") | |
| result_count: int = Field(20, description="๋ฐํํ ํค์๋ ์", ge=1) | |
| class DemographicsLookupInput(BaseModel): | |
| """demographics_lookup ๋๊ตฌ ์ ๋ ฅ ์คํค๋ง.""" | |
| query: str = Field(..., description="์ธ๊ตฌํต๊ณ ๋ถ์ ๋์ ์ง์๋ฌธ") | |
| date_from: Optional[str] = Field( | |
| None, description="๋ถ์ ์์์ผ (YYYYMMDD ํ์). ์: '20260101'" | |
| ) | |
| date_to: Optional[str] = Field(None, description="๋ถ์ ์ข ๋ฃ์ผ (YYYYMMDD ํ์). ์: '20260408'") | |
| # --------------------------------------------------------------------------- | |
| # ํฉํ ๋ฆฌ | |
| # --------------------------------------------------------------------------- | |
| def build_analysis_tools( | |
| api_lookup_action: Optional[Any] = None, | |
| ) -> list: | |
| """๋ถ์ ๊ด๋ จ StructuredTool ๋ชฉ๋ก์ ์์ฑํ๋ค. | |
| Parameters | |
| ---------- | |
| api_lookup_action : Optional[MinwonAnalysisAction] | |
| ๊ณต๊ณต๋ฐ์ดํฐํฌํธ API Action ์ธ์คํด์ค. None์ด๋ฉด ๋น ๊ฒฐ๊ณผ ๋ฐํ. | |
| Returns | |
| ------- | |
| list[StructuredTool] | |
| [issue_detector_tool, stats_lookup_tool, keyword_analyzer_tool, demographics_lookup_tool] | |
| """ | |
| from src.inference.graph.capabilities.demographics_lookup import DemographicsLookupCapability | |
| from src.inference.graph.capabilities.issue_detector import IssueDetectorCapability | |
| from src.inference.graph.capabilities.keyword_analyzer import KeywordAnalyzerCapability | |
| from src.inference.graph.capabilities.stats_lookup import StatsLookupCapability | |
| # -- issue_detector -- | |
| _issue_cap = IssueDetectorCapability(action=api_lookup_action) | |
| async def _issue_detector( | |
| query: str, | |
| analysis_time: Optional[str] = None, | |
| max_result: int = 10, | |
| ) -> str: | |
| context: dict[str, Any] = {"max_result": max_result} | |
| if analysis_time is not None: | |
| context["analysis_time"] = analysis_time | |
| # search_date๋ฅผ analysis_time์์ ์๋ ์ถ์ถ (YYYYMMDDHH -> YYYYMMDD) | |
| if len(analysis_time) >= 8: | |
| context["search_date"] = analysis_time[:8] | |
| try: | |
| result = await _issue_cap.execute(query=query, context=context, session=None) | |
| return json.dumps(result.to_dict(), ensure_ascii=False) | |
| except Exception as e: | |
| return json.dumps({"error": str(e), "success": False}, ensure_ascii=False) | |
| issue_detector_tool = StructuredTool.from_function( | |
| coroutine=_issue_detector, | |
| name="issue_detector", | |
| description=( | |
| "๋ฏผ์ ๋ฐ์ดํฐ์์ ๋ฐ๋ณต๋๋ ์ด์ ํจํด๊ณผ ํธ๋ ๋๋ฅผ ํ์งํฉ๋๋ค. " | |
| "๋ฏผ์ ๊ธ์ฆ, ๋ฐ๋ณต ๋ถ๋ง, ์ ๊ท ์ด์๋ฅผ ํ์ ํ ๋ ์ฌ์ฉํ์ธ์. " | |
| "๋ฐํ๊ฐ: ํ์ง๋ ์ด์ ๋ชฉ๋ก (์ด์๋ช , ๊ฑด์, ์ฌ๊ฐ๋)" | |
| ), | |
| args_schema=IssueDetectorInput, | |
| metadata={"requires_approval": False}, | |
| ) | |
| # -- stats_lookup -- | |
| _stats_cap = StatsLookupCapability(action=api_lookup_action) | |
| async def _stats_lookup( | |
| query: str, | |
| date_from: Optional[str] = None, | |
| date_to: Optional[str] = None, | |
| period: Optional[str] = None, | |
| ) -> str: | |
| context: dict[str, Any] = {} | |
| if date_from is not None: | |
| context["date_from"] = date_from | |
| if date_to is not None: | |
| context["date_to"] = date_to | |
| if period is not None: | |
| context["period"] = period | |
| try: | |
| result = await _stats_cap.execute(query=query, context=context, session=None) | |
| return json.dumps(result.to_dict(), ensure_ascii=False) | |
| except Exception as e: | |
| return json.dumps({"error": str(e), "success": False}, ensure_ascii=False) | |
| stats_lookup_tool = StructuredTool.from_function( | |
| coroutine=_stats_lookup, | |
| name="stats_lookup", | |
| description=( | |
| "๋ฏผ์ ์ ์ ํต๊ณ๋ฅผ ๊ธฐ๊ฐ๋ณ/์ ํ๋ณ๋ก ์กฐํํฉ๋๋ค. " | |
| "๋ฏผ์ ํํฉ ํ์ , ์ถ์ด ๋ถ์์ ์ฌ์ฉํ์ธ์. " | |
| "๋ฐํ๊ฐ: ํต๊ณ ๋ฐ์ดํฐ (๊ธฐ๊ฐ, ์ ์๊ฑด์, ์ ํ๋ณ ๋ถํฌ)" | |
| ), | |
| args_schema=StatsLookupInput, | |
| metadata={"requires_approval": False}, | |
| ) | |
| # -- keyword_analyzer -- | |
| _kw_cap = KeywordAnalyzerCapability(action=api_lookup_action) | |
| async def _keyword_analyzer( | |
| query: str, | |
| date_from: Optional[str] = None, | |
| date_to: Optional[str] = None, | |
| result_count: int = 20, | |
| ) -> str: | |
| context: dict[str, Any] = {"result_count": result_count} | |
| if date_from is not None: | |
| context["date_from"] = date_from | |
| if date_to is not None: | |
| context["date_to"] = date_to | |
| # searchword๋ฅผ query์์ ์๋ ์ค์ (์ฐ๊ด์ด ๋ถ์์ฉ) | |
| context["searchword"] = query | |
| try: | |
| result = await _kw_cap.execute(query=query, context=context, session=None) | |
| return json.dumps(result.to_dict(), ensure_ascii=False) | |
| except Exception as e: | |
| return json.dumps({"error": str(e), "success": False}, ensure_ascii=False) | |
| keyword_analyzer_tool = StructuredTool.from_function( | |
| coroutine=_keyword_analyzer, | |
| name="keyword_analyzer", | |
| description=( | |
| "๋ฏผ์ ํ ์คํธ์์ ํต์ฌ ํค์๋์ ๋น๋๋ฅผ ๋ถ์ํฉ๋๋ค. " | |
| "๋ฏผ์ ์ด์์ ํต์ฌ์ด๋ฅผ ํ์ ํ ๋ ์ฌ์ฉํ์ธ์. " | |
| "๋ฐํ๊ฐ: ํค์๋ ๋ชฉ๋ก (ํค์๋, ๋น๋, ๊ด๋ จ๋)" | |
| ), | |
| args_schema=KeywordAnalyzerInput, | |
| metadata={"requires_approval": False}, | |
| ) | |
| # -- demographics_lookup -- | |
| _demo_cap = DemographicsLookupCapability(action=api_lookup_action) | |
| async def _demographics_lookup( | |
| query: str, | |
| date_from: Optional[str] = None, | |
| date_to: Optional[str] = None, | |
| ) -> str: | |
| context: dict[str, Any] = {"searchword": query} | |
| if date_from is not None: | |
| context["date_from"] = date_from | |
| if date_to is not None: | |
| context["date_to"] = date_to | |
| try: | |
| result = await _demo_cap.execute(query=query, context=context, session=None) | |
| return json.dumps(result.to_dict(), ensure_ascii=False) | |
| except Exception as e: | |
| return json.dumps({"error": str(e), "success": False}, ensure_ascii=False) | |
| demographics_lookup_tool = StructuredTool.from_function( | |
| coroutine=_demographics_lookup, | |
| name="demographics_lookup", | |
| description=( | |
| "๋ฏผ์์ธ์ ์ธ๊ตฌํต๊ณ ์ ๋ณด(์ฐ๋ น๋, ์ง์ญ ๋ฑ)๋ฅผ ์กฐํํฉ๋๋ค. " | |
| "๋ฏผ์ ๋์ ๋ถ์์ ์ฌ์ฉํ์ธ์. " | |
| "๋ฐํ๊ฐ: ์ธ๊ตฌํต๊ณ ๋ถํฌ ๋ฐ์ดํฐ" | |
| ), | |
| args_schema=DemographicsLookupInput, | |
| metadata={"requires_approval": False}, | |
| ) | |
| return [issue_detector_tool, stats_lookup_tool, keyword_analyzer_tool, demographics_lookup_tool] | |