项目完整代码

# 导入streamlit库
import streamlit as st
import os
from openai import OpenAI
from datetime import datetime
import json


#保存会话信息的函数
def save_session():
    # 格式化
    if st.session_state.current_session:
        session_data = {
            "nick_name": st.session_state.nick_name,
            "character": st.session_state.character,
            "current_session": st.session_state.current_session,
            "messages": st.session_state.messages
        }

        # 如果session文件不存在,则创建文件
        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:
            print("------->会话标识:",st.session_state.current_session)
            json.dump(session_data, f, ensure_ascii=False, indent=4)


#加载所有的会话信息
def load_sessions():
    #创建一个空列表
    file_list = []
    if os.path.exists("sessions"):
        all_file = os.listdir("sessions")
        for filename in all_file:
            if filename.endswith(".json"):
               file_list.append(filename[:-5])
    file_list.sort(reverse=True)#倒序
    return file_list

#加载会话信息
def load_session(session_name):
    try:
        with open(f"sessions/{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("会话加载失败")


# 删除会话信息
def delete_session(session_name):
    try:
        if os.path.exists(f"sessions/{session_name}.json"):
            os.remove(f"sessions/{session_name}.json")
            st.success("会话删除成功")
    except Exception:
        st.error("会话删除失败")

#保存当前时间的函数
def save_current_time():
        return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

# 初始化聊天信息
if "messages" not in st.session_state:
    st.session_state.messages = [] #列表用来存放value:{"role": "user", "content": "你好"}

# 初始化昵称
if "nick_name" not in st.session_state:
    st.session_state.nick_name = "小灵"

# 初始化性格
if "character" not in st.session_state:
    st.session_state.character = "活泼开朗的台湾姑娘"

#初始化会话信息
if "current_session" not in st.session_state:
    st.session_state.current_session = save_current_time()

# 页面布局
st.set_page_config(
    page_title="Ai智能伴侣",
    page_icon="😻",
    layout="wide",
    initial_sidebar_state="expanded",
    menu_items={}
)

# 大标题
st.title("Ai智能伴侣")
st.text(f"当前会话名称:{st.session_state.current_session}")
#左侧的侧边栏
with st.sidebar:
    #会话信息
    st.subheader("Ai控制面板")

    if st.button("新建会话",width="stretch",icon="✏️"):

        #保存会话信息
        save_session()

        #新建会话信息
        if st.session_state.messages:
            st.session_state.messages = []
            st.session_state.current_session = save_current_time()
            save_session()

    #会话历史
    st.text("历史会话")
    session_list = load_sessions()
    for session in session_list:
        print("------------>session目前的值为:",session)
        coil1,coil2 = st.columns([4,1])
        #加载会话信息
        with coil1:
            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 coil2:
            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

    #性格
    character = st.text_area("性格",placeholder="请输入伴侣的性格",value=st.session_state.character)
    if character:
        st.session_state.character = character


# 遍历聊天信息
for message in st.session_state.messages:
    if message["role"] == "user":
        st.chat_message("user").write(message["content"])
    else:
        st.chat_message("assistant").write(message["content"])


# 创建Openai的客户端client
client = OpenAI(api_key=os.environ.get('DEEPSEEK_API_KEY'),base_url="https://api.deepseek.com")

# 系统设定
System_prompt = """
    你叫%s,现在是用户的真实伴侣,请完全代入伴侣角色。
    规则:
        1. 每次只回1条消息
        2.禁止任何场景或状态描述性文字
        3.匹配用户的语言
        4.回复简短,像微信聊天一样
        5.有需要的话可以用❤️🌸等emoji表情
        6.用符合伴侣性格的方式对话
        7.回复的内容,要充分体现伴侣的性格特征
    伴侣性格:
        %s
    你必须严格遵守上述规则来回复用户。
"""

# 输入框
prompt = st.chat_input("快来和我一起聊天吧")
if prompt:# 判断输入框是否为空,如果不为空,则执行以下代码
    st.chat_message("user").write(prompt)
    #保存用户输入的提示词
    st.session_state.messages.append({"role": "user", "content": prompt})

    # 调用Ai大模型进行思考
    response = client.chat.completions.create(
        model="deepseek-v4-pro",
        messages=[
            {"role": "system", "content": System_prompt%( st.session_state.nick_name,st.session_state.character)},
            {"role": "user", "content": prompt},
            #模型每次思考,都会保存在session_state.messages中,所以解包后的内容会作为模型输入
            *st.session_state.messages
        ],
        stream=True,
    )
    # #模型的回复(非流式输出)
    # print("-------------->模型的回答:",response.choices[0].message.content)
    # st.chat_message("assistant").write(response.choices[0].message.content)

    # 创建一个空容器,用来保存模型回复
    response_message = st.empty ()
    # 流式输出
    full_message = ""
    for chunk in response:
        if chunk.choices[0].delta.content is not None:
            content = chunk.choices[0].delta.content
            full_message += content
            response_message.chat_message("assistant").write(full_message)
    # 保存Ai的回复
    st.session_state.messages.append({"role": "assistant", "content": full_message})
    print("-------->",full_message)
    save_session()

Ai应用开发核心技术模块

大模型部署方案

基于Ollama的本地部署方案

Ollama的概念:

  • Ollama 是一个开源的本地大语言模型运行框架,专为在本地机器上便捷部署和运行大型语言模型(LLM)而设计。

  • Ollama 支持多种操作系统,包括 macOS、Windows、Linux 以及通过 Docker 容器运行。

  • Ollama 提供对模型量化的支持,可以显著降低显存要求,使得在普通家用计算机上运行大型模型成为可能。

Ollama本地部署方法:

1. 在ollama中搜索要运行的模型,输入:

ollama run 模型名

如果模型未下载,输入命令可以自动下载,相反就是在终端运行模型并进行对话

2. 设置ollama服务开机自动运行

3. ollama详细学习地址:https://www.runoob.com/ollama/ollama-tutorial.html

基于云端APi调用的方案

部署方案:

1. 通过国产或国外模型服务商调用API_key和模型进行交互

2. 参照官方文档格式进行调用

3. 例如deepseek官方文档:https://api-docs.deepseek.com/zh-cn/

HTTP协议

重要知识概念:

  • HTTP(超文本传输协议,Hypertext Transfer Protocol)是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上,通常使用端口 80。

  • HTTPS(超文本传输安全协议,Hypertext Transfer Protocol Secure)是 HTTP 的安全版本,它在 HTTP 下增加了 SSL/TLS 协议,提供了数据加密、完整性校验和身份验证。HTTPS 通常使用端口 443。

  • HTTP/HTTPS都是基于C/S的架构(C/S:客户端/服务端)

HTTP请求数据格式:

HTTP响应数据格式:

状态码:

1. 200:客户端请求成功

3. 400:请求参数错误

4. 404:请求资源不存在,url输入有误或者网站资源已删除

5. 500:服务器端出现错误

参看方式:

1. 在浏览器查阅网页时按F12可以进入开发者工具

2. 点击数据后,可以看到HTTP的请求数据帧和响应数据帧

大模型交互方案

Ollama

# 导入requests 库

import requests

response = requests.post(

    "http://localhost:11434/api/chat",

    json={

        "model": "deepseek-coder",

        "messages": [

            {

                "role": "user",

                "content": "你好,你能帮我写一段 Python 代码吗?"

            }

        ],

        "stream": False

    }

)

print(response.json())

云端API调用模型(以deepseek为例)

# Please install OpenAI SDK first: pip3 install openai

import os

from openai import OpenAI

client = OpenAI(

    api_key=os.environ.get('DEEPSEEK_API_KEY'),

    base_url="https://api.deepseek.com")

response = client.chat.completions.create(

    model="deepseek-v4-pro",

    messages=[

        {"role": "system", "content": "You are a helpful assistant"},

        {"role": "user", "content": "Hello"},

    ],

    stream=False,

)

print(response.choices[0].message.content)
api_key=os.environ.get('DEEPSEEK_API_KEY')
  • 指的是在环境变量中获取API_key(目的是为保护API_key)

  • 具体步骤:

1. 在高级系统设置中添加用户的环境变量

2. 变量名:DEEPSEEK_API_KEY

3. 变量值为:获取的API_key

流式响应即( stream=true)

- 在选择流式响应后应更改输出的方式,因为流式输出时模型会一个一个token往外吐

- 在借助API访问模型实际上是通过HTTP协议来进行获取,所以我们需要查看模型响应时的响应数据格式

- 可以通过Apifox工具来进行获取,以下如图所示(仍需要查看官方文档,以下以deepseek为例):

/curl https://api.deepseek.com/chat/completions \

  -H "Content-Type: application/json" \

  -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \

  -d '{

        "model": "deepseek-v4-pro",

        "messages": [

          {"role": "system", "content": "You are a helpful assistant."},

          {"role": "user", "content": "Hello!"}

        ],

        "thinking": {"type": "enabled"},

        "reasoning_effort": "high",

        "stream": true # 开启流式响应 

      }'
  • -H:表示请求头

  1. Content-Type: application/json:表示为json格式的数据

  2. Authorization: Bearer

  • -d:表示请求体

  • ${DEEPSEEK_API_KEY}:表示要输入的Api_key

