跳过内容

使用 Delphic 构建全栈 LlamaIndex Web 应用指南#

本指南旨在引导您使用 LlamaIndex 和一个名为 Delphic 的生产就绪型 Web 应用入门模板。此处所有代码示例均可从 Delphic 仓库获取。

我们正在构建什么#

这是 Delphic 开箱即用功能的快速演示

https://user-images.githubusercontent.com/5049984/233236432-aa4980b6-a510-42f3-887a-81485c9644e6.mp4

架构概览#

Delphic 利用 LlamaIndex Python 库,允许用户创建自己的文档集,然后可以在响应式前端中查询这些文档集。

我们选择了一个技术栈,它提供了响应迅速、健壮的技术组合,可以 (1) 协调复杂的 Python 处理任务,同时提供 (2) 现代、响应式的前端和 (3) 安全的后端,以便在此基础上构建附加功能。

核心库包括

  1. Django
  2. Django Channels
  3. Django Ninja
  4. Redis
  5. Celery
  6. LlamaIndex
  7. Langchain
  8. React
  9. Docker & Docker Compose

凭借这个基于超稳定 Django Web 框架构建的现代技术栈,Delphic 入门应用拥有流畅的开发者体验、内置身份验证和用户管理、异步向量存储处理以及基于 WebSocket 的查询连接,从而实现了响应式 UI。此外,我们的前端采用 TypeScript 构建,并基于 MUI React,提供了响应迅速、现代的用户界面。

系统要求#

Celery 不支持 Windows。它可能可以通过 Windows Subsystem for Linux 进行部署,但配置过程超出了本教程的范围。因此,我们建议您仅在运行 Linux 或 OSX 时遵循本教程。您需要安装 Docker 和 Docker Compose 来部署应用。本地开发需要 Node Version Manager (nvm)。

Django 后端#

项目目录概览#

Delphic 应用的后端目录结构清晰,遵循常见的 Django 项目惯例。从仓库根目录的 ./delphic 子文件夹开始,主要文件夹有

  1. contrib: 此目录包含对 Django 内置 contrib 应用的自定义修改或添加。
  2. indexes: 此目录包含与文档索引和 LLM 集成相关的核心功能。它包括

  3. admin.py: 应用的 Django admin 配置

  4. apps.py: 应用配置
  5. models.py: 包含应用的数据库模型
  6. migrations: 包含应用数据库模式迁移的目录
  7. signals.py: 定义应用的任何信号
  8. tests.py: 应用的单元测试

  9. tasks: 此目录包含使用 Celery 进行异步处理的任务。index_tasks.py 文件包含用于创建向量索引的任务。

  10. users: 此目录专门用于用户管理,包括
  11. utils: 此目录包含在整个应用中使用的实用模块和函数,例如自定义存储后端、路径辅助函数和集合相关实用工具。

数据库模型#

Delphic 应用有两个核心模型:DocumentCollection。这些模型代表了应用在使用 LLM 索引和查询文档时处理的中心实体。它们在 ./delphic/indexes/models.py 中定义。

  1. Collection:

  2. api_key: 将集合链接到 API 密钥的外键。这有助于将任务与源 API 密钥关联。

  3. title: 一个字符字段,提供集合的标题。
  4. description: 一个文本字段,提供集合的描述。
  5. status: 一个字符字段,存储集合的处理状态,利用 CollectionStatus 枚举。
  6. created: 一个日期时间字段,记录集合创建的时间。
  7. modified: 一个日期时间字段,记录集合最后修改的时间。
  8. model: 一个文件字段,存储与集合关联的模型。
  9. processing: 一个布尔字段,指示集合当前是否正在处理。

  10. Document:

  11. collection: 将文档链接到集合的外键。这表示文档和集合之间的关系。

  12. file: 一个文件字段,存储上传的文档文件。
  13. description: 一个文本字段,提供文档的描述。
  14. created: 一个日期时间字段,记录文档创建的时间。
  15. modified: 一个日期时间字段,记录文档最后修改的时间。

