AlauStone commited on
Commit
34c2286
·
verified ·
1 Parent(s): 13e704d

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -106
app.py CHANGED
@@ -107,73 +107,34 @@ def inject_custom_css():
107
  }
108
  /* 减少标题上方空白 */
109
  .block-container { padding-top: 1.5rem !important; }
110
-
111
- /* 固定标题区域 */
112
- .main-title {
113
- position: sticky !important;
114
- top: 0 !important;
115
- background: white !important;
116
- z-index: 100 !important;
117
- padding: 0.5rem 0 !important;
118
- }
119
-
120
- /* 固定搜索框在底部 */
121
- [data-testid="stChatInput"] {
122
- position: fixed !important;
123
- bottom: 1rem !important;
124
- left: 50% !important;
125
- transform: translateX(-50%) !important;
126
- width: 90% !important;
127
- max-width: 800px !important;
128
- z-index: 99 !important;
129
- background: white !important;
130
- padding: 0.5rem !important;
131
- border-radius: 8px !important;
132
- }
133
-
134
- /* 给主内容区底部留出搜索框空间 */
135
- .main .block-container {
136
- padding-bottom: 5rem !important;
137
- }
138
  </style>
139
  """, unsafe_allow_html=True)
140
 
141
 
142
  inject_custom_css()
143
-
144
- # =========================
145
- # 标题和欢迎页一起渲染(优化加载体验)
146
- # =========================
147
- # 标题区域 - 使用 main-title class 固定
148
  st.markdown(
149
  """
150
- <div class="main-title">
151
- <h1 style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:clamp(1.4rem, 5vw, 2.2rem); margin:0 0 0.5rem; text-align:center;">
152
- 🤖 智答 AI 助手
153
- </h1>
154
- </div>
155
  """,
156
  unsafe_allow_html=True,
157
  )
158
 
159
  # 欢迎页占位(登录后若有消息则清除)
160
  _welcome_hero = st.empty()
161
-
162
- # 立即渲染欢迎页(避免延迟显示)
163
- if not st.session_state.get("current_user") and not st.session_state.get("messages"):
164
  _welcome_hero.markdown(
165
  """
166
- <div style="display:flex; align-items:center; justify-content:center; min-height:45vh;">
167
  <div style="text-align:center; color:#888;">
168
- <div style="font-size:48px; margin-bottom:12px;">🤖</div>
169
- <div style="font-size:18px; font-weight:600; margin-bottom:8px;">欢迎使用智答 AI 助手</div>
170
- <div style="font-size:14px; line-height:1.8;">
171
  🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
172
  📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
173
  </div>
174
- <div style="margin-top:12px; font-size:13px; color:#999;">
175
- 🙋 当前为<b>游客模式</b>,可直接在下方提问体验
176
- </div>
177
  </div>
178
  </div>