具体操作如下:

  1. 点击发送,查看响应的数据格式

  2. 可以看到数据是一条一条传输,如下所示:

  1. 每一条的响应数据格式如下:

  1. 具体代码如下:

#导入模块

import os

from openai import OpenAI

#创建OpenAI类的实例对象client,传入api_key,设置base_url

client = OpenAI(api_key=os.environ.get('DEEPSEEK_API_KEY'),base_url="https://api.deepseek.com")

#创建一个会话,用response接收模型回答

response = client.chat.completions.create(

    model="deepseek-v4-pro",

    messages=[

        {"role": "system", "content": "You are a helpful assistant"},

        {"role": "user", "content": "Hello"},

    ],

    stream=True# 启用流式输出

)

# #输出模型回答(非流式)

# print(response.choices[0].message.content)

#输出模型回答(流式)

# 创建一个对象存储模型的回答

full_content = ""

for chunk in response:

    current_answer = chunk.choices[0].delta.content

    if current_answer:  # 最后一个chunk可能为None,需要跳过

        full_content += current_answer

        print(current_answer, end="", flush=True)  # 只打印新增内容,立即刷新缓冲区

print()  # 最后换行
  • 具体修改:

大模型会话记忆方案(滚雪球式的)

思路:将自己与Ai的对话在喂给Ai,不断滚雪球