这些模型为文档集以及使用 LlamaIndex 从中创建的索引提供了坚实的基础。

Django Ninja API#

Django Ninja 是一个 Web 框架,用于使用 Django 和 Python 3.7+ 类型提示构建 API。它提供了一种简单、直观、富有表现力的方式来定义 API 端点,利用 Python 的类型提示自动生成输入验证、序列化和文档。

在 Delphic 仓库中,./config/api/endpoints.py 文件包含 API 端点的路由和逻辑。现在,我们简要介绍一下 endpoints.py 文件中每个端点的用途

  1. /heartbeat: 一个简单的 GET 端点,用于检查 API 是否正常运行。如果 API 可访问,则返回 True。这对于希望能够查询您的容器以确保其正常运行的 Kubernetes 设置很有帮助。

  2. /collections/create: 一个 POST 端点,用于创建新的 Collection。接受表单参数,例如 titledescriptionfiles 列表。为每个文件创建新的 CollectionDocument 实例,并安排一个 Celery 任务来创建索引。

@collections_router.post("/create")
async def create_collection(
    request,
    title: str = Form(...),
    description: str = Form(...),
    files: list[UploadedFile] = File(...),
):
    key = None if getattr(request, "auth", None) is None else request.auth
    if key is not None:
        key = await key

    collection_instance = Collection(
        api_key=key,
        title=title,
        description=description,
        status=CollectionStatusEnum.QUEUED,
    )

    await sync_to_async(collection_instance.save)()

    for uploaded_file in files:
        doc_data = uploaded_file.file.read()
        doc_file = ContentFile(doc_data, uploaded_file.name)
        document = Document(collection=collection_instance, file=doc_file)
        await sync_to_async(document.save)()

    create_index.si(collection_instance.id).apply_async()

    return await sync_to_async(CollectionModelSchema)(...)
  1. /collections/query — 一个 POST 端点,用于使用 LLM 查询文档集。接受包含 collection_idquery_str 的 JSON payload,并返回通过查询集合生成的响应。我们实际上并未在聊天 GUI 中使用此端点(我们使用 WebSocket - 见下文),但您可以构建一个应用来集成此 REST 端点以查询特定的集合。
@collections_router.post(
    "/query",
    response=CollectionQueryOutput,
    summary="Ask a question of a document collection",
)
def query_collection_view(
    request: HttpRequest, query_input: CollectionQueryInput
):
    collection_id = query_input.collection_id
    query_str = query_input.query_str
    response = query_collection(collection_id, query_str)
    return {"response": response}
  1. /collections/available: 一个 GET 端点,返回使用用户 API 密钥创建的所有集合的列表。输出使用 CollectionModelSchema 进行序列化。
@collections_router.get(
    "/available",
    response=list[CollectionModelSchema],
    summary="Get a list of all of the collections created with my api_key",
)
async def get_my_collections_view(request: HttpRequest):
    key = None if getattr(request, "auth", None) is None else request.auth
    if key is not None:
        key = await key

    collections = Collection.objects.filter(api_key=key)

    return [{...} async for collection in collections]
  1. /collections/{collection_id}/add_file: 一个 POST 端点,用于向现有集合添加文件。接受 collection_id 路径参数以及 filedescription 等表单参数。将文件作为与指定集合关联的 Document 实例添加。
@collections_router.post(
    "/{collection_id}/add_file", summary="Add a file to a collection"
)
async def add_file_to_collection(
    request,
    collection_id: int,
    file: UploadedFile = File(...),
    description: str = Form(...),
):
    collection = await sync_to_async(Collection.objects.get)(id=collection_id)

Websockets 简介#

WebSockets 是一种通信协议,通过单个长连接实现客户端和服务器之间的双向全双工通信。WebSocket 协议设计用于与 HTTP 和 HTTPS(端口 80 和 443)相同的端口工作,并使用类似的握手过程来建立连接。一旦连接建立,数据可以作为“帧”在两个方向发送,而无需像传统 HTTP 请求那样每次都重新建立连接。