179
  """,
@@ -374,7 +335,6 @@ with st.sidebar:
374
  st.session_state.login_attempts = 0
375
  st.session_state.current_user = input_username
376
  st.session_state.current_role = role
377
- st.toast(f"✅ 登录成功,欢迎 {input_username}!")
378
  st.rerun()
379
 
380
  else: # 注册
@@ -392,7 +352,6 @@ with st.sidebar:
392
  st.session_state.current_user = reg_user
393
  st.session_state.current_role = "user"
394
  st.session_state.login_attempts = 0
395
- st.toast(f"✅ 注册成功,欢迎 {reg_user}!")
396
  st.rerun()
397
  else:
398
  st.error(f"❌ {msg}")
@@ -724,9 +683,10 @@ SYSTEM_PROMPT = (
724
  )
725
 
726
  SYSTEM_PROMPT_WEB = (
727
- "你是一个专业的AI助手,请基于互联网搜索获取的实时信息回答用户问题。"
728
- "回答要准确、有条理、简洁,并尽可能提供最新的信息。"
729
- "如果搜索结果不足以回答问题,请基于你的知识库作答并说明。"
 
730
  )
731
 
732
 
@@ -958,7 +918,8 @@ else:
958
 
959
 
960
  with st.sidebar:
961
- st.caption("👇 展开下方面板管理知识库和设置")
 
962
  # --- 模型设置(游客和登录用户均可用) ---
963
  with st.expander("⚙️ 模型设置"):
964
  _model_list = list(_active_models.keys())
@@ -1165,13 +1126,10 @@ with st.sidebar:
1165
  hist_tab_new, hist_tab_history = st.tabs(["当前对话", "历史记录"])
1166
 
1167
  with hist_tab_new:
1168
- st.caption("清空当前对话(页面显示)")
1169
  if st.button("🧹 清空当前对话", use_container_width=True, type="secondary", key="btn_clear_chat"):
1170
  st.session_state.messages = []
1171
- st.toast("当前对话已清空")
1172
- st.rerun()
1173
 
1174
- st.divider()
1175
  st.caption("清空所有历史记录(不可恢复)")
1176
  if st.button("🗑️ 清空全部记录", use_container_width=True, type="secondary", key="btn_clear_all_hist"):
1177
  _async_run(_clear_chat_history_db, CURRENT_USER)
@@ -1222,14 +1180,19 @@ def llm_answer(query, context_docs, selected_display_name, web_enabled):
1222
  all_context = ""
1223
  curr_time = datetime.now().strftime("%Y-%m-%d %H:%M")
1224
 
1225
- if context_docs:
1226
- all_context += "【知识库资料】:\n" + "\n".join(context_docs) + "\n"
1227
-
1228
  if web_enabled:
 
1229
  search_res = web_search(query)
1230
- all_context += f"\n【互联网实时资料】:\n{search_res}"
 
 
 
 
 
 
 
 
1231
 
1232
- prompt_content = f"当前时间:{curr_time}\n\n参考资料:\n{all_context[:6500]}\n\n用户问题:{query}"
1233
  input_tokens = estimate_tokens(prompt_content)
1234
 
1235
  or_client = get_or_client()
@@ -1258,10 +1221,8 @@ def llm_answer(query, context_docs, selected_display_name, web_enabled):
1258
  if selected_id != p_id:
1259
  retry_queue.append((p_client, p_id, f"💰 收费兜底-{p_label}"))
1260
 
1261
- # 联网模式和知识库模式使用不同的 system prompt
1262
- _system_prompt = SYSTEM_PROMPT_WEB if web_enabled else SYSTEM_PROMPT
1263
  messages = [
1264
- {"role": "system", "content": _system_prompt},
1265
  {"role": "user", "content": prompt_content},
1266
  ]
1267
 
@@ -1323,9 +1284,25 @@ if not st.session_state.get("messages"):
1323
  else:
1324
  st.session_state.messages = []
1325
 
1326
- # 有消息时清除欢迎页
1327
  if st.session_state.messages:
1328
  _welcome_hero.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1329
 
1330
 
1331
  @st.fragment
@@ -1336,48 +1313,24 @@ def _chat_fragment():
1336
  _top_k = st.session_state.get("sel_topk", 5)
1337
  _threshold = st.session_state.get("sel_threshold", 0.25)
1338
 
1339
- # 聊天区域:消息在上(固定高度可滚动),输入框在下(固定)
 
1340
  _has_msgs = bool(st.session_state.messages)
1341
-
1342
- # 消息区域 - 固定高度,可滚动
1343
- if _has_msgs:
1344
- # 固定高度的消息容器(可滚动)
1345
- msg_container = st.container(height=480)
1346
- with msg_container:
1347
- for m in st.session_state.messages:
1348
- with st.chat_message(m["role"]):
1349
- st.markdown(m["content"])
1350
- if "meta" in m:
1351
- st.caption(m["meta"])
1352
-
1353
- # 自动滚动到底部
1354
- st.markdown("""
1355
- u003cscriptu003e
1356
- (function() {
1357
- const containers = document.querySelectorAll('[data-testid="stVerticalBlock"]');
1358
- containers.forEach(c => {
1359
- if (c.scrollHeight u003e c.clientHeight) {
1360
- c.scrollTop = c.scrollHeight;
1361
- }
1362
- });
1363
- })();
1364
- u003c/scriptu003e
1365
- """, unsafe_allow_html=True)
1366
- else:
1367
- # 无消息时不占高度
1368
- msg_container = st.container()
1369
-
1370
- # 搜索框固定在底部
1371
  q = st.chat_input("输入问题...", key="chat_input_v3")
1372
 
1373
- if q:
1374
- _welcome_hero.empty() # 首次发消息时清除欢迎页
1375
- st.session_state.messages.append({"role": "user", "content": q})
1376
- if not IS_GUEST:
1377
- _save_chat_message(CURRENT_USER, "user", q)
1378
-
1379
- # 立即显示用户消息
1380
- with msg_container:
 
 
 
 
1381
  with st.chat_message("user"):
1382
  st.markdown(q)
1383
 
@@ -1385,6 +1338,7 @@ def _chat_fragment():
1385
  container = st.empty()
1386
 
1387
  # 联网模式:大模型 + 网络搜索,不查知识库
 
1388
  if _web_on:
1389
  container.markdown("*🌐 正在联网搜索...*")
1390
  relevant_docs = []
 
107
  }
108
  /* 减少标题上方空白 */
109
  .block-container { padding-top: 1.5rem !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </style>
111
  """, unsafe_allow_html=True)
