前言
作为一个平时跟 React、Vue 打交道的前端人,谁能想到某天我会抱着 Python 文件抓耳挠腮?起因很简单 ------ 我想搞个属于自己的极简 AI 聊天框,不用复杂的前端框架,不用写 HTML 和 CSS,几行 Python 代码搞定,主打一个 "能用就行,还得有点排面"。
一、先搭骨架:把依赖和环境变量安排明白
写代码第一步,永远是 "先把工具备好" 。就像做饭得先买菜,写 Python 项目得先装依赖、配环境。
首先,我得把需要的库都 import 进来:
Python
import os # 管环境变量的工具人
from dotenv import load_dotenv # 从.env文件里加载密钥,避免硬编码泄露
from openai import OpenAI # 连接LLM的通用SDK,DeepSeek也能用
import streamlit as st # 一键生成Web界面的神器,前端人的福音
这里得重点说一下dotenv和环境变量。很多新手写代码喜欢把 API 密钥直接写在代码里,比如api_key="sk-xxxx",这要是不小心传到 GitHub 上,分分钟被薅秃。用dotenv就不一样了,密钥写在.env文件里,load_dotenv()一调用就能读出来,安全又优雅。
Python
# 加载.env文件里的环境变量
load_dotenv()
# 优先读环境变量,没有的话读Streamlit的secrets(部署的时候用)
api_key = os.getenv('API_KEY') or st.secrets.get('API_KEY')
二、打通 AI 接口:让 Python 和 DeepSeek 说上话
骨架搭好了,接下来得让代码能连上 DeepSeek 的 API。OpenAI 的 SDK 对兼容接口非常友好,只要把base_url改成 DeepSeek 的地址,就能直接用,完全不用改太多逻辑。
Python
client = OpenAI(
api_key=api_key,
base_url="https://api.deepseek.com"
)
搞定客户端,就可以试试调用接口了。一开始我写了个最简单的请求,看看能不能拿到回复:
Python
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[
{"role": "system", "content": "你是一个友好的 AI 助手"},
{"role": "user", "content": "用三句话解释什么是人工智能"}
]
)
print(response.choices[0].message.content)
print(response.usage)
运行一下,控制台直接打印出了 AI 的回复和 token 消耗,以及 DeepSeek 确实是扣钱了,完成!

不过光有控制台输出肯定不行,我要的是一个能交互的 Web 界面,不然怎么叫聊天框呢?
三、搞个 Web 界面:Streamlit 五分钟救我狗命
作为一个前端人,本来还想着要写 HTML、CSS、JS,结果 Streamlit 直接给我整明白了。几行代码就能生成一个带标题、聊天框、输入框的界面。
1. 先给界面整个 "门面"
Python
st.title("🤖 我是你的专属助理")
st.caption("基于 deepseek-v4-flash 模型")
运行一下,页面上直接出现了标题和副标题,比我写 HTML 快了 100 倍。
2.初始化对话历史:让 AI 记得我们聊过啥
聊天嘛,总得有上下文,不然每次提问都是 "裸奔",AI 根本不知道我们聊了啥。Streamlit 的session_state就派上用场了,它能在用户交互过程中保存数据,相当于一个全局状态管理器。
Python
# 如果会话里没有messages列表,就初始化一个空的
if "messages" not in st.session_state:
st.session_state.messages = []
3. 显示历史消息:让聊天记录 "看得见"
每次刷新页面,我们得把之前的聊天记录重新渲染出来,不然用户一刷新,对话就没了,体验太差。
Python
for message in st.session_state.messages:
# 根据消息的role创建对应的聊天容器(user是用户,assistant是AI)
with st.chat_message(message["role"]):
st.write(message["content"])
4. 接收用户输入:做个能打字的输入框
Streamlit 的chat_input方法直接给你生成一个聊天输入框,还自带样式,不用自己写:
Python
if prompt := st.chat_input("请输入你的问题..."):
# 把用户的问题加到对话历史里
st.session_state.messages.append({"role": "user", "content": prompt})
# 在页面上显示用户的问题
with st.chat_message("user"):
st.write(prompt)
到这里,界面的交互逻辑就差不多了,接下来就是把 AI 的回复也加上。
四、流式回复:让聊天框更有 "灵魂"
普通的请求要等 AI 生成完所有内容才会一次性返回,用户体验不好,看着干等很容易以为程序卡了。所以我给它加上了流式回复,让文字像打字机一样一点点出来。
首先,调用接口的时候加上stream=True参数,开启流式返回:
Python
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[
{"role": "system", "content": "你是一个友好的 AI 助手"},
# 用解包运算符把历史消息都传进去,这样AI就能记住上下文
*st.session_state.messages
],
stream=True
)
然后用 Streamlit 的write_stream方法处理流式数据,实现打字机效果:
Python
with st.chat_message("assistant"):
full_response = st.write_stream(
# 把每个chunk里的delta内容拿出来,没有就返回空字符串
chunk.choices[0].delta.content or ""
for chunk in response
if chunk.choices[0].delta.content
)
# 把AI的回复也加到对话历史里,下次提问就能带上上下文了
st.session_state.messages.append({"role": "assistant", "content": full_response})
运行一下,输入问题,AI 的回复就像真人打字一样一点点出来,那一刻我感觉自己离 "AI 产品经理" 又近了一步。