使用 WebSockets 有几个原因,特别是在处理需要很长时间加载到内存但一旦加载后运行速度很快的代码时

  1. 性能: WebSockets 消除了每次请求打开和关闭多个连接的开销,减少了延迟。
  2. 效率: WebSockets 允许实时通信,无需轮询,从而更有效地利用资源并提供更好的响应能力。
  3. 可扩展性: WebSockets 可以处理大量并发连接,使其成为需要高并发的应用的理想选择。

在 Delphic 应用中,使用 WebSockets 是有意义的,因为 LLM 加载到内存中可能成本较高。通过建立 WebSocket 连接,LLM 可以保留在内存中,从而使后续请求能够快速处理,而无需每次都重新加载模型。

ASGI 配置文件 ./config/asgi.py 定义了应用应如何处理传入连接,使用 Django Channels 的 ProtocolTypeRouter 根据连接的协议类型进行路由。在这种情况下,我们有两种协议类型:“http”和“websocket”。

“http”协议类型使用标准的 Django ASGI 应用来处理 HTTP 请求,而“websocket”协议类型使用自定义的 TokenAuthMiddleware 来验证 WebSocket 连接。TokenAuthMiddleware 中的 URLRouterCollectionQueryConsumer 定义了一个 URL 模式,后者负责处理与查询文档集相关的 WebSocket 连接。

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
        "websocket": TokenAuthMiddleware(
            URLRouter(
                [
                    re_path(
                        r"ws/collections/(?P<collection_id>\w+)/query/$",
                        CollectionQueryConsumer.as_asgi(),
                    ),
                ]
            )
        ),
    }
)

此配置允许客户端与 Delphic 应用建立 WebSocket 连接,以便使用 LLM 高效查询文档集,而无需为每个请求重新加载模型。

Websocket 处理器#

config/api/websockets/queries.py 中的 CollectionQueryConsumer 类负责处理与查询文档集相关的 WebSocket 连接。它继承自 Django Channels 提供的 AsyncWebsocketConsumer 类。

CollectionQueryConsumer 类有三个主要方法

  1. connect: 在 WebSocket 作为连接过程的一部分进行握手时调用。
  2. disconnect: 在 WebSocket 因任何原因关闭时调用。
  3. receive: 在服务器从 WebSocket 接收到消息时调用。

Websocket 连接监听器#

connect 方法负责建立连接,从连接路径中提取集合 ID,加载集合模型,并接受连接。

async def connect(self):
    try:
        self.collection_id = extract_connection_id(self.scope["path"])
        self.index = await load_collection_model(self.collection_id)
        await self.accept()

    except ValueError as e:
        await self.accept()
        await self.close(code=4000)
    except Exception as e:
        pass

Websocket 断开连接监听器#

在这种情况下,disconnect 方法是空的,因为当 WebSocket 关闭时没有需要采取的额外操作。

Websocket 接收监听器#

receive 方法负责处理来自 WebSocket 的传入消息。它接收传入消息,对其进行解码,然后使用提供的查询来查询已加载的集合模型。然后将响应格式化为 Markdown 字符串,并通过 WebSocket 连接发送回客户端。

async def receive(self, text_data):
    text_data_json = json.loads(text_data)

    if self.index is not None:
        query_str = text_data_json["query"]
        modified_query_str = f"Please return a nicely formatted markdown string to this request:\n\n{query_str}"
        query_engine = self.index.as_query_engine()
        response = query_engine.query(modified_query_str)

        markdown_response = f"## Response\n\n{response}\n\n"
        if response.source_nodes:
            markdown_sources = (
                f"## Sources\n\n{response.get_formatted_sources()}"
            )
        else:
            markdown_sources = ""

        formatted_response = f"{markdown_response}{markdown_sources}"

        await self.send(json.dumps({"response": formatted_response}, indent=4))
    else:
        await self.send(
            json.dumps(
                {"error": "No index loaded for this connection."}, indent=4
            )
        )

