Skip to content

Commit 1f439a4

Browse files
committed
get_glossary_terms tool 추가, 프롬프트 수정
1 parent 7d8a108 commit 1f439a4

File tree

4 files changed

+207
-24
lines changed

4 files changed

+207
-24
lines changed

interface/app_pages/chatbot.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,25 @@ def initialize_session_state():
5454
or "gpt-4o-mini"
5555
)
5656

57+
# DataHub 서버 URL 가져오기 (config에서 로드)
58+
config = load_config()
59+
gms_server = config.datahub_server
60+
5761
# ChatBot 인스턴스 생성 또는 모델 업데이트
5862
if "chatbot_instance" not in st.session_state:
5963
st.session_state.chatbot_instance = ChatBot(
60-
openai_api_key, model_name=model_name
64+
openai_api_key, model_name=model_name, gms_server=gms_server
6165
)
6266
else:
63-
# 기존 인스턴스가 있는 경우, 모델이나 API 키가 변경되었는지 확인
67+
# 기존 인스턴스가 있는 경우, 모델이나 API 키, gms_server가 변경되었는지 확인
6468
existing_bot = st.session_state.chatbot_instance
6569
if (
6670
existing_bot.model_name != model_name
6771
or existing_bot.openai_api_key != openai_api_key
72+
or existing_bot.gms_server != gms_server
6873
):
6974
st.session_state.chatbot_instance = ChatBot(
70-
openai_api_key, model_name=model_name
75+
openai_api_key, model_name=model_name, gms_server=gms_server
7176
)
7277

7378

utils/llm/chatbot.py

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@
33
OpenAI의 ChatGPT 모델을 사용하여 대화 기록을 유지하는 챗봇 구현
44
"""
55

6+
from typing import Annotated, Sequence, TypedDict
7+
8+
from langchain_core.messages import BaseMessage, SystemMessage
69
from langchain_openai import ChatOpenAI
7-
from langchain_core.messages import SystemMessage
810
from langgraph.checkpoint.memory import MemorySaver
9-
from langgraph.graph import START, MessagesState, StateGraph
11+
from langgraph.graph import START, StateGraph
12+
from langgraph.graph.message import add_messages
1013
from langgraph.prebuilt import ToolNode
1114

1215
from utils.llm.tools import (
1316
get_weather,
1417
get_famous_opensource,
1518
search_database_tables,
19+
get_glossary_terms,
1620
)
1721

1822

23+
class ChatBotState(TypedDict):
24+
"""
25+
챗봇 상태 - 사용자 질문을 SQL로 변환 가능한 구체적인 질문으로 만들어가는 과정 추적
26+
"""
27+
28+
# 기본 메시지 (MessagesState와 동일)
29+
messages: Annotated[Sequence[BaseMessage], add_messages]
30+
31+
# datahub 서버 정보
32+
gms_server: str
33+
34+
1935
class ChatBot:
2036
"""
2137
LangGraph를 사용한 대화형 챗봇 클래스
@@ -31,21 +47,29 @@ class ChatBot:
3147
"gpt-3.5-turbo": "GPT-3.5 Turbo",
3248
}
3349

