如何构建一个互连的多页面Streamlit应用程序

构建互连多页面Streamlit应用程序

从规划到执行:我如何构建GPT实验室

照片由Clément Hélardot在Unsplash上提供

注意:本文最初在Streamlit博客上发布。我希望在这里与VoAGI社区分享。

哇!自从我首次发布了关于构建GPT Lab的教训的博客文章以来,已经过去了令人难以置信的三个月!🚀

在您的大力支持下,GPT Lab已经获得了超过9K的应用程序查看次数,1150多个独特的登录用户,900多个与助手的会话,650多个测试提示和180多个创建的助手。该应用程序还在Streamlit应用程序库中与其他优秀的应用程序一起展示。

http://gptlab.streamlit.app/?embed=true

你们中的许多人问我:“你是如何计划和构建如此大型的Streamlit应用程序的?”我急于回答,我决定开源GPT Lab。

在本文中,我将分享这个雄心勃勃项目背后的策略和思考过程。我希望它能激励您将Streamlit推到极限,让您的雄心勃勃的应用程序变为现实。

💡 想要快速了解吗?查看应用程序和代码。

规划大型的Streamlit应用程序

构建像GPT Lab这样的大型Streamlit应用程序需要仔细的规划,而不仅仅是随便编写代码。对于GPT Lab,我专注于规划以下四个关键方面:

  1. 功能和用户体验。应用程序将做什么?我们希望提供什么样的用户体验?
  2. 数据模型。数据将如何持久化?数据库和会话状态变量应存储哪些数据?
  3. 代码结构。应用程序应该如何设计架构,以确保模块化、可维护性和可扩展性?
  4. 会话状态。哪些会话状态变量需要用于链接用户界面?

了解这些方面为我构建提供了更清晰的视角,并为系统地处理这个复杂任务提供了框架。

让我们更详细地探讨每个方面。

功能和用户体验:创建初始规范和低保真UX模型

首先,我创建了一个简单的规范文档(或“规范”),概述了总体范围和方法。我还包括了一个详细说明我想要支持的用例的网站地图。规范为我提供了一个清晰的路线图和衡量进度的手段。

以下是原始规范的摘录:

范围

构建一个平台,让生成式人工智能(GA)机器人爱好者为他们的朋友和家人构建自己的基于GPT-3提示的聊天机器人。目标是测试足够多的GA机器人爱好者是否希望构建他们的细分领域机器人。

方法

一个公共的Streamlit网站,允许用户与四个预训练的教练机器人之一进行交互,或创建并与他们的机器人进行交互。

与大多数开发项目一样,我进行了一些修改。但是原始的网站地图在很大程度上保持不变,因为我能够实现大部分计划中的功能。

以下是网站地图的最终版本:

GPT Lab│├── 首页│├── 休息室│├── 助手│   ├── 搜索助手│   ├── 助手详情│   ├── 活跃聊天│   └── 聊天回顾│├── 实验室│   ├── 第1步:初始提示+模型配置│   ├── 第2步:测试聊天│   ├── 第3步:其他配置│   └── 第4步:确认│├── 常见问题│└── 法律    ├── 条款    └── 隐私政策

无法过分强调功能规划的重要性。它提供了一个路线图,衡量进展的方式,并为思考数据模型提供了一个起点。

数据模型:确定模式

从一开始,我就意识到后端数据存储对于持久化用户、助手和会话记录至关重要。在考虑了各种选择后,我决定使用Google Firestore,因为它具有可扩展性、实时能力和慷慨的免费套餐。为了支持未来的扩展,我还战略性地设计了数据模型。虽然当前应用只使用了它的一小部分潜力,但是将来可以在GPT实验室中添加提示版本控制。这将使用户能够编辑或还原他们的助手。

💡 注意:在应用的后端和数据模型中,助手被称为机器人,尽管我之前坚持不在用户界面上称它们为机器人 😅。

现在,让我们来探索GPT实验室中的四个主要Firestore集合:users(用户)、user_hash(用户哈希)、bots(机器人)和sessions(会话)。

Users和user_hash