为了加载集合模型,使用了 load_collection_model 函数,该函数位于 delphic/utils/collections.py 中。此函数检索具有给定集合 ID 的集合对象,检查集合模型的 JSON 文件是否存在,如果不存在则创建一个。然后,在使用缓存文件加载 VectorStoreIndex 之前,它会设置 LLMSettings

from llama_index.core import Settings


async def load_collection_model(collection_id: str | int) -> VectorStoreIndex:
    """
    Load the Collection model from cache or the database, and return the index.

    Args:
        collection_id (Union[str, int]): The ID of the Collection model instance.

    Returns:
        VectorStoreIndex: The loaded index.

    This function performs the following steps:
    1. Retrieve the Collection object with the given collection_id.
    2. Check if a JSON file with the name '/cache/model_{collection_id}.json' exists.
    3. If the JSON file doesn't exist, load the JSON from the Collection.model FileField and save it to
       '/cache/model_{collection_id}.json'.
    4. Call VectorStoreIndex.load_from_disk with the cache_file_path.
    """
    # Retrieve the Collection object
    collection = await Collection.objects.aget(id=collection_id)
    logger.info(f"load_collection_model() - loaded collection {collection_id}")

    # Make sure there's a model
    if collection.model.name:
        logger.info("load_collection_model() - Setup local json index file")

        # Check if the JSON file exists
        cache_dir = Path(settings.BASE_DIR) / "cache"
        cache_file_path = cache_dir / f"model_{collection_id}.json"
        if not cache_file_path.exists():
            cache_dir.mkdir(parents=True, exist_ok=True)
            with collection.model.open("rb") as model_file:
                with cache_file_path.open(
                    "w+", encoding="utf-8"
                ) as cache_file:
                    cache_file.write(model_file.read().decode("utf-8"))

        # define LLM
        logger.info(
            f"load_collection_model() - Setup Settings with tokens {settings.MAX_TOKENS} and "
            f"model {settings.MODEL_NAME}"
        )
        Settings.llm = OpenAI(
            temperature=0, model="gpt-3.5-turbo", max_tokens=512
        )

        # Call VectorStoreIndex.load_from_disk
        logger.info("load_collection_model() - Load llama index")
        index = VectorStoreIndex.load_from_disk(
            cache_file_path,
        )
        logger.info(
            "load_collection_model() - Llamaindex loaded and ready for query..."
        )

    else:
        logger.error(
            f"load_collection_model() - collection {collection_id} has no model!"
        )
        raise ValueError("No model exists for this collection!")

    return index

React 前端#

概览#

我们选择使用 TypeScript、React 和 Material-UI (MUI) 作为 Delphic 项目的前端,原因有几个。首先,作为最流行的前端框架 (React) 中最流行的组件库 (MUI),这个选择使得这个项目对庞大的开发者社区来说易于访问。其次,React 在目前是一个稳定且普遍受欢迎的框架,它以虚拟 DOM 的形式提供了有价值的抽象,同时仍然相对稳定,并且在我们看来,相当容易学习,这再次使其易于访问。

前端项目结构#

前端代码位于仓库的 /frontend 目录中,其中与 React 相关的组件位于 /frontend/src。您会注意到 frontend 目录中有一个 DockerFile,以及一些与配置我们的前端 Web 服务器 — nginx 相关的文件夹和文件。

/frontend/src/App.tsx 文件是应用的入口点。它定义了主要的组件,例如登录表单、抽屉布局和集合创建模态框。主要组件根据用户是否已登录并持有身份验证令牌进行条件渲染。

DrawerLayout2 组件在 DrawerLayour2.tsx 文件中定义。此组件管理应用的布局,并提供导航和主要内容区域。

由于应用相对简单,我们可以不使用像 Redux 这样复杂的状态管理解决方案,而只使用 React 的 useState 钩子。

从后端获取集合#

