1 基础
- AI:人工智能(Artificial Intelligence),是一个学科领域的统称,目标就是使机器能够像人类一样思考、学习、推理和解决问题。
- AI大模型:也称为大语言模型(Large Language Models, LLM),是AI技术的一个分支。其实就是一个用代码模拟人脑神经网络的程序(参数量极其庞大,通常达到数十亿至数千亿级别),通过大量的数据训练后,使其具备理解人类语言、思考、推理并输出人类语言的能力。
- AI应用:是指将AI大模型技术落地到具体的业务场景中,用来解决实际问题的产品或者服务。
1.1 大模型部署
- 本地部署
- 优点:数据安全、自主可控、长期成本低
- 缺点:初始成本高、需长期维护、性能受限
- 官方开放API
- 优点:前期成本低、无需部署和维护、随时访问
- 缺点:隐私不能保障、长期成本高、可控性差
- 云服务平台
- 优点:前期成本低、无需部署和维护、选择度高
- 缺点:安全及隐私不能保障、长期成本高
API:应用程序编程接口(Application Programming Interface),是软件间的标准化的 “桥梁”,允许开发者无需知晓内部细节即可调用外部功能或数据。
1.1.1 本地部署
Ollama是一个在本地运行、管理大语言模型的工具。官网:https://ollama.com/
- 下载后双击即可安装, 默认Ollama是安装在C盘中的,具体的安装位置如下:
- 默认安装后的目录:C:\\Users\\用户名\\AppData\\Local\\Programs\\Ollama
- 默认安装的模型目录:C:\\Users\\用户名\\ .ollama
- 默认的配置文件目录:C:\\Users\\用户名\\AppData\\Local\\Ollama
- 自定义安装:
- 在OllamaSetup.exe所在目录打开cmd命令行,然后命令: OllamaSetup.exe /DIR=你要安装的目录位置
- 配置环境变量:OLLAMA_MODELS=你想要保存下载的模型的目录

- 查找模型 ollama是一个模型管理工具和平台,它提供了很多国内外常见的模型,我们可以在其官网上搜索自己需要的模型:https://ollama.com/search,如图,我们可以直接在搜索栏搜索deepseek,目前热度排第一的就是deepseek-r1,点击进入deepseek-r1页面,会发现deepseek-r1也有很多版本:1.5b,7b,8b,14b,32b,70b,671b。这些就是模型的参数大小,越大推理能力就越强,需要的算力也越高。671b版本就是最强的满血版deepseek-r1了。需要注意的是,Ollama提供的DeepSeek是量化压缩版本,对比官网的蒸馏版会更小,对显卡要求更低。对比如下:

- 运行模型 选择自己合适的模型后,点击对应的链接,ollama会给出运行模型的命令:

注意:
- 首次运行命令需要下载模型,根据模型大小不同下载时长在5分钟~1小时不等,请耐心等待下载完成。
- ollama控制台是一个封装好的AI对话产品,与ChatGPT、DeepSeek官方提供的对话产品类似,具备会话记忆功能。
- Ollama是一个模型管理工具,所有的命令如下:
- ollama serve # 启动Ollama
- ollama create # 创建模型
- ollama show # 查看模型的详细信息
- ollama run # 运行一个模型
- ollama stop # 停止正在运行的模型
- ollama pull # 推送一个模型到远端
- ollama push # 拉取(下载)一个模型
- ollama list # 列出所有的模型
- ollama ps # 列出正在运行的模型
- ollama cp # 拷贝模型
- ollama rm # 删除模型
- ollama help # 查看帮助文档
1.1.2 官方开放API
主流的大模型(如:DeepSeek、Kimi等)官方都提供了开放API,无需部署,就可以直接调用访问。
- 注册账号
- 登录DeepSeek
- 充值
- 创建API Key
1.2 大模型调用
1.2.1 网络基础知识
- IP地址可以理解为就是设备在互联网中的地址(唯一身份证),每一个连入网络的设备都有一个自己的IP地址,用来定位设备在互联网中的位置。IPv4地址:32位二进制,IPv6地址:128位二进制
- 域名(Domain Name),是由一串用点分隔的英文字母组成的。IP地址不便于记忆,因此设计出了域名,并通过DNS(域名解析服务器)来将域名和IP地址相互映射,便于记忆和访问。
- 端口号(Port) 是整数,取值范围在0-65535,它是用来标识计算机设备中的运行中的程序。

