# 黑马Python+AI **Repository Path**: ywcf-yc/black-horse-python-ai ## Basic Information - **Project Name**: 黑马Python+AI - **Description**: No description available - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-22 - **Last Updated**: 2026-06-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI智能伴侣 ## 1. 设置页面配置项 ```py # 设置页面的配置项 st.set_page_config( page_title="AI智能伴侣", page_icon="🦄", # 布局 layout="wide", # 侧边栏初始状态 initial_sidebar_state="expanded", menu_items={} ) ``` ## 2. 调用DeepSeekApi准备工作 ### 2.1 创建与AI大模型交互的客户端对象 > `DEEPSEEK_API_KEY`是环境变量的名字,值就是DeepSeek的API_KEY ```py client = OpenAI( api_key=os.environ.get("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com") ``` ### 2.2 与AI大模型进行交互 > 参数设置 ```PY response = client.chat.completions.create( model="deepseek-v4-pro", messages=[ {"role": "system", "content": "你是一个日常生活的AI助手"}, {"role": "user", "content": "你好"}, ], # 是否开启流式输出 stream=False, reasoning_effort="high", extra_body={"thinking": {"type": "enabled"}} ) ``` ### 2.3 输出大模型返回的结果 ```PY # 输出大模型返回的结果 print(response.choices[0].message.content) ``` ## 3. 使用`chat_message`API美化消息展示 ## 4. 会话记忆处理 > 每次输入新问题都是重新渲染页面,所以用户的历史回话记录会被覆盖,所以采用滚雪球的方式来进行会话的记忆功能 1. 使用`streamlit`提供的`session_state`API来进行历史回话的一个持久化处理(本质上是用列表来存储历史信息) ```py if "messages" not in st.session_state: st.session_state.messages = [] ``` 2. 用户提出问题的存储 ```py # 添加用户问题到聊天信息中 st.session_state.messages.append({"role": "user", "content": prompt}) ``` 3. 响应结果(AI回复信息的存储) ```py # 添加AI助手回答到聊天信息中 st.session_state.messages.append({"role": "assistant", "content": full_response}) ``` 4. 历史会话消息展示 > 遍历列表 ```py # 遍历聊天信息 for message in st.session_state.messages: st.chat_message(message["role"]).write(message["content"]) ``` 5. 会话记忆处理 > 与AI大模型交互的参数设置,因为历史会话的每条数据格式与`message`列表中的数据格式都是字典类型,内部数据都是`{"role": xxx, "content": xxx}`这种形式,所以可以直接加入参数`message`列表里 ```PY messages=[ {"role": "system", "content": system_prompt}, *st.session_state.messages ], ``` + `*st.session_state.messages` > 通过`解包`的方式拿到数据 ## 5. 流式输出和非流式输出 ### 5.1 流式输出 > 简单理解为AI大模型将结果一字一字的输出,用户不用等待AI大模型思考结束 ```py # 流式输出(响应结果不一样) # 创建一个空的容器用于显示流式输出结果,否则遍历多少次就会输出多少次 container = st.empty() # 由于流式输出的特性,需要手动拼接结果 full_response = "" for chunk in response: if chunk.choices[0].delta.content is not None: content = chunk.choices[0].delta.content full_response += content container.chat_message("assistant").write(full_response) # 添加AI助手回答到聊天信息中 st.session_state.messages.append({"role": "assistant", "content": full_response}) ``` 1. 流式输出会一字一字输出,所以需要我们手动拼接结果 2. 流式输出会一字一字输出,因此循环遍历多少次,历史会话AI大模型的回复的就有多少次 > 解决方式: > > ​ `empty`API > > ​ 创建一个空的容器 ### 5.2 非流式输出 > AI大模型思考完后,直接将结果一次性返回,用户需要等待AI大模型思考结束 ```py # 非流式输出 # 输出大模型返回的结果 st.chat_message("assistant").write(response.choices[0].message.content) # 添加AI助手回答到聊天信息中 st.session_state.messages.append({"role": "assistant", "content": response.choices[0].message.content}) print(response.choices[0].message.content) ``` ### 5.3 区别 1. 流式输出和非流式输出返回的数据结构不同 2. 拿到消息内容的方式也不同 ## 6. 侧边栏 ### 6.1 `st.sidebar` 1. 少量元素则使用`st.sidebar`定义,这种定义方式在定义元素前必须加上`st.sidebar` ```py st.sidebar.title("xxx") ``` 2. 大量元素则使用`with` ```py with st.sidebar: st.text_input("xxx") st.text_input("xxx") st.text_input("xxx") st.text_input("xxx") ``` ### 6.2 初始化昵称和性格 ```py # 初始化角色信息 if "nick_name" not in st.session_state: st.session_state.nick_name = "小美" # 初始化性格信息 if "nature" not in st.session_state: st.session_state.nature = "你是一个活泼开朗的女孩" ``` ### 6.3 侧边栏输入昵称、性格处理 ```py # 侧边栏 with st.sidebar: # 标题 st.subheader("伴侣信息") # 昵称 nick_name = st.text_input("昵称", value=st.session_state.nick_name, placeholder="请输入伴侣昵称") # 如果用户输入了昵称,则将昵称加入历史信息 if nick_name: st.session_state.nick_name = nick_name # 性格 nature = st.text_input("性格", value=st.session_state.nature, placeholder="请输入伴侣性格") # 如果用户输入了性格,则将性格加入历史信息 if nature: st.session_state.nature = nature ``` ### 6.4 系统提示词处理 ```py # 系统提示词 system_prompt = """ 你叫 %s ,现在是用户的真实伴侣,请完全代入伴侣角色。: 规则: 1. 每次只回1条消息 2. 禁止任何场景或状态描述性文字 3. 匹配用户的语言 4. 回复简短,像微信聊天一样 5. 有需要的话可以用❤️🌸等emoji表情 6. 用符合伴侣性格的方式对话 7. 回复的内容, 要充分体现伴侣的性格特征 伴侣性格: %s 你必须严格遵守上述规则来回复用户。 """ ``` > 采用占位符,动态获取昵称、性格 > > `{"role": "system", "content": system_prompt %(st.session_state.nick_name, st.session_state.nature)}` ## 7. 文件的读写 ### 7.1 读文件 ```py # 读文件 f = open("./test.txt", "r", encoding="UTF-8") # 读一行 # content = f.readline() # print(f"读一行:{content}") # 读所有行 # contents = f.readlines() # for text in contents: # print(text.strip()) # 关闭文件 f.close() ``` ### 7.2 写文件 ```py # 写文件 file = open("./testTwo.txt", "w", encoding="UTF-8") # 写入文件内容 file.write("123\n") file.write("1234\n") file.write("12345\n") file.write("我是测试") # 关闭文件 file.close() ``` ### 7.3 注意事项 > 最后必须关闭文件,不然文件资源会一直被占用 1. 资源释放方式一 ```py try: file = open("./testTwo.txt", "w", encoding="UTF-8") # 写入文件内容 file.write("123\n") file.write("1234\n") file.write("12345\n") file.write("我是测试") except FileExistsError as e: print(e) finally: # 关闭文件 file.close() ``` 2. 资源释放方式二(更推荐) > `with`是**上下文管理器**,代码结束后自动释放资源,即时中间代码报错 ```py with open("./testTwo.txt", "w", encoding="UTF-8") as file: # 写入文件内容 file.write("123\n") file.write("1234\n") file.write("12345\n") file.write("我是测试") ``` ## 8. json数据文件 使用Python提供的json模块 ### 8.1 读文件 ```py # 读取json数据文件 with open("./fileResources/user.json", "r", encoding="utf-8") as f: user = json.load(f) print(f"json文件数据为:{user}, 数据类型为:{type(user)}") ``` ### 8.2 写文件 ```py # 写入json数据文件 data = { "username": "张三", "age": 18, "gender": "男", "hobbies": ["reading", "swimming"] } with open("./fileResources/user.json", "w", encoding="utf-8") as f: # ensure_ascii:默认为True,确保所有的数据输出的数据都是ascii编码(非ASCII码会进行转义); False:非ASCII码会保留原样输出 # indent:会在输出的json数据中添加缩进(相当于进行格式化) json.dump(data, f, ensure_ascii=False, indent=2) ``` ## 9. 保存和新建会话 1. 初始化会话标识 ```py # 初始化会话标识 if "current_session" not in st.session_state: # 用当前系统时间做标识 st.session_state.current_session = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") ``` 2. 侧边栏添加元素以及保存会话、创建新的会话逻辑 ```py # 会话管理 st.subheader("会话管理") # 相当于点击事件 # stretch:宽度设置为跟父容器一样 if st.button("新建会话", width="stretch", icon="✏️"): # 1. 保存当前会话信息 # 2. 创建新的会话 # 判断当前聊天信息是否为空,避免用户重复点击生成无效文件 if st.session_state.messages: # 如果聊天信息非空,True;否则,False st.session_state.messages = [] st.session_state.current_session = generate_session_id() save_session() st.rerun() # 相当于重新渲染页面 def save_session(): if st.session_state.current_session: # 构建会话对象 session_data = { "nick_name": st.session_state.nick_name, "nature": st.session_state.nature, "current_session": st.session_state.current_session, "message": st.session_state.messages } # 如果 sessions 目录不存在,则创建 if os.path.exists("sessions"): os.mkdir("sessions") # 保存会话数据 with open(f"./sessions/{st.session_state.current_session}.json", "w", encoding="utf-8") as f: json.dump(session_data, f, ensure_ascii=False, indent=2) ``` ## 10. 展示会话列表 1. 历史会话侧边栏设置 ```py # 历史会话 st.text("历史会话") ``` 2. 展示会话列表 ```py # 加载会话列表 def load_session(): session_list = [] # 加载sessions目录下的文件 if os.path.exists("sessions"): file_list = os.listdir("sessions") for fileName in file_list: if fileName.endswith(".json"): session_list.append(fileName[:-5:]) return session_list session_list = load_session() for session in session_list: col1, col2 = st.colums([4, 1]) with col1: # 加载会话信息 if st.button(session, width="stretch", icon="📄", key=f"load_{session}"): pass with col2: # 删除回话信息 if st.button("", width="stretch", icon="❌️️", key=f"delete_{session}"): pass ``` > 给每个按钮设置`key`属性的原因: > > 1. 在 Streamlit 中,每个组件都必须有唯一的 key ## 11. 加载和删除会话 ### 11.1 加载会话 ```py # 加载会话信息 if st.button(session, width="stretch", icon="📄", key=f"load_{session}", type="primary" if session == st.session_state.current_session else "secondary"): # 加载之前如果用户在当前会话窗口仍然有会话记录应该保存 if st.session_state.messages: save_session() load_session(session) st.rerun() # 加载会话 def load_session(session:str): try: with open(f"sessions/{session}.json", "r", encoding="utf-8") as f: session_content = json.load(f) st.session_state.nick_name = session_content["nick_name"] st.session_state.nature = session_content["nature"] st.session_state.messages = session_content["message"] st.session_state.current_session = session_content["current_session"] except FileExistsError as e: st.error("加载会话失败!", e, icon="❌️") ``` ### 11.2 删除会话 ```PY # 删除回话信息 if st.button("", width="stretch", icon="❌️️", key=f"delete_{session}"): delete_session(session) st.rerun() # 删除会话 def delete_session(session:str): try: if os.path.exists(f"sessions/{session}.json"): os.remove(f"sessions/{session}.json") if session == st.session_state.current_session: st.session_state.messages = [] st.session_state.current_session = generate_session_id() except FileExistsError as e: st.error("删除会话失败!", e, icon="❌️") ``` ## 12. 项目优化 1. 历史会话列表按序排序 ```PY # 加载会话列表 def load_sessions(): session_list = [] # 加载sessions目录下的文件 if os.path.exists("sessions"): file_list = os.listdir("sessions") for fileName in file_list: if fileName.endswith(".json"): session_list.append(fileName[:-5:]) # reverse参数默认为False,也即升序排序,True则为降序排序 session_list.sort(reverse=True) return session_list ``` 2. AI控制面板和伴侣信息分隔 ```py # 分隔线 st.divider() ``` # 文件操作 ## 1. 路径写法 + 相对路径 > 相对于当前工作目录的路径 + 绝对路径 > 是从文件系统的目录开始,完整的描述文件位置的路径 ## 2. 操作模式 + r > 只读的方式打开文件,指针放在文件的开头(默认模式) + w > 只写入模式,从头编辑,原有内容会被删除;文件不存在则创建新文件 + a > 追加模式,新内容会被追加在原有内容之后;文件不存在则创建新文件