Python AI应用开发实训知识点总结
·
一、数据容器复习(Day05)
1.1 列表 list
基本概念
# 数据容器:一种可以容纳多份数据的数据类型
# 每个元素可以是任意类型的数据
# 列表特点:
# 1. 可以存储不同类型的元素
# 2. 元素有序、可以重复、元素可以修改
# 3. 索引:正向从 0 开始,反向从 -1 开始
切片操作
# 语法:序列数据 [开始索引:结束索引:步长]
s = ['A','B','C','D','E','F']
print(s[0:5:1]) # ['A', 'B', 'C', 'D', 'E'] 等价于 s[:5]
print(s[0:5:2]) # ['A', 'C', 'E']
print(s[0:-2:2]) # 截取时不包括最后一个
print(s[:-4]) # 去掉最后 4 个元素
常用方法
s = [56, 90, 88, 65, 90, 100, 209, 72, 145]
# append() - 追加元素
s.append(10086)
# insert() - 插入元素
s.insert(0, 92)
# remove() - 移除第一个匹配值
s.remove(75)
# pop() - 删除元素
s.pop(2) # 删除索引 2 的元素
s.pop() # 默认删除最后一个
# sort() - 排序(元素类型需一致)
s.sort()
# reverse() - 反转
s.reverse()
二、AI应用概述与大模型部署(Day06)
2.1 网络基础知识
IP 地址与域名
# IP 地址:唯一定位互联网设备
# 127.0.0.1 = localhost (本机地址/本地回环地址)
# 110.242.69.21 = www.baidu.com
# 端口号:0-65535
# HTTP 默认端口:80
# HTTPS 默认端口:443
TCP/IP 四层网络模型
1. 应用层
2. 传输层
3. 网络层
4. 网络接口层
HTTP 协议特点
- 超文本传输协议
- 基于文本的协议
- 基于请求响应模型
- 无状态
2.2 HTTP 请求方式
GET vs POST
# GET请求:
# - 请求参数在请求行中,没有请求体
# - 如:/api/courses?name=Python&status=1
# - 请求参数大小有限制
# POST请求:
# - 请求参数在请求体中
# - 请求大小无限制
HTTP 请求/响应格式
# 请求格式:
# 1. 请求行(请求方式、资源路径)
# 2. 请求头(key:value)
# 3. 请求体(POST 方式)
# 响应格式:
# 1. 响应行(状态码)
# 2. 响应头(key:value)
# 3. 响应体
2.3 JSON 数据格式
JSON 基本语法
{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": false
}
JSON 数据类型
# 对象:{} 表示,键值对形式,键必须是双引号字符串
# 数字:整数和小数,如 12、3.14
# 字符串:双引号括起来,如 "jack"
# 布尔:true 或 false
# 列表:[] 表示,如 [1, 2, 3]
2.4 大模型消息交互格式
messages 结构
messages = [
# system: 设定 AI 的身份和行为准则
{"role": "system", "content": "你是一名可爱的 AI 助手..."},
# user: 用户提出的问题或指令
{"role": "user", "content": "你是谁"},
# assistant: AI 模型的回复
{"role": "assistant", "content": "我是小甜甜..."}
]
2.5 会话记忆方案(滚雪球模式)
无状态特性
# 与 AI 大模型的交互本质是无状态的
# 每一次请求响应都是相互独立的
# DeepSeek 官方网址有会话记忆能力
滚雪球实现方案
# 第一轮
messages = [
{"role": "system", "content": "你是一名可爱的 AI 助手..."},
{"role": "user", "content": "12 个苹果,3 个人怎么均分"}
]
# 第二轮(添加上一轮对话)
messages = [
{"role": "system", "content": "你是一名可爱的 AI 助手..."},
{"role": "user", "content": "12 个苹果,3 个人怎么均分"},
{"role": "assistant", "content": "嘻嘻,每个人可以分到 4 个苹果"},
{"role": "user", "content": "那 2 个人呢?"}
]
# 第三轮(继续累加)
messages = [
{"role": "system", "content": "你是一名可爱的 AI 助手..."},
{"role": "user", "content": "12 个苹果,3 个人怎么均分"},
{"role": "assistant", "content": "嘻嘻,每个人可以分到 4 个苹果"},
{"role": "user", "content": "那 2 个人呢?"},
{"role": "assistant", "content": "哎呀,2 个人的话每个人 6 个"},
{"role": "user", "content": "那 4 个人呢?"}
]
2.6 大模型部署方案
方案 1:Ollama本地部署
# 使用 curl 调用本地 Ollama 服务
curl http://localhost:11434/api/chat -d '{
"model": "gemma3",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
]
}'
URL 组成部分
| 组成部分 | 含义 |
|---|---|
| http:// | 通信协议(未加密) |
| localhost | 本地主机(127.0.0.1) |
| 11434 | Ollama 默认端口 |
| /api/chat | 聊天交互 API 端点 |
特点:
- 只对本地运行的 Ollama 服务生效
- 外网无法访问
- 不需要充值/付费
- 模型跑在自己电脑上
- 也需要手动实现消息滚雪球
方案 2:DeepSeek 官方 API
- 需要 API Key
- 通过互联网访问
- 按使用量计费
- 自带会话记忆功能
三、提示词工程(Day07)
3.1 核心概念
Prompt(提示词)
- 是引导大模型(LLM)进行内容生成的命令
- 可以是一句话、一个问题等
Prompt Engineering(提示词工程)
- 通过有技巧地编写提示词
- 使大模型生成尽可能符合预期的内容
- 是一个持续性的过程
3.2 提示词设计六要素
01 定角色
# 给大模型设定角色与能力
"你是一名经验丰富的高中历史老师,擅长生动有趣的讲解复杂历史事件"
02 给任务
# 明确核心请求与任务
"请向一位高中生解释法国大革命爆发的主要原因"
03 拆步骤
# 按步骤拆解复杂任务
"请先概述背景,然后分政治、经济、思想三个方面阐述,每点配一个例子"
04 定风格
# 指定语气与风格
"请使用简洁、口语化、充满热情的语气(避免使用过于学术的术语)"
05 明格式
# 明确要求输出格式
"请严格按照如下结构输出:..."
06 举例子
# 提供输入输出的示例
"可以参考《人类群星闪耀时》的叙事风格。请不要列出长的日期列表"
3.3 总结模板
定角色 → 给任务 → 提要求
四、Streamlit 框架入门(Day07-Day08)
4.1 Streamlit 简介
官方文档
- 安装文档:https://docs.streamlit.io/get-started/installation
- API 参考:https://docs.streamlit.io/develop/api-reference
核心特点
- 快速构建 Web 应用
- 纯 Python 编写
- 无需前端知识
- 适合数据科学和 AI应用
4.2 页面基础配置
st.set_page_config()
import streamlit as st
st.set_page_config(
page_title="streamlit 的入门", # 页面标题
page_icon="🧊", # 页面图标
layout="wide", # 布局宽度(wide/centered)
initial_sidebar_state="expanded", # 侧边栏状态(expanded/auto/collapsed)
menu_items={
'Get Help': 'https://www.extremelycoolapp.com/help',
'Report a bug': "https://www.extremelycoolapp.com/bug",
'About': "# 这是一个 streamlit 的入门页面提示信息!"
}
)
4.3 文本与标题
标题层级
st.title("Streamlit入门演示") # 一级标题
st.header("Streamlit 一级标题") # 二级标题
st.subheader("Streamlit 二级标题") # 三级标题
段落文字
# st.write() 类似 div,可以包含多种内容
st.write("从因地制宜发展产业到和美乡村建设...")
4.4 多媒体元素
图片
# 基础用法
st.image("./resources/1.jpg",
caption="Sunrise by the mountains",
width=500)
# 居中显示(推荐用 columns布局)
col1, col2, col3 = st.columns([1, 5, 1]) # 1:5:1 的列宽比例
with col2:
st.image("./resources/1.jpg", caption="Sunrise by the mountains")
音频和视频
# 音频
st.audio("./resources/1.mp3")
# 视频
st.video("./resources/1.mp4")
Logo
st.logo("./resources/1.jpg")
4.5 表格与数据展示
st.table()
student_data = {
"姓名": ["张三","李四","王五","ll","张三","对的"],
"学号": ["001","02","03","04","05","06"],
"语文": [98,88,56,85,96,85]
}
st.table(student_data)
4.6 用户输入控件
文本输入框
# 普通输入框
name = st.text_input('请输入姓名:')
st.write(f'您输入的姓名为{name}')
# 密码输入框
pass_word = st.text_input('请输入密码:', type='password')
st.write(f'您输入的密码为{pass_word}')
单选按钮
gender = st.radio('请输入您的性别', ['男','女','未知'], index=1)
st.write(f'您的性别为{gender}')
五、文件操作与 JSON 处理(Day08)
5.1 文件操作基础
文件操作三步骤
# 1. 打开文件
f = open('resources/望庐山瀑布.txt', 'r', encoding='utf-8')
# 2. 读/写
content = f.read()
print(content)
# 3. 关闭文件
f.close()
写入文件
# 1. 打开文件
f = open("resources/静夜思.txt", "w", encoding="utf-8")
# 2. 写入文件
f.write("窗前明月光,\n")
f.write("疑是地上霜。\n")
f.write("举头望明月,\n")
f.write("低头思故乡。\n")
# 3. 关闭文件
f.close()
5.2 异常处理与资源释放
方案 A:try-finally(繁琐)
f = None
try:
f = open('file.txt', 'r')
content = f.read()
finally:
if f:
f.close()
方案 B:with 语句(推荐)
# with 语句(上下文管理器)的核心作用:
# 确保资源总是被正确获取和释放
# 即使发生异常,也会被正确释放
with open('resources/望庐山瀑布.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
# 自动关闭文件,无需手动调用 close()
5.3 编码说明
# 编码:将字符转换为计算机能够存储和处理的数字代码的规则系统
# 常见编码:ASCII、GBK、UTF-8
# encoding="utf-8" 解决中文乱码问题
注意:如果操作完文件未调用 close 方法,程序也未停止运行,文件将一直被 Python 程序占用,无法操作。
5.4 JSON 模块操作
序列化(写入 JSON 文件)
import json
user1 = {
"name": "张三",
"age": 18,
"gender": "男",
"hobbies": ["football", "swimming"]
}
with open("user.json", "w", encoding="utf-8") as f:
json.dump(user1, f, ensure_ascii=False, indent=2)
# ensure_ascii=False: 让非 ASCII 码保持原样输出(解决中文乱码)
# indent=2: 格式化缩进两个空格
反序列化(读取 JSON 文件)
import json
with open("user.json", "r", encoding="utf-8") as f:
user = json.load(f)
print(user)
JSON 核心方法
# 序列化
json.dump(...) # 将 Python 对象 => JSON 并写入文件
# 反序列化
json.load(...) # 将 JSON 文件 => Python 对象
六、AI应用实战——智能伴侣(Day08)
6.1 完整项目结构
项目文件清单
# 主要文件
- 04AIdemo_侧边栏.py # 完整版 AI 伴侣(带侧边栏和会话管理)
- 07AIdemo_流式输出滚雪球记忆.py # 流式输出 + 滚雪球记忆
- 07AIdemo_滚雪球记忆.py # 滚雪球记忆版本
- 07AIdemo_无记忆.py # 无记忆版本
6.2 核心功能模块
1. 保存会话信息函数
def save_session():
if st.session_state.current_session:
session_data = {
"nick_name": st.session_state.nick_name,
"nature": st.session_state.nature,
"messages": st.session_state.messages,
"current_session": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# 如果 sessions 目录不存在,则创建
if not os.path.exists("sessions"):
os.mkdir("sessions")
# 保存会话数据到 JSON 文件
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)
2. 系统提示词模板
system_prompt_template = """
你叫 %s,现在是用户的真实伴侣,请完全代入伴侣角色。
规则:
1. 每次只回 1 条消息
2. 禁止任何场景或状态描述性文字
3. 匹配用户的语言
4. 回复简短,像微信聊天一样
5. 有需要的话可以用 ❤️ 🌸 等 emoji 表情
6. 用符合伴侣性格的方式对话
7. 回复的内容,要充分体现伴侣的性格特征
伴侣性格:
- %s
你必须严格遵守上述规则来回复用户。
"""
6.3 Session State 管理
初始化 Session State
# messages - 存储对话历史
if "messages" not in st.session_state:
st.session_state.messages = []
# nick_name - 昵称
if "nick_name" not in st.session_state:
st.session_state.nick_name = "小甜甜"
# nature - 性格
if "nature" not in st.session_state:
st.session_state.nature = "活泼开朗的东北姑娘"
# current_session - 会话标识
if "current_session" not in st.session_state:
st.session_state.current_session = ""
6.4 侧边栏设置
侧边栏布局
with st.sidebar:
st.header("⚙️ 伴侣设置")
# 新建会话按钮
if st.button("新建会话", width="stretch", icon="✏️"):
save_session() # 先保存当前会话
# TODO: 创建新会话
# 昵称输入
input_nick = st.text_input("昵称",
value=st.session_state.nick_name,
placeholder="请输入昵称...")
if input_nick:
st.session_state.nick_name = input_nick
# 性格输入
input_nature = st.text_input("性格",
value=st.session_state.nature,
placeholder="请输入性格...")
if input_nature:
st.session_state.nature = input_nature
st.divider()
# API Key 输入
api_key_input = st.text_input(
"API Key",
type="password",
value=os.environ.get("ARK_API_KEY", ""),
placeholder="sk-..."
)
# 清空聊天记录
if st.button("🗑️ 清空聊天记录"):
st.session_state.messages = []
st.rerun()
6.5 API 客户端初始化
# 检查 API Key
api_key = api_key_input if api_key_input else os.environ.get("ARK_API_KEY")
if not api_key:
st.warning("⚠️ 请在侧边栏输入 API Key 或在环境变量中配置 ARK_API_KEY")
st.stop()
# 初始化客户端
client = OpenAI(
api_key=api_key,
base_url="https://ark.cn-beijing.volces.com/api/v1"
# 建议改用 v1 接口更稳定,配合 chat.completions
)
6.6 渲染历史消息
# 遍历并显示所有历史消息
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
6.7 处理用户输入
基础流程
prompt = st.chat_input("请输入您要问的问题...")
if prompt:
# 1. 显示用户消息
with st.chat_message("user"):
st.markdown(prompt)
# 2. 保存用户消息到历史
st.session_state.messages.append({"role": "user", "content": prompt})
# 3. 准备 AI 回复
with st.chat_message("assistant"):
message_placeholder = st.empty() # 创建占位符用于流式输出
full_response = ""
# 构造完整消息列表(System + History)
system_content = system_prompt_template % (
st.session_state.nick_name,
st.session_state.nature
)
messages_to_send = [
{"role": "system", "content": system_content},
*st.session_state.messages
]
# 4. 调用 API
try:
response = client.chat.completions.create(
model="doubao-seed-2-0-pro-260215",
messages=messages_to_send,
stream=True # 开启流式输出
)
# 5. 处理流式响应(逐字打印)
for chunk in response:
if chunk.choices and chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
full_response += content
# 实时更新界面,产生打字机效果
message_placeholder.markdown(full_response + "▌")
# 6. 移除光标,显示最终结果
message_placeholder.markdown(full_response)
# 7. 保存 AI 回复到历史
st.session_state.messages.append({
"role": "assistant",
"content": full_response
})
except Exception as e:
st.error(f"❌ 调用 AI 失败:{e}")
print(f"错误详情:{e}")
message_placeholder.markdown("**出错了,请检查 API Key 或网络连接。**")
6.8 关键技术点
流式输出
# 关键参数:stream=True
response = client.chat.completions.create(
model="doubao-seed-2-0-pro-260215",
messages=messages_to_send,
stream=True # 开启流式
)
# 逐块处理响应
for chunk in response:
if chunk.choices and chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
full_response += content
message_placeholder.markdown(full_response + "▌") # 打字机效果
滚雪球记忆
# 每次请求都带上完整的对话历史
messages_to_send = [
{"role": "system", "content": system_content}, # 系统设定
*st.session_state.messages # 展开所有历史对话
]
七、会话管理完整实现思路
7.1 功能模块划分
1. 保存当前会话
- 将当前对话数据保存到 JSON 文件
- 文件名使用时间戳命名
def save_session():
"""保存当前会话到 JSON 文件"""
if st.session_state.current_session:
session_data = {
"nick_name": st.session_state.nick_name,
"nature": st.session_state.nature,
"messages": st.session_state.messages,
"current_session": st.session_state.current_session,
"create_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# 确保 sessions 目录存在
if not os.path.exists("sessions"):
os.mkdir("sessions")
# 保存到 JSON 文件
filename = f"sessions/{st.session_state.current_session}.json"
with open(filename, 'w', encoding="utf-8") as f:
json.dump(session_data, f, ensure_ascii=False, indent=2)
print(f"✓ 会话已保存:{filename}")
2. 新建会话
- 先保存当前会话
- 清空 session_state.messages
- 生成新的会话标识
# 侧边栏中的新建会话按钮
if st.sidebar.button("新建会话", width="stretch", icon="✏️"):
# 1. 保存当前会话
save_session()
# 2. 清空消息历史
st.session_state.messages = []
# 3. 生成新的会话标识(使用时间戳)
new_session_id = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
st.session_state.current_session = new_session_id
# 4. 重置昵称和性格为默认值
st.session_state.nick_name = "小甜甜"
st.session_state.nature = "活泼开朗的东北姑娘"
# 5. 重新运行应用
st.rerun()
3. 展示会话列表
- 读取 sessions 目录下所有 JSON 文件
- 解析文件名提取时间信息
- 在侧边栏展示可点击的列表
def load_session_list():
"""加载会话列表"""
session_list = []
# 检查 sessions 目录是否存在
if os.path.exists("sessions"):
# 遍历目录下所有 JSON 文件
for filename in os.listdir("sessions"):
if filename.endswith(".json"):
filepath = os.path.join("sessions", filename)
try:
# 读取文件获取会话信息
with open(filepath, 'r', encoding="utf-8") as f:
session_data = json.load(f)
# 提取关键信息
session_info = {
"filename": filename,
"nick_name": session_data.get("nick_name", "未知"),
"create_time": session_data.get("create_time", ""),
"message_count": len(session_data.get("messages", []))
}
session_list.append(session_info)
except Exception as e:
print(f"读取会话文件失败:{filename}, 错误:{e}")
# 按创建时间排序(最新的在前)
session_list.sort(key=lambda x: x["create_time"], reverse=True)
return session_list
# 在侧边栏中展示会话列表
with st.sidebar:
st.header("💬 会话列表")
# 加载会话列表
sessions = load_session_list()
if sessions:
for session in sessions:
# 显示会话信息
display_text = f"{session['nick_name']} ({session['message_count']}条消息)"
# 创建可点击的按钮
if st.button(display_text, key=session['filename'], width="stretch"):
# 用户点击后,设置当前会话标识(用于加载)
st.session_state.selected_session = session['filename']
st.rerun()
else:
st.info("暂无历史会话")
4. 加载会话
- 用户点击某个会话
- 读取对应的 JSON 文件
- 恢复到 session_state
def load_session(filename):
"""加载指定会话文件"""
filepath = os.path.join("sessions", filename)
if not os.path.exists(filepath):
st.error(f"会话文件不存在:{filename}")
return False
try:
# 读取 JSON 文件
with open(filepath, 'r', encoding="utf-8") as f:
session_data = json.load(f)
# 恢复到 session_state
st.session_state.nick_name = session_data.get("nick_name", "小甜甜")
st.session_state.nature = session_data.get("nature", "温柔可爱")
st.session_state.messages = session_data.get("messages", [])
st.session_state.current_session = session_data.get("current_session", "")
st.success(f"✓ 已加载会话:{session_data.get('nick_name')}")
return True
except Exception as e:
st.error(f"加载会话失败:{e}")
print(f"错误详情:{e}")
return False
# 在应用启动时检查是否有选中的会话
if "selected_session" in st.session_state and st.session_state.selected_session:
# 加载用户选中的会话
load_session(st.session_state.selected_session)
# 清除选择标记
st.session_state.selected_session = None
st.rerun()
5. 删除会话
- 用户确认删除
- 删除对应的 JSON 文件
- 刷新会话列表
def delete_session(filename):
"""删除指定会话文件"""
filepath = os.path.join("sessions", filename)
if not os.path.exists(filepath):
st.error(f"会话文件不存在:{filename}")
return False
try:
# 删除文件
os.remove(filepath)
st.success(f"✓ 会话已删除:{filename}")
# 如果删除的是当前会话,清空当前状态
if st.session_state.current_session == filename.replace(".json", ""):
st.session_state.messages = []
st.session_state.current_session = ""
return True
except Exception as e:
st.error(f"删除会话失败:{e}")
return False
# 在侧边栏中为每个会话添加删除按钮
with st.sidebar:
st.header("💬 会话列表")
sessions = load_session_list()
if sessions:
for session in sessions:
# 使用两列布局:左边是会话名称,右边是删除按钮
col1, col2 = st.columns([4, 1])
with col1:
display_text = f"{session['nick_name']} ({session['message_count']}条)"
if st.button(display_text, key=f"load_{session['filename']}", width="stretch"):
st.session_state.selected_session = session['filename']
st.rerun()
with col2:
# 删除按钮(红色)
if st.button("🗑️", key=f"del_{session['filename']}"):
# 二次确认
if st.warning("确定删除?"):
delete_session(session['filename'])
st.rerun()
7.2 完整整合示例
import streamlit as st
from openai import OpenAI
import os
import datetime
import json
# ==========================================
# 1. 工具函数定义
# ==========================================
def save_session():
"""保存当前会话到 JSON 文件"""
if st.session_state.current_session:
session_data = {
"nick_name": st.session_state.nick_name,
"nature": st.session_state.nature,
"messages": st.session_state.messages,
"current_session": st.session_state.current_session,
"create_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
if not os.path.exists("sessions"):
os.mkdir("sessions")
filename = f"sessions/{st.session_state.current_session}.json"
with open(filename, 'w', encoding="utf-8") as f:
json.dump(session_data, f, ensure_ascii=False, indent=2)
def load_session_list():
"""加载会话列表"""
session_list = []
if os.path.exists("sessions"):
for filename in os.listdir("sessions"):
if filename.endswith(".json"):
filepath = os.path.join("sessions", filename)
try:
with open(filepath, 'r', encoding="utf-8") as f:
session_data = json.load(f)
session_info = {
"filename": filename,
"nick_name": session_data.get("nick_name", "未知"),
"create_time": session_data.get("create_time", ""),
"message_count": len(session_data.get("messages", []))
}
session_list.append(session_info)
except Exception as e:
print(f"读取会话文件失败:{e}")
session_list.sort(key=lambda x: x["create_time"], reverse=True)
return session_list
def load_session(filename):
"""加载指定会话"""
filepath = os.path.join("sessions", filename)
if not os.path.exists(filepath):
return False
try:
with open(filepath, 'r', encoding="utf-8") as f:
session_data = json.load(f)
st.session_state.nick_name = session_data.get("nick_name", "小甜甜")
st.session_state.nature = session_data.get("nature", "温柔可爱")
st.session_state.messages = session_data.get("messages", [])
st.session_state.current_session = session_data.get("current_session", "")
return True
except Exception as e:
st.error(f"加载会话失败:{e}")
return False
def delete_session(filename):
"""删除会话文件"""
filepath = os.path.join("sessions", filename)
if os.path.exists(filepath):
os.remove(filepath)
if st.session_state.current_session == filename.replace(".json", ""):
st.session_state.messages = []
st.session_state.current_session = ""
# ==========================================
# 2. 初始化 Session State
# ==========================================
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 "nature" not in st.session_state:
st.session_state.nature = "活泼开朗的东北姑娘"
if "current_session" not in st.session_state:
st.session_state.current_session = ""
# 检查是否有待加载的会话
if "selected_session" in st.session_state and st.session_state.selected_session:
load_session(st.session_state.selected_session)
st.session_state.selected_session = None
st.rerun()
# ==========================================
# 3. 侧边栏设置
# ==========================================
with st.sidebar:
st.header("⚙️ 伴侣设置")
# 新建会话
if st.button("新建会话", width="stretch", icon="✏️"):
save_session()
st.session_state.messages = []
st.session_state.current_session = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
st.session_state.nick_name = "小甜甜"
st.session_state.nature = "活泼开朗的东北姑娘"
st.rerun()
st.divider()
# 会话列表
st.header("💬 会话列表")
sessions = load_session_list()
if sessions:
for session in sessions:
col1, col2 = st.columns([4, 1])
with col1:
display_text = f"{session['nick_name']} ({session['message_count']}条)"
if st.button(display_text, key=f"load_{session['filename']}", width="stretch"):
st.session_state.selected_session = session['filename']
st.rerun()
with col2:
if st.button("🗑️", key=f"del_{session['filename']}"):
delete_session(session['filename'])
st.rerun()
else:
st.info("暂无历史会话")
st.divider()
# 昵称和性格设置
input_nick = st.text_input("昵称", value=st.session_state.nick_name)
if input_nick:
st.session_state.nick_name = input_nick
input_nature = st.text_input("性格", value=st.session_state.nature)
if input_nature:
st.session_state.nature = input_nature
# API Key 设置
api_key_input = st.text_input("API Key", type="password", value=os.environ.get("ARK_API_KEY", ""))
# ==========================================
# 4. AI 客户端初始化
# ==========================================
api_key = api_key_input if api_key_input else os.environ.get("ARK_API_KEY")
if not api_key:
st.warning("⚠️ 请在侧边栏输入 API Key")
st.stop()
client = OpenAI(api_key=api_key, base_url="https://ark.cn-beijing.volces.com/api/v1")
# ==========================================
# 5. 主界面 - 显示历史消息
# ==========================================
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# ==========================================
# 6. 处理用户输入
# ==========================================
prompt = st.chat_input("请输入问题...")
if prompt:
# 显示用户消息
with st.chat_message("user"):
st.markdown(prompt)
st.session_state.messages.append({"role": "user", "content": prompt})
# AI 回复
with st.chat_message("assistant"):
message_placeholder = st.empty()
full_response = ""
system_content = f"""你叫 {st.session_state.nick_name},是用户的真实伴侣。
性格:{st.session_state.nature}
规则:每次只回 1 条消息,简短亲切,像微信聊天一样。"""
messages_to_send = [
{"role": "system", "content": system_content},
*st.session_state.messages
]
try:
response = client.chat.completions.create(
model="doubao-seed-2-0-pro-260215",
messages=messages_to_send,
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
full_response += chunk.choices[0].delta.content
message_placeholder.markdown(full_response + "▌")
message_placeholder.markdown(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})
# 自动保存会话
save_session()
except Exception as e:
st.error(f"调用 AI 失败:{e}")
7.3 文件结构说明
项目目录/
├── readme2.md
├── 04AIdemo_侧边栏.py
└── sessions/ # 会话存储目录
├── 20260305_143022.json # 会话文件 1
├── 20260305_151245.json # 会话文件 2
└── 20260305_162030.json # 会话文件 3
7.4 JSON 文件格式示例
{
"nick_name": "小甜甜",
"nature": "活泼开朗的东北姑娘",
"messages": [
{"role": "user", "content": "你好呀"},
{"role": "assistant", "content": "嗨~亲爱的!今天过得怎么样呀?❤️"}
],
"current_session": "20260305_143022",
"create_time": "2026-03-05 14:30:22"
}
7.2 文件操作应用场景
# 1. 资源释放:with open() 确保文件正确关闭
# 2. JSON 序列化:保存会话数据
# 3. JSON 反序列化:加载历史会话
# 4. 目录管理:创建 sessions 文件夹
八、知识扩展
8.1 网络机器人(Web Bot)
Robots 协议
- 网站告诉爬虫哪些页面可以抓取
- 位于网站根目录的 robots.txt 文件
- 是一种君子协议,不强制遵守
入门程序
# 使用 requests 库抓取网页
import requests
response = requests.get('https://www.example.com')
print(response.text)
8.2 网页结构解析
HTML 基础
- 标签(Tag):
<div>,<p>,<a>等 - 属性(Attribute):class, id, href 等
- 层级结构:树形结构
解析工具
- BeautifulSoup:Python 库,简单易用
- lxml:速度快,功能强大
- Selenium:模拟浏览器,支持 JavaScript
8.3 AI应用开发最佳实践
1. 提示词优化
- 持续迭代提示词
- 收集用户反馈
- A/B 测试不同版本
2. 性能优化
- 使用缓存减少 API 调用
- 流式输出提升用户体验
- 合理控制上下文长度
3. 安全考虑
- API Key 不要硬编码在代码中
- 使用环境变量或配置文件
- 敏感信息加密存储
4. 错误处理
- 完善的异常捕获
- 友好的错误提示
- 自动重试机制
九、学习路线总结
Day05:数据容器复习
- ✅ 列表的切片操作
- ✅ 列表的常用方法
- ✅ 数据容器的选择
Day06:AI应用概述与大模型部署
- ✅ 网络基础知识(IP、域名、端口、HTTP)
- ✅ HTTP 请求方式(GET/POST)
- ✅ JSON 数据格式
- ✅ 大模型消息交互格式
- ✅ 会话记忆方案(滚雪球)
- ✅ 本地部署 vs 官方 API
Day07:提示词工程与 Streamlit入门
- ✅ 提示词工程六要素
- ✅ Streamlit 页面配置
- ✅ 文本与多媒体展示
- ✅ 用户输入控件
Day08:文件操作与 AI应用实战
- ✅ 文件操作三步骤
- ✅ with 语句与资源释放
- ✅ JSON 序列化与反序列化
- ✅ AI 伴侣完整实现
- ✅ 会话管理思路分析
十、环境配置指南
10.1 必需依赖
# 安装依赖
pip install streamlit openai requests json
10.2 环境变量配置
# Windows 系统
setx ARK_API_KEY "your-api-key-here"
# Linux/Mac系统
export ARK_API_KEY="your-api-key-here"
10.3 运行 Streamlit 应用
# 运行应用
streamlit run 04AIdemo_侧边栏.py
# 默认访问地址
# http://localhost:8501
备注: 本总结基于 Day05-Day08 的练习文件整理,涵盖 AI应用开发的核心知识点,包括数据容器、网络基础、提示词工程、Streamlit 框架、文件操作和完整的 AI 伴侣项目实战。
十一、网络机器人(Web Bot)- Day09
11.1 网络机器人介绍
什么是网络机器人
# 网络机器人(Web Bot):是一种自动化程序
# 能够模拟人类行为浏览网页、获取数据
# 也称为:网络爬虫(Web Crawler)、蜘蛛(Spider)
# 应用场景:
# 1. 搜索引擎抓取网页(Google、百度)
# 2. 数据采集与分析
# 3. 价格监控
# 4. 新闻聚合
11.2 Robots协议
什么是 Robots协议
"""
Robots协议(君子协议):
- 网站告诉爬虫哪些页面可以抓取
- 位于网站根目录的 robots.txt 文件
- 是一种君子协议,不强制遵守但建议遵守
示例:访问 https://www.baidu.com/robots.txt
"""
robots.txt 文件示例
User-agent: * # 适用于所有爬虫
Disallow: /admin # 禁止访问 admin 目录
Disallow: /private # 禁止访问 private 目录
Allow: /public # 允许访问 public 目录
Sitemap: https://www.example.com/sitemap.xml # 网站地图位置
11.3 入门程序
使用 requests 库访问网页
import requests
# 发送 HTTP GET请求
response = requests.get('https://www.baidu.com')
# 检查响应状态码
print(f'状态码:{response.status_code}') # 200 表示成功
# 获取网页内容(文本)
print(response.text)
# 获取网页编码
print(response.encoding) # utf-8
# 设置编码,防止中文乱码
response.encoding = 'utf-8'
print(response.text)
添加请求头(User-Agent)
import requests
# 伪装成浏览器
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get('https://www.baidu.com', headers=headers)
print(response.text)
11.4 网页结构基础
HTML 基本结构
<!DOCTYPE html>
<html>
<head>
<title>网页标题</title>
</head>
<body>
<div class="container">
<h1>主标题</h1>
<p class="content">这是一个段落</p>
<a href="https://example.com">链接</a>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
</ul>
</div>
</body>
</html>
常见 HTML 标签
"""
<div> - 块级容器
<span> - 行内容器
<p> - 段落
<a> - 超链接
<img> - 图片
<ul>/<ol> - 列表
<li> - 列表项
<h1>-<h6> - 标题
table - 表格
tr - 表格行
td - 表格单元格
"""
11.5 网页解析方法
BeautifulSoup 解析
from bs4 import BeautifulSoup
import requests
# 获取网页内容
response = requests.get('https://example.com')
html = response.text
# 创建 BeautifulSoup 对象
soup = BeautifulSoup(html, 'html.parser')
# 查找元素
# 1. 通过标签名查找
title = soup.find('h1') # 找到第一个 h1 标签
all_links = soup.find_all('a') # 找到所有 a 标签
# 2. 通过 class 查找
div = soup.find('div', class_='container')
# 3. 通过 id 查找
header = soup.find(id='header')
# 4. 获取文本内容
text = title.get_text()
# 5. 获取属性值
link = soup.find('a')
href = link['href'] # 获取 href 属性
实际案例:爬取小说章节
import requests
from bs4 import BeautifulSoup
# 1. 发送请求
url = 'https://example.com/novel/chapter1'
response = requests.get(url)
response.encoding = 'utf-8'
# 2. 解析 HTML
soup = BeautifulSoup(response.text, 'html.parser')
# 3. 提取标题和内容
title = soup.find('h1').get_text()
content = soup.find('div', class_='content').get_text()
# 4. 保存到文件
with open('novel.txt', 'w', encoding='utf-8') as f:
f.write(f'{title}\n\n{content}')
11.6 XPath语法
XPath 介绍
"""
XPath(XML Path Language):
- 用于在 XML/HTML 文档中定位节点的语言
- 使用路径表达式来选择节点
- 比 BeautifulSoup 更强大、更灵活
安装:pip install lxml
"""
XPath 常用语法
from lxml import etree
html = '''
<div>
<ul>
<li><a href="/book/1">书 1</a></li>
<li><a href="/book/2">书 2</a></li>
<li><a href="/book/3">书 3</a></li>
</ul>
</div>
'''
# 解析 HTML
tree = etree.HTML(html)
# 1. 绝对路径(从根节点开始)
result = tree.xpath('/html/body/div/ul/li/a')
# 2. 相对路径(// 表示任意位置)
result = tree.xpath('//a') # 选择所有 a 标签
# 3. 指定标签名
result = tree.xpath('//li/a') # li 下的 a 标签
# 4. 属性选择
result = tree.xpath('//a[@href="/book/1"]') # href 属性为/book/1 的 a 标签
# 5. class 选择(注意 class 是关键字,用 @class)
result = tree.xpath('//div[@class="content"]')
# 6. 文本内容
result = tree.xpath('//a/text()') # 获取 a 标签的文本
# 7. 属性值
result = tree.xpath('//a/@href') # 获取所有 href 属性值
# 8. 索引(从 1 开始)
result = tree.xpath('//li[1]') # 第一个 li
result = tree.xpath('//li[last()]') # 最后一个 li
XPath 实战案例
import requests
from lxml import etree
url = 'https://example.com'
response = requests.get(url)
html = response.text
# 解析 HTML
tree = etree.HTML(html)
# 1. 获取所有电影标题
movies = tree.xpath('//div[@class="movie-title"]/text()')
# 2. 获取所有评分
scores = tree.xpath('//span[@class="score"]/text()')
# 3. 获取详情链接
links = tree.xpath('//a[@class="detail-link"]/@href')
for i in range(len(movies)):
print(f'{movies[i]} - {scores[i]}')
11.7 CSV 文件操作
CSV 文件介绍
"""
CSV(Comma-Separated Values):
- 逗号分隔值文件
- 一种简单的数据存储格式
- 可以用 Excel 打开
- 每行一条记录,字段用逗号分隔
"""
写入 CSV 文件
import csv
# 准备数据
movies = [
['排名', '电影名', '评分', '上映年份'],
[1, '肖申克的救赎', 9.7, 1994],
[2, '霸王别姬', 9.6, 1993],
[3, '阿甘正传', 9.5, 1994]
]
# 写入 CSV 文件
with open('movie_data.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerows(movies)
print('✓ 数据已保存到 movie_data.csv')
读取 CSV 文件
import csv
# 读取 CSV 文件
with open('movie_data.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
# 遍历每一行
for row in reader:
print(row) # row 是一个列表
# 读取为字典格式
with open('movie_data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['电影名'], row['评分'])
11.8 综合案例:豆瓣电影榜单爬虫
思路分析
"""
目标:爬取豆瓣电影 Top250 数据
步骤:
1. 分析网页结构(F12 开发者工具)
2. 确定要抓取的数据:电影名、评分、简介等
3. 找出数据对应的 HTML 标签和 XPath
4. 处理分页(每页 25 部电影,共 10 页)
5. 保存数据到 CSV 文件
注意事项:
- 添加 User-Agent 避免被屏蔽
- 设置延时,避免请求过快
- 遵守 robots.txt 协议
"""
核心逻辑实现
import requests
from lxml import etree
import csv
import time
class MovieSpider:
def __init__(self):
self.base_url = 'https://movie.douban.com/top250?start='
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
self.movies = []
def get_movie_list(self, page):
"""获取单页电影列表"""
start = page * 25 # 每页 25 部
url = f'{self.base_url}{start}'
response = requests.get(url, headers=self.headers)
html = response.text
# 解析 HTML
tree = etree.HTML(html)
# 获取所有电影项
items = tree.xpath('//li[@class="list-item"]')
page_movies = []
for item in items:
# 提取电影信息
movie = {
'title': item.xpath('.//span[@class="title"][1]/text()')[0],
'score': item.xpath('.//span[@class="rating_num"]/text()')[0],
'year': item.xpath('.//span[@class="year"]/text()')[0].strip('()'),
'actors': item.xpath('.//span[@class="actors"]/text()')[0]
}
page_movies.append(movie)
return page_movies
def crawl_all_pages(self, total_pages=10):
"""爬取所有页面"""
for page in range(total_pages):
print(f'正在爬取第 {page + 1} 页...')
movies = self.get_movie_list(page)
self.movies.extend(movies)
# 延时,避免请求过快
time.sleep(1)
print(f'✓ 爬取完成,共 {len(self.movies)} 部电影')
def save_to_csv(self, filename='movies.csv'):
"""保存到 CSV 文件"""
if not self.movies:
print('没有数据可保存')
return
# CSV 字段名
fieldnames = ['title', 'score', 'year', 'actors']
with open(filename, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
# 写入表头
writer.writeheader()
# 写入数据
writer.writerows(self.movies)
print(f'✓ 数据已保存到 {filename}')
# 使用示例
if __name__ == '__main__':
spider = MovieSpider()
spider.crawl_all_pages(2) # 爬取前 2 页
spider.save_to_csv()
获取电影详情
def get_movie_detail(self, detail_url):
"""获取电影详情页信息"""
response = requests.get(detail_url, headers=self.headers)
html = response.text
tree = etree.HTML(html)
# 提取详细信息
detail = {
'director': tree.xpath('//span[text()="导演"]/following-sibling::a/text()')[0],
'writer': tree.xpath('//span[text()="编剧"]/following-sibling::a/text()')[0],
'type': tree.xpath('//span[text()="类型"]/following-sibling::text()')[0],
'country': tree.xpath('//span[text()="制片国家/地区"]/following-sibling::text()')[0]
}
return detail
完善程序(异常处理)
import requests
from requests.exceptions import RequestException
def safe_get_movie_list(self, page):
"""带异常处理的爬取函数"""
try:
start = page * 25
url = f'{self.base_url}{start}'
response = requests.get(url, headers=self.headers, timeout=10)
response.raise_for_status() # 检查响应状态
response.encoding = 'utf-8'
html = response.text
tree = etree.HTML(html)
items = tree.xpath('//li[@class="list-item"]')
page_movies = []
for item in items:
try:
movie = {
'title': item.xpath('.//span[@class="title"][1]/text()')[0],
'score': item.xpath('.//span[@class="rating_num"]/text()')[0],
'year': item.xpath('.//span[@class="year"]/text()')[0].strip('()')
}
page_movies.append(movie)
except IndexError:
continue # 跳过有问题的数据
return page_movies
except RequestException as e:
print(f'请求失败:{e}')
return []
except Exception as e:
print(f'未知错误:{e}')
return []
11.9 反爬虫机制与应对
常见反爬虫手段
"""
1. User-Agent 检测
应对:设置真实的 User-Agent
2. IP 频率限制
应对:使用代理 IP、设置延时
3. Cookie 验证
应对:携带 Cookie 或使用 session
4. 验证码
应对:接入打码平台或人工识别
5. JavaScript 动态加载
应对:使用 Selenium 或分析 API
"""
使用 Session 保持会话
import requests
# 创建 session 对象
session = requests.Session()
# 第一次请求
response1 = session.get('https://example.com/login')
# session 会自动保存 Cookie
# 第二次请求会自动带上 Cookie
response2 = session.get('https://example.com/profile')
11.10 知识扩展
JSON 数据处理
import json
# Python 对象转 JSON 字符串
data = {'name': '张三', 'age': 18}
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)
# JSON 字符串转 Python 对象
data = json.loads(json_str)
print(data['name'])
正则表达式基础
import re
# 匹配邮箱
pattern = r'\w+@\w+\.com'
text = '请联系 admin@example.com 或 support@test.com'
emails = re.findall(pattern, text)
print(emails) # ['admin@example.com', 'support@test.com']
# 匹配数字
numbers = re.findall(r'\d+', '价格:100 元,折扣:20%')
print(numbers) # ['100', '20']
十二、完整学习路线总结
Day05:数据容器复习
- ✅ 列表的切片操作
- ✅ 列表的常用方法
- ✅ 数据容器的选择
Day06:AI应用概述与大模型部署
- ✅ 网络基础知识(IP、域名、端口、HTTP)
- ✅ HTTP 请求方式(GET/POST)
- ✅ JSON 数据格式
- ✅ 大模型消息交互格式
- ✅ 会话记忆方案(滚雪球)
- ✅ 本地部署 vs 官方 API
Day07:提示词工程与 Streamlit入门
- ✅ 提示词工程六要素
- ✅ Streamlit 页面配置
- ✅ 文本与多媒体展示
- ✅ 用户输入控件
Day08:文件操作与 AI应用实战
- ✅ 文件操作三步骤
- ✅ with 语句与资源释放
- ✅ JSON 序列化与反序列化
- ✅ AI 伴侣完整实现
- ✅ 会话管理思路分析
Day09:网络机器人进阶
- ✅ 网络机器人介绍
- ✅ Robots协议
- ✅ requests 库使用
- ✅ 网页结构基础
- ✅ BeautifulSoup 解析
- ✅ XPath语法
- ✅ CSV 文件操作
- ✅ 综合案例:豆瓣电影爬虫
- ✅ 反爬虫机制与应对
十三、开发工具与资源
13.1 必需依赖
# AI应用开发
pip install streamlit openai
# 网络爬虫
pip install requests beautifulsoup4 lxml
# 数据处理
pip install pandas csv
13.2 开发工具
- Apifox: API 接口测试工具
- Chrome DevTools: 网页调试与分析
- VS Code: 代码编辑器
- Ollama: 本地大模型部署工具
13.3 学习资源
- Streamlit 官方文档:https://docs.streamlit.io
- Requests 官方文档:https://docs.python-requests.org
- BeautifulSoup 文档:https://www.crummy.com/software/BeautifulSoup/
- XPath 教程:https://www.w3schools.com/xml/xpath_intro.asp
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)