users集合是应用存储有关用户信息的地方。为了保护用户隐私,该应用不会存储任何关于用户的可识别信息(PII)。相反,每个用户仅关联其OpenAI API密钥的单向哈希值。当用户创建助手或与助手开始/结束会话时,度量字段会递增。这允许在应用内进行基本的分析数据收集。

Users集合   |   | - id:(Firestore自动生成的ID)   | - user_hash:字符串(OpenAI API密钥的单向哈希值)   | - created_date:日期时间   | - last_modified_date:日期时间   | - sessions_started:数字   | - sessions_ended:数字   | - bots_created:数字

Google Firestore不提供一种在集合内确保文档字段值唯一性的方法,因此我创建了一个名为user_hash的单独集合。这确保每个唯一的API密钥只有一个关联的用户记录。每个用户文档与一个user_hash文档唯一关联,每个user_hash文档可以与一个用户文档关联。该数据模型足够灵活,以适应未来更改其API密钥的用户(用户可以使用其旧API密钥登录,然后将其更换为新的API密钥)。

User_hash集合   |   | - id = OpenAI API密钥的单向哈希值   | - user_hash_type:字符串(open_ai_key)   | - created_date:日期时间

Bots

bots集合存储AI助手的配置。每个AI助手的关键是其大型语言模型(LLM)、模型配置和提示。为了将来能够正确控制提示和模型配置的版本,model_configs和prompts被建模为子集合(GPT实验室的愿景之一是成为您提示的存储库)。

为了最小化子集合的读取次数(这样您就不需要不断查询活动记录的子集合),活动子集合的文档ID也存储在文档级别。session_type字段指示助手是处于头脑风暴还是辅导会话中,这会影响会话消息截断技术。

最后,当用户与助手开始或结束会话时,度量字段会递增。

Bots集合   |   | - id:(Firestore自动生成的ID)   | - name:字符串   | - tag_line:字符串   | - description:字符串   | - session_type:数字   | - creator_user_id:字符串   | - created_date:日期时间   | - last_modified_date:日期时间   | - active_initial_prompt_id:字符串   | - active_model_config_id:字符串   | - active_summary_prompt_id:字符串   | - showcased:布尔值   | - is_active:布尔值   |   v   |--> Model_configs子集合   |     |   |     | - config:映射   |     |     | - model:字符串    |     |     | - max_tokens:数字    |     |     | - temperature:数字    |     |     | - top_p:数字    |     |     | - frequency_penalty:数字    |     |     | - presence_penalty:数字    |     | - created_date:日期时间   |     | - is_active:布尔值   |   v   |--> Prompts子集合         |         | - message_type:字符串         | - message:字符串         | - created_date:日期时间         | - is_active:布尔值         | - sessions_started:数字         | - sessions_ended:数字

Sessions

sessions集合存储会话数据。它包含两种类型的会话:实验室会话(用于测试提示)和助手会话(用于与创建的助手聊天)。为了减少频繁检索机器人文档的需要,其信息被缓存在会话文档中。这在概念上是有意义的,因为如果以后实现了编辑助手的用例,机器人文档可能会发生变化。

字段messages_str存储了最近发送给OpenAI LLM的有效载荷。该功能允许用户恢复之前的助手会话。子集合messages存储实际的聊天消息。请注意,实验室会话的聊天消息不会被存储。

为了保证用户的机密性和隐私,OpenAI会在保存到数据库之前对请求有效载荷和会话消息进行加密。这个数据模型允许用户重新启动之前的会话,并继续与助手聊天。

Sessions Collection   |   | - id: (Firestore自动ID)   | - user_id: 字符串   | - bot_id: 字符串   | - bot_initial_prompt_msg: 字符串   |   | - bot_model_config: 映射   |     | - model: 字符串    |     | - max_tokens: 数字    |     | - temperature: 数字    |     | - top_p: 数字    |     | - frequency_penalty: 数字    |     | - presence_penalty: 数字    |   | - bot_session_type: 数字   | - bot_summary_prompt_msg: 字符串   | - created_date: 日期时间   | - session_schema_version: 数字   | - status: 数字   | - message_count: 数字   | - messages_str: 字符串(已加密)   |   v   |--> Messages子集合         |         | - created_date: 日期时间         | - message: 字符串(已加密)         | - role: 字符串

