• 当我们进行传统的构建LLM时需要正式的搭建前端的各种内容,例如渲染啊、数据的传递啊,但是当我们了解到Chainlit之后就会发现,原来搭建一个LLM居然这么便捷,只需要对照着Chainlit的开发者文档,或者跟着辅助工具前端它居然一站式的给你完成了?

一、Chainlit对比其他的辅助工具

在选择LLM应用前端框架时,我们常会在Chainlit、Streamlit、Gradio之间纠结。三者各有侧重,以下是详细对比(官网已整理,感兴趣可深入探索):

  • Chainlit:https://docs.chainlit.io/get-started/overview(推荐)
  • Streamlit:https://streamlit.io/
  • Gradio:https://www.gradio.app/
对比维度 Chainlit Streamlit Gradio
主要定位 面向 大语言模型(LLM)应用 的对话式前端框架 通用型 数据应用/仪表盘 开发框架 面向 机器学习模型演示与交互 的前端框架
核心特点 - 专注对话交互
- 原生支持聊天消息、工具调用、思维链可视化
- 与 LLM 工具链(LangChain, LlamaIndex)深度集成
- 快速构建数据分析可视化应用
- 支持多种图表、表单、交互组件
- 简单 Python 脚本式开发
- 快速为模型生成 Web UI
- 支持输入输出多模态(文本、图像、音频、视频)
- 一键分享 Demo
使用门槛 中等(需要了解 LLM 应用开发逻辑) 低(写 Python 脚本即可,学习曲线平缓) 低(几行代码即可跑通 Demo)
前端能力 - 内置聊天 UI
- 支持多轮对话、消息流
- 思维链/工具调用可视化
- 丰富 UI 组件(按钮、下拉、图表、表单等)
- 适合数据可视化
- 输入输出组件为主(文本框、上传、播放、显示)
- UI 较固定,灵活性一般
生态与扩展 - 与 LangChain、LlamaIndex 集成紧密
- 支持自定义事件和工具
- 与数据科学生态结合紧密(pandas、matplotlib、Plotly 等) - Hugging Face 官方支持
- 与 HF Hub 模型生态无缝衔接
部署方式 本地运行,支持 Docker/云端部署 本地运行,支持一键部署到 Streamlit Cloud 本地运行,支持 Hugging Face Spaces / Colab 等
典型应用场景 - AI 助手
- LLM 工具调用演示
- 智能问答平台
- 数据可视化
- 商业智能 BI
- 原型工具快速展示
- 模型 Demo 展示
- 快速生成交互式原型
- ML 竞赛分享
上手示例 chainlit run app.py streamlit run app.py gr.Interface(fn, inputs, outputs).launch()

从对比可见,Chainlit是LLM对话应用的“专属工具”:它不追求“万能”,而是聚焦对话交互场景,原生适配LLM工具链,能轻松实现多轮聊天、工具调用可视化等LLM应用核心需求——这也是我们选择它搭建LLM前端的核心原因。

二、Chainlit快速上手:从安装到启动

相比传统前端开发的“环境配置→框架搭建→组件开发”流程,Chainlit的安装启动仅需3步,还能避开常见版本坑:

2.1 安装Chainlit(避坑指南)

直接使用pip安装,但需注意pydantic版本兼容性(文档中提到的常见报错原因):

# 1. 卸载可能冲突的旧版pydantic
pip uninstall pydantic -y

# 2. 安装兼容版本(2.9.2)
pip install pydantic==2.9.2

# 3. 安装Chainlit
pip install chainlit

2.2 测试安装是否成功

运行官方测试命令,自动生成默认项目并启动服务:

chainlit hello

若成功,终端会提示“Your app is available at http://localhost:8000”,访问该地址即可看到Chainlit默认聊天界面,验证环境正常。

2.3 启动自定义应用

创建app.py作为入口文件后,用以下命令启动(-w参数支持热重载,修改代码无需重启服务):

# 基础启动
chainlit run app.py

# 热重载启动(开发推荐)
chainlit run app.py -w

# 自定义端口(避免端口冲突)
chainlit run app.py --port 8080 -w

三、Chainlit核心功能实践:打造生产级LLM应用