112
 
113
 
114
  inject_custom_css()
 
 
 
 
 
115
  st.markdown(
116
  """
117
+ <h1 style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:clamp(1.4rem, 5vw, 2.2rem); margin:0 0 0.5rem; text-align:center;">
118
+ 🤖 智答 AI 助手
119
+ </h1>
 
 
120
  """,
121
  unsafe_allow_html=True,
122
  )
123
 
124
  # 欢迎页占位(登录后若有消息则清除)
125
  _welcome_hero = st.empty()
126
+ if not st.session_state.get("current_user"):
127
+ # 未登录:欢迎页(不含游客模式提示,避免闪现)
 
128
  _welcome_hero.markdown(
129
  """
130
+ <div style="display:flex; align-items:center; justify-content:center; min-height:50vh;">
131
  <div style="text-align:center; color:#888;">
132
+ <div style="font-size:52px; margin-bottom:16px;">🤖</div>
133
+ <div style="font-size:20px; font-weight:600; margin-bottom:10px;">欢迎使用智答 AI 助手</div>
134
+ <div style="font-size:15px; line-height:2;">
135
  🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
136
  📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
137
  </div>
 
 
 
138
  </div>
139
  </div>
140
  """,
 
335
  st.session_state.login_attempts = 0
336
  st.session_state.current_user = input_username
337
  st.session_state.current_role = role
 
338
  st.rerun()
339
 
340
  else: # 注册
 
352
  st.session_state.current_user = reg_user
353
  st.session_state.current_role = "user"
354
  st.session_state.login_attempts = 0
 
355
  st.rerun()
356
  else:
357
  st.error(f"❌ {msg}")
 
683
  )
684
 
685
  SYSTEM_PROMPT_WEB = (
686
+ "你是一个智能AI助手,擅长结合互联网搜索结果回答用户问题。"
687
+ "请根据搜索结果提供准确、有条理的回答。"
688
+ "如果搜索结果不足以回答问题,请结合你自身的知识进行补充。"
689
+ "回答要简洁实用,注明信息来源。"
690
  )
691
 
692
 
 
918
 
919
 
920
  with st.sidebar:
921
+ if not IS_GUEST:
922
+ st.caption("👇 展开下方面板管理知识库和设置")
923
  # --- 模型设置(游客和登录用户均可用) ---
924
  with st.expander("⚙️ 模型设置"):
925
  _model_list = list(_active_models.keys())
 
1126
  hist_tab_new, hist_tab_history = st.tabs(["当前对话", "历史记录"])
1127
 
1128
  with hist_tab_new:
1129
+ st.caption("清空当前对话(数据库记录保留)")
1130
  if st.button("🧹 清空当前对话", use_container_width=True, type="secondary", key="btn_clear_chat"):
1131
  st.session_state.messages = []
 
 
1132
 
 
1133
  st.caption("清空所有历史记录(不可恢复)")