- OSI网络模型:全球网络互连标准模型,TCP/IP网络模型:可以认为是OSI的简化版

- HTTP协议概念:Hyper Text Transfer Protocol,超文本传输协议,规定了客户端和服务器之间数据传输的规则。(只有在请求及响应中都遵循了统一的规则,服务端才能读懂客户端发送来的请求,客户端才能解析服务端响应的结果)
- 特点:
- 基于文本的协议:请求和响应的部分的协议内容为文本格式,底层通过TCP协议传输, 稳定性强。
- 基于请求-响应模型:一次请求对应一次响应,必须由客户端先发起请求,服务端才会返回响应。
- 无状态:服务端不会记忆与客户端的历史交互信息,每次请求-响应都是独立的。
- HTTP协议-请求数据格式
- 请求方式: GET: 请求参数在请求行中,没有请求体。如:/api/courses?name=Python&status=1 。GET请求请求参数大小在浏览器中是有限制的。 POST: 请求参数在请求体中,POST请求大小是没有限制的。
- HTTP协议-响应数据格式

- 特点:
1.2.2 Apifox测试
- Apifox是一款API设计、开发、测试的一体化协作平台,是项目开发中进行API接口测试的神器。
- Apifox调用deepseek

与AI大模型的交互本质是无状态的,每一次请求响应都是相互独立的。(AI大模型本身没有真正的会话记忆能力)
处理方式:会话历史滚雪球 
知识回顾:JSON
- JSON(JavaScript Object Notation)是前端的一种对象表示方法。表示形式类似于Python中的字典,都是key:value这种形式,不过所有的key都必须使用双引号引起来 ,值可以是任何类型:
- 对象:用{}表示,{}之间是键值对形式,键是字符串,值可以是任意其它类型
- 数字:整数和小数都是数字,例如:12、3.14
- 字符串:用""引起来,例如:“jack”
- 布尔:有两种值:true或false
- 列表:用[]表示,[]中是列表的元素,多个元素以,分割。

1.2.3 代码调用测试
- 调用DeepSeek
第一次使用需要从第三方库安装openai
将DEEPSEEK_API_KEY的值配置到环境变量中(配置完成后将pycharm重启才能使用这个值) 
- 如何安装第三方的软件包?
- 安装软件包(最新版本):pip install openai
- 安装软件包(指定版本):pip install openai=2.13.0
- 卸载软件包:pip uninstall openai
- 列出已安装的包:pip list
- 查看包详情:pip show openai
PyPI:全程为 Python Package Index,是由Python官方和社区共同维护的Python第三方软件包的官方仓库 。 pip:pip是Python官方提供的Python包的管理工具,提供了对Python包的查找、下载、安装、卸载等功能 。
1.3 提示词工程
- 提示词(Prompt):是引导大模型(LLM)进行内容生成的命令(一句话、一个问题等),一个好的提示词需要引导AI思考,约束其输出范围,并明确你期望的结果
- 提示词工程(Prompt Engineering):通过有技巧的编写提示词,使大模型生成出尽可能符合预期的内容,这一持续性的过程就称为提示词工程。
- 角色
- 给大模型设定角色与能力,eg:你是一名经验丰富的高中历史老师,擅长生动有趣的讲解复杂历史事件
- 任务
- 明确核心请求与任务,eg:请向一位高中生解释法国大革命爆发的主要原因
- 按步骤拆解复杂任务,eg:请先概述背景,然后分政治、经济、思想三个方面阐述,每点配一个例子
- 要求
- 指定风格与语气,eg:请使用简洁、口语化、充满热情的语气(避免使用过于学术的术语)
- 明确要求输出格式,eg:请严格按照如下结构输出: …
- 提供输入输出的示例,eg:可以参考《人类群星闪耀时》的叙事风格。请不要列出冗长的日期列表

- 角色
2 AI应用
2.1 Streamlit
- Streamlit是一个开源的Python库,专为数据工程师及机器学习工程师设计,用来快速基于Python代码构建交互式的web网站(无需掌握前端技术)。官方网站: https://streamlit.io
- 入门使用:
- 安装streamlit:pip install streamlit
- 在python文件中引入streamlit模块
- 基于streamlit中提供的API来构建Web应用
- 运行程序:streamlit run xxxx.py

