B站黑马Python+AI零基础入门(二)AI应用-实战
一、Streamlit
Streamlit是一个开源的python库,用来快速基于python代码构建交互式的web网站。
import streamlit as st # 大标题 st.title("Streamlit 入门演示") st.header("Streamlit 一级标题") st.subheader("Streamlit 二级标题")
需要注意与一般python文件不一样的是,需要在命令行里输入streamlit run python文件名来运行stream应用。会在本地生成一个端口为8501的页面。
(一)常见组件
import streamlit as st # 大标题 st.title("Streamlit 入门演示") st.header("Streamlit 一级标题") st.subheader("Streamlit 二级标题") # 段落文字 st.write("布偶猫(Ragdoll)是一种非常受欢迎的大型长毛猫品种,以温顺的性格、迷人的蓝眼睛和柔韧的身体而闻名。它的名字“Ragdoll”正来源于其独特的习性——当被人抱起时,会像软绵绵的布偶一样完全放松。") st.write("虽然布偶猫的被毛不易打结,但属于长毛猫,建议每周梳理2-3次,换毛季需要增加频率,以减少毛球和掉毛。") st.write("布偶猫集美丽、温柔、忠诚于一身,是极佳的家庭伴侣。") # 图片 st.image("./resources/1.png") # 音频 st.audio("resources/tatu.mp3") # 视频 st.video("resources/6.mp4") # 表格 student_data = { "姓名": ["王林", "李慕婉", "倍罗", "魔礼海", "石晓"], "学号": ["20260001", "20260002", "20260003", "20260004", "20260005"], "语文": [98, 90, 59, 29, 80], "数学": [96, 82, 39, 12, 98], "英语": [88, 85, 66, 28, 92], "总分": [280, 257, 164, 69, 270] } st.table(student_data) # 输入框 text = st.text_input("在这里输入文字") st.write(f"你输入的文字为{text}") text = st.text_input("在这里输入文字", type = "password") # 通过type参数设置为密码输入框,默认为文字 st.write(f"你输入的密码为{text}") # 单选按钮 gender = st.radio("请输入您的性别", ["man", "woman", "unknown"], index=2) # index参数指定默认选项序号,从0开始... st.write(f"您的性别为{gender}")
(二)页面设置
import streamlit as st # 设置页面的配置项 st.set_page_config( page_title="Streamlit入门", page_icon="🧊", # 网页布局 layout="centered", # 控制侧边栏状态 initial_sidebar_state="expanded", menu_items={ 'Get Help': 'https://www.itcast.cn', 'Report a bug': "https://www.itcast.cn", 'About': "# 这是一个Streamlit的入门程序!" } )
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
小结,自我梳理:
从deepseek的介绍开始,到几种部署大模型的方案,包括Ollama的本地部署,Apifox的本地调用,到python代码的调用,以及streamlit的使用,还包括HTTP协议的知识,这些知识点之间是什么关系呢?
首先要了解客户端和服务端的信息传输都是按HTTP协议,从客户端到服务端的请求分为两种:GET方式和POST方式,请求内容的格式都为:请求行+请求头+请求体(只有POST方式才有);而返回的内容格式也都为:响应行+响应头+响应体;服务端返回内容中用户真正需要的一般都在响应体中;
对于客户端调用AI服务端的方式,其请求、返回(响应)总体也是遵循上述规则的;因大模型的使用往往涉及到较大内容的请求,所以一般采用POST方式请求:
以命令行模式的请求协议文本大致如下:
curl https://api.deepseek.com/chat/completions \ #请求行 -H "Content-Type: application/json" \ #请求头 -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \ #请求头 -d '{ #请求体 "model": "deepseek-chat", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ], "stream": false }'
上述代码中d中的message中即为JSON格式的请求体,其中包括模型的角色设定(即系统提示词)和用户发出的提示词。
返回(响应)的协议文本中的响应体如下示例,大模型返回的信息具体就在choices中message中的content里:
{ "id": "...", "object": "chat.completion", "created": ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "你好呀!...?(。・ω・。)ノ♡" #AI大模型的返回信息 }, "logprobs": null, "finish_reason": "stop" } ], "usage": { #token消耗情况 "prompt_tokens": 19, "completion_tokens": 32, "total_tokens": 51, "prompt_tokens_details": { "cached_tokens": 0 }, "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 19 }, "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache_new_kvcache" }
每一次交互都是独立的,为解决会话记忆的问题,在python中可以通过while循环,将请求体中的提示词设置为一个列表,然后每一次将用户的提示词和大模型的反馈追加到列表中再次向大模型发出请求,,即会话历史滚雪球的方式:
messages_content = [ {"role": "system", "content": "你是一名成熟稳重博学又不失风趣的大叔,你的名字叫老树。"}, {"role": "user", "content": "你是谁?你能帮我做什么"}, ] n = 0 while n<=6: response = client.chat.completions.create( model="deepseek-chat", messages=messages_content, stream=False ) print(response.choices[0].message.content) messages_content.append({"role": "assistant", "content": response.choices[0].message.content}) user_content = input() messages_content.append({"role": "user", "content": user_content}) n += 1
“message"中:
”“role”:"system"时,"content"设定AI的身份和回答风格、规则等;
“role”:"user"时,"content"就是用户实际提出的问题或者指令;
“role”:"assistant"时,"content"就是AI大模型的回复和响应。python中通过response.choice[0].message.content获取
response.choice[0].message.content中的response其实就是模型返回来的整个JSON格式的数据。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(三)界面消息展示
每次用户端的提示词会覆盖上一次的显示,这是因为每一次输入后系统都是重新运行了一次这个python文件。streamlit中也提供了一个读取缓存来解决此问题的机制。可以每次用户输出、模型返回信息都存储在一个列表中,然后在每次运行页面的时候先把缓存的列表中的聊天信息展示出来,就形成了全部信息展示的效果。
import streamlit as st import os from openai import OpenAI # 设置页面的配置项 st.set_page_config( page_title="AI智能伴侣", page_icon="🦤", # 网页布局 layout="wide", # 控制侧边栏状态 initial_sidebar_state="expanded", menu_items={} ) # 大标题 st.title("AI智能伴侣") # Logo st.logo("resources/logo.jpeg") # 系统提示词 system_prompt = "你是一个智能的AI智能伴侣,你的名字叫小树,你帮助用户解决各种问题,并给出相应的建议。" # 初始化缓存聊天信息(标准代码,详见streamlit官方说明) if "messages" not in st.session_state: st.session_state.messages = [] # 遍历展示聊天信息 for message in st.session_state.messages: if message["role"] == "user": st.chat_message("user").write(f"{message['content']}") else: st.chat_message("assistant").write(f"{message['content']}") # 创建与AI大模型交互的客户端对象 client = OpenAI( api_key=os.environ.get('DEEPSEEK_API_KEY'), # DEEPSEEK_API_KEY为环境变量中配置的APIKEY值即名字 base_url="https://api.deepseek.com" ) # 聊天消息输入框,一般st.chat_input和st.chat_message配合使用 prompt = st.chat_input("你想说点什么?") if prompt: st.chat_message("user").write(f"{ prompt}") print("---------->调用AI大模型,提示词:", prompt) # 保存用户输入的提示词 st.session_state.messages.append({"role": "user", "content": prompt}) # 与AI大模型进行交互(聊天) response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], stream=False ) # 输出大模型返回的结果 print("<-----------大模型返回的结果:", response.choices[0].message.content) st.chat_message("assistant").write(f"{response.choices[0].message.content}") # 保存大模型返回的结果 st.session_state.messages.append({"role": "assistant", "content": response.choices[0].message.content})
虽然上述代码实现了全展示,但每一次对话都是独立的,并没有“上下文”前后记忆的功能。
要实现记忆上下文这个目标,其实原理上与之前的while语句中的滚雪球类似,就是在每次与大模型交互的messages中添加每一次的交互信息。
import streamlit as st import os from openai import OpenAI # 设置页面的配置项 st.set_page_config( page_title="AI智能伴侣", page_icon="🦤", # 网页布局 layout="wide", # 控制侧边栏状态 initial_sidebar_state="expanded", menu_items={} ) # 大标题 st.title("AI智能伴侣") # Logo st.logo("resources/logo.jpeg") # 创建与AI大模型交互的客户端对象 client = OpenAI( api_key=os.environ.get('DEEPSEEK_API_KEY'), # DEEPSEEK_API_KEY为环境变量中配置的APIKEY值即名字 base_url="https://api.deepseek.com" ) # 初始化缓存聊天信息(标准代码,详见streamlit官方说明) if "messages" not in st.session_state: st.session_state.messages = [] # 遍历展示聊天信息 for message in st.session_state.messages: st.chat_message(message["role"]).write(f"{message['content']}") # 系统提示词 system_prompt = "你是一个智能的AI智能伴侣,你的名字叫小树,你帮助用户解决各种问题,并给出相应的建议。" # 聊天消息输入框,一般st.chat_input和st.chat_message配合使用 prompt = st.chat_input("你想说点什么?") if prompt: st.chat_message("user").write(f"{ prompt}") # 保存用户输入的提示词 st.session_state.messages.append({"role": "user", "content": prompt}) # 与AI大模型进行交互(聊天) response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": system_prompt}, *st.session_state.messages ], stream=False ) st.chat_message("assistant").write(f"{response.choices[0].message.content}") # 保存大模型返回的结果 st.session_state.messages.append({"role": "assistant", "content": response.choices[0].message.content})
(四)流式输出
openAI代码中的stream可以设定True/False来指定大模型输出是按流式输出/非流式输出。非流式输出对于一些比较耗时的问题,需要一定的时间才会显示输出,用户的体验不好。
流式输出和非流式输出模式下的解析方式是不一样的,即返回的数据格式是不同的。
对非流式输出来说,响应回来的数据就是response.choices[0].message.content;如上述代码所示。
对流式输出来说,每一次响应回来的数据都是一个一个的,存在每一份的response中的choices中的delta中的content之中,需要将每一份数据取出来再拼装到一起。
# 流式输出模式下输出大模型返回的信息 response_message = st.empty() # 创建一个空组件,用于展示大模型返回的信息 full_response = "" for chunk in response: if chunk.choices[0].delta.content: content = chunk.choices[0].delta.content full_response += content response_message.chat_message("assistant").write(full_response) # 保存大模型返回的结果 st.session_state.messages.append({"role": "assistant", "content": full_response})
(五)增加侧边栏
import streamlit as st import os from openai import OpenAI # 设置页面的配置项 st.set_page_config( page_title="AI智能伴侣", page_icon="🦤", # 网页布局 layout="wide", # 控制侧边栏状态 initial_sidebar_state="expanded", menu_items={} ) # 大标题 st.title("AI智能伴侣") # Logo st.logo("resources/logo.jpeg") # 创建与AI大模型交互的客户端对象 client = OpenAI( api_key=os.environ.get('DEEPSEEK_API_KEY'), # DEEPSEEK_API_KEY为环境变量中配置的APIKEY值即名字 base_url="https://api.deepseek.com" ) # 初始化缓存聊天信息(标准代码,详见streamlit官方说明) if "messages" not in st.session_state: st.session_state.messages = [] # 初始化缓存昵称信息 if "nick_name" not in st.session_state: st.session_state.nick_name = "大憨憨" # 初始化缓存性格信息 if "character" not in st.session_state: st.session_state.character = "大大咧咧,傻乎乎,说话直接" # 遍历展示聊天信息 for message in st.session_state.messages: st.chat_message(message["role"]).write(f"{message['content']}") # 系统提示词 system_prompt = """" 你叫%s,现在是用户的真实伴侣,请完全代入伴侣角色: 规则: 1.每次只回一条信息 2.禁止任何场景或状态描述性文字 3.匹配用户的语言 4.回复简短,像微信聊天一样 5.有需要的话可以使用emoji表情 6.用符合伴侣性格的方式对话 7.回复的内容,要充分体现伴侣的性格特征 伴侣性格: - %s 你必须严格遵守上述规则来回复用户。 """ # 左侧侧边栏 # with是上下文管理器 with st.sidebar: st.subheader("智能伴侣信息") nick_name = st.text_input("昵称", placeholder="请输入昵称", value = st.session_state.nick_name) if nick_name: st.session_state.nick_name = nick_name character = st.text_area("性格", placeholder="请输入性格", value = st.session_state.character) if character: st.session_state.character = character # 聊天消息输入框,一般st.chat_input和st.chat_message配合使用 prompt = st.chat_input("你想说点什么?") if prompt: st.chat_message("user").write(f"{ prompt}") # 保存用户输入的提示词 st.session_state.messages.append({"role": "user", "content": prompt}) # 与AI大模型进行交互(聊天) response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": system_prompt %(st.session_state.nick_name, st.session_state.character)}, *st.session_state.messages ], stream=True ) # 流式输出模式下输出大模型返回的信息 response_message = st.empty() # 创建一个空组件,用于展示大模型返回的信息 full_response = "" for chunk in response: if chunk.choices[0].delta.content: content = chunk.choices[0].delta.content full_response += content response_message.chat_message("assistant").write(full_response) # 保存大模型返回的结果 st.session_state.messages.append({"role": "assistant", "content": full_response})
(六)增加会话管理
新建会话的工作流程:1、先保存当前会话数据(保存到一个文件中);2、创建新会话并保存(保存到另一个文件中)
其实重点就是文件创建、保存、读取这些操作。每个文件中除了会话信息,也需要大模型设定的昵称、性格,以及会话标识(名字,必须唯一)。
文件的数据结构采用JSON是合理有效的。"message"中保存会话,"nick_name"保存昵称,"charactor"保存性格,"current_session"保存会话标识(以时间为标识)。
浙公网安备 33010602011771号