通过从一开始就仔细考虑所有潜在的用例,我创建了一个未来可扩展并能够适应应用程序不断发展的需求和功能的数据模型。在下一节中,我们将研究后端应用程序代码的结构,以了解它如何支持和实现这个强大的数据模型。

代码结构:可扩展性和模块化的结构

我创建了GPT Lab,以赋予用户低或无技术技能的能力,构建自己的基于提示的LLM AI应用程序,而无需担心底层基础设施。我的目标是最终提供后端API,将用户的自定义前端应用程序(无论是否使用Streamlit)与他们的AI助手连接起来。这促使我设计了一个解耦的架构,将前端Streamlit应用程序与后端逻辑分离开来。

后端代码的结构如下:

+----------------+     +-------------------+     +-------------------+     +------------+|                |     |                   |     |                   |     |            ||  Streamlit应用程序 |<--->| util_collections  |<--->| api_util_firebase |<--->|  Firestore ||                |     | (users, sessions, |     |                   |     |            ||                |     |  bots)            |     |                   |     |            |+----------------+     +-------------------+     +-------------------+     +------------+                             |                             |                             v                     +-----------------+     +------------+                     |                 |     |            |                     | api_util_openai |<--->|   OpenAI   |                     |                 |     |            |                     +-----------------+     +------------+

模块如下:

  • api_util_firebase处理与Firestore数据库的CRUD操作。
  • api_util_openai与OpenAI的模型进行交互,为上游模型提供统一的聊天模型,修剪聊天消息,并尝试检测和防止提示注入攻击。
  • api_util_usersapi_util_sessionsapi_util_bots是它们对应的Firestore集合的接口。它们与api_util_firebase和api_util_openai进行交互,并实现特定于GPT Lab的业务逻辑。

这种设计使得不同部分的代码可以分别开发、测试和扩展。它还为将后端util_collections模块转换为Google Cloud Functions提供了更简单的迁移路径,这些函数可以通过API Gateways公开。

会话状态:管理UI和用户流程

如第一篇博客文章所述,我使用会话状态变量来控制和管理Streamlit页面上的功能。以下说明了这些变量在整个应用程序中的使用方式:

home.py

  • user控制是否呈现OpenAI API密钥模块

pages/1_lounge.py

  • user控制是否呈现OpenAI API密钥模块,启用助手选择,并显示“我的助理”选项卡。
  • 用户选择与助手交互后,助手的详细信息存储在bot_info中。

pages/2_assistant.py

  • user 控制是否呈现OpenAI API密钥模块。
  • bot_infosession_idsession_ended确定显示哪个屏幕变体。
  • bot_info不存在:检查URL参数中是否存在assistant_id。否则,提示用户搜索助手。
  • bot_infosession_id存在,且session_ended为false:显示聊天会话屏幕。
  • bot_infosession_id存在,且session_ended为true:显示聊天会话回顾屏幕。
  • 在聊天会话中,session_msg_list存储对话内容。

pages/3_lab.py

  • user 控制是否呈现OpenAI API密钥模块以及是否允许用户在实验室中开始创建助手。
  • lab_active_step 控制要呈现的实验室会话状态:
  • 如果为1:呈现步骤1的用户界面以设置助手的初始提示和模型。
  • 如果为2:呈现步骤2的用户界面以与助手进行聊天测试。
  • 如果为3:呈现步骤3的用户界面以完成助手的详细信息。在创建时,会在Firestore DB中创建机器人记录,并将文档ID保存到lab_bot_id中。
  • 如果为4且lab_bot_id已设置:呈现步骤4的用户界面以显示助手创建确认。
  • 在测试聊天会话期间,lab_msg_list存储测试消息。通过使用单独的lab_bot_idbot_info,我可以允许用户在休息室/助手和实验室之间来回切换,而不会丢失每个部分的进度。

通过预先计划,其余的执行变得更加可管理。

总结

在本文中,我介绍了创建GPT Lab所需的预先计划,包括功能、数据模型、代码和会话状态。希望这能激发您构建自己雄心勃勃的Streamlit应用程序。

欢迎在Twitter或Linkedin上与我联系。我很愿意听到您的声音。

Streamlit愉快!🎈