2.2 AI智能伴侣-基本交互
准备工作:在pycharm中安装lingma插件,安装完成后重启IDE

- 界面基本布局:根据之前学习的内容完成页面基本布局
import streamlit as st
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get('DEEPSEEK_API_KEY'),base_url="https://api.deepseek.com")
system_prompt = "你是一名可爱的AI助手,你的名字叫小甜甜"
# 整体布局
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.png")
#输入框
prompt = st.chat_input("请输入您的问题")
if prompt:
st.chat_message("user").write(prompt)
print("——–>调用AI大模型,提示词:",prompt)
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt},
],
stream=False
)
st.chat_message("assistant").write(response.choices[0].message.content)
print("<——–AI大模型返回结果:",response.choices[0].message.content)
- 界面消息展示 问题:运行界面基本布局代码会发现每次问题都会覆盖上次的问题以及回复,如何显示所有的记录 回答:使用st.session_state将历史信息存入
# 初始化聊天信息
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"])
#存入每次提问
st.session_state.messages.append({"role": "user", "content": prompt})
#存入每次回复
st.session_state.messages.append({"role": "assistant", "content": response.choices[0].message.content})
2.3 AI智能伴侣-会话记忆
- 会话记忆问题
- 可以用之前提到的会话历史滚雪球: *st.session_state.messages
- 流式输出
- 将stream修改为True
- 修改回复代码 通过Apifox调试,分析响应信息

# 流式输出
response_messages = st.empty() # 创建一个空元素,用于显示结果
full_response = ""
for chunk in response:
if chunk.choices[0].delta.content is not None:
full_response += chunk.choices[0].delta.content
response_messages.chat_message("assistant").write(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})
print("<——–AI大模型返回结果:",full_response)
2.4 AI智能伴侣-侧边栏功能
...
system_prompt = """
你叫%s,现在是用户的真实伴侣,请完全代入伴侣角色。:
规则:
1. 每次只回1条消息
2. 禁止任何场景或状态描述性文字
3. 匹配用户的语言
4. 回复简短,像微信聊天一样
5. 有需要的话可以用❤️🌸等emoji表情
6. 用符合伴侣性格的方式对话
7. 回复的内容, 要充分体现伴侣的性格特征
伴侣性格:
– %s
你必须严格遵守上述规则来回复用户。
"""
...
#初始化昵称
if "nick_name" not in st.session_state:
st.session_state.nick_name = "小甜甜"
#初始化性格
if "character" not in st.session_state:
st.session_state.character = "一个豪爽的东北姑娘"
#侧边栏
with st.sidebar:
st.sidebar.subheader("伴侣信息")
#昵称输入框
nick_name = st.text_input("昵称", value="小甜甜",placeholder="请输入昵称")
if nick_name:
st.session_state.nick_name = nick_name
#性格输入框
character = st.text_area("性格", value="一个豪爽的东北姑娘",placeholder="请输入昵称")
if character:
st.session_state.character = character
...
messages=[
{"role": "system", "content": system_prompt % (st.session_state.nick_name, st.session_state.character)},
*st.session_state.messages,
],
...
2.5 文件操作
- 文件的操作(读/写)
- 打开:open()函数
- 读/写:read()方法、write()方法
- 关闭:close()方法
编码:是将字符(文字、数字、符号)转换为计算机能够存储和处理的数字代码的规则系统,如:ASCII、GBK、UTF-8 。 注意:如果操作完文件,并未调用close方法关闭文件,同时程序没有停止运行,那么这个文件将一直被Python程序占用,无法操作 。

- 问题:如果操作文件过程中出现了异常,文件就无法关闭了,怎么解决?
- try…finally
- with open()方法 (推荐,最佳实践)

提示:with语句(上下文管理器) 的核心作用就是确保资源的总是被正确获取和释放(即使发生异常,也会被正确释放),也是项目开发中的推荐方式 。
- 读取json格式
- JSON是软件开发中最常用的数据交换格式,而为了简化JSON数据的处理,在Python标准库中就提供了处理JSON数据的核心模块 json。
- dump() :将python对象序列化为json格式字符串并写入文件
- load() :从文件中读取json格式数据,并将其反序列化为python对象
- ensure_ascii:默认值为True,确保所有数据输出都是ascii编码(非ASCII码会进行转义);值为False非ascii码保留原样输出
- indent:会在输出的json数据中添加缩进(格式化)

