从 0 到 1,用 Chainlit 打造「多文件知识库对话」Web 应用
一、为什么要写这篇文章?
过去做 PoC,总是卡在「界面丑、数据掉、文件散」三板斧:
- Gradio/Streamlit 组件不够灵活;
- 刷新页面聊天记录全没;
- 用户上传 PDF/Word/图片后,下次打开还得重新传。
直到我遇到 Chainlit——专为 LLM 聊天而生的开源框架,自带「会话持久化 + 多文件上传 + 前端主题可改」三大 Buff。
于是我花了几天时间,基于 Chainlit 撸了一套 支持多文件知识库对话、登录鉴权、PDF 预览、数据落库 的完整方案。
今天把全部踩坑与源码整理成这篇「多文件知识库对话」的实战笔记,让你 30 分钟就能原样复刻,直接上线!
二、最终效果先睹为快
功能 | 效果 |
---|---|
登录页 | 极简账号密码,支持 OAuth 扩展 |
多文件上传 | 拖拽 20 个文件,秒级切片向量化 |
侧边 PDF 预览 | 点击消息直接 cl.Pdf 渲染,无需跳转 |
聊天记录永久化 | 换电脑登录也能看到昨天对话 |
暗黑主题 | 改一行配置即可切换 |
三、技术选型
层级 | 选型 | 理由 |
---|---|---|
前端 | Chainlit(React 内核) | 专为 LLM 聊天优化,组件即插即用 |
向量索引 | LlamaIndex + BGE-Small | 本地模型,离线也跑,CPU 可接受 |
大模型 | DeepSeek-Chat | 中文 32K 长上下文,价格只有 GPT-4 1/10 |
关系库 | PostgreSQL | 官方推荐,asyncpg 异步驱动不阻塞 |
文件存储 | MinIO(S3 协议) | Docker 一键启动,后续无缝迁移到云厂商 |
部署 | Docker Compose | 一条命令拉起 PG+MinIO+App |
四、30 分钟复刻指南
电脑已经装好 Python 3.11、Docker、Node≥18
① 拉取项目模板
git clone https://github.com/yourname/chainlit-rag-minio.git
cd chainlit-rag-minio
② 环境变量一键生成
cp .env.example .env
# 自动生成 CHAINLIT_AUTH_SECRET
chainlit create-secret
把 MinIO 密钥、数据库连接串、DeepSeek API Key 填进去即可。
③ 启动基础设施
docker compose up -d
# 浏览器访问 http://localhost:9001 创建 bucket:docs
④ 安装后端依赖 & 运行
python -m venv .venv
source .venv/bin/activate # Win 用 .venv\Scripts\activate
pip install -r requirements.txt
chainlit run app.py --port 8080 --watch
看到 🚀 Chainlit server running on http://0.0.0.0:8080
就成功了!
⑤ 在线体验
- 打开
http://localhost:8080/login
账号admin
/ 密码admin123
- 拖拽 1个 PDF,输入「国庆放假几天」
- 侧边即时预览,回答带引用页码,刷新页面记录仍在。
五、核心代码走读
1. 数据层:PostgreSQL 异步增删改查
# persistent/postgresql_data_layer.py
class PostgreSQLDataLayer(BaseDataLayer):async def create_user(self, user: User) -> PersistedUser:# 不存在就 INSERT,存在就 UPDATE metadata...async def update_thread(self, thread_id: str, **kw):# UPSERT,返回最新 thread...
Chainlit 在每次用户消息前后会自动调用 create_step
、create_element
,我们只需专注业务 SQL,无需手写 CRUD。
2. 文件层:MinIO 上传 + 预签名 URL
# persistent/minio_storage_client.py
async def upload_file(self, object_key, data, mime='application/pdf'):bucket = os.getenv("MINIO_BUCKET_NAME", "docs")self.client.put_object(bucket, object_key, io.BytesIO(data), len(data), mime)url = self.client.get_presigned_url("GET", bucket, object_key)return {"object_key": object_key, "url": url}
前端点击 PDF 时,Chainlit 会自动用返回的 url
渲染 <iframe>
,实现「边聊边看」。
3. 聊天逻辑:RAG 索引动态切换
@cl.on_message
async def main(message: cl.Message):files = [f.path for f in message.elements if isinstance(f, (cl.File, cl.Image))]if files:nodes = SentenceSplitter().get_nodes_from_documents(SimpleDirectoryReader(input_files=files).load_data())index = VectorStoreIndex(nodes)chat_engine = index.as_chat_engine(chat_mode="context")cl.user_session.set("chat_engine", chat_engine)...
一条消息里既能「纯 LLM 闲聊」,也能「基于刚传的 10 份合同做问答」,零配置切换。
4. 主题 & 文案:改 TOML 即可
# .chainlit/config.toml
[UI]
name = "合同审查助手"
default_collapse_content = false
cot = "hidden" # 隐藏思维链,界面更清爽[UI.theme.dark]
background = "#0e1117"
paper = "#1c1e24"
官方支持自定义 CSS/JS,甚至整包替换 React(custom_build
字段),自由度拉满。
六、踩坑与彩蛋
坑 | 解决 |
---|---|
asyncpg 未装报 ModuleNotFoundError | 明确写进 requirements.txt |
IDM 浏览器插件抢占 9000 端口,PDF 预览报 ConnectionReset | 关掉 IDM 或改端口 |
上传 50 MB 文件失败 | 改 config.toml 里 max_size_mb = 500 |
想对接公司 LDAP | 只需实现 @cl.header_auth_callback ,返回 cl.User 即可 |
七、一键部署到生产
# docker-compose.prod.yml
services:app:build: .environment:- PG_CONNECTION_STRING=postgresql+asyncpg://user:pwd@rds.xxx.com/chainlit- MINIO_ENDPOINT=s3.xxx.comlabels:- "traefik.enable=true"- "traefik.http.routers.app.rule=Host(`ai.xxx.com`)"
把镜像推到阿里云 ACR,再配 RDS + OSS,10 分钟完成 SaaS 化。
八、后续还能这么玩!
- 多租户:在
users.metadata
里加tenant_id
,所有 SQL 加过滤条件即可。 - 知识库版本管理:给
threads
加kb_version
字段,用户可切换「2024Q1 合同」或「2024Q2 合同」。 - 在线标注:利用
feedbacks
表,业务人员可对答案点赞/点踩,跑 RLHF。 - 移动端:Chainlit 0.9 已支持 PWA,开启
pwa = true
即可生成安装包。
九、总结
- 链式开发真香:用 Chainlit 把「聊天界面 + 文件上传 + 数据持久化」这一整套通用需求,浓缩成 200 行不到的 Python,PoC 速度提升 10 倍。
- 架构可进可退:本地跑 Docker 就能演示,上云只需换连接串;今天用 DeepSeek,明天切 GPT-4 仅改一行
Settings.llm
。
到此,从 0 到 1 的「多文件知识库对话」系统就交付了。