登录用户可用的集合在 DrawerLayout2 组件中检索并显示。该过程可以分解为以下步骤

  1. 初始化状态变量
const [collections, setCollections] = useState<CollectionModelSchema[]>([]);
const [loading, setLoading] = useState(true);

在这里,我们初始化两个状态变量:collections 用于存储集合列表,loading 用于跟踪是否正在获取集合。

  1. 使用 fetchCollections() 函数为登录用户获取集合
const
fetchCollections = async () = > {
try {
const accessToken = localStorage.getItem("accessToken");
if (accessToken) {
const response = await getMyCollections(accessToken);
setCollections(response.data);
}
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};

fetchCollections 函数通过调用 getMyCollections API 函数并使用用户的访问令牌来检索登录用户的集合。然后,它使用检索到的数据更新 collections 状态,并将 loading 状态设置为 false,表示获取已完成。

显示集合#

最新集合像这样显示在抽屉中

< List >
{collections.map((collection) = > (
    < div key={collection.id} >
    < ListItem disablePadding >
    < ListItemButton
    disabled={
    collection.status != = CollectionStatus.COMPLETE | |
    !collection.has_model
    }
    onClick={() = > handleCollectionClick(collection)}
selected = {
    selectedCollection & &
    selectedCollection.id == = collection.id
}
>
< ListItemText
primary = {collection.title} / >
          {collection.status == = CollectionStatus.RUNNING ? (
    < CircularProgress
    size={24}
    style={{position: "absolute", right: 16}}
    / >
): null}
< / ListItemButton >
    < / ListItem >
        < / div >
))}
< / List >

您会注意到,集合的 ListItemButtondisabled 属性是根据集合状态是否不是 CollectionStatus.COMPLETE 或集合是否没有模型 (!collection.has_model) 来设置的。如果这些条件中的任何一个为真,则按钮被禁用,阻止用户选择未完成或没有模型的集合。当 CollectionStatus 为 RUNNING 时,我们还在按钮上显示一个加载轮。

在另一个 useEffect 钩子中,我们检查 collections 状态中的任何集合状态是否为 CollectionStatus.RUNNINGCollectionStatus.QUEUED。如果是,我们设置一个间隔,每 15 秒(15,000 毫秒)重复调用 fetchCollections 函数,以更新集合状态。通过这种方式,应用会定期检查已完成的集合,并在处理完成后相应地更新 UI。

useEffect(() = > {
    let
interval: NodeJS.Timeout;
if (
    collections.some(
        (collection) = >
collection.status == = CollectionStatus.RUNNING | |
collection.status == = CollectionStatus.QUEUED
)
) {
    interval = setInterval(() = > {
    fetchCollections();
}, 15000);
}
return () = > clearInterval(interval);
}, [collections]);

聊天视图组件#

frontend/src/chat/ChatView.tsx 中的 ChatView 组件负责处理和显示用户与集合交互的聊天界面。该组件建立 WebSocket 连接,与服务器进行实时通信,发送和接收消息。

ChatView 组件的主要功能包括

  1. 建立和管理与服务器的 WebSocket 连接。
  2. 以聊天格式显示用户和服务器的消息。
  3. 处理用户输入以向服务器发送消息。
  4. 根据从服务器接收到的消息更新 messages 状态和 UI。
  5. 显示连接状态和错误,例如加载消息、连接到服务器或加载集合时遇到错误。

总而言之,所有这些使得用户能够以非常流畅、低延迟的体验与他们选择的集合进行交互。

聊天 Websocket 客户端#

ChatView 组件中的 WebSocket 连接用于建立客户端和服务器之间的实时通信。WebSocket 连接在 ChatView 组件中按如下方式设置和管理

首先,我们想要初始化 WebSocket 引用

const websocket = useRef(null);

使用 useRef 创建了一个 websocket 引用,该引用持有将用于通信的 WebSocket 对象。useRef 是 React 中的一个钩子,允许您创建一个可变引用对象,该对象在多次渲染之间保持不变。当您需要持有一个可变对象的引用(例如 WebSocket 连接)而不想引起不必要的重新渲染时,它特别有用。

ChatView 组件中,WebSocket 连接需要在组件的整个生命周期内建立和维护,并且在连接状态改变时不应触发重新渲染。通过使用 useRef,您可以确保 WebSocket 连接作为一个引用被保留,并且组件仅在实际状态发生变化时(例如更新消息或显示错误)才重新渲染。

setupWebsocket 函数负责建立 WebSocket 连接并设置事件处理器来处理不同的 WebSocket 事件。

总的来说,setupWebsocket 函数如下所示

const setupWebsocket = () => {
  setConnecting(true);
  // Here, a new WebSocket object is created using the specified URL, which includes the
  // selected collection's ID and the user's authentication token.

  websocket.current = new WebSocket(
    `ws://localhost:8000/ws/collections/${selectedCollection.id}/query/?token=${authToken}`,
  );

  websocket.current.onopen = (event) => {
    //...
  };

  websocket.current.onmessage = (event) => {
    //...
  };

  websocket.current.onclose = (event) => {
    //...
  };

  websocket.current.onerror = (event) => {
    //...
  };

  return () => {
    websocket.current?.close();
  };
};

注意,在很多地方,我们根据 WebSocket 客户端的信息触发 GUI 的更新。

当组件首次打开并尝试建立连接时,会触发 onopen 监听器。在回调函数中,组件更新状态以反映连接已建立,清除任何先前的错误,并且没有消息正在等待响应。

websocket.current.onopen = (event) => {
  setError(false);
  setConnecting(false);
  setAwaitingMessage(false);

  console.log("WebSocket connected:", event);
};

当通过 WebSocket 连接从服务器接收到新消息时,会触发 onmessage。在回调函数中,解析接收到的数据,并使用来自服务器的新消息更新 messages 状态。

websocket.current.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("WebSocket message received:", data);
  setAwaitingMessage(false);

  if (data.response) {
    // Update the messages state with the new message from the server
    setMessages((prevMessages) => [
      ...prevMessages,
      {
        sender_id: "server",
        message: data.response,
        timestamp: new Date().toLocaleTimeString(),
      },
    ]);
  }
};