Chainlit的优势不仅是“快”,更在于它能覆盖LLM应用的核心需求:界面定制、文件处理、数据持久化、权限控制等,且全部用Python实现,无需前端知识。

3.1 界面风格定制:一行配置改样式

Chainlit的界面配置集中在项目根目录的.chainlit/config.toml文件中,无需写CSS/JS,修改参数即可实现定制,常用配置如下:

# .chainlit/config.toml
[project]
# 会话超时时间(秒)
session_timeout = 3600
# 允许跨域(开发环境可设为"*")
allow_origins = ["*"]

[features.spontaneous_file_upload]
# 允许用户主动上传文件
enabled = true
# 支持的文件类型(*/*表示所有类型)
accept = ["*/*"]
# 最大上传文件数
max_files = 20
# 单个文件最大大小(MB)
max_size_mb = 500

[UI]
# 助手名称(聊天界面显示)
name = "私域知识AI助手"
# 默认折叠大内容(保持界面整洁)
default_collapse_content = true
# 思维链(CoT)显示模式(full=完整显示)
cot = "full"
# 自定义主题(dark/light)
[UI.theme]
default = "dark"

# 深色主题配色(可自定义)
[UI.theme.dark.primary]
main = "#1890ff"  # 主色调(蓝色)

3.2 文件上传检测:自动处理用户上传内容

在LLM应用中,用户常需要上传PDF、文档等作为上下文,Chainlit可通过cl.Message对象的elements属性检测上传文件,并结合LlamaIndex处理:

# app_chat_ui.py
import chainlit as cl
from llama_index.core import SimpleDirectoryReader
from rag.base_rag import RAG
from llama_index.core.chat_engine.types import ChatMode

@cl.on_message
async def main(message: cl.Message):
    # 1. 初始化聊天引擎和空消息
    chat_engine = cl.user_session.get("chat_engine")
    msg = cl.Message(content="", author="Assistant")
    
    # 2. 检测用户上传的文件/图片
    files = []
    for element in message.elements:
        # 筛选文件或图片类型
        if isinstance(element, cl.File) or isinstance(element, cl.Image):
            files.append(element.path)
    
    # 3. 若有文件,用LlamaIndex处理并更新聊天引擎
    if len(files) > 0:
        # 读取文件内容
        data = SimpleDirectoryReader(input_files=files).load_data()
        # 创建本地向量索引(RAG核心步骤)
        index = await RAG.create_index_local(data)
        # 基于索引生成聊天引擎(支持上下文问答)
        chat_engine = index.as_chat_engine(chat_mode=ChatMode.CONTEXT)
        # 更新会话中的聊天引擎
        cl.user_session.set("chat_engine", chat_engine)
    
    # 4. 流式生成回复
    res = await cl.make_async(chat_engine.stream_chat)(message.content)
    for token in res.response_gen:
        await msg.stream_token(token)
    await msg.send()

3.3 数据持久化:聊天记录与文件不丢失

LLM应用需要持久化两类数据:聊天对话记录(用PostgreSQL)和上传文件(用MinIO),Chainlit可通过封装数据层实现。

