-
Notifications
You must be signed in to change notification settings - Fork 0
feat(ai): 매물 추천 Text-to-SQL 및 LLM 기반 의도 분류 구현 (#29) #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e51cff7
feat(ai): 매물 추천 Text-to-SQL 구현 (#29)
crolvlee 6d5ee51
fix(ai): GMS API 파라미터 및 psycopg SET 구문 오류 수정 (#29)
crolvlee 9df8b7c
feat(ai): 의도 분류를 키워드 매칭에서 LLM 기반으로 전환 (#29)
crolvlee 376e17a
Merge remote-tracking branch 'origin/develop' into feat/29-chat-prope…
crolvlee f46d915
docs: md 파일 수정
crolvlee 18be704
fix(ai): LLM 응답 타입 검증 및 매물 검색 장애 격리 (#29)
crolvlee 63c6d69
docs(api): properties 응답 필드를 snake_case로 수정 (#29)
crolvlee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,69 +1,37 @@ | ||
| from app.graph.state import AgentState, Intent | ||
| from typing import Literal | ||
|
|
||
| from pydantic import BaseModel, ValidationError | ||
|
|
||
| KOREAN_LEGAL_KEYWORDS = [ | ||
| "법", | ||
| "권리", | ||
| "돌려받", | ||
| "반환", | ||
| "대항력", | ||
| "우선변제", | ||
| "최우선변제", | ||
| "임대차", | ||
| "임차권", | ||
| "임대인", | ||
| "임차인", | ||
| "계약갱신", | ||
| "묵시적 갱신", | ||
| "전세사기", | ||
| ] | ||
| KOREAN_PROPERTY_KEYWORDS = [ | ||
| "추천", | ||
| "찾아", | ||
| "매물", | ||
| "원룸", | ||
| "오피스텔", | ||
| "아파트", | ||
| "월세", | ||
| "전세", | ||
| "관악구", | ||
| ] | ||
| from app.clients.llm_client import LLMClient | ||
| from app.graph.state import AgentState, Intent | ||
|
|
||
|
|
||
| LEGAL_KEYWORDS = [ | ||
| "법", | ||
| "계약", | ||
| "임대차", | ||
| "대항력", | ||
| "확정일자", | ||
| "보증금 반환", | ||
| "전세사기", | ||
| "묵시적 갱신", | ||
| ] | ||
| PRICE_KEYWORDS = ["시세", "실거래", "가격", "비싼", "싼", "평균가", "전고점"] | ||
| SAFETY_KEYWORDS = ["안전", "치안", "cctv", "비상벨", "보안등", "파출소", "범죄"] | ||
| HUG_KEYWORDS = ["hug", "보증보험", "보증 가능", "보증 가입", "보증금 보험"] | ||
| PROPERTY_KEYWORDS = ["추천", "찾아", "매물", "원룸", "오피스텔", "아파트", "월세", "전세", "관악구"] | ||
| class RouteDecision(BaseModel): | ||
| intent: Literal[ | ||
| "PROPERTY_SEARCH", | ||
| "LEGAL_CONSULT", | ||
| "PRICE_ANALYSIS", | ||
| "SAFETY_ANALYSIS", | ||
| "HUG_CALC", | ||
| "GENERAL_CHAT", | ||
| ] | ||
| reasoning: str | ||
|
|
||
|
|
||
| def classify_message(message: str) -> Intent: | ||
| normalized = message.lower() | ||
| if any(keyword in normalized for keyword in KOREAN_LEGAL_KEYWORDS): | ||
| return Intent.LEGAL_CONSULT | ||
| if any(keyword in normalized for keyword in LEGAL_KEYWORDS): | ||
| return Intent.LEGAL_CONSULT | ||
| if any(keyword in normalized for keyword in HUG_KEYWORDS): | ||
| return Intent.HUG_CALC | ||
| if any(keyword in normalized for keyword in PRICE_KEYWORDS): | ||
| return Intent.PRICE_ANALYSIS | ||
| if any(keyword in normalized for keyword in SAFETY_KEYWORDS): | ||
| return Intent.SAFETY_ANALYSIS | ||
| if any(keyword in normalized for keyword in KOREAN_PROPERTY_KEYWORDS): | ||
| return Intent.PROPERTY_SEARCH | ||
| if any(keyword in normalized for keyword in PROPERTY_KEYWORDS): | ||
| return Intent.PROPERTY_SEARCH | ||
| def classify_intent_fallback(message: str) -> Intent: | ||
| return Intent.FALLBACK | ||
|
|
||
|
|
||
| def classify_intent_llm(message: str) -> Intent: | ||
| raw = LLMClient().classify(message) | ||
| if raw is None: | ||
| return classify_intent_fallback(message) | ||
| try: | ||
| decision = RouteDecision.model_validate(raw) | ||
| return Intent(decision.intent) | ||
| except (ValueError, ValidationError): | ||
| return classify_intent_fallback(message) | ||
|
|
||
|
|
||
| def classify_intent(state: AgentState) -> AgentState: | ||
| return {**state, "intent": classify_message(state["message"])} | ||
| return {**state, "intent": classify_intent_llm(state["message"])} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,27 @@ | ||
| from app.clients.spring_client import SpringClient | ||
| from app.clients.llm_client import LLMClient | ||
| from app.clients.supabase_client import SupabaseVectorClient | ||
| from app.graph.state import AgentState | ||
|
|
||
|
|
||
| def property_search(state: AgentState) -> AgentState: | ||
| client = SpringClient() | ||
| result = client.search_properties( | ||
| message=state["message"], | ||
| context=state.get("context", {}), | ||
| ) | ||
| message = state["message"] | ||
| criteria = LLMClient().extract_property_criteria(message) | ||
| try: | ||
| properties = SupabaseVectorClient().search_properties(criteria) | ||
| property_search_meta = {"count": len(properties), "criteria": criteria} | ||
| except Exception as exc: | ||
| properties = [] | ||
| property_search_meta = { | ||
| "count": 0, | ||
| "criteria": criteria, | ||
| "error": "PROPERTY_SEARCH_UNAVAILABLE", | ||
| "errorDetail": str(exc), | ||
| } | ||
| return { | ||
| **state, | ||
| "properties": result["properties"], | ||
| "properties": properties, | ||
| "tool_results": { | ||
| **state.get("tool_results", {}), | ||
| "propertySearch": result["meta"], | ||
| "propertySearch": property_search_meta, | ||
| }, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.