一、项目介绍
项目的目标是开发一个仿照 ChatGPT、文心一言的 AI 聊天助手网站,重点实现 “带上下文记忆的连续对话” 功能,以下从界面结构、核心交互逻辑、关键功能亮点三方面整理具体效果:
1、界面结构:侧边栏 + 主聊天区
网站界面分为两大核心区域,布局简洁且符合主流 AI 聊天工具的使用习惯:
- 左侧侧边栏
- 核心功能:供用户输入自定义 API 密钥(用于调用 AI 模型生成内容)。
- 辅助功能:提供可跳转的OpenAI 官方 API 密钥获取链接,帮助用户快速了解如何获取密钥,降低使用门槛。
- 主聊天区
2、核心交互逻辑:密钥验证→连续对话
整个助手的交互流程围绕 “密钥有效性” 和 “上下文记忆” 展开,步骤清晰:
2.1、前置:API 密钥验证
- 若用户未输入 API 密钥:聊天功能无法开启,系统会主动提醒 “请输入 API 密钥”,避免因密钥缺失导致模型调用失败。
- 若用户输入有效 API 密钥:验证通过后,聊天功能解锁,用户可开始与 AI 助手交互。
2.2、核心:带记忆的连续对话
这是项目三与前两个项目(仅支持一次性互动)的核心区别,具体效果如下:
- 多轮对话连贯性:用户可发起多轮追问,所有消息(用户提问 + AI 回答)会按时间顺序依次展示在主聊天区(新消息自动追加在历史消息下方)。
- 上下文记忆能力:AI 能识别并关联历史对话信息。例如:
- 用户第一轮提问 “牛顿第二定律是什么?”,AI 给出解释;
- 用户第二轮追问 “它的公式如何推导?”,AI 会自动识别 “它” 指代 “牛顿第二定律”,基于上一轮内容给出推导过程,无需用户重复说明主语。
3、关键功能亮点
- 用户自主性高:支持自定义 API 密钥,用户可使用自己的账号资源调用模型,无需依赖项目方提供的 API 服务,灵活度更高。
- 易用性设计:通过 “官方链接指引”“无密钥提醒” 等细节,降低用户操作成本,尤其适合对 API 密钥获取不熟悉的新手。
- 核心能力突破:实现 “上下文记忆”,解决前序项目 “单次互动无关联” 的问题,让对话更贴近真实交流场景(如日常咨询、知识问答、任务协作等)。
二、创建AI请求
1、前期准备:环境搭建
创建项目结构: 新建项目文件夹,将
requirements.txt
文件放入其中(包含项目所需依赖)。txtaiohttp==3.9.3 aiosignal==1.3.1 altair==5.2.0 annotated-types==0.6.0 anyio==4.3.0 async-timeout==4.0.3 attrs==23.2.0 blinker==1.7.0 cachetools==5.3.3 certifi==2024.2.2 charset-normalizer==3.3.2 click==8.1.7 dataclasses-json==0.6.4 distro==1.9.0 exceptiongroup==1.2.0 frozenlist==1.4.1 gitdb==4.0.11 GitPython==3.1.42 greenlet==3.0.3 h11==0.14.0 httpcore==1.0.4 httpx==0.27.0 idna==3.6 Jinja2==3.1.3 jsonpatch==1.33 jsonpointer==2.4 jsonschema==4.21.1 jsonschema-specifications==2023.12.1 langchain==0.1.12 langchain-community==0.0.28 langchain-core==0.1.31 langchain-openai==0.0.8 langchain-text-splitters==0.0.1 langsmith==0.1.25 markdown-it-py==3.0.0 MarkupSafe==2.1.5 marshmallow==3.21.1 mdurl==0.1.2 multidict==6.0.5 mypy-extensions==1.0.0 numpy==1.26.4 openai==1.14.0 orjson==3.9.15 packaging==23.2 pandas==2.2.1 pillow==10.2.0 protobuf==4.25.3 pyarrow==15.0.1 pydantic==2.6.4 pydantic_core==2.16.3 pydeck==0.8.1b0 Pygments==2.17.2 python-dateutil==2.9.0.post0 pytz==2024.1 PyYAML==6.0.1 referencing==0.33.0 regex==2023.12.25 requests==2.31.0 rich==13.7.1 rpds-py==0.18.0 six==1.16.0 smmap==5.0.1 sniffio==1.3.1 SQLAlchemy==2.0.28 streamlit==1.32.1 tenacity==8.2.3 tiktoken==0.6.0 toml==0.10.2 toolz==0.12.1 tornado==6.4 tqdm==4.66.2 typing-inspect==0.9.0 typing_extensions==4.10.0 tzdata==2024.1 urllib3==2.2.1 yarl==1.9.4
安装依赖: 在终端执行命令,安装所有依赖包:
shpip install -r requirements.txt
新建代码文件: 创建一个 Python 文件(
utils.py
),用于编写与 AI 模型交互的核心逻辑。
2、核心函数
utils.py
中定义get_chat_response
函数,封装与 AI 模型的交互过程,关键是传入 “外部记忆” 以维持对话连贯性。
函数参数设计
函数需接收 3 个核心参数:
prompt
:用户当前的输入提示(字符串)。openai_api_key
:用户提供的 API 密钥(用于调用 AI 模型)。memory
:外部传入的记忆实例(储存历史对话,确保跨函数调用时记忆不丢失)。
函数内部逻辑
utils.py
完整代码
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI
import os
from langchain.memory import ConversationBufferMemory
def get_chat_response(prompt, memory, openai_api_key):
model = ChatOpenAI(model="gpt-3.5-turbo", openai_api_key=openai_api_key)
chain = ConversationChain(llm=model, memory=memory)
response = chain.invoke({"input": prompt})
return response["response"]
关键逻辑解析
- 模型初始化: 使用
ChatOpenAI
创建模型实例,通过openai_api_key
参数传入用户提供的密钥,确保模型调用权限。 - 对话链与记忆关联:
ConversationChain
自动处理 “加载历史记忆→生成回应→更新记忆” 的全流程,无需手动调用load_memory_variables
或save_context
,简化逻辑并减少出错概率。 - 回应提取: 对话链的
invoke
方法返回字典(含input
、history
、response
等字段),仅提取response
字段(AI 的回答内容)作为函数返回值,符合实际需求。
3、功能测试:验证记忆有效性
为确保函数能维持对话记忆,需进行多轮调用测试,在utils.py
中增加如下测试代码:
memory = ConversationBufferMemory(return_messages=True)
print(get_chat_response("牛顿提出过哪些知名的定律?", memory, os.getenv("OPENAI_API_KEY")))
print(get_chat_response("我上一个问题是什么?", memory, os.getenv("OPENAI_API_KEY")))
测试预期结果
- 第一轮:AI 正确回答牛顿第二定律的内容。
- 第二轮:AI 能识别 “它” 指代 “牛顿第二定律”,基于历史对话给出公式,证明记忆生效(对话连贯性成立)。
4、核心亮点
- 记忆外部化:记忆实例从外部传入函数,而非在函数内部初始化,确保多轮调用时历史对话不丢失,是实现连续对话的关键。
- 流程自动化:借助
ConversationChain
自动处理记忆加载与更新,避免手动操作可能的遗漏或错误。 - 灵活性:支持用户自定义 API 密钥,模型型号可调整,适配不同场景需求。
三、创建网站页面
通过 Streamlit 构建聊天助手的前端界面,实现 “API 密钥输入→对话展示→用户交互” 的完整功能,最终打造一个可部署、支持连贯对话的仿 ChatGPT 网站。以下是详细实现步骤与解析:
1、前期准备:代码结构与依赖
- 清理测试代码: 删除或注释掉
utils.py
中get_chat_response
函数的测试代码(避免干扰前端调用)。 - 新建前端文件: 创建前端代码文件(如
main.py
),作为网站主页入口。 - 导入核心库: 需导入 Streamlit(构建前端)和后端的
get_chat_response
函数,代码如下:
import streamlit as st
from langchain.memory import ConversationBufferMemory
from utils import get_chat_response
2、前端界面搭建:分模块实现
2.1、页面标题与基础配置
设置网站标题,提升视觉辨识度:
st.title("💬 克隆ChatGPT")
2.2、侧边栏:API 密钥输入
添加侧边栏用于用户输入 API 密钥(复用前序项目逻辑,确保安全性与自主性):
with st.sidebar:
openai_api_key = st.text_input("请输入OpenAI API Key:", type="password")
st.markdown("[获取OpenAI API key](https://platform.openai.com/account/api-keys)")
2.3、会话状态初始化:解决 “记忆重置” 问题
Streamlit 有一个关键特性:用户交互(如输入、点击按钮)或代码修改后,会从头重新运行代码。若直接初始化记忆,会导致每轮对话后记忆被清空,无法维持连贯性。
解决方案:利用st.session_state
(会话状态)储存记忆和对话历史,仅在首次加载时初始化,后续复用已有状态:
if "memory" not in st.session_state:
st.session_state["memory"] = ConversationBufferMemory(return_messages=True)
st.session_state["messages"] = [{"role": "ai",
"content": "你好,我是你的AI助手,有什么可以帮你的吗?"}]
2.4、对话历史展示:区分角色与内容
通过循环迭代st.session_state.messages
,利用 Streamlit 的st.chat_message
函数展示对话,自动区分 “AI” 和 “人类” 角色(含默认图标):
for message in st.session_state["messages"]:
st.chat_message(message["role"]).write(message["content"])
2.5、用户输入框:接收消息并触发对话
使用st.chat_input
创建用户输入框,实现 “输入→验证→调用后端→展示回应” 的流程:
# 用户输入框:获取用户消息
prompt = st.chat_input()
if prompt:
# 1. 验证API密钥:无密钥则提示并终止
if not openai_api_key:
st.info("请输入你的OpenAI API Key")
st.stop()
# 2. 储存并展示用户消息
# 加入对话历史
st.session_state["messages"].append({"role": "human", "content": prompt})
# 前端展示用户消息
st.chat_message("human").write(prompt)
# 3. 调用后端函数获取AI回应(带加载提示)
with st.spinner("AI正在思考中,请稍等..."):
# 传入会话状态中的记忆,维持连贯性
response = get_chat_response(prompt, st.session_state["memory"],
openai_api_key)
# 4. 储存并展示AI回应
msg = {"role": "ai", "content": response}
# 加入对话历史
st.session_state["messages"].append(msg)
# 前端展示AI回应
st.chat_message("ai").write(response)
2.6、完整代码
main.py
import streamlit as st
from langchain.memory import ConversationBufferMemory
from utils import get_chat_response
st.title("💬 克隆ChatGPT")
with st.sidebar:
openai_api_key = st.text_input("请输入OpenAI API Key:", type="password")
st.markdown("[获取OpenAI API key](https://platform.openai.com/account/api-keys)")
if "memory" not in st.session_state:
st.session_state["memory"] = ConversationBufferMemory(return_messages=True)
st.session_state["messages"] = [{"role": "ai",
"content": "你好,我是你的AI助手,有什么可以帮你的吗?"}]
for message in st.session_state["messages"]:
st.chat_message(message["role"]).write(message["content"])
prompt = st.chat_input()
if prompt:
if not openai_api_key:
st.info("请输入你的OpenAI API Key")
st.stop()
st.session_state["messages"].append({"role": "human", "content": prompt})
st.chat_message("human").write(prompt)
with st.spinner("AI正在思考中,请稍等..."):
response = get_chat_response(prompt, st.session_state["memory"],
openai_api_key)
msg = {"role": "ai", "content": response}
st.session_state["messages"].append(msg)
st.chat_message("ai").write(response)
3、功能测试与验证
- 运行前端: 启动 Streamlit 服务
- 测试流程:
- 步骤 1:在侧边栏输入 OpenAI API 密钥。
- 步骤 2:在输入框发送问题(如 “牛顿第二定律是什么?”),观察 AI 回应是否展示。
- 步骤 3:继续追问(如 “它的公式是什么?”),验证 AI 是否能关联历史对话(记忆生效)。
- 预期效果:
- 对话按 “用户→AI” 顺序展示,角色气泡清晰区分。
- 多轮追问时,AI 能理解上下文(如 “它” 指代前序问题中的 “牛顿第二定律”)。
4、可选功能:对话清空(挑战任务)
为提升用户体验,可添加 “清空对话” 按钮,实现 “重置记忆 + 清空历史” 的功能,代码示例如下:
# 在侧边栏添加“清空对话”按钮
with st.sidebar:
if st.button("清空对话"):
# 重置记忆和对话历史
st.session_state.memory = ConversationBufferMemory(return_messages=True)
st.session_state.messages = [{"role": "ai", "content": "你好!我是你的AI助手,有什么可以帮你的吗?"}]
st.rerun() # 重新加载页面,生效清空操作
5、核心亮点
- 会话状态管理:通过
st.session_state
解决 Streamlit 代码重跑导致的 “记忆丢失” 问题,是实现连贯对话的关键。 - 角色化展示:利用
st.chat_message
自动区分 AI 与人类角色,界面贴近 ChatGPT 等主流工具,用户体验友好。 - 流程完整性:包含 “密钥验证→加载提示→对话储存→回应展示” 全流程,逻辑闭环且容错性强(如无密钥时提示)。
6、部署与扩展(后续方向)
- 部署:可通过 Streamlit Community Cloud(免费)、Heroku 等平台部署网站,生成公开链接供他人使用。
- 扩展:可添加 “模型选择”(如 GPT-3.5/GPT-4)、“对话导出”(保存为 TXT/CSV)等功能,提升工具实用性。