当 WebSocket 连接关闭时,会触发 onclose。在回调函数中,组件检查特定的关闭代码(4000)以显示警告提示并相应地更新组件状态。它还会记录关闭事件。

websocket.current.onclose = (event) => {
  if (event.code === 4000) {
    toast.warning(
      "Selected collection's model is unavailable. Was it created properly?",
    );
    setError(true);
    setConnecting(false);
    setAwaitingMessage(false);
  }
  console.log("WebSocket closed:", event);
};

最后,当 WebSocket 连接发生错误时,会触发 onerror。在回调函数中,组件更新状态以反映错误并记录错误事件。

websocket.current.onerror = (event) => {
  setError(true);
  setConnecting(false);
  setAwaitingMessage(false);

  console.error("WebSocket error:", event);
};

渲染我们的聊天消息#

ChatView 组件中,布局是使用 CSS 样式和 Material-UI 组件确定的。主要布局包含一个设置了 flex display 和 flexDirection 为 column 的容器。这确保了容器内的内容垂直排列。

布局中有三个主要部分

  1. 聊天消息区域:此部分占据了大部分可用空间,并显示用户和服务器之间交换的消息列表。它将 overflow-y 设置为 'auto',以便在内容溢出可用空间时允许滚动。消息使用 ChatMessage 组件为每条消息渲染,并使用 ChatMessageLoading 组件在等待服务器响应时显示加载状态。
  2. 分隔线:使用 Material-UI 的 Divider 组件将聊天消息区域与输入区域分开,创建了两个部分之间的清晰视觉区分。
  3. 输入区域:此部分位于底部,允许用户输入和发送消息。它包含来自 Material-UI 的 TextField 组件,设置为接受最多 2 行的多行输入。输入区域还包括一个 Button 组件用于发送消息。用户可以点击“发送”按钮或按键盘上的“Enter”键发送消息。