五、完整代码
Python
import os # 导入 os 模块,用于获取环境变量
from dotenv import load_dotenv # 导入 load_dotenv 函数,用于加载环境变量
from openai import OpenAI # 专门用来连接 LLM 的库
import streamlit as st # 专门用来创建应用页面的库
load_dotenv() # 从.env 文件中读取内容
api_key = os.getenv('API_KEY') or st.secrets.get('API_KEY')
client = OpenAI(
api_key=api_key,
base_url="https://api.deepseek.com"
)
# ============================== 创建 Web 应用 ============================== #
st.title("🤖 我是你的专属助理") # 有一个 html 文件,里面写了一个 <h1>🤖 我是你的专属助理</h1>
st.caption("基于 deepseek-v4-flash 模型")
# ============================== 初始化对话历史 ============================== #
# session_state 是 st 提供的会话状态管理器,用于在用户的交互过程中保存数据
if "messages" not in st.session_state:
st.session_state.messages = []
# ============================== 显示历史消息 =============================== #
for message in st.session_state.messages:
with st.chat_message(message["role"]): # 创建一个消息容器
st.write(message["content"]) # 往容器中写入内容
# ========================= 处理用户输入的内容 =============================== #
if prompt := st.chat_input("请输入你的问题..."): # 创建一个 input 框,用于接收用户的问题
# 将用户的问题添加到对话历史中
st.session_state.messages.append({"role": "user", "content": prompt})
# 在页面上展示用户的问题
with st.chat_message("user"):
st.write(prompt)
# 创建一个 AI 响应的容器
with st.chat_message("assistant"):
# 调用 deepseek,并获取到响应,写入容器中
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[ # 用户的问题
{"role": "system", "content": "你是一个友好的 AI 助手"},
# * 解包运算符,将一个数组中的内容,解包到另一个数组中
*st.session_state.messages
],
stream=True
)
# 处理流式资源
full_response = st.write_stream(
chunk.choices[0].delta.content or ""
for chunk in response
if chunk.choices[0].delta.content
)
# 将 AI 返回的内容添加到历史消息中
st.session_state.messages.append({"role": "assistant", "content": full_response})
DeepSeek API 文档:api-docs.deepseek.com/zh-cn/
streamlit官网:streamlit.io/
六、踩坑记录:写 demo 哪有不踩坑的?
当然,写代码的过程也不是一帆风顺的,这里给大家分享几个我踩过的坑,帮你们少走弯路:
- API 密钥泄露风险 :一开始我直接把密钥写在代码里,后来想想太危险了,赶紧改成了
.env文件 +dotenv的方式,部署的时候用 Streamlit 的 secrets,安全多了。 - 忘记加
stream=True:一开始没开流式,用户要等好久才能看到回复,体验极差,加上之后瞬间丝滑。 - 对话历史没传全 :第一次调用接口的时候,只传了用户的当前问题,没传历史消息,AI 根本不知道上下文,每次回答都像失忆了一样,加上
*st.session_state.messages就好了。 - Streamlit 会话状态的坑 :一开始没初始化
messages,刷新页面就报错,后来加上if "messages" not in st.session_state:就解决了。
结语
短短几十分钟,一个极简的 AI 聊天框 demo 就搞定了。它没有复杂的 UI,没有花哨的功能,但麻雀虽小五脏俱全 ------ 环境变量配置、API 调用、流式回复、对话历史、Web 界面,一个 AI 聊天应用该有的基础功能它都有了。
如果你也想试试,那就来!毕竟,谁不是从写 demo 开始的呢?