3.3.1 聊天记录持久化(PostgreSQL)
  1. 安装PostgreSQL:从官网(https://www.postgresql.org/download/windows/)下载,参考教程配置环境。
  2. 创建数据库与表:用pgAdmin4(可视化工具)创建chainlit_db数据库,执行SQL脚本创建表(用户表、线程表、步骤表等):
    -- 创建用户表
    CREATE TABLE users (
      "id" UUID PRIMARY KEY,
      "identifier" TEXT NOT NULL UNIQUE,
      "metadata" JSONB NOT NULL,
      "createdAt" TEXT
    );
    
    -- 创建线程表(关联用户)
    CREATE TABLE IF NOT EXISTS threads (
      "id" UUID PRIMARY KEY,
      "createdAt" TEXT,
      "name" TEXT,
      "userId" UUID,
      "userIdentifier" TEXT,
      "tags" TEXT[],
      "metadata" JSONB,
      FOREIGN KEY ("userId") REFERENCES users("id") ON DELETE CASCADE
    );
    
    -- 其他表(steps/elements/feedbacks)省略,参考文档SQL脚本
    
  3. 封装数据层:创建postgresql_data_layer.py,实现用户、线程的CRUD操作,核心代码如下:
    # persistent/postgresql_data_layer.py
    from chainlit.data.base import BaseDataLayer
    from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
    from sqlalchemy.orm import sessionmaker
    import uuid
    from datetime import datetime
    
    class PostgreSQLDataLayer(BaseDataLayer):
        def __init__(self, conninfo: str, storage_provider=None):
            # 创建异步数据库引擎
            self.engine = create_async_engine(conninfo)
            # 创建会话工厂
            self.async_session = sessionmaker(
                bind=self.engine, class_=AsyncSession, expire_on_commit=False
            )
            self.storage_provider = storage_provider
        
        # 异步创建用户(示例方法)
        async def create_user(self, user):
            user_id = str(uuid.uuid4())
            created_at = datetime.now().isoformat() + "Z"
            query = """
                INSERT INTO users ("id", "identifier", "createdAt", "metadata")
                VALUES (:id, :identifier, :createdAt, :metadata)
            """
            await self.execute_sql(
                query=query,
                parameters={
                    "id": user_id,
                    "identifier": user.identifier,
                    "createdAt": created_at,
                    "metadata": user.metadata
                }
            )
            return await self.get_user(user.identifier)
    
3.3.2 文件持久化(MinIO)
  1. 安装MinIO:用Docker快速启动(避免本地环境配置):

    # 拉取MinIO镜像
    docker pull minio/minio
    
    # 启动服务(9000=API端口,9001=控制台端口)
    docker run -p 9000:9000 -p 9001:9001 minio/minio server /data --address ":9000" --console-address ":9001"
    
  2. MinIO配置:访问http://localhost:9001,登录后创建bucket(存储桶,如jdxh)和访问秘钥(Access Key/Secret Key)。

  3. 项目配置:在根目录创建.env文件,存储MinIO和PostgreSQL连接信息:

    # .env文件
    MINIO_ENDPOINT="127.0.0.1:9000"
    MINIO_ACCESS_KEY="你的Access Key"
    MINIO_SECRET_KEY="你的Secret Key"
    MINIO_BUCKET_NAME="jdxh"
    PG_CONNECTION_STRING="postgresql+asyncpg://postgres:密码@localhost/chainlit_db"
    
  4. 封装MinIO客户端:创建minio_storage_client.py,实现文件上传:

    # persistent/minio_storage_client.py
    from chainlit.data.storage_clients.base import BaseStorageClient
    from minio import Minio
    import os
    import io
    
    class MinioStorageClient(BaseStorageClient):
        def __init__(self):
            # 从环境变量加载配置
            self.client = Minio(
                endpoint=os.environ["MINIO_ENDPOINT"],
                access_key=os.getenv("MINIO_ACCESS_KEY"),
                secret_key=os.getenv("MINIO_SECRET_KEY"),
                secure=False
            )
        
        # 异步上传文件
        async def upload_file(self, object_key, data, mime='application/octet-stream'):
            try:
                # 转换数据为BytesIO
                if isinstance(data, str):
                    data = io.BytesIO(data.encode('utf-8'))
                else:
                    data = io.BytesIO(data)
                # 上传到MinIO
                self.client.put_object(
                    bucket_name=os.getenv("MINIO_BUCKET_NAME"),
                    object_name=object_key,
                    data=data,
                    length=len(data.getvalue()),
                    content_type=mime
                )
                # 返回文件URL
                url = self.client.get_presigned_url("GET", os.getenv("MINIO_BUCKET_NAME"), object_key)
                return {"object_key": object_key, "url": url}
            except Exception as e:
                print(f"MinIO上传失败: {e}")
                return {}
    
  5. 启用持久化:在app_chat_ui.py中加载配置,绑定数据层:

    # app_chat_ui.py
    from dotenv import load_dotenv
    from persistent.minio_storage_client import MinioStorageClient
    from persistent.postgresql_data_layer import PostgreSQLDataLayer
    import chainlit.data as cl_data
    
    # 加载环境变量
    load_dotenv()
    
    # 初始化存储客户端和数据层
    storage_client = MinioStorageClient()
    cl_data._data_layer = PostgreSQLDataLayer(
        conninfo=os.environ["PG_CONNECTION_STRING"],
        storage_provider=storage_client
    )
    

3.4 登录权限控制:简单密码认证

Chainlit提供@cl.password_auth_callback装饰器,快速实现登录功能,无需额外开发登录页面:

# app_chat_ui.py
@cl.password_auth_callback
def auth_callback(username: str, password: str):
    # 验证用户名密码(实际项目建议从数据库读取)
    if (username, password) == ("admin", "admin123"):
        return cl.User(
            identifier="admin",  # 用户唯一标识
            metadata={
                "role": "admin",  # 角色(用于后续权限控制)
                "provider": "credentials"
            }
        )
    # 认证失败返回None
    return None

启动后访问http://localhost:8000,会自动跳转到登录页,输入正确凭据才能进入聊天界面。

3.5 PDF预览:提升用户体验

用户上传PDF后,Chainlit可直接预览,无需下载,核心代码如下:

# app_chat_ui.py
from chainlit.element import ElementBased
from typing import List

async def view_pdf(elements: List[ElementBased]):
    """筛选PDF文件并预览"""
    pdf_files = []
    pdf_names = []
    # 遍历元素,筛选PDF
    for element in elements:
        if element.name.endswith(".pdf"):
            # 创建PDF预览对象(display="side"表示侧边显示)
            pdf = cl.Pdf(name=element.name, display="side", path=element.path)
            pdf_files.append(pdf)
            pdf_names.append(element.name)
    
    # 发送预览消息
    if pdf_files:
        await cl.Message(
            content=f"已加载PDF文件: {','.join(pdf_names)}",
            elements=pdf_files
        ).send()

# 在@cl.on_message中调用
@cl.on_message
async def main(message: cl.Message):
    # ... 其他逻辑 ...
    # 检测到文件时预览PDF
    if len(files) > 0:
        await view_pdf(message.elements)
    # ... 流式回复逻辑 ...

四、Chainlit+RAG:实战私域知识问答

结合LlamaIndex的RAG能力,Chainlit可快速搭建“私域知识AI助手”——用户上传企业文档后,AI基于文档内容回答,避免幻觉。核心流程如下:

  1. 创建RAG索引:用app_chat_basic_rag.py处理私域文档,生成向量索引:
    # app_chat_basic_rag.py
    from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
    from llms import deepseek_llm
    from embeddings import embed_model_local_bge_small
    
    # 配置LLM和嵌入模型
    Settings.llm = deepseek_llm()
    Settings.embed_model = embed_model_local_bge_small()
    
    def index_data():
        # 加载data目录下的文档
        data = SimpleDirectoryReader(input_dir="data").load_data()
        # 构建向量索引
        index = VectorStoreIndex.from_documents(data, show_progress=True)
        # 持久化索引(避免重复构建)
        index.storage_context.persist(persist_dir="index")
    
    if __name__ == "__main__":
        index_data()
    
  2. Chainlit集成RAG:在app_chat_ui.py中加载索引,生成聊天引擎:
    # app_chat_ui.py
    from app_chat_basic_rag import create_chat_engine_rag
    
    @cl.on_chat_start
    async def start():
        # 初始化RAG聊天引擎
        chat_engine = await create_chat_engine_rag()
        cl.user_session.set("chat_engine", chat_engine)
        # 发送欢迎消息
        await cl.Message(content="你好!我是私域知识AI助手,可基于上传文档回答问题。").send()
    

五、总结:为什么选择Chainlit做LLM前端?

  1. 开发效率高:无需前端技术,Python一行代码启动服务,配置文件实现界面定制;
  2. LLM场景适配:原生支持对话流、思维链可视化、文件上传,贴合LLM应用需求;
  3. 生态集成强:与LangChain、LlamaIndex无缝衔接,快速实现RAG、工具调用等高级功能;
  4. 生产级能力:支持数据持久化(PostgreSQL+MinIO)、权限控制,可从原型直接过渡到生产环境。

更多推荐