qunwang commited on
Commit
5915dde
·
1 Parent(s): d11f56a

fix: Weaviate 连接优化 - 缓存 embedding、超时、UI 状态显示

Browse files
Files changed (2) hide show
  1. app.py +77 -28
  2. config.py +13 -0
app.py CHANGED
@@ -48,42 +48,75 @@ CLARE_RUN_PATH = "Clare_Run.png"
48
  CLARE_READING_PATH = "Clare_reading.png" # 确保存在
49
 
50
  # ================== Weaviate 直连 / GenAICoursesDB 检索 ==================
51
- def _retrieve_from_weaviate(question: str, top_k: int = 5) -> str:
52
- """直接连接 Weaviate Cloud 检索 GENAI 课程。Embedding 需与索引一致(all-MiniLM-L6-v2)。"""
53
- if not USE_WEAVIATE_DIRECT or len(question.strip()) < 5:
54
- return ""
55
- try:
56
- import weaviate
57
- from weaviate.classes.init import Auth
58
- from llama_index.core import Settings, VectorStoreIndex
59
- from llama_index.embeddings.huggingface import HuggingFaceEmbedding
60
- from llama_index.vector_stores.weaviate import WeaviateVectorStore
61
 
62
- # 必须与 GenAICoursesDB 建索引时一致
63
- Settings.embed_model = HuggingFaceEmbedding(
 
 
 
 
64
  model_name="sentence-transformers/all-MiniLM-L6-v2"
65
  )
66
- client = weaviate.connect_to_weaviate_cloud(
67
- cluster_url=WEAVIATE_URL,
68
- auth_credentials=Auth.api_key(WEAVIATE_API_KEY),
69
- )
 
 
 
 
 
 
70
  try:
71
- if not client.is_ready():
72
- return ""
73
- vs = WeaviateVectorStore(
74
- weaviate_client=client,
75
- index_name=WEAVIATE_COLLECTION,
 
 
 
 
76
  )
77
- index = VectorStoreIndex.from_vector_store(vs)
78
- nodes = index.as_retriever(similarity_top_k=top_k).retrieve(question)
79
- return "\n\n---\n\n".join(n.get_content() for n in nodes) if nodes else ""
80
- finally:
81
- client.close()
82
- except Exception as e:
83
- print(f"[weaviate] retrieve failed: {repr(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  return ""
85
 
86
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  def _retrieve_from_genai_courses(question: str, top_k: int = 5, timeout_sec: float = 25.0) -> str:
88
  """调用 GenAICoursesDB Space 的 retrieve 接口(Weaviate 未配置时回退)。"""
89
  if not GENAI_COURSES_SPACE or len(question.strip()) < 5:
@@ -538,6 +571,8 @@ with gr.Blocks(
538
  interactive=False,
539
  )
540
 
 
 
541
  with gr.Accordion(
542
  "User Guide", open=True, elem_classes="main-user-guide"
543
  ):
@@ -1077,6 +1112,10 @@ with gr.Blocks(
1077
  if USE_WEAVIATE_DIRECT:
1078
  course_chunks = _retrieve_from_weaviate(message)
1079
  course_source = "Weaviate Cloud (GENAI COURSES)"
 
 
 
 
1080
  elif GENAI_COURSES_SPACE:
1081
  course_chunks = _retrieve_from_genai_courses(message)
1082
  course_source = "GenAICoursesDB"
@@ -1090,6 +1129,16 @@ with gr.Blocks(
1090
  "_rag_score": 1.0,
1091
  }
1092
  ]
 
 
 
 
 
 
 
 
 
 
1093
  else:
1094
  rag_context_text, rag_used_chunks = "", []
1095
 
 
48
  CLARE_READING_PATH = "Clare_reading.png" # 确保存在
49
 
50
  # ================== Weaviate 直连 / GenAICoursesDB 检索 ==================
51
+ _WEAVIATE_EMBED_MODEL = None # 缓存,避免每次请求都加载 sentence-transformers
52
+
 
 
 
 
 
 
 
 
53
 
54
+ def _get_weaviate_embed_model():
55
+ """懒加载并缓存 embedding 模型(与建索引时一致)。"""
56
+ global _WEAVIATE_EMBED_MODEL
57
+ if _WEAVIATE_EMBED_MODEL is None:
58
+ from llama_index.embeddings.huggingface import HuggingFaceEmbedding
59
+ _WEAVIATE_EMBED_MODEL = HuggingFaceEmbedding(
60
  model_name="sentence-transformers/all-MiniLM-L6-v2"
61
  )
62
+ return _WEAVIATE_EMBED_MODEL
63
+
64
+
65
+ def _retrieve_from_weaviate(question: str, top_k: int = 5, timeout_sec: float = 25.0) -> str:
66
+ """直接连接 Weaviate Cloud 检索 GENAI 课程。带超时,避免 HF Space 阻塞。"""
67
+ if not USE_WEAVIATE_DIRECT or len(question.strip()) < 5:
68
+ return ""
69
+ import concurrent.futures
70
+
71
+ def _call():
72
  try:
73
+ import weaviate
74
+ from weaviate.classes.init import Auth
75
+ from llama_index.core import Settings, VectorStoreIndex
76
+ from llama_index.vector_stores.weaviate import WeaviateVectorStore
77
+
78
+ Settings.embed_model = _get_weaviate_embed_model()
79
+ client = weaviate.connect_to_weaviate_cloud(
80
+ cluster_url=WEAVIATE_URL,
81
+ auth_credentials=Auth.api_key(WEAVIATE_API_KEY),
82
  )
83
+ try:
84
+ if not client.is_ready():
85
+ return ""
86
+ vs = WeaviateVectorStore(
87
+ weaviate_client=client,
88
+ index_name=WEAVIATE_COLLECTION,
89
+ )
90
+ index = VectorStoreIndex.from_vector_store(vs)
91
+ nodes = index.as_retriever(similarity_top_k=top_k).retrieve(question)
92
+ return "\n\n---\n\n".join(n.get_content() for n in nodes) if nodes else ""
93
+ finally:
94
+ client.close()
95
+ except Exception as e:
96
+ print(f"[weaviate] retrieve failed: {repr(e)}")
97
+ return ""
98
+
99
+ try:
100
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as ex:
101
+ fut = ex.submit(_call)
102
+ return fut.result(timeout=timeout_sec)
103
+ except concurrent.futures.TimeoutError:
104
+ print(f"[weaviate] retrieve timeout after {timeout_sec}s")
105
  return ""
106
 
107
 
108
+ def get_weaviate_status_text() -> str:
109
+ """用于 UI 显示的 Weaviate 连接状态。"""
110
+ if not USE_WEAVIATE_DIRECT:
111
+ return (
112
+ "⚠️ **Weaviate**: 未配置\n\n"
113
+ "在 Space 页点击 **Settings → Repository secrets → New secret** 添加:\n"
114
+ "- `WEAVIATE_URL` = `https://iydyvd4wqnekotfiftma.c0.us-west3.gcp.weaviate.cloud`\n"
115
+ "- `WEAVIATE_API_KEY` = 你的 Weaviate API Key"
116
+ )
117
+ return "✅ **Weaviate**: 已配置 — GENAI 课程检索已启用"
118
+
119
+
120
  def _retrieve_from_genai_courses(question: str, top_k: int = 5, timeout_sec: float = 25.0) -> str:
121
  """调用 GenAICoursesDB Space 的 retrieve 接口(Weaviate 未配置时回退)。"""
122
  if not GENAI_COURSES_SPACE or len(question.strip()) < 5:
 
571
  interactive=False,
572
  )