- JSON是软件开发中最常用的数据交换格式,而为了简化JSON数据的处理,在Python标准库中就提供了处理JSON数据的核心模块 json。
- 文件操作扩展

- 路径写法:
- 相对路径:从当前文件所在目录开始查找
- .:当前目录,如./resource/test.txt(./可以省略)
- … :上一级目录,如…/第二章/file/test.txt
- 绝对路径:从文件系统根目录开始查找,文件位置的完整路径(注意:\\ 在字符串中标识转义字符,\\n \\t)
- 方式一:D://project//python_project//py_p1//resources//静夜思.txt
- 方式二:D:/project/python_project/py_p1/resources\\静夜思.txt
- 相对路径:从当前文件所在目录开始查找
2.6 保存当前会话
# 生成会话标识函数
def generate_session_name():
return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 保存会话信息函数
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,
"messages": st.session_state.messages
}
# 如果 sessions 目录不存在, 则创建
if not 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)
2.7 新建会话
# 会话标识
if "current_session" not in st.session_state:
st.session_state.current_session = generate_session_name()
......
# 新建会话
if st.button("新建会话", width="stretch", icon="✏️"):
# 1. 保存当前会话信息
save_session()
# 2. 创建新的会话
if st.session_state.messages: # 如果聊天信息非空, True; 否则, False
st.session_state.messages = []
st.session_state.current_session = generate_session_name()
save_session()
st.rerun() # 重新运行当前页面
2.8 展示历史会话
# 加载所有的会话列表信息
def load_session_list():
session_list = []
if os.path.exists("./session"):
for file in os.listdir("./session"):
if file.endswith(".json"):
session_list.append(file[:–5])
return session_list
......
#展示历史会话
st.text("历史会话")
for session in load_session_list():
col1,col2 = st.columns([4,1])
with col1:
if st.button(session,key=f"load_{session}",icon="📝",width="stretch"):
pass
with col2:
if st.button("",key=f"delete_{session}",icon="🗑️",width="stretch"):
pass
st.button如果是空的,需要指定唯一key,否则会报错 st.columns将一行分为若干列
2.9 加载指定历史会话
三元运算符:语法:<true_value> if 条件表达式 else <false_value>,如果if条件为真,返回前面那个值,否则返回后面那个值 选中历史会话时,高亮按钮 在主页面显示当前会话名称 每次模型回复后,保存一下会话信息
# 加载指定会话
def load_session(session_name):
try:
if os.path.exists(f"./session/{session_name}.json"):
with open(f"./session/{session_name}.json", "r", encoding="utf-8") as f:
session_data = json.load(f)
st.session_state.nick_name = session_data["nick_name"]
st.session_state.character = session_data["character"]
st.session_state.current_session = session_name
st.session_state.messages = session_data["messages"]
except Exception:
st.error("加载会话失败!!")
...............
with col1:
if st.button(session,key=f"load_{session}",icon="📝",width="stretch",type="primary" if session == st.session_state.current_session else "secondary"):
load_session(session)
st.rerun()
...................
#展示当前会话名称
st.text(f"当前会话:{st.session_state.current_session}")
2.10 删除指定会话
# 删除指定会话
def delete_session(session_name):
try:
if os.path.exists(f"./session/{session_name}.json"):
#删除文件
os.remove(f"./session/{session_name}.json")
if session_name == st.session_state.current_session:
st.session_state.current_session = generate_session_name()
st.session_state.messages.clear()
except Exception:
st.error("删除失败!")
.......
with col2:
if st.button("",key=f"delete_{session}",icon="🗑️",width="stretch"):
delete_session(session)
st.rerun()
2.11 完整代码
import streamlit as st
import os
from openai import OpenAI
from datetime import datetime
import json
# 设置页面的配置项
st.set_page_config(
page_title="AI智能伴侣",
page_icon="🤖",
layout="wide", # 布局
initial_sidebar_state="expanded", # 控制的是侧边栏的状态
menu_items={}
)
# 生成会话标识函数
def generate_session_name():
return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 保存会话信息函数
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,
"messages": st.session_state.messages
}
# 如果 sessions 目录不存在, 则创建
if not 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)
# 加载所有的会话列表信息
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])
session_list.sort(reverse=True) # 排序, 降序排列
return session_list
# 加载指定的会话信息
def load_session(session_name):
try:
if os.path.exists(f"sessions/{session_name}.json"):
# 读取会话数据
with open(f"sessions/{session_name}.json", "r", encoding="utf-8") as f:
session_data = json.load(f)
st.session_state.messages = session_data["messages"]
st.session_state.nick_name = session_data["nick_name"]
st.session_state.nature = session_data["nature"]
st.session_state.current_session = session_name
except Exception:
st.error("加载会话失败!")
# 删除会话信息函数
def delete_session(session_name):
try:
if os.path.exists(f"sessions/{session_name}.json"):
os.remove(f"sessions/{session_name}.json") # 删除文件
# 如果删除的是当前会话, 则需要更新消息列表
if session_name == st.session_state.current_session:
st.session_state.messages = []
st.session_state.current_session = generate_session_name()
except Exception:
st.error("删除会话失败!")
# 大标题
st.title("AI智能伴侣")
# Logo
st.logo("resources/logo.png")
# 系统提示词
system_prompt = """
你叫 %s,现在是用户的真实伴侣,请完全代入伴侣角色。
规则:
1. 每次只回1条消息
2. 禁止任何场景或状态描述性文字
3. 匹配用户的语言
4. 回复简短,像微信聊天一样
5. 有需要的话可以用❤️🌸等emoji表情
6. 用符合伴侣性格的方式对话
7. 回复的内容, 要充分体现伴侣的性格特征
伴侣性格:
– %s
你必须严格遵守上述规则来回复用户。
"""
# 初始化聊天信息
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 = generate_session_name()
# 展示聊天信息
st.text(f"会话名称: {st.session_state.current_session}")
for message in st.session_state.messages: # {"role": "user", "content": prompt}
st.chat_message(message["role"]).write(message["content"])
# 创建与AI大模型交互的客户端对象 (DEEPSEEK_API_KEY 环境变量的名字, 值就是DeepSeek的API_KEY的)
client = OpenAI(api_key=os.environ.get('DEEPSEEK_API_KEY'), base_url="https://api.deepseek.com")
# 左侧的侧边栏 – with: streamlit中上下文管理器
with st.sidebar:
# 会话信息
st.subheader("AI控制面板")
# 新建会话
if st.button("新建会话", width="stretch", icon="✏️"):
# 1. 保存当前会话信息
save_session()
# 2. 创建新的会话
if st.session_state.messages: # 如果聊天信息非空, True; 否则, False
st.session_state.messages = []
st.session_state.current_session = generate_session_name()
save_session()
st.rerun() # 重新运行当前页面
# 会话历史
st.text("会话历史")
session_list = load_sessions()
for session in session_list:
col1,col2 = st.columns([4,1])
with col1:
# 加载会话信息
# 三元运算符: 如果条件为真, 则返回第一个表达式的值; 否则, 返回第二个表达式的值 –> 语法: 值1 if 条件 else 值2
if st.button(session, width="stretch", icon="📄", key=f"load_{session}", type="primary" if session == st.session_state.current_session else "secondary"):
load_session(session)
st.rerun()
with col2:
# 删除会话信息
if st.button("", width="stretch", icon="❌️", key=f"delete_{session}"):
delete_session(session)
st.rerun()
# 分割线
st.divider()
# 伴侣信息
st.subheader("伴侣信息")
# 昵称输入框
nick_name = st.text_input("昵称", placeholder="请输入昵称", value=st.session_state.nick_name)
if nick_name:
st.session_state.nick_name = nick_name
# 性格输入框
nature = st.text_area("性格", placeholder="请输入性格", value=st.session_state.nature)
if nature:
st.session_state.nature = nature
# 消息输入框
prompt = st.chat_input("请输入您要问的问题")
if prompt: # 字符串会自动转换为布尔值, 如果字符串非空, 则为True; ""否则为False
st.chat_message("user").write(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 % (st.session_state.nick_name, st.session_state.nature)},
*st.session_state.messages
],
stream=True
)
# 输出大模型返回的结果 (流式输出的解析方式)
response_message = 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
response_message.chat_message("assistant").write(full_response)
# 保存大模型返回的结果
st.session_state.messages.append({"role": "assistant", "content": full_response})
# 保存会话信息
save_session()