具体代码如下:

Streamlit构建页面

  • 依据官方文档直接调用就行

  • 官方地址:https://docs.streamlit.io/

文件基本操作

思路:就三步

  1. 打开文件

  2. 写入、读取文件

  3. 关闭文件

代码:

#读文件

# 打开文件

file = open("resource/新建 文本文档.txt", "r", encoding="utf-8")

#读取文件

print(file.read())

#关闭文件

file.close()

#创建一个文件

# 打开文件

file = open("resource/message.txt", "w", encoding="utf-8")

# 写入文件

file.write("静夜思\n\n")

file.write("床前明月光\n")

file.write("疑是地上霜\n")

file.write("举头望明月\n")

file.write("低头思故乡\n")

进阶思路:如果操作文件过程中出现异常,应该怎么处理?

繁琐,不推荐:

# 写文件

# 打开文件

file = open("../第三章/resource/新建文本文档.txt", "w", encoding="utf-8")

try :

    # 写入文件

    file.write("静夜思\n\n")

    file.write("床前明月光\n")

    file.write("疑是地上霜\n")

    file.write("举头望明月\n")

    file.write("低头思故乡\n")

finally:

    # 关闭文件

    file.close()

# 关闭文件

file.close()

推荐写法:

# 写文件

# 打开文件

with open("../第三章/resource/新建文本文档.txt", "w", encoding="utf-8") as file:

    # 写入文件

    file.write("静夜思\n\n")

    file.write("床前明月光\n")

    file.write("疑是地上霜\n")

    file.write("举头望明月\n")

    file.write("低头思故乡\n")

```

- with 是上下文管理器(Context Manager) 语句,用于自动管理资源的获取和释放,确保退出代码块时一定执行清理操作,即使发生异常。

## json操作

```

import  json

user_info = {

    "name": "王林",

    "age": 18,

    "sex": "男",

    "habit":["看电影", "看小说", "看动画"],

       }

# 写入json文件

with open("resource/user.json", "w", encoding="utf-8") as f:

    json.dump(user_info, f, ensure_ascii=False, indent=4)

    print("写入成功")

# 读取json文件

with open("resource/user.json", "r", encoding="utf-8") as f:

    user_info = json.load(f)

    print(user_info)
  1. 在使用json存储数据时,需调用json库

  2. json.dump(“要写入的数据”,''存储的文件名'')方法:

  • ensure_ascii=False: 关闭ascil,因为ascil只能存储数字与字母,无法存储中文字体,因为默认为true,所以在存储中文时要进行关闭

  • indent=:表示缩进,默认为None,建议进行缩进,数据格式清晰易查看

os/datetime模块

os:os 模块是 Python 与操作系统之间的桥梁,提供文件系统操作、进程管理、环境变量访问等跨平台能力。

以代码举例:

os.path.exists("sessions"):表示检验sessions路径是否存在,存在返回true; 不存在返回flase

os.listdir("sessions"):返回指定目录下的所有文件和子目录名称的列表(仅名字,不含路径前缀)。

os.mkdir(""): # 创建单层目录(父目录必须存在)

os.remove("") # 删除单层目录