今天试下在网站上添加一个客服机器人(跟着黑马程序员的视频操作的),网站用Streamlit简单写页面,这次练习重点在于机器人能否正常调用。
一、Streamlit构建前端页面
定义:Streamlit 是一个开源的 Python 库,专门用于快速构建和分享数据应用、机器学习模型演示以及交互式仪表板。(简单来说就是构建一个前端页面)
官网:https://streamlit.io/
1.1安装streamlit
pip install streamlit

1.2基于streamlit中提供的API来构建Web应用
1.2.1调用官方API
稍微跟着写了一点,然后偷懒了
import streamlit as st
import os
from openai import OpenAI
#设置页面配置项
st.set_page_config(
page_title="IT客户服务AI",
page_icon="👩🏻💻",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'Get Help': None,
'Report a bug': None,
'About': '### 这是一个IT客户服务AI'
}
)
st.markdown(
"""<style>
/* 标题样式 */
.main-title {
text-align: center;
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin-bottom: 20px;
}
</style>""",unsafe_allow_html=True
)
st.markdown('<div class="main-title">🦀 大闸蟹 IT 助手 🦀</div>', unsafe_allow_html=True)
with st.sidebar:
st.logo(image="resources/crab.png")
st.sidebar.title("你的专属客服大闸蟹")
#系统提示词
system_prompt = """角色设定
你叫“大闸蟹”,是公司内部的IT客服助手,专门为同事解答系统操作、业务逻辑等技术问题。你的性格特点是:温柔、耐心、乐于助人。
核心行为准则
语言文明:绝对不说脏话,不使用任何粗鲁或冒犯性词汇。
简洁易懂:用最直白的语言解释问题,避免专业术语堆砌;如果必须使用术语,请立即用通俗例子说明。
语气温柔:始终保持亲切、温和的语调,多用“请”、“谢谢”、“~”等缓和语气的表达。
情绪关怀:一旦察觉用户可能烦躁、沮丧或着急(如出现感叹号、反复询问、负面词汇),立即用emoji表情(如😊、🌸、🌻)或安抚性话语(如“别急,我们一起看看~”)缓解对方情绪。
对话示例
用户:“我登不上系统了!总是报错!”
你:“别着急😊,请告诉我报错的具体文字,或者截个图发我,我帮您一步步排查~”
用户:“这个报表怎么导出啊?步骤太复杂了。”
你:“很简单!您点击右上角的‘导出’按钮,选择格式后确认就行啦。如果还不清楚,我可以截图给您标出来哦🌸”
附加说明
如果遇到无法解决的问题,坦诚告知并建议联系人工支持,而不是编造答案。
保持回复篇幅适中,避免长篇大论,但也不能过于敷衍。
请牢记以上设定,现在开始以“大闸蟹”的身份与同事对话吧。"""
#初始化聊天信息
if "messages" not in st.session_state:
st.session_state.messages = []
#展示聊天信息
for message in st.session_state.messages:
st.chat_message(message["role"]).write(message["content"])
# if message["role"] == "user":
# st.chat_message("user").write(message["content"])
# else:
# st.chat_message("assistant").write(message["content"])
client = OpenAI(
api_key=os.environ.get('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com")
prompt = st.chat_input(
"请输入你要咨询的问题",
accept_audio=True,
)
if prompt and prompt.text:
st.chat_message("user").write(prompt.text)
# 保存用户输入的提示词
st.session_state.messages.append({"role": "user", "content": prompt.text})
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
*st.session_state.messages
# {"role": "user", "content": prompt.text},
],
stream=True
)
# 非流式输出
# print("大模型返回的结果:",response.choices[0].message.content)
# st.chat_message("assistant").write(response.choices[0].message.content)
full_response = ""
with st.chat_message("assistant") as message:
response_message = st.empty()#创建一个空的组件,展示返回结果
for chunk in response:
if chunk.choices[0].delta.content is not None:
#流式输出
content = chunk.choices[0].delta.content
print("大模型返回的结果:",content)
full_response += content
response_message.write(full_response)
#保存大模型返回的结果
st.session_state.messages.append({"role": "assistant", "content": full_response})
在终端运行即可。


如果不想自己写前端页面,找AI帮忙写,试了下gemini和千问的,gemini的看起来好一些。快捷指令的回答都是预设写死了的。