1134
  if st.button("🗑️ 清空全部记录", use_container_width=True, type="secondary", key="btn_clear_all_hist"):
1135
  _async_run(_clear_chat_history_db, CURRENT_USER)
 
1180
  all_context = ""
1181
  curr_time = datetime.now().strftime("%Y-%m-%d %H:%M")
1182
 
 
 
 
1183
  if web_enabled:
1184
+ # 联网模式:只用网络搜索,不用知识库
1185
  search_res = web_search(query)
1186
+ all_context = f"【互联网搜索结果】:\n{search_res}"
1187
+ prompt_content = f"当前时间:{curr_time}\n\n{all_context[:6500]}\n\n用户问题:{query}"
1188
+ system_prompt = SYSTEM_PROMPT_WEB
1189
+ else:
1190
+ # 知识库模式:用知识库资料
1191
+ if context_docs:
1192
+ all_context = "【知识库资料】:\n" + "\n".join(context_docs)
1193
+ prompt_content = f"当前时间:{curr_time}\n\n参考资料:\n{all_context[:6500]}\n\n用户问题:{query}"
1194
+ system_prompt = SYSTEM_PROMPT
1195
 
 
1196
  input_tokens = estimate_tokens(prompt_content)
1197
 
1198
  or_client = get_or_client()
 
1221
  if selected_id != p_id:
1222
  retry_queue.append((p_client, p_id, f"💰 收费兜底-{p_label}"))
1223
 
 
 
1224
  messages = [
1225
+ {"role": "system", "content": system_prompt},
1226
  {"role": "user", "content": prompt_content},
1227
  ]
1228
 
 
1284
  else:
1285
  st.session_state.messages = []
1286
 
1287
+ # 登录后处理欢迎页:有消息则清除,无消息则显示(不含登录提示)
1288
  if st.session_state.messages:
1289
  _welcome_hero.empty()
1290
+ else:
1291
+ _welcome_hero.markdown(
1292
+ """
1293
+ <div style="display:flex; align-items:center; justify-content:center; min-height:50vh;">
1294
+ <div style="text-align:center; color:#888;">
1295
+ <div style="font-size:52px; margin-bottom:16px;">🤖</div>
1296
+ <div style="font-size:20px; font-weight:600; margin-bottom:10px;">欢迎使用智答 AI 助手</div>
1297
+ <div style="font-size:15px; line-height:2;">
1298
+ 🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
1299
+ 📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
1300
+ </div>
1301
+ </div>
1302
+ </div>
1303
+ """,
1304
+ unsafe_allow_html=True,
1305
+ )
1306
 
1307
 
1308
  @st.fragment
 
1313
  _top_k = st.session_state.get("sel_topk", 5)
1314
  _threshold = st.session_state.get("sel_threshold", 0.25)
1315
 
1316
+ # 消息容器(可滚动) + 输入框固定在下方
1317
+ # 有消息时使用固定高度实现滚动;无消息时不设高度避免空白
1318
  _has_msgs = bool(st.session_state.messages)
1319
+ chat_box = st.container(height=520) if _has_msgs else st.container()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1320
  q = st.chat_input("输入问题...", key="chat_input_v3")
1321
 
1322
+ with chat_box:
1323
+ for m in st.session_state.messages:
1324
+ with st.chat_message(m["role"]):
1325
+ st.markdown(m["content"])
1326
+ if "meta" in m:
1327
+ st.caption(m["meta"])
1328
+
1329
+ if q:
1330
+ _welcome_hero.empty() # 首次发消息时清除欢迎页
1331
+ st.session_state.messages.append({"role": "user", "content": q})
1332
+ if not IS_GUEST:
1333
+ _save_chat_message(CURRENT_USER, "user", q)
1334
  with st.chat_message("user"):
1335
  st.markdown(q)
1336
 
 
1338
  container = st.empty()
1339
 
1340
  # 联网模式:大模型 + 网络搜索,不查知识库
1341
+ # 知识库模式:RAG 检索本地知识库
1342
  if _web_on:
1343
  container.markdown("*🌐 正在联网搜索...*")
1344
  relevant_docs = []