ChatView 组件中接受的用户输入是用户在 TextField 中输入的文本消息。组件处理这些文本输入,并通过 WebSocket 连接将其发送到服务器。

部署#

前提条件#

要部署此应用,您需要安装 Docker 和 Docker Compose。如果您使用 Ubuntu 或其他常见的 Linux 发行版,DigitalOcean 提供了一篇 很棒的 Docker 教程 以及另一篇关于 Docker Compose 的很棒的教程供您参考。如果这些不适合您,请尝试 官方 Docker 文档。

构建和部署#

该项目基于 django-cookiecutter,很容易将其部署到虚拟机并配置为特定域提供 HTTPs 流量。然而,配置过程有点复杂——并非项目本身的原因,而是配置证书、DNS 等本身就是一个相当复杂的话题。

就本指南而言,我们先在本地运行。也许我们会发布一份关于生产部署的指南。同时,您可以先查阅 Django Cookiecutter 项目文档

本指南假设您的目标是使应用能够运行并使用。如果您想进行开发,很可能不会希望使用 — profiles fullstack 标志启动 compose 堆栈,而是希望使用 node 开发服务器启动 React 前端。

要部署,首先克隆仓库

git clone https://github.com/yourusername/delphic.git

进入项目目录

cd delphic

复制示例环境文件

mkdir -p ./.envs/.local/
cp -a ./docs/sample_envs/local/.frontend ./frontend
cp -a ./docs/sample_envs/local/.django ./.envs/.local
cp -a ./docs/sample_envs/local/.postgres ./.envs/.local

编辑 .django.postgres 配置文件,包含您的 OpenAI API 密钥并为您的数据库用户设置一个唯一密码。您还可以在 .django 文件中设置响应 token 限制或切换您想要使用的 OpenAI 模型。如果授权访问,GPT4 也支持。

使用 --profiles fullstack 标志构建 Docker Compose 堆栈

sudo docker-compose --profiles fullstack -f local.yml build

fullstack 标志指示 compose 从 frontend 文件夹构建一个 Docker 容器,该容器将与所有必需的后端容器一起启动。然而,构建生产环境的 React 容器需要很长时间,因此我们不建议您以这种方式进行开发。请遵循 项目 readme.md 中的说明进行开发环境设置。

最后,启动应用

sudo docker-compose -f local.yml up

现在,在浏览器中访问 localhost:3000 以查看前端,并在本地使用 Delphic 应用。

使用应用#

设置用户#

为了实际使用该应用(目前,我们打算让未经身份验证的用户也可以共享某些模型),您需要一个登录。您可以使用超级用户或非超级用户。无论哪种情况,都需要先使用控制台创建一个超级用户

为什么要设置 Django 超级用户? Django 超级用户拥有应用中的所有权限,可以管理系统的各个方面,包括创建、修改和删除用户、集合以及其他数据。设置超级用户可以使您完全控制和管理应用。

如何创建 Django 超级用户

1 运行以下命令创建超级用户

sudo docker-compose -f local.yml run django python manage.py createsuperuser

2 系统将提示您提供超级用户的用户名、电子邮件地址和密码。输入所需信息。

如何使用 Django admin 创建其他用户

  1. 按照部署说明在本地启动 Delphic 应用。
  2. 在浏览器中访问 http://localhost:8000/admin 以访问 Django admin 界面。
  3. 使用您之前创建的超级用户凭据登录。
  4. 点击“Authentication and Authorization”部分下的“Users”。
  5. 点击右上角的“Add user +”按钮。
  6. 输入新用户所需的信息,例如用户名和密码。点击“Save”创建用户。
  7. 要授予新用户其他权限或将其设置为超级用户,请在用户列表中点击其用户名,向下滚动到“Permissions”部分,并相应地配置其权限。保存更改。