Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -9,6 +9,7 @@ import markdown
|
|
| 9 |
import tempfile
|
| 10 |
import base64
|
| 11 |
from datetime import datetime
|
|
|
|
| 12 |
|
| 13 |
# 로깅 설정
|
| 14 |
logging.basicConfig(
|
|
@@ -151,12 +152,52 @@ def extract_keywords(text: str, top_k: int = 5) -> str:
|
|
| 151 |
2) 공백 기준 토큰 분리
|
| 152 |
3) 최대 top_k개만
|
| 153 |
"""
|
| 154 |
-
import re
|
| 155 |
text = re.sub(r"[^a-zA-Z0-9가-힣\s]", "", text)
|
| 156 |
tokens = text.split()
|
| 157 |
key_tokens = tokens[:top_k]
|
| 158 |
return " ".join(key_tokens)
|
| 159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
# 웹 검색 함수
|
| 161 |
def do_web_search(query: str) -> str:
|
| 162 |
"""
|
|
@@ -171,9 +212,9 @@ def do_web_search(query: str) -> str:
|
|
| 171 |
"q": query,
|
| 172 |
"domain": "google.com",
|
| 173 |
"serp_type": "web", # 기본 웹 검색
|
| 174 |
-
"device": "desktop",
|
| 175 |
-
"lang": "
|
| 176 |
-
"num": "
|
| 177 |
}
|
| 178 |
|
| 179 |
headers = {
|
|
@@ -183,8 +224,13 @@ def do_web_search(query: str) -> str:
|
|
| 183 |
logging.info(f"SerpHouse API 호출 중... 검색어: {query}")
|
| 184 |
logging.info(f"요청 URL: {url} - 파라미터: {params}")
|
| 185 |
|
| 186 |
-
#
|
| 187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
response.raise_for_status()
|
| 189 |
|
| 190 |
logging.info(f"SerpHouse API 응답 상태 코드: {response.status_code}")
|
|
@@ -209,13 +255,13 @@ def do_web_search(query: str) -> str:
|
|
| 209 |
|
| 210 |
if not organic:
|
| 211 |
logging.warning("응답에서 organic 결과를 찾을 수 없습니다.")
|
| 212 |
-
logging.debug(f"응답 구조: {list(data.keys())}")
|
| 213 |
if isinstance(results, dict):
|
| 214 |
logging.debug(f"results 구조: {list(results.keys())}")
|
| 215 |
-
return "
|
| 216 |
|
| 217 |
# 결과 수 제한 및 컨텍스트 길이 최적화
|
| 218 |
-
max_results = min(
|
| 219 |
limited_organic = organic[:max_results]
|
| 220 |
|
| 221 |
# 결과 형식 개선 - 마크다운 형식으로 출력하여 가독성 향상
|
|
@@ -248,9 +294,12 @@ def do_web_search(query: str) -> str:
|
|
| 248 |
logging.info(f"검색 결과 {len(limited_organic)}개 처리 완료")
|
| 249 |
return search_results
|
| 250 |
|
|
|
|
|
|
|
|
|
|
| 251 |
except Exception as e:
|
| 252 |
logging.error(f"Web search failed: {e}")
|
| 253 |
-
return f"Web search failed: {str(e)}"
|
| 254 |
|
| 255 |
def chatbot_interface():
|
| 256 |
st.title("Ginigen Blog")
|
|
@@ -320,7 +369,6 @@ def chatbot_interface():
|
|
| 320 |
latest_blog = msg["content"]
|
| 321 |
|
| 322 |
# 타이틀 추출 시도 (첫 번째 제목 태그 사용)
|
| 323 |
-
import re
|
| 324 |
title_match = re.search(r'# (.*?)(\n|$)', latest_blog)
|
| 325 |
if title_match:
|
| 326 |
latest_blog_title = title_match.group(1).strip()
|
|
@@ -402,14 +450,20 @@ def chatbot_interface():
|
|
| 402 |
system_prompt = get_system_prompt()
|
| 403 |
if st.session_state.use_web_search:
|
| 404 |
with st.spinner("웹에서 관련 정보를 검색 중..."):
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
# API 호출
|
| 415 |
with client.messages.stream(
|
|
|
|
| 9 |
import tempfile
|
| 10 |
import base64
|
| 11 |
from datetime import datetime
|
| 12 |
+
import re
|
| 13 |
|
| 14 |
# 로깅 설정
|
| 15 |
logging.basicConfig(
|
|
|
|
| 152 |
2) 공백 기준 토큰 분리
|
| 153 |
3) 최대 top_k개만
|
| 154 |
"""
|
|
|
|
| 155 |
text = re.sub(r"[^a-zA-Z0-9가-힣\s]", "", text)
|
| 156 |
tokens = text.split()
|
| 157 |
key_tokens = tokens[:top_k]
|
| 158 |
return " ".join(key_tokens)
|
| 159 |
|
| 160 |
+
# Mock 검색 결과 생성 함수 추가 (API 키가 없거나 연결 실패 시 대체 사용)
|
| 161 |
+
def generate_mock_search_results(query):
|
| 162 |
+
"""API 연결이 안될 때 사용할 가상 검색 결과 생성"""
|
| 163 |
+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 164 |
+
mock_results = [
|
| 165 |
+
{
|
| 166 |
+
"title": f"{query}에 관한 최신 정보",
|
| 167 |
+
"link": "https://example.com/article1",
|
| 168 |
+
"snippet": f"{query}에 관한 가상 검색 결과입니다. 이 결과는 API 연결 문제로 인해 생성된 가상 데이터입니다. 실제 검색 결과가 아님을 참고하세요. 생성 시간: {current_time}",
|
| 169 |
+
"displayed_link": "example.com/article1"
|
| 170 |
+
},
|
| 171 |
+
{
|
| 172 |
+
"title": f"{query} 관련 연구 동향",
|
| 173 |
+
"link": "https://example.org/research",
|
| 174 |
+
"snippet": "이것은 API 연결 문제로 인한 가상 검색 결과입니다. 실제 검색 결과를 보여드리지 못해 죄송합니다. 대신 AI의 기존 지식을 활용하여 답변드리겠습니다.",
|
| 175 |
+
"displayed_link": "example.org/research"
|
| 176 |
+
}
|
| 177 |
+
]
|
| 178 |
+
|
| 179 |
+
summary_lines = []
|
| 180 |
+
for idx, item in enumerate(mock_results, start=1):
|
| 181 |
+
title = item.get("title", "No title")
|
| 182 |
+
link = item.get("link", "#")
|
| 183 |
+
snippet = item.get("snippet", "No description")
|
| 184 |
+
displayed_link = item.get("displayed_link", link)
|
| 185 |
+
|
| 186 |
+
summary_lines.append(
|
| 187 |
+
f"### Result {idx}: {title}\n\n"
|
| 188 |
+
f"{snippet}\n\n"
|
| 189 |
+
f"**출처**: [{displayed_link}]({link})\n\n"
|
| 190 |
+
f"---\n"
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
notice = """
|
| 194 |
+
# 가상 검색 결과 (API 연결 문제로 인해 생성됨)
|
| 195 |
+
아래는 API 연결 문제로 인해 생성된 가상 검색 결과입니다. 실제 검색 결과가 아님을 참고하세요.
|
| 196 |
+
대신 AI의 기존 지식을 활용하여 최대한 정확한 답변을 드리겠습니다.
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
return notice + "\n".join(summary_lines)
|
| 200 |
+
|
| 201 |
# 웹 검색 함수
|
| 202 |
def do_web_search(query: str) -> str:
|
| 203 |
"""
|
|
|
|
| 212 |
"q": query,
|
| 213 |
"domain": "google.com",
|
| 214 |
"serp_type": "web", # 기본 웹 검색
|
| 215 |
+
"device": "desktop",
|
| 216 |
+
"lang": "ko", # 한국어 검색 결과를 위해 변경
|
| 217 |
+
"num": "10" # 결과 수를 10개로 줄임 (빠른 응답을 위해)
|
| 218 |
}
|
| 219 |
|
| 220 |
headers = {
|
|
|
|
| 224 |
logging.info(f"SerpHouse API 호출 중... 검색어: {query}")
|
| 225 |
logging.info(f"요청 URL: {url} - 파라미터: {params}")
|
| 226 |
|
| 227 |
+
# Mock 응답 (실제 API 연결이 어려울 경우)
|
| 228 |
+
if not SERPHOUSE_API_KEY or "mock" in SERPHOUSE_API_KEY.lower():
|
| 229 |
+
logging.warning("API 키가 없거나 Mock 모드입니다. 모의 검색 결과를 반환합니다.")
|
| 230 |
+
return generate_mock_search_results(query)
|
| 231 |
+
|
| 232 |
+
# 타임아웃 줄임 (30초)
|
| 233 |
+
response = requests.get(url, headers=headers, params=params, timeout=30)
|
| 234 |
response.raise_for_status()
|
| 235 |
|
| 236 |
logging.info(f"SerpHouse API 응답 상태 코드: {response.status_code}")
|
|
|
|
| 255 |
|
| 256 |
if not organic:
|
| 257 |
logging.warning("응답에서 organic 결과를 찾을 수 없습니다.")
|
| 258 |
+
logging.debug(f"응답 구조: {list(data.keys()) if isinstance(data, dict) else 'not a dict'}")
|
| 259 |
if isinstance(results, dict):
|
| 260 |
logging.debug(f"results 구조: {list(results.keys())}")
|
| 261 |
+
return "검색 결과를 찾을 수 없습니다. 대신 기존 지식을 활용하여 답변하겠습니다."
|
| 262 |
|
| 263 |
# 결과 수 제한 및 컨텍스트 길이 최적화
|
| 264 |
+
max_results = min(10, len(organic))
|
| 265 |
limited_organic = organic[:max_results]
|
| 266 |
|
| 267 |
# 결과 형식 개선 - 마크다운 형식으로 출력하여 가독성 향상
|
|
|
|
| 294 |
logging.info(f"검색 결과 {len(limited_organic)}개 처리 완료")
|
| 295 |
return search_results
|
| 296 |
|
| 297 |
+
except requests.exceptions.Timeout:
|
| 298 |
+
logging.error("Web search timed out")
|
| 299 |
+
return "Web search timed out. 검색 시간이 초과되었습니다. 기존 지식을 활용하여 답변하겠습니다."
|
| 300 |
except Exception as e:
|
| 301 |
logging.error(f"Web search failed: {e}")
|
| 302 |
+
return f"Web search failed: {str(e)}. 검색 중 오류가 발생했습니다. 기존 지식을 활용하여 답변하겠습니다."
|
| 303 |
|
| 304 |
def chatbot_interface():
|
| 305 |
st.title("Ginigen Blog")
|
|
|
|
| 369 |
latest_blog = msg["content"]
|
| 370 |
|
| 371 |
# 타이틀 추출 시도 (첫 번째 제목 태그 사용)
|
|
|
|
| 372 |
title_match = re.search(r'# (.*?)(\n|$)', latest_blog)
|
| 373 |
if title_match:
|
| 374 |
latest_blog_title = title_match.group(1).strip()
|
|
|
|
| 450 |
system_prompt = get_system_prompt()
|
| 451 |
if st.session_state.use_web_search:
|
| 452 |
with st.spinner("웹에서 관련 정보를 검색 중..."):
|
| 453 |
+
try:
|
| 454 |
+
search_query = extract_keywords(prompt, top_k=5)
|
| 455 |
+
search_results = do_web_search(search_query)
|
| 456 |
+
|
| 457 |
+
if "search failed" not in search_results.lower() and "timed out" not in search_results.lower():
|
| 458 |
+
# 시스템 프롬프트에 검색 결과 추가
|
| 459 |
+
system_prompt += f"\n\n검색 결과:\n{search_results}\n"
|
| 460 |
+
st.success(f"검색 완료: '{search_query}'에 대한 정보를 수집했습니다.")
|
| 461 |
+
else:
|
| 462 |
+
st.warning("웹 검색을 진행했으나 결과를 가져오는데 실패했습니다. 기존 지식을 활용하여 답변합니다.")
|
| 463 |
+
logging.warning(f"웹 검색 실패 또는 타임아웃: {search_results}")
|
| 464 |
+
except Exception as e:
|
| 465 |
+
st.error(f"웹 검색 중 오류가 발생했습니다: {str(e)}")
|
| 466 |
+
logging.error(f"웹 검색 오류: {str(e)}")
|
| 467 |
|
| 468 |
# API 호출
|
| 469 |
with client.messages.stream(
|