Files
agriculture-qa/app.py
zhenghu 4f27eb6c42 refactor(ui): 使用 Streamlit 原生组件简化界面实现
- 移除大量自定义 CSS 样式,仅保留字体设置
  - 将自定义 HTML 结构替换为 st.title/st.caption/st.container/st.info 等原生组件
  - 简化快捷问题、输入框、回答展示、错误提示和页脚的实现
  - 减少维护复杂度,提升代码可读性
2026-04-14 17:57:57 +08:00

139 lines
5.7 KiB
Python

"""
农技智能问答
基于大模型的农业技术知识问答应用
"""
import json
import httpx
import streamlit as st
from config import CHAT_API_URL, CHAT_MODEL, HEADERS
# ─── Page Config ────────────────────────────────────────────────────────────
st.set_page_config(
page_title="农技问答",
page_icon="🌾",
layout="centered",
initial_sidebar_state="collapsed",
)
# ─── Minimal CSS ─────────────────────────────────────────────────────────────
st.markdown("""
<style>
html, body, [class*="css"] {
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif;
}
</style>
""", unsafe_allow_html=True)
# ─── Quick Questions ─────────────────────────────────────────────────────────
QUICK_QUESTIONS = [
"水稻稻瘟病怎么防治?",
"小麦锈病怎么处理?",
"玉米什么时候浇水最合适?",
"种榴莲需要注意什么?",
]
# ─── Header ──────────────────────────────────────────────────────────────────
st.title("🌾 农技问答")
st.caption("有农业问题,随时来问")
st.markdown("<br>", unsafe_allow_html=True)
# ─── Quick Questions ─────────────────────────────────────────────────────────
with st.container(border=False):
cols = st.columns(len(QUICK_QUESTIONS))
for i, q in enumerate(QUICK_QUESTIONS):
with cols[i]:
if st.button(q, key=f"chip_{q}", use_container_width=True):
st.session_state.user_input = q
st.rerun()
st.markdown("<br>", unsafe_allow_html=True)
# ─── Input ───────────────────────────────────────────────────────────────────
user_input = st.text_area(
"请输入您的问题",
value=st.session_state.get("user_input", ""),
height=110,
placeholder="例如:水稻稻瘟病怎么防治?",
label_visibility="collapsed",
)
submitted = st.button("发送", type="primary")
# ─── Settings Panel ──────────────────────────────────────────────────────────
with st.expander("⚙️ 模型设置"):
col_t, col_p, col_c = st.columns(3)
with col_t:
temperature = st.slider("Temperature", 0.0, 1.0, 0.7, 0.1)
with col_p:
top_p = st.slider("Top P", 0.0, 1.0, 0.8, 0.1)
with col_c:
enable_thinking = st.checkbox("显示推理过程", value=True)
# ─── Request & Stream ────────────────────────────────────────────────────────
if submitted and user_input.strip():
try:
payload = {
"model": CHAT_MODEL,
"messages": [{"role": "user", "content": user_input.strip()}],
"temperature": temperature,
"top_p": top_p,
"presence_penalty": 1.5,
"chat_template_kwargs": {"enable_thinking": enable_thinking},
"stream": True,
}
thinking_placeholder = st.empty()
answer_placeholder = st.empty()
full_reasoning = ""
full_content = ""
with httpx.stream(
"POST", CHAT_API_URL, headers=HEADERS, json=payload, timeout=120
) as resp:
resp.raise_for_status()
for line in resp.iter_lines():
if not line.startswith("data: "):
continue
data_str = line[6:]
if data_str == "[DONE]":
break
try:
chunk = json.loads(data_str)
except json.JSONDecodeError:
continue
delta = chunk.get("choices", [{}])[0].get("delta", {})
reasoning_piece = delta.get("reasoning_content", "")
if reasoning_piece:
full_reasoning += reasoning_piece
if enable_thinking:
thinking_placeholder.info(
"**正在思考...**\n\n" + full_reasoning
)
content_piece = delta.get("content", "")
if content_piece:
full_content += content_piece
with answer_placeholder.container(border=True):
st.markdown("### 🌱 回答")
st.markdown(full_content)
if full_reasoning and enable_thinking:
thinking_placeholder.empty()
with st.expander("查看推理过程"):
st.markdown(full_reasoning)
except httpx.HTTPStatusError as e:
st.error(f"请求失败 (HTTP {e.response.status_code}): {e.response.text}")
except Exception as e:
st.error(f"请求异常: {e}")
# ─── Footer ───────────────────────────────────────────────────────────────────
st.divider()
st.caption("农业技术知识问答 · 仅供参考")