573
 
574
+ gr.Markdown(get_weaviate_status_text())
575
+
576
  with gr.Accordion(
577
  "User Guide", open=True, elem_classes="main-user-guide"
578
  ):
 
1112
  if USE_WEAVIATE_DIRECT:
1113
  course_chunks = _retrieve_from_weaviate(message)
1114
  course_source = "Weaviate Cloud (GENAI COURSES)"
1115
+ if course_chunks:
1116
+ print(f"[ClareVoice] Weaviate 检索成功, 约 {len(course_chunks)} 字符")
1117
+ else:
1118
+ print("[ClareVoice] Weaviate 检索无结果 (object count 可能为 0,请运行 build_weaviate_index.py)")
1119
  elif GENAI_COURSES_SPACE:
1120
  course_chunks = _retrieve_from_genai_courses(message)
1121
  course_source = "GenAICoursesDB"
 
1129
  "_rag_score": 1.0,
1130
  }
1131
  ]
1132
+ elif USE_WEAVIATE_DIRECT and course_source:
1133
+ # 已配置 Weaviate 但无结果,在 References 中说明
1134
+ rag_used_chunks = list(rag_used_chunks or []) + [
1135
+ {
1136
+ "source_file": course_source,
1137
+ "section": "已尝试检索,无结果(请先运行 build_weaviate_index.py 上传索引)",
1138
+ "text": "",
1139
+ "_rag_score": 0.0,
1140
+ }
1141
+ ]
1142
  else:
1143
  rag_context_text, rag_used_chunks = "", []
1144
 
config.py CHANGED
@@ -1,6 +1,13 @@
1
  # config.py
2
  import os
3
  from typing import List, Dict
 
 
 
 
 
 
 
4
  from openai import OpenAI
5
  from langchain_openai import ChatOpenAI, OpenAIEmbeddings
6
 
@@ -43,6 +50,12 @@ USE_WEAVIATE_DIRECT = bool(WEAVIATE_URL and WEAVIATE_API_KEY)
43
  if USE_WEAVIATE_DIRECT and not WEAVIATE_URL.startswith("http"):
44
  WEAVIATE_URL = "https://" + WEAVIATE_URL
45
 
 
 
 
 
 
 
46
  # ---------- 默认 GenAI 课程大纲 ----------
47
  DEFAULT_COURSE_TOPICS: List[str] = [
48
  "Week 0 – Welcome & What is Generative AI; course outcomes LO1–LO5.",
 
1
  # config.py
2
  import os
3
  from typing import List, Dict
4
+
5
+ # 确保加载 .env(本地开发);HF Space 的 Secrets 在启动时已注入
6
+ try:
7
+ from dotenv import load_dotenv
8
+ load_dotenv()
9
+ except Exception:
10
+ pass
11
  from openai import OpenAI
12
  from langchain_openai import ChatOpenAI, OpenAIEmbeddings
13
 
 
50
  if USE_WEAVIATE_DIRECT and not WEAVIATE_URL.startswith("http"):
51
  WEAVIATE_URL = "https://" + WEAVIATE_URL
52
 
53
+ # 启动时打印,便于排查
54
+ if USE_WEAVIATE_DIRECT:
55
+ print("[ClareVoice] Weaviate 直连: 已配置, collection=", WEAVIATE_COLLECTION)
56
+ else:
57
+ print("[ClareVoice] Weaviate 直连: 未配置 (在 Secrets 添加 WEAVIATE_URL, WEAVIATE_API_KEY)")
58
+
59
  # ---------- 默认 GenAI 课程大纲 ----------
60
  DEFAULT_COURSE_TOPICS: List[str] = [
61
  "Week 0 – Welcome & What is Generative AI; course outcomes LO1–LO5.",