34-
def __init__(self, openai_api_key: str, model_name: str = "gpt-4o-mini"):
50+
def __init__(
51+
self,
52+
openai_api_key: str,
53+
model_name: str = "gpt-4o-mini",
54+
gms_server: str = "http://localhost:8080",
55+
):
3556
"""
3657
ChatBot 인스턴스 초기화
3758
3859
Args:
3960
openai_api_key: OpenAI API 키
4061
model_name: 사용할 모델명 (기본값: gpt-4o-mini)
62+
gms_server: DataHub GMS 서버 URL (기본값: http://localhost:8080)
4163
"""
4264
self.openai_api_key = openai_api_key
4365
self.model_name = model_name
66+
self.gms_server = gms_server
4467
# SQL 생성을 위한 데이터베이스 메타데이터 조회 도구
4568
self.tools = [
4669
search_database_tables, # 데이터베이스 테이블 정보 검색
4770
get_weather, # 테스트용 도구 (추후 제거 가능)
4871
get_famous_opensource, # 테스트용 도구 (추후 제거 가능)
72+
get_glossary_terms, # 용어집 조회 도구
4973
]
5074
self.llm = self._setup_llm() # LLM 인스턴스 설정
5175
self.app = self._setup_workflow() # LangGraph 워크플로우 설정
@@ -75,10 +99,10 @@ def _setup_workflow(self):
7599
Returns:
76100
CompiledGraph: 컴파일된 LangGraph 워크플로우
77101
"""
78-
# MessagesState를 사용하는 StateGraph 생성
79-
workflow = StateGraph(state_schema=MessagesState)
102+
# ChatBotState를 사용하는 StateGraph 생성
103+
workflow = StateGraph(state_schema=ChatBotState)
80104

81-
def call_model(state: MessagesState):
105+
def call_model(state: ChatBotState):
82106
"""
83107
LLM 모델을 호출하는 노드 함수
84108
LLM이 응답을 생성하거나 tool 호출을 결정합니다.
@@ -89,26 +113,38 @@ def call_model(state: MessagesState):
89113
Returns:
90114
dict: LLM 응답이 포함된 상태 업데이트
91115
"""
92-
# SQL 생성 전문 어시스턴트 시스템 메시지
116+
# 질문 구체화 전문 어시스턴트 시스템 메시지
93117
sys_msg = SystemMessage(
94118
content="""# 역할
95-
당신은 사용자가 SQL 쿼리를 생성하도록 돕는 전문 AI 어시스턴트입니다.
119+
당신은 사용자의 모호한 질문을 명확하고 구체적인 질문으로 만드는 전문 AI 어시스턴트입니다.
96120
97121
# 주요 임무
98-
- 사용자의 자연어 질문을 이해하고 SQL 쿼리 생성에 필요한 정보를 파악합니다
99-
- 필요한 경우 데이터베이스 스키마나 메타데이터를 확인하기 위해 도구를 활용합니다
100-
- 단계별로 사용자와 대화하며 명확한 SQL 쿼리를 만들어갑니다
101-
- 생성된 SQL에 대해 이해하기 쉽게 설명합니다
122+
- 사용자의 자연어 질문을 이해하고 의도를 정확히 파악합니다
123+
- 대화를 통해 날짜, 지표, 필터 조건 등 구체적인 정보를 수집합니다
124+
- 단계별로 사용자와 대화하며 명확하고 구체적인 질문으로 다듬어갑니다
102125
103126
# 작업 프로세스
104-
1. 사용자의 의도를 명확히 파악
105-
2. 필요한 테이블/컬럼 정보 확인 (도구 사용)
106-
3. 사용자의 질문을 바탕으로 정보를 추출할 수 있는 명확한 질문으로 변환합니다
127+
1. 사용자의 최초 질문에서 의도 파악
128+
2. 질문을 명확히 하기 위해 필요한 정보 식별 (날짜, 지표, 대상, 조건 등)
129+
3. **도구를 적극 활용하여 데이터베이스 스키마, 테이블 정보, 용어집 등을 확인**
130+
4. 부족한 정보를 자연스럽게 질문하여 수집
131+
5. 수집된 정보를 바탕으로 질문을 점진적으로 구체화
132+
6. 충분히 구체화되면 최종 질문 확정
133+
134+
# 도구 사용 가이드
135+
- **search_database_tables**: 사용자와의 대화를 데이터와 연관짓기 위해 관련 테이블을 적극적으로 확인
136+
- **get_glossary_terms**: 사용자가 사용한 용어의 정확한 의미를 확인할 때 사용
137+
- 도구를 사용하면 더 정확하고 구체적인 질문을 만들 수 있습니다
138+
- 불확실한 정보가 있다면 추측하지 말고 도구를 사용하여 확인하세요
139+
140+
# 예시
141+
- 모호한 질문: "KPI가 궁금해"
142+
- 대화 후 구체화: "2025-01-02 날짜의 신규 유저가 발생시킨 매출이 궁금해"
107143
108144
# 주의사항
109145
- 항상 친절하고 명확하게 대화합니다
110146
- 이전 대화 맥락을 고려하여 일관성 있게 응답합니다
111-
- 사용자가 SQL을 이해할 수 있도록 단계별로 설명합니다
147+
- 한 번에 너무 많은 것을 물어보지 않고 단계적으로 진행합니다
112148
113149
---
114150
다음은 사용자와의 대화입니다:"""
@@ -118,7 +154,7 @@ def call_model(state: MessagesState):
118154
response = self.llm.invoke(messages)
119155
return {"messages": response}
120156

121-
def route_model_output(state: MessagesState):
157+
def route_model_output(state: ChatBotState):
122158
"""
123159
LLM 출력에 따라 다음 노드를 결정하는 라우팅 함수
124160
Tool 호출이 필요한 경우 'tools' 노드로, 아니면 대화를 종료합니다.
@@ -161,10 +197,15 @@ def chat(self, message: str, thread_id: str):
161197
Returns:
162198
dict: LLM 응답을 포함한 결과 딕셔너리
163199
"""
164-
return self.app.invoke(
165-
{"messages": [{"role": "user", "content": message}]},
166-
{"configurable": {"thread_id": thread_id}}, # thread_id로 대화 기록 관리
167-
)
200+
config = {"configurable": {"thread_id": thread_id}}
201+
202+
# 상태 준비
203+
input_state = {
204+
"messages": [{"role": "user", "content": message}],
205+
"gms_server": self.gms_server, # DataHub 서버 URL을 상태에 포함
206+
}
207+
208+
return self.app.invoke(input_state, config)
168209

169210
def update_model(self, model_name: str):
170211
"""