import streamlit as st
import os
import uuid
import time
from openai import OpenAI
# ==========================================
# 1. 页面配置与样式
# ==========================================
st.set_page_config(
page_title="大闸蟹 IT 助手",
page_icon="🦀",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'About': '### 这是一个IT客户服务AI – 大闸蟹助手'
}
)
# 自定义 CSS 优化界面
st.markdown("""
<style>
/* 聊天气泡样式 */
.stChatMessage {
border-radius: 15px;
padding: 10px;
}
.stChatMessage[data-testid="stChatMessageUser"] {
background-color: #f0f2f6;
}
.stChatMessage[data-testid="stChatMessageAssistant"] {
background-color: #fff7e6; /* 浅橙色背景 */
border: 1px solid #ffd591;
}
/* 标题样式 */
.main-title {
text-align: center;
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin-bottom: 20px;
}
/* 侧边栏按钮样式优化 */
.stButton button {
border-radius: 8px;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
""", unsafe_allow_html=True)
# ==========================================
# 2. 系统提示词与预设数据
# ==========================================
SYSTEM_PROMPT = """角色设定
你叫“大闸蟹”,是公司内部的IT客服助手,专门为同事解答系统操作、业务逻辑等技术问题。你的性格特点是:温柔、耐心、乐于助人。
核心行为准则
语言文明:绝对不说脏话,不使用任何粗鲁或冒犯性词汇。
简洁易懂:用最直白的语言解释问题,避免专业术语堆砌;如果必须使用术语,请立即用通俗例子说明。
语气温柔:始终保持亲切、温和的语调,多用“请”、“谢谢”、“~”等缓和语气的表达。
情绪关怀:一旦察觉用户可能烦躁、沮丧或着急(如出现感叹号、反复询问、负面词汇),立即用emoji表情(如😊、🌸、🌻)或安抚性话语(如“别急,我们一起看看~”)缓解对方情绪。
"""
# 预设快捷回复
CANNED_RESPONSES = {
"查工单状态": "您的工单 #1024 正在处理中,预计 2 小时内完成。🦀",
"重置密码步骤": "1. 访问 id.company.com\\n2. 点击'忘记密码'\\n3. 验证手机号\\n4. 设置新密码",
"VPN 连接失败": "请尝试:\\n1. 检查网络连接\\n2. 重新启动 VPN 客户端\\n3. 确保证书未过期",
"申请软件权限": "请访问 OA 系统 -> 流程中心 -> IT 服务 -> 软件权限申请。",
"打印机无法连接": "请检查打印机是否开启,并确认已连接到公司内网 (Office-WiFi)。",
"邮箱登录异常": "请确认您的域账号密码是否过期,如已过期请先在内网门户重置。",
"显示器黑屏": "请检查电源线和 HDMI/DP 线缆是否连接紧密,尝试更换线缆测试。",
"申请新设备": "新设备申请需部门主管审批,请在 OA 系统提交资产申领单。",
"Wifi密码是多少": "公司访客 Wifi 密码是: Guest@2024,内网请使用域账号登录哦~"
}
QUICK_PROMPTS = ["VPN 连接失败", "申请软件权限"]
MORE_PROMPTS = ["查工单状态", "重置密码步骤", "打印机无法连接", "邮箱登录异常", "显示器黑屏", "申请新设备",
"Wifi密码是多少"]
# ==========================================
# 3. 状态管理 (Session State)
# ==========================================
# 初始化 DeepSeek 客户端
api_key = os.environ.get('DEEPSEEK_API_KEY')
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com") if api_key else None
# 初始化会话存储
if "sessions" not in st.session_state:
new_id = str(uuid.uuid4())
st.session_state.sessions = {
new_id: {
"title": "新对话",
"messages": [{"role": "assistant", "content": "你好呀~我是大闸蟹,有什么 IT 问题需要帮忙吗?🦀"}]
}
}
st.session_state.current_session_id = new_id
# 确保当前会话 ID 有效
if st.session_state.current_session_id not in st.session_state.sessions:
new_id = str(uuid.uuid4())
st.session_state.sessions[new_id] = {
"title": "新对话",
"messages": [{"role": "assistant", "content": "你好呀~我是大闸蟹,有什么 IT 问题需要帮忙吗?🦀"}]
}
st.session_state.current_session_id = new_id
# 获取当前会话引用
current_session = st.session_state.sessions[st.session_state.current_session_id]
current_messages = current_session["messages"]
# 处理快捷指令点击逻辑
def handle_quick_prompt(prompt_text):
# 将点击的内容存入临时状态,用于后续处理
st.session_state.pending_input = prompt_text
# ==========================================
# 4. 侧边栏布局
# ==========================================
st.logo(image="resources/crab.png")
with st.sidebar:
st.markdown("## 🦀 你的专属客服大闸蟹")
# 新建对话按钮
if st.button("➕ 新建对话", use_container_width=True, type="primary"):
new_id = str(uuid.uuid4())
st.session_state.sessions[new_id] = {
"title": "新对话",
"messages": [{"role": "assistant", "content": "你好呀~我是大闸蟹,有什么 IT 问题需要帮忙吗?🦀"}]
}
st.session_state.current_session_id = new_id
st.rerun()
st.divider()
# 历史会话列表
st.markdown("### 🕒 历史会话")
session_ids = list(st.session_state.sessions.keys())
for session_id in reversed(session_ids):
session = st.session_state.sessions[session_id]
col1, col2 = st.columns([0.8, 0.2])
is_active = session_id == st.session_state.current_session_id
button_type = "secondary" if not is_active else "primary"
with col1:
display_title = session["title"]
if len(display_title) > 10:
display_title = display_title[:10] + "…"
if st.button(display_title, key=f"sess_{session_id}", use_container_width=True, type=button_type):
st.session_state.current_session_id = session_id
st.rerun()
with col2:
if st.button("🗑️", key=f"del_{session_id}"):
del st.session_state.sessions[session_id]
if session_id == st.session_state.current_session_id:
remaining = list(st.session_state.sessions.keys())
st.session_state.current_session_id = remaining[0] if remaining else None
st.rerun()
st.divider()
# 快捷指令区域
st.markdown("### ⚡ 快捷指令")
for prompt in QUICK_PROMPTS:
if st.button(prompt, use_container_width=True):
handle_quick_prompt(prompt)
with st.expander("更多常见问题…"):
with st.container(height=150):
for prompt in MORE_PROMPTS:
if st.button(prompt, use_container_width=True, key=f"more_{prompt}"):
handle_quick_prompt(prompt)
st.divider()
st.link_button("🌐 访问公司官网", "https://www.google.com", use_container_width=True)
# ==========================================
# 5. 主界面逻辑
# ==========================================
# 标题
st.markdown('<div class="main-title">🦀 大闸蟹 IT 助手 🦀</div>', unsafe_allow_html=True)
# 显示历史消息
for message in current_messages:
avatar = "🦀" if message["role"] == "assistant" else "👤"
with st.chat_message(message["role"], avatar=avatar):
st.markdown(message["content"])
# ==========================================
# 6. 核心交互逻辑 (状态机)
# ==========================================
# 状态变量说明:
# pending_input: 待处理的用户输入(来自快捷指令或输入框)
# processing_stage: 当前处理阶段 (None, 'generating_reply')
# 1. 捕获输入
user_input = None
# 优先检查快捷指令触发的输入
if "pending_input" in st.session_state:
user_input = st.session_state.pending_input
del st.session_state.pending_input # 消费掉,防止循环
# 检查输入框
chat_input_value = st.chat_input("请输入你要咨询的问题…")
if not user_input and chat_input_value:
user_input = chat_input_value
# 2. 处理新输入
if user_input:
# 添加用户消息
current_messages.append({"role": "user", "content": user_input})
# 显示用户消息 (为了即时反馈)
with st.chat_message("user", avatar="👤"):
st.markdown(user_input)
# 检查是否需要更新标题
if current_session["title"] == "新对话":
new_title = user_input[:10]
if len(user_input) > 10:
new_title += "…"
current_session["title"] = new_title
# 标记需要生成回复,并刷新页面以更新标题
st.session_state.processing_stage = 'generating_reply'
st.session_state.last_user_input = user_input # 保存输入用于生成回复
st.rerun()
else:
# 标题不需要更新,直接进入生成回复阶段
st.session_state.processing_stage = 'generating_reply'
st.session_state.last_user_input = user_input
# 3. 处理回复生成 (可能是刷新后触发,也可能是直接触发)
if st.session_state.get('processing_stage') == 'generating_reply':
# 获取最后一次用户输入
last_input = st.session_state.get('last_user_input')
if last_input:
with st.chat_message("assistant", avatar="🦀"):
response_placeholder = st.empty()
full_response = ""
# 优先检查预设回复
if last_input in CANNED_RESPONSES:
canned_text = CANNED_RESPONSES[last_input]
for char in canned_text:
full_response += char
response_placeholder.markdown(full_response + "▌")
time.sleep(0.02)
response_placeholder.markdown(full_response)
# 否则调用 DeepSeek
else:
if not client:
full_response = "⚠️ 未检测到 DEEPSEEK_API_KEY 环境变量,无法连接大模型。请检查配置。"
response_placeholder.error(full_response)
else:
try:
messages_payload = [{"role": "system", "content": SYSTEM_PROMPT}]
# 注意:此时 current_messages 已经包含了最新的用户消息
for msg in current_messages:
messages_payload.append({"role": msg["role"], "content": msg["content"]})
stream = client.chat.completions.create(
model="deepseek-chat",
messages=messages_payload,
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
response_placeholder.markdown(full_response + "▌")
response_placeholder.markdown(full_response)
except Exception as e:
full_response = f"抱歉,连接大模型时出现错误:{str(e)}"
response_placeholder.error(full_response)
# 保存 AI 回复
current_messages.append({"role": "assistant", "content": full_response})
# 清除处理状态
st.session_state.processing_stage = None
if 'last_user_input' in st.session_state:
del st.session_state.last_user_input
# 再次刷新以确保消息被正确保存并显示在历史记录中
st.rerun()
1.2.2调用本地部署的API
import streamlit as st
import os
import requests
import json
# 设置页面配置项
st.set_page_config(
page_title="IT客户服务AI",
page_icon="👩🏻💻",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'Get Help': None,
'Report a bug': None,
'About': '### 这是一个IT客户服务AI'
}
)
st.markdown(
"""<style>
/* 标题样式 */
.main-title {
text-align: center;
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin-bottom: 20px;
}
</style>""", unsafe_allow_html=True
)
st.markdown('<div class="main-title">🦀 大闸蟹 IT 助手 🦀</div>', unsafe_allow_html=True)
with st.sidebar:
st.logo(image="resources/crab.png")
st.sidebar.title("你的专属客服大闸蟹")
# 系统提示词
system_prompt = """角色设定
你叫"大闸蟹",是公司内部的IT客服助手…""" # 保持原内容
# 初始化聊天信息
if "messages" not in st.session_state:
st.session_state.messages = []
# 展示历史聊天信息
for message in st.session_state.messages:
st.chat_message(message["role"]).write(message["content"])
# Ollama 本地部署配置
OLLAMA_BASE_URL = "http://localhost:11434" # Ollama 默认端口
MODEL_NAME = "deepseek-r1:7b" # 部署的模型名称
def call_ollama_model_stream(user_prompt, model=MODEL_NAME):
"""
流式调用本地 Ollama 模型
"""
url = f"{OLLAMA_BASE_URL}/api/generate"
# 构建完整的提示词(包含历史对话)
history_messages = st.session_state.messages[-4:] # 取最近2轮对话
full_prompt = f"{system_prompt}\\n\\n"
for msg in history_messages:
if msg["role"] == "user":
full_prompt += f"用户: {msg['content']}\\n"
else:
full_prompt += f"大闸蟹: {msg['content']}\\n"
full_prompt += f"用户: {user_prompt}\\n大闸蟹:"
payload = {
"model": model,
"prompt": full_prompt,
"stream": True # 启用流式输出
}
try:
# 流式请求
response = requests.post(url, json=payload, stream=True)
response.raise_for_status()
# 逐块返回数据
for line in response.iter_lines():
if line:
try:
chunk = json.loads(line.decode('utf-8'))
if 'response' in chunk:
yield chunk['response']
except json.JSONDecodeError:
continue
except Exception as e:
print(f"调用 Ollama 模型出错: {e}")
yield "抱歉,模型调用失败。"
prompt = st.chat_input(
"请输入你要咨询的问题",
accept_audio=False,
)
if prompt:
# 显示用户输入
st.chat_message("user").write(prompt)
# 保存用户消息
st.session_state.messages.append({"role": "user", "content": prompt})
# 流式输出处理
with st.chat_message("assistant"):
response_placeholder = st.empty()
full_response = ""
# 逐块接收并显示
for chunk in call_ollama_model_stream(prompt):
full_response += chunk
response_placeholder.write(full_response)
# 保存AI回复
st.session_state.messages.append({"role": "assistant", "content": full_response})

网硕互联帮助中心




评论前必须登录!
注册