utils/llm/tools/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
get_weather,
99
get_famous_opensource,
1010
search_database_tables,
11+
get_glossary_terms,
1112
)
1213

1314
__all__ = [
@@ -17,4 +18,5 @@
1718
"get_weather",
1819
"get_famous_opensource",
1920
"search_database_tables",
21+
"get_glossary_terms",
2022
]

utils/llm/tools/chatbot_tool.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from langchain_core.tools import tool
66
from utils.llm.retrieval import search_tables
7+
from utils.data.datahub_services.base_client import DataHubBaseClient
8+
from utils.data.datahub_services.glossary_service import GlossaryService
79

810

911
@tool
@@ -118,3 +120,136 @@ def search_database_tables(
118120
return search_tables(
119121
query=query, retriever_name=retriever_name, top_n=top_n, device=device
120122
)
123+
124+
125+
def _simplify_glossary_data(glossary_data):
126+
"""
127+
용어집 데이터를 name, description, children만 포함하는 간단한 형태로 변환
128+
129+
Args:
130+
glossary_data: 처리된 용어집 데이터
131+
132+
Returns:
133+
list: 간소화된 용어집 데이터 (name, description, children만 포함)
134+
"""
135+
if "error" in glossary_data:
136+
return glossary_data
137+
138+
result = []
139+
140+
for node in glossary_data.get("nodes", []):
141+
simplified_node = {
142+
"name": node.get("name"),
143+
"description": node.get("description"),
144+
}
145+
146+
# children 정보가 있으면 추가
147+
if "details" in node and "children" in node["details"]:
148+
children = []
149+
for child in node["details"]["children"]:
150+
child_info = {
151+
"name": child.get("name"),
152+
"description": child.get("description"),
153+
}
154+
children.append(child_info)
155+
156+
if children:
157+
simplified_node["children"] = children
158+
159+
result.append(simplified_node)
160+
161+
return result
162+
163+
164+
@tool
165+
def get_glossary_terms(gms_server: str = "http://35.222.65.99:8080") -> list:
166+
"""
167+
DataHub에서 용어집(Glossary) 정보를 조회합니다.
168+
169+
이 함수는 DataHub 서버에 연결하여 전체 용어집 데이터를 가져옵니다.
170+
용어집은 비즈니스 용어, 도메인 지식, 데이터 정의 등을 표준화하여 관리하는 곳입니다.
171+
172+
**중요**: 사용자의 질문이나 대화에서 다음과 같은 상황이 발생하면 반드시 이 도구를 사용하세요:
173+
1. 이해되지 않거나 모호한 단어가 나왔을 때
174+
2. 특정 조직이나 도메인에서 고유하게 사용되는 전문 용어가 나왔을 때
175+
3. 일반적이지 않은 약어나 줄임말이 나왔을 때
176+
4. 조직 내부에서만 통용되는 용어가 나왔을 때
177+
5. 표준 정의가 필요한 비즈니스 용어가 나왔을 때
178+
179+
Args:
180+
gms_server (str, optional): DataHub GMS 서버 URL입니다.
181+
기본값은 "http://35.222.65.99:8080"
182+
183+
Returns:
184+
list: 간소화된 용어집 데이터 리스트입니다.
185+
각 항목은 name, description, children(선택적) 필드를 포함합니다.
186+
187+
예시 형태:
188+
[
189+
{
190+
"name": "가짜연구소",
191+
"description": "스터디 단체 가짜연구소를 의미하며...",
192+
"children": [
193+
{
194+
"name": "빌더",
195+
"description": "가짜연구소 스터디 리더를 지칭..."
196+
}
197+
]
198+
},
199+
{
200+
"name": "PII",
201+
"description": "개인 식별 정보...",
202+
"children": [
203+
{
204+
"name": "identifier",
205+
"description": "개인식별정보중 github 아이디..."
206+
}
207+
]
208+
}
209+
]
210+
211+
Examples:
212+
>>> get_glossary_terms()
213+
[{'name': '가짜연구소', 'description': '...', 'children': [...]}]
214+
215+
Note:
216+
이 도구는 다음과 같은 경우에 **반드시** 사용하세요:
217+
218+
[명시적 요청]
219+
- "용어집을 보여줘"
220+
- "비즈니스 용어가 뭐가 있어?"
221+
- "데이터 사전 정보를 알려줘"
222+
- "정의된 용어들을 보여줘"
223+
224+
[이해되지 않는 단어 감지 - 매우 중요!]
225+
- 일반적이지 않은 약어나 전문 용어가 대화에 등장할 때
226+
- 표준 정의가 없는 도메인 특화 용어가 나올 때
227+
- 질문의 맥락에서 모호하거나 불명확한 용어가 있을 때
228+
229+
[조직/도메인 특화 상황]
230+
- 특정 조직에서만 사용하는 내부 용어가 나올 때
231+
- 업계/도메인 전문 용어가 필요할 때
232+
- 데이터나 테이블 관련 비즈니스 컨텍스트를 이해하기 위해
233+
234+
**핵심**: 응답하기 전에 사용자의 질문에 조직 특화 용어나 모호한 단어가
235+
있는지 확인하고, 있다면 먼저 이 도구를 호출하여 정확한 정의를 파악하세요.
236+
"""
237+
try:
238+
# DataHub 클라이언트 초기화
239+
client = DataHubBaseClient(gms_server=gms_server)
240+
241+
# GlossaryService 초기화
242+
glossary_service = GlossaryService(client)
243+
244+
# 전체 용어집 데이터 가져오기
245+
glossary_data = glossary_service.get_glossary_data()
246+
247+
# 간소화된 데이터 반환
248+
simplified_data = _simplify_glossary_data(glossary_data)
249+
250+
return simplified_data
251+
252+
except ValueError as e:
253+
return {"error": True, "message": f"DataHub 서버 연결 실패: {str(e)}"}
254+
except Exception as e:
255+
return {"error": True, "message": f"용어집 조회 중 오류 발생: {str(e)}"}

0 commit comments

Comments
 (0)