LobeChat + Logto + MinIO

源码深度解析 — 三位一体 AI 平台架构全景

v2.1.47 • Next.js 16 + Better Auth + RustFS • 源码级剖析
平台入口 → lobehub.kang-kang.com
62
Monorepo 包
47
tRPC 路由
29
数据库 Schema
40+
LLM 供应商
11
SSO 提供方

1 系统总览 三位一体

💬
LobeChat
AI 对话平台主体
Next.js 16 + Vite SPA
端口 3210
🔐
Logto (SSO)
统一身份认证
OIDC 协议接入
Better Auth 集成
📦
RustFS / MinIO
S3 兼容对象存储
文件上传/预览/RAG
端口 9000/9001
LobeChat ← OAuth2/OIDC → Logto   |   LobeChat ← S3 API → RustFS/MinIO   |   LobeChat ← SQL → PostgreSQL   |   LobeChat ← Cache → Redis

LobeChat 是一个 全栈 AI 对话平台,采用 monorepo 架构(62 个包),支持 Web/桌面/移动端三端部署。认证通过 Better Auth 框架 + Logto 作为 OIDC SSO 提供方;文件存储使用 S3 兼容协议,生产环境用 RustFS(Rust 实现的 MinIO 替代品)。

LobeChat 技术栈
  • 前端: React 19 + Vite SPA + React Router v7
  • 后端: Next.js 16 App Router + tRPC v11.8
  • 认证: Better Auth v1.4.6 (会话/密码/2FA/Passkey/OAuth)
  • 状态: Zustand 5.0 + SWR + React Query
  • 样式: antd-style (CSS-in-JS) + Ant Design 6 + @lobehub/ui v5.5
  • 动画: Motion (Framer Motion) v12
  • 校验: Zod v3 (tRPC 输入校验) + superjson (序列化)
  • 数据库: Drizzle ORM v0.45 + PostgreSQL 17 (ParadeDB) + pgvector
  • 缓存: Redis 7 (ioredis) — 会话 + 文件预签名 URL
  • AI: 40+ LLM 供应商 + OpenAI SDK v4 + 多 Agent 编排
  • 国际化: react-i18next v16, 20+ 语言
  • 图片处理: Sharp v0.34 (服务端, 仅 AI 生图/视频用)
  • 桌面端: Electron (desktop-bridge, electron-client-ipc)
  • PWA: vite-plugin-pwa (离线缓存 + Service Worker)
  • 可观测性: OpenTelemetry (分布式链路追踪)
  • 安全: ssrf-safe-fetch (SSRF 防护) + RBAC 权限表 (已建未接入)
  • 搜索: SearXNG (自托管元搜索引擎)
  • Monorepo: pnpm workspace, 60 个包 (agent-runtime, model-runtime, 30+ builtin-tool, database, types, const...)
  • 测试: Vitest v3 + Playwright (E2E)
  • 构建: Vite v7 (SPA) + Next.js 16 (SSR) 双构建, bun 运行脚本
Logto 认证体系
  • 认证框架: Better Auth v1.4.6 (开源 Clerk 替代)
  • Logto 接入: Generic OIDC Provider (PKCE 增强)
  • SSO 11 种: Google, GitHub, Azure, Apple, Logto, Auth0, Keycloak, Okta, Casdoor, Authentik, Cloudflare ZT
  • 登录方式: 邮箱密码 (bcrypt) / Magic Link (15min) / OTP (5min) / Passkey (WebAuthn) / OAuth 社交登录
  • 会话管理: Cookie (30 天过期) + Redis 二级缓存 (10 分钟 TTL) + DB 兜底
  • Webhook: Logto User.Data.Updated (同步头像/姓名) + User.SuspensionStatus.Updated (自动登出)
  • 安全: CSRF 防护, Rate Limit (3 次/60 秒), JWKS (RS256), Webhook 签名验证 (HMAC-SHA256)
  • 密码策略: 8-64 字符, bcrypt 哈希, Clerk 迁移兼容
  • 双因素: TOTP + 备份码 (twoFactorEnabled)
  • 邮箱验证: 可选, 验证链接 1 小时过期
  • 白名单: AUTH_ALLOWED_EMAILS 限制注册域名/邮箱
  • OIDC 服务: 可选启用 JWKS_KEY, LobeChat 自身作为 OIDC Provider
  • DB 表: users, accounts, sessions, verifications, two_factor (Better Auth 管理)
  • tRPC 中间件链: OpenTelemetry → oidcAuth → userAuth → serverDatabase → 业务逻辑
RustFS/MinIO 存储系统
  • 协议: AWS S3 兼容 (@aws-sdk/client-s3 v3)
  • 生产后端: RustFS (Rust 重写 MinIO) / MinIO / AWS S3 / Cloudflare R2
  • 上传流程: 预签名 PUT URL (1 小时) → 浏览器直传 S3 (不经服务器) → DB 记录
  • 下载流程: /f/{fileId} 代理 → Redis 缓存预签名 GET URL (4 分钟 TTL, URL 5 分钟过期) → 302 重定向
  • 去重: SHA256 哈希, globalFiles 表 (多用户共享同一 S3 对象)
  • 两表设计: files (用户级, 按 userId 隔离) + global_files (全局去重, hash_id 主键)
  • 代理 URL: /f/{fileId} 统一入口, 对客户端隐藏 S3 细节
  • ACL 模式: S3_SET_ACL=1 公开读 + PUBLIC_DOMAIN 直链 / =0 全部预签名 URL
  • 路径样式: S3_ENABLE_PATH_STYLE=1 (MinIO/RustFS) / =0 (AWS 虚拟主机样式)
  • 媒体缓存: uploadMedia() 自动设 Cache-Control: public, max-age=31536000 (1 年)
  • 大小验证: 服务端通过 S3 HeadObject 验证实际文件大小 (防客户端伪造)
  • RAG 管道: 文件上传 → 异步分块 (chunks) → 批量向量嵌入 (batch=50, concurrency=10) → pgvector 语义搜索
  • Docker 初始化: rustfs-init 容器用 minio/mc 创建 bucket + 设置匿名读策略
  • 配额控制: checkFileStorageUsage 中间件, FileModel.countUsage() 统计用量

2 整体架构 Architecture

2.1 Monorepo 结构

~/Projects/business/20260330-LobeChat公司AI平台/
├── src/                    ← 主应用源码 (Next.js + Vite SPA)
│   ├── spa/                ← SPA 入口 (web/mobile/desktop)
│   ├── app/                ← Next.js App Router (SSR + API)
│   ├── store/              ← Zustand 状态管理 (29+ stores)
│   ├── server/             ← tRPC 路由 + 服务层 + 模块
│   ├── libs/               ← Better Auth / tRPC / i18n 配置
│   ├── features/           ← 按领域组织的 UI 组件
│   ├── routes/             ← React Router 页面
│   ├── services/           ← 客户端服务层
│   ├── envs/               ← 环境变量定义 (14 个文件)
│   └── config/             ← 应用配置
├── packages/               ← 62 个 Monorepo 包
│   ├── database/           ← Drizzle ORM schemas + models + migrations
│   ├── agent-runtime/      ← Agent 执行引擎
│   ├── model-runtime/      ← 40+ LLM 供应商适配器
│   ├── builtin-tool-*/     ← 30+ 内置工具
│   └── ...                 ← types, const, utils, config 等
├── apps/desktop/           ← Electron 桌面应用
├── docker-compose/         ← 部署编排
│   ├── deploy/             ← 生产部署 (LobeChat+PG+Redis+RustFS+SearXNG)
│   └── dev/                ← 开发环境 (只有基础设施)
├── Dockerfile             ← 多阶段构建 (343 行)
├── drizzle.config.ts      ← 数据库迁移配置
└── package.json           ← pnpm + bun, v2.1.47

2.2 请求生命周期

浏览器/客户端
    │
    ▼
React SPA (Vite 构建, React Router v7)
    │  ← Zustand Store 管理状态
    ▼
tRPC Client (自动生成 React hooks)
    │  ← superjson 序列化 (Date/Map/Set)
    ▼
Next.js API Routes (/trpc, /api/*)
    │
    ▼
tRPC Middleware 链
    │  1. OpenTelemetry (分布式追踪)
    │  2. OIDC Auth (JWT/JWKS 验证, 可选)
    │  3. Better Auth (Session Cookie 验证)
    │  4. Server Database (DB 连接注入)
    │  5. Key Vaults (密钥解密)
    ▼
tRPC Router Handler (47 个子路由)
    │  ← Zod 输入校验
    ▼
Service Layer (业务逻辑)
    │
    ├──→ Database Models (Drizzle ORM → PostgreSQL)
    ├──→ S3 Module (AWS SDK → RustFS/MinIO)
    ├──→ Redis (会话缓存, 文件 URL 缓存)
    └──→ LLM Runtime (40+ 供应商, 流式响应)

3 LobeChat 核心源码 LobeChat

3.1 入口文件

src/spa/entry.web.tsx 24 行

Web SPA 入口,导入 desktopRoutes 配置,支持开发代理模式 (/_dangerous_local_dev_proxy),BootErrorBoundary 错误边界包裹。

src/spa/entry.desktop.tsx 13 行

Electron 桌面入口,使用相同路由配置,无代理 basename。

src/spa/entry.mobile.tsx 13 行

移动端入口,切换到 mobileRoutes 配置。

3.2 路由系统

文件: src/spa/router/desktopRouter.config.tsx (579 行)

完整路由结构 (点击展开)
/ (主布局 - 持久侧边栏 + 主内容区)
├── /agent/:aid              → 对话页面
│   ├── /                    → 聊天消息
│   ├── /profile             → Agent 资料
│   ├── /cron/:cronId        → 定时任务
│   └── /channel             → 频道管理
├── /group/:gid              → 多 Agent 群聊
├── /community               → 发现市场
│   ├── /agent               → 浏览 Agent
│   ├── /model               → 浏览模型
│   ├── /skill               → 浏览技能
│   ├── /mcp                 → 浏览 MCP
│   └── (detail)/:slug       → 详情页
├── /resource                → 知识库
│   └── /library/:id/:slug   → KB 详情
├── /settings                → 用户设置
│   ├── /profile             → 个人资料
│   ├── /provider/:id        → LLM 供应商配置
│   └── /:tab                → 其他标签页
├── /memory                  → 用户记忆系统
│   ├── /identities          → 身份
│   ├── /contexts            → 上下文
│   ├── /preferences         → 偏好
│   ├── /experiences         → 经验
│   └── /activities          → 活动
├── /video                   → 视频生成
├── /image                   → 图片生成
├── /eval                    → 评估基准
│   └── /bench/:id/runs/:id  → 评测结果
├── /page                    → 笔记/页面
│   └── /:id                 → 编辑页面
├── /onboarding              → 新手引导 (独立布局)
└── /share/t/:id             → 话题分享 (独立布局)

3.3 LLM / Agent 集成

packages/model-runtime/ 40+ 供应商

每个 LLM 供应商一个目录 (openai/, anthropic/, azure/, ollama/, bedrock/ 等),实现统一的 ModelRuntime 接口。运行时通过 runtimeMap.ts 注册表动态路由。

packages/agent-runtime/ Agent 编排

核心组件:GeneralChatAgent (单 Agent), GroupOrchestrationRuntime (多 Agent 协调), GroupOrchestrationSupervisor (Agent 路由/委派), UsageCounter (Token 计费), InterventionChecker (人工审批)。

4 认证系统 & Logto 集成 Logto

4.1 认证架构概览

核心设计
LobeChat 使用 Better Auth v1.4.6 作为认证框架(开源 Clerk 替代品),Logto 作为 Generic OIDC Provider 接入。认证状态通过 Cookie + Redis 管理(10 分钟缓存),tRPC 中间件链确保每个请求都经过验证。

4.2 核心配置文件

src/libs/better-auth/define-config.ts (333 行) — 主认证配置

代理配置 (开发环境)

检测 NODE_ENV=development 时读取 HTTPS_PROXY/HTTP_PROXY,用 undici ProxyAgent 设置全局代理——因为 Node.js 原生 fetch 不走系统代理,开发时 OAuth Token Exchange 需要。

认证常量

常量说明
VERIFICATION_LINK_EXPIRES_IN3600s (1h)邮箱验证链接过期
MAGIC_LINK_EXPIRES_IN900s (15min)Magic Link 过期
OTP_EXPIRES_IN300s (5min)手机验证码过期

Passkey 配置

getPasskeyRpID()APP_URL 安全提取 hostname 作为 WebAuthn Relying Party ID。未设置 APP_URL 时返回 undefined(兼容 E2E 测试)。

密码策略

8-64 字符,bcrypt 哈希,支持 Clerk 迁移(clerkMigration 钩子自动转换旧密码哈希)。

会话管理

session: {
  cookieCache: { enabled: true, maxAge: 600 },  // 10 分钟 Cookie 缓存
  expiresIn: 2592000,                            // 30 天过期
  updateAge: 86400,                              // 24h 刷新
}
secondaryStorage: Redis                          // Redis 作为二级会话存储

钩子 (Hooks)

  • after createUser: 新用户注册后创建 UserModel 关联记录
  • after linkAccount: OAuth 关联后同步头像/姓名到 LobeChat 用户表
  • before deleteUser: 删除所有关联数据(消息/文件/设置)

速率限制

rateLimit: {
  storage: 'secondary-storage',  // Redis
  customRules: {
    '/sign-in/email':    { max: 3, window: 60 },
    '/sign-up/email':    { max: 3, window: 60 },
    '/forget-password':  { max: 3, window: 60 },
    '/magic-link/*':     { max: 3, window: 60 },
    '/email-otp/*':      { max: 3, window: 60 },
  }
}
src/libs/better-auth/sso/providers/logto.ts — Logto OIDC 接入

配置

{
  providerId: 'logto',
  type: 'oidc',                     // Generic OIDC
  discoveryUrl: AUTH_LOGTO_ISSUER + '/.well-known/openid-configuration',
  clientId: AUTH_LOGTO_ID,
  clientSecret: AUTH_LOGTO_SECRET,
  scopes: ['openid', 'profile', 'email', 'offline_access'],
  pkce: true,                       // PKCE 安全增强
}

Profile Mapping

mapProfileToUser(profile) {
  return {
    email:    profile.email,
    name:     profile.name || profile.username,
    image:    profile.picture,          // Logto 头像字段
    username: profile.username,
  }
}
关键点
Logto 以 offline_access scope 获取 Refresh Token,实现长期会话。PKCE 防止授权码拦截攻击。
src/app/(backend)/api/webhooks/logto/route.ts — Logto Webhook

路径: POST /api/webhooks/logto

事件处理

事件处理逻辑
User.Data.Updated同步用户资料:avatar / email / fullName → Better Auth users 表
User.SuspensionStatus.Updated用户被封禁时自动登出所有会话(revoke sessions)

签名验证

// 使用 LOGTO_WEBHOOK_SIGNING_KEY 验证请求签名
const signature = headers['logto-signature-sha-256'];
const expected = crypto.createHmac('sha256', signingKey)
                       .update(rawBody)
                       .digest('hex');
if (signature !== expected) return 401;

4.3 全部 11 个 SSO 提供方

#提供方类型环境变量
1GoogleOAuth 2.0AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET
2GitHubOAuth 2.0AUTH_GITHUB_ID / AUTH_GITHUB_SECRET
3MicrosoftOAuth 2.0AUTH_MICROSOFT_ENTRA_ID / SECRET / TENANT_ID
4AppleOAuth 2.0AUTH_APPLE_ID / SECRET / TEAM_ID / KEY_ID
5LogtoOIDC GenericAUTH_LOGTO_ID / ISSUER / SECRET
6Auth0OIDC GenericAUTH_AUTH0_ID / ISSUER / SECRET
7KeycloakOIDC GenericAUTH_KEYCLOAK_ID / ISSUER / SECRET
8OktaOIDC GenericAUTH_OKTA_ID / ISSUER / SECRET
9CasdoorOIDC GenericAUTH_CASDOOR_ID / ISSUER / SECRET
10AuthentikOIDC GenericAUTH_AUTHENTIK_ID / ISSUER / SECRET
11Cloudflare ZTOIDC GenericAUTH_CLOUDFLARE_ZERO_TRUST_*

4.4 认证中间件链

tRPC Request
    │
    ▼
createLambdaContext()
    │  认证优先级:
    │  1. X-API-Key header → 查 DB apiKeys 表
    │  2. Oidc-Auth header → JWKS 验证 (ENABLE_OIDC=true)
    │  3. Cookie → Better Auth session 验证
    ▼
oidcAuth middleware
    │  如果有 OIDC token → 验证 JWT 签名
    │  注入 ctx.oidcAuth = { sub, payload }
    ▼
userAuth middleware
    │  从 ctx 获取 userId
    │  注入 ctx.userId
    ▼
serverDatabase middleware
    │  创建 Drizzle DB 实例
    │  注入 ctx.serverDB
    ▼
Router Handler
    │  ctx.userId + ctx.serverDB 可用
    │  创建 Model 实例处理业务逻辑

4.5 数据库 Schema

文件: packages/database/src/schemas/betterAuth.ts

表名关键字段用途
usersid, email, fullName, avatar, phone, username, role, banned, twoFactorEnabled用户主表 (email 归一化)
accountsid, userId, providerId, accountId, accessToken, refreshTokenOAuth 关联账号
sessionsid, userId, token, expiresAt, ipAddress, userAgent会话记录
verificationsid, identifier, value, expiresAt验证码/Magic Link
two_factorid, userId, secret, backupCodes双因素认证

5 文件存储 & MinIO/RustFS MinIO

5.1 存储架构

┌─────────────────────────────────────────────┐
│  CLIENT (浏览器)                            │
│  UploadService → XMLHttpRequest PUT         │
└──────────────┬──────────────────────────────┘
               │ 1. 获取预签名 URL
               ▼
┌──────────────────────────────────────────────┐
│  tRPC API 层                                │
│  upload.createS3PreSignedUrl({pathname})     │
│  file.createFile({hash, type, name, size})   │
│  /(backend)/f/[id] (文件代理路由)             │
└──────────────┬──────────────────────────────┘
               │ 2. S3 命令
               ▼
┌──────────────────────────────────────────────┐
│  Service 层                                 │
│  FileService (门面) → S3StaticFileImpl       │
└──────────────┬──────────────────────────────┘
               │ 3. AWS SDK v3
               ▼
┌──────────────────────────────────────────────┐
│  S3 兼容存储                                │
│  RustFS / MinIO / AWS S3 / Cloudflare R2    │
└──────────────────────────────────────────────┘

5.2 核心源码文件

src/server/modules/S3/index.ts (215 行) — S3 客户端模块

S3 基类方法

方法功能细节
constructor()初始化 S3Clientcredentials + endpoint + forcePathStyle + region
createPreSignedUrl(key)生成上传 URLPutObjectCommand, 1 小时有效
createPreSignedUrlForPreview(key, expiresIn?)生成预览 URLGetObjectCommand, 默认 2 小时
uploadBuffer(path, buffer, contentType)服务端上传PutObjectCommand + Cache-Control
uploadMedia(key, buffer)上传媒体文件Cache-Control: public, max-age=31536000 (1 年缓存)
getFileContent(key)获取文本内容UTF-8 字符串
getFileByteArray(key)获取二进制Uint8Array
getFileMetadata(key)获取元数据HeadObjectCommand → contentLength + contentType
deleteFile(key)删除文件DeleteObjectCommand
deleteFiles(keys)批量删除DeleteObjectsCommand (批量)

FileS3 包装类

继承 S3,从 fileEnv 自动读取配置初始化,用于依赖注入。

src/server/services/file/index.ts (366 行) — FileService 门面

关键方法

方法功能核心逻辑
createFileRecord()创建文件记录SHA256 去重 via globalFiles → 返回代理 URL /f/{fileId}
getFullFileUrl()智能 URL 解析ACL 公开 + 有 PUBLIC_DOMAIN → 直链; 否则 → 预签名 URL
getKeyFromFullUrl()URL → S3 Key/f/{fileId} → DB 查询; legacy S3 URL → pathname 解析
uploadBase64()Base64 上传base64 → Buffer → uploadMedia → createFileRecord
uploadFromUrl()外部 URL 上传fetch → Buffer → uploadMedia → createFileRecord
downloadFileToLocal()下载到临时目录返回 cleanup() 清理函数
去重机制
createFileRecord() 先查 globalFiles 表(SHA256 哈希),如果已存在则复用同一 S3 对象,只创建新的 files 记录(不同 fileId)。删除时只有没有任何 files 引用该 hash 时才删 S3 对象。
src/app/(backend)/f/[id]/route.ts (91 行) — 文件代理路由

流程

GET /f/{fileId}
    │
    ├─ 1. Redis 查缓存: key = 'file-proxy:{fileId}'
    │     命中? → 302 重定向到缓存的预签名 URL
    │
    ├─ 2. 未命中 → DB 查询: FileModel.getFileById(fileId)
    │     ⚠️ 无 userId 过滤 (公开可访问)
    │
    ├─ 3. 生成预签名 GET URL (5 分钟有效)
    │
    ├─ 4. 写入 Redis: TTL = 240s (4 分钟)
    │     (比 URL 有效期短 1 分钟,安全余量)
    │
    └─ 5. 返回 302 Location → 预签名 URL

5.3 上传完整流程

步骤 1: 生成路径
uploadService.generateFilePathMetadata()
→ filename = '{uuid}.{ext}'
→ dirname  = 'files/{timestamp_hour}'
→ pathname = 'files/2026-04-07/{uuid}.png'

步骤 2: 获取预签名 URL
tRPC: upload.createS3PreSignedUrl({pathname})
→ server: FileS3.createPreSignedUrl(pathname)
→ AWS SDK: getSignedUrl(PutObjectCommand, {expiresIn: 3600})
← 返回: https://rustfs:9000/lobe/files/2026-04-07/{uuid}.png?X-Amz-Signature=...

步骤 3: 直传 S3
XMLHttpRequest PUT → 预签名 URL
→ 浏览器直接上传到 RustFS/MinIO (不经过 LobeChat 服务器)
→ 支持进度回调 (xhr.upload.onprogress)
← 200 OK

步骤 4: 创建数据库记录
tRPC: file.createFile({hash, fileType, name, size, url})
→ server: FileModel.checkHash(sha256) → 检查去重
→ server: FileService.getFileMetadata(url) → 验证实际文件大小 (防伪造)
→ server: FileModel.create() → INSERT files + globalFiles
← 返回: {fileId: 'xxx', url: '/f/xxx'}

步骤 5: 使用
客户端使用 /f/{fileId} 作为统一 URL
→ 图片: <img src="/f/{fileId}" />
→ 下载: window.open('/f/{fileId}')

5.4 数据库 Schema (文件相关)

关键字段用途
global_fileshash_id (PK, SHA256), file_type, size, url, metadata, creator去重层 — 相同内容只存一份 S3 对象
filesid, user_id, file_hash (FK→global_files), name, size, url, source, parent_id, chunk_task_id, embedding_task_id用户文件记录 — 每个用户独立引用
documentsid, title, content, file_type, pages, source_type, file_id, knowledge_base_id, parent_id文档/文件夹 — RAG 知识库组织
两表设计的精妙之处
files 表是用户级别的(每个用户看到自己的文件列表),global_files 是全局去重的(SHA256 唯一)。User A 和 User B 上传同一文件,S3 只存一份,但各自有独立的 fileId 和代理 URL。删除 User A 的文件不影响 User B。只有当所有 files 记录都删除后,才清理 S3 对象。

6 数据库层 Drizzle ORM

6.1 技术选型

6.2 全部 29 个 Schema 文件

点击展开完整 Schema 列表
#文件说明
1agent.tsagents, agent configsAI Agent 定义
2agentBotProvider.tsagent_bot_providersAgent 供应商配置
3agentCronJob.tsagent_cron_jobs定时 Agent 任务
4agentDocuments.tsagent_documentsAgent 知识文档
5agentEvals.tsagent_evals, benchmarksAgent 评估
6agentSkill.tsagent_skillsAgent 技能
7aiInfra.tsai_infra configsAI 基础设施配置
8apiKey.tsapi_keys用户 API Key (带过期)
9asyncTask.tsasync_tasks后台任务队列
10betterAuth.tsusers, accounts, sessions, verifications, two_factorBetter Auth 认证表
11chatGroup.tschat_groups多方对话群组
12file.tsfiles, global_files, documents文件 + 去重 + 文档
13generation.tsgenerations图片/视频生成结果
14message.tsmessages, message_groups, thread_mappings聊天消息 + 嵌套树
15nextauth.ts(legacy NextAuth)旧版认证 (保留兼容)
16notification.tsnotifications用户通知
17oidc.tsoidc configsOIDC 服务配置
18rag.tschunks, embeddings, knowledge_bases, file_chunksRAG 系统
19ragEvals.tsrag evaluation datasetsRAG 评估
20rbac.tsroles, permissionsRBAC 权限控制
21relations.ts(Drizzle relations 定义)表关系映射
22session.tssessions (chat)聊天会话
23task.tstasks任务管理
24topic.tstopics, topic_threads对话主题
25user.tsuser_settings, user_installed_plugins用户设置 + 插件
26-29userMemories/*.tspersona, contexts, preferences, experiences, activities用户记忆系统

6.3 消息表设计 (核心)

messages 表 — LobeChat 最复杂的 schema
messages {
  id           TEXT PK
  role         TEXT           -- 'user' | 'assistant' | 'system' | 'tool'
  content      TEXT           -- 消息内容 (Markdown)
  editorData   JSONB          -- 富文本编辑器数据
  summary      TEXT           -- AI 摘要
  reasoning    TEXT           -- 思维链 (Chain of Thought)
  search       JSONB          -- 搜索结果
  metadata     JSONB          -- 自定义元数据
  model        TEXT           -- 使用的模型 (gpt-4o, claude-3.5-sonnet...)
  provider     TEXT           -- 供应商 (openai, anthropic...)
  tools        JSONB          -- 工具调用记录
  error        JSONB          -- 错误信息
  traceId      TEXT           -- OpenTelemetry 追踪 ID
  favorite     BOOLEAN

  -- 外键关系
  userId       FK → users
  sessionId    FK → sessions
  topicId      FK → topics
  threadId     FK → threads
  parentId     FK → messages (自引用, 树结构)
  quotaId      FK → messages (引用消息)
  agentId      FK → agents
  groupId      FK → chat_groups
  messageGroupId FK → message_groups (并行多模型)
}

message_groups {                -- 并行多模型对话
  id, topicId, userId
  parentGroupId  FK (自引用)
  parentMessageId FK
  type           'parallel' | 'compression'
  content, editorData, metadata
}

7 状态管理 Zustand

7.1 Store 组合模式

文件: src/store/chat/store.ts (82 行)

// Zustand Store 用 Slice 模式组合,每个领域一个 slice
ChatStore = ChatStoreState & ChatStoreAction

ChatStoreAction 包含:
├── ChatMessageAction      -- 消息 CRUD, 线程
├── ChatThreadAction       -- 线程操作
├── ChatAIChatAction       -- LLM 流式对话控制
├── ChatTopicAction        -- 主题管理
├── ChatTranslateAction    -- 消息翻译
├── ChatTTSAction          -- 文本转语音
├── ChatPluginAction       -- 插件执行
├── ChatBuiltinToolAction  -- 搜索/代码解释器
├── ChatPortalAction       -- 弹窗/侧边栏
├── OperationActions       -- 撤销/重做
└── ChatAIAgentAction      -- Agent 编排

// 创建方式
createWithEqualityFn()(
  subscribeWithSelector(   // 细粒度订阅
    devtools(              // Redux DevTools
      createStore          // 组合所有 slices
    )
  ),
  shallow                  // 浅比较
)

7.2 全部 Store 列表

chat/ 核心

消息、线程、主题、AI 对话、插件、TTS、翻译、Agent 编排。最复杂的 store,12 个 slices。

agent/

Agent CRUD、群组管理、收藏。

user/

用户设置、偏好、主题切换。

file/

上传进度、文件列表管理。

global/

全局设置、主题、语言、侧边栏状态。

session/

聊天会话生命周期。

7.3 Slice 内部结构

src/store/chat/slices/message/
├── actions/
│   ├── index.ts           -- 导出所有 action
│   ├── publicApi.ts       -- 用户触发: addMessage, updateMessage
│   ├── internals.ts       -- 内部操作
│   ├── optimisticUpdate.ts -- 乐观更新 (先改 UI, 再同步 DB)
│   ├── query.ts           -- 消息查询
│   └── runtimeState.ts    -- loading/error 状态
├── selectors/
│   ├── displayMessage.ts  -- UI 格式化
│   ├── dbMessage.ts       -- 原始 DB 数据
│   ├── messageState.ts    -- 元数据 (加载中, 错误)
│   └── chat.ts            -- 对话专用查询
├── reducer.ts             -- 不可变更新
└── supervisor.ts          -- 批量操作协调

8 tRPC API 层 47 路由

8.1 路由架构

文件: src/server/routers/lambda/index.ts (122 行)

全部 47+ 个 tRPC 子路由
分类路由说明
AgentagentAgent CRUD
agentBotProviderBot 供应商配置
agentCronJob定时任务
agentDocumentAgent 文档
agentEvalAgent 评估
agentSkillsAgent 技能
agentGroupAgent 群组
AIaiChatAI 对话 (流式)
aiModel模型配置
aiProviderAI 供应商管理
对话message消息 CRUD
topic主题管理
thread线程操作
session / sessionGroup会话管理
文件file文件 CRUD + 去重
upload预签名 URL 生成
document文档管理
知识库knowledge / knowledgeBase知识库管理
chunk文档分块 + 嵌入
ragEvalRAG 评估
生成image图片生成
video视频生成
generation / generationBatch / generationTopic生成记录
用户user用户配置
userMemory / userMemories用户记忆
其他plugin, search, market, config, exporter, importer, share, notification, notebook, apiKey, usage, task, brief, home, device, comfyui, klavis, oauthDeviceFlow各功能模块
商业accountDeletion, referral, spend, subscription, topUp付费/订阅功能

8.2 tRPC 中间件栈

// 公开路由
publicProcedure = t.procedure.use(openTelemetry)

// 认证路由
authedProcedure = t.procedure
  .use(openTelemetry)      // 分布式追踪
  .use(oidcAuth)           // OIDC JWT 验证 (可选)
  .use(userAuth)           // Better Auth Session 验证

// 文件路由 (额外检查)
fileProcedure = authedProcedure
  .use(checkFileStorageUsage)  // 存储配额检查
  .use(serverDatabase)         // DB 连接注入

9 Docker 部署 编排

9.1 生产环境架构

文件: docker-compose/deploy/docker-compose.yml

┌─────────────────────────────────────────────────────────────┐
│                    docker-compose (lobe-network)            │
│                                                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────────┐  │
│  │ lobehub  │  │ postgres │  │  redis   │  │  rustfs    │  │
│  │  :3210   │  │  :5432   │  │  :6379   │  │ :9000/:9001│  │
│  │          │  │          │  │          │  │            │  │
│  │ Next.js  │  │ ParadeDB │  │ Redis 7  │  │ S3 兼容    │  │
│  │ + tRPC   │  │ PG 17    │  │ Alpine   │  │ 对象存储   │  │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘  └─────┬──────┘  │
│        │             │             │              │          │
│        ├── SQL ──────┘             │              │          │
│        ├── Redis ──────────────────┘              │          │
│        └── S3 API ────────────────────────────────┘          │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐                         │
│  │ rustfs-init  │  │   searxng   │                         │
│  │ (one-shot)   │  │   :8080      │                         │
│  │ 创建 bucket  │  │ 搜索引擎    │                         │
│  └──────────────┘  └──────────────┘                         │
└─────────────────────────────────────────────────────────────┘

9.2 Dockerfile 多阶段构建

阶段镜像职责产出
basenode:24-slim依赖安装 + 证书node_modules
builderbaseNext.js 构建 + Vite SPA + Drizzle.next/standalone + .next/static + spa/
appbusybox资源收集精简的应用文件
scratchnode:24-slim最终运行镜像~200MB, 非 root (nodejs:1001)

9.3 RustFS 初始化

# rustfs-init 服务 (一次性容器)
mc alias set rustfs "http://rustfs:9000" admin ${SECRET}
mc mb "rustfs/lobe" --ignore-existing            # 创建 bucket
mc anonymous set-json "/bucket.config.json" "rustfs/lobe"  # 设置匿名读权限

bucket.config.json 设置 lobe bucket 的匿名只读策略,允许公开访问已上传文件。

10 环境变量全表 配置中心

变量说明默认值
APP_URL公开访问 URL (https://chat.example.com)必填
INTERNAL_APP_URL内部服务间 URL (Docker 内部通信)必填
PORT服务端口3210
NODE_ENV运行环境production
ENABLE_BUSINESS_FEATURES启用商业功能false
变量说明默认值
AUTH_SECRET32 字节 base64 随机字符串必填
AUTH_SSO_PROVIDERS逗号分隔的 SSO 列表 (google,logto...)
AUTH_ALLOWED_EMAILS注册白名单 (域名或完整邮箱)空 (允许所有)
AUTH_DISABLE_EMAIL_PASSWORD禁用邮箱密码, 强制 SSOfalse
AUTH_EMAIL_VERIFICATION要求邮箱验证false
AUTH_LOGTO_IDLogto 应用 ID-
AUTH_LOGTO_ISSUERLogto Endpoint URL-
AUTH_LOGTO_SECRETLogto 应用密钥-
LOGTO_WEBHOOK_SIGNING_KEYLogto Webhook 签名密钥-
JWKS_KEYOIDC 服务模式 JWKS (RS256 RSA)-
变量说明默认值
S3_ENDPOINTS3 端点 URL必填
S3_ACCESS_KEY_ID访问密钥必填
S3_SECRET_ACCESS_KEY秘密密钥必填
S3_BUCKET存储桶名必填
S3_REGION区域us-east-1
S3_ENABLE_PATH_STYLE路径样式 (MinIO/RustFS 必须为 1)false
S3_PUBLIC_DOMAIN公开域名 (CDN)
S3_SET_ACL设置 public-read ACL0
S3_PREVIEW_URL_EXPIRE_IN预签名 URL 过期秒数7200
变量说明默认值
DATABASE_URLPostgreSQL 连接字符串必填
DATABASE_DRIVER驱动 (node / neon)node
KEY_VAULTS_SECRET32 字节 base64, 加密敏感字段必填
MIGRATION_DB设为 "1" 启动时跑迁移
变量说明默认值
REDIS_URLRedis 连接字符串必填
REDIS_DATABASE数据库索引0
REDIS_PREFIXKey 前缀lobechat
REDIS_TLS启用 TLSfalse

11 关键数据流 端到端

11.1 用户登录流程 (Logto SSO)

1. 用户点击 "使用 Logto 登录"
   → Better Auth Client: signIn.social({ provider: 'logto' })
   → 302 重定向 → Logto 授权页

2. Logto 授权
   → 用户输入凭据 / 选择社交登录
   → Logto 验证 → 生成 authorization_code
   → 302 回调 → LobeChat /api/auth/callback/logto

3. Token Exchange
   → Better Auth Server: 用 code 换 access_token + id_token
   → 解析 id_token → 获取 email, name, picture
   → mapProfileToUser() → 映射到 LobeChat 用户结构

4. 用户创建/关联
   → 查找已有用户 (by email)
   → 已有: linkAccount (关联 Logto 账号)
   → 新用户: createUser → Hook: UserModel.create()
   → 同步头像/姓名到 LobeChat users 表

5. 会话创建
   → 创建 Better Auth Session (30 天)
   → 写入 Redis (10 分钟缓存)
   → Set-Cookie: better-auth.session_token=...
   → 302 重定向 → /

6. 后续请求
   → Cookie → tRPC middleware → userAuth
   → Redis 缓存命中 → 直接获取 userId (无 DB 查询)
   → Redis 未命中 → 查 sessions 表 → 刷新缓存

11.2 发送消息 → AI 回复

1. 用户输入消息
   → ChatStore.sendMessage(content)
   → 乐观更新: 立即显示用户消息

2. tRPC 调用
   → aiChat.sendMessageInServer({
       messages: [...history, newMessage],
       model: 'gpt-4o',
       provider: 'openai'
     })

3. 服务端处理
   → 认证中间件链 (OIDC → Session → DB)
   → initModelRuntimeFromDB(db, userId, 'openai')
     → 从 DB 加载用户配置的 API Key (解密)
     → 实例化 OpenAI Provider

4. LLM 调用
   → modelRuntime.generateStream({
       messages, tools, temperature, ...
     })
   → OpenAI API: POST /v1/chat/completions (stream: true)

5. 流式响应
   → SSE: data: {delta: "你", ...}
   → StreamingHandler 实时更新 Store
   → UI 实时渲染 (打字机效果)

6. 完成
   → 保存 assistant 消息到 DB
   → 更新 Token 计数 (UsageCounter)
   → OpenTelemetry: 记录 traceId

11.3 文件上传 → RAG 嵌入

1. 用户拖入 PDF
   → UploadService.uploadFileToS3(file)

2. 预签名 + 直传
   → tRPC: upload.createS3PreSignedUrl → 获取 PUT URL
   → XHR PUT → RustFS/MinIO (浏览器直传)
   → tRPC: file.createFile → DB 记录 (files + globalFiles)

3. 文档解析 (异步)
   → chunk.createParseFileTask({fileId})
   → AsyncTask: 解析 PDF → 提取文本 → 分块 (chunks)
   → 写入 chunks 表 + file_chunks 关联

4. 向量嵌入 (异步)
   → chunk.createEmbeddingChunksTask({fileId})
   → 批量调用 Embedding API (batch=50, concurrency=10)
   → pgvector: INSERT embeddings (1536 维向量)

5. 语义搜索 (使用时)
   → chunk.semanticSearchChunks({query, topK: 10})
   → pgvector: cosine_distance 搜索
   → 返回最相关的文档块 + 相似度分数

12 Manus 平台规划 米乐优品

Manus AI 生成的 MILEJOY 内部 AI 平台定制化全案(品牌 / 权限 / 业务插件 / 部署运维)。

12.1 平台整体规划

平台定位

米乐优品 80 人跨境电商团队内部 AI 平台,基于 LobeChat 二开。解决生图、财务核对、视频生成、物流数据对接四大核心痛点。

部署架构

Docker Compose 容器化 → LobeChat + PostgreSQL + Redis + RustFS + SearXNG。Dashboard 监控 API 调用量/Token 消耗/模型响应时间。

12.2 权限与角色

改造项具体内容涉及文件
用户类型感知LobeUser / UserInitializationStaterolepackages/types/, lambda/user.ts
导航过滤useCategory.tsx 按 admin/member 隐藏菜单settings/hooks/
路由守卫非管理员访问 /settings/provider 等 → 拦截跳转settings/index.tsx
管理员页面用户列表 / 角色分配 / 封禁解禁新建 settings/admin/
成本控制按部门分配 Token + 存储配额RBAC 表 + tRPC 中间件

12.3 业务插件规划

生图 + 视频
  • 接入 DALL-E 3 / Midjourney / Stable Diffusion
  • 接入 Runway / Kling 视频生成
  • "商品主图助手"/"营销海报设计师" 预设 Prompt
  • 生成结果自动推送到素材库
财务核对
  • 对接 ERP / PayPal / Payoneer API
  • Code Interpreter 分析 Excel/CSV
  • 自动对账 + 差异分析 + 可视化报告
  • RBAC 限制仅财务部可访问
物流追踪
  • 集成 17TRACK / AfterShip / DHL / FedEx
  • "物流客服助手" 输入订单号自动查轨迹
  • Cron Job 定期批量查询异常件
  • 自动向物流专员发预警通知
插件架构
  • Default 插件:标准 API + 可选 UI
  • Markdown 插件:纯文本返回
  • Standalone 插件:iframe 独立应用
  • Plugin Gateway 网关 + Plugin Index 市场

12.4 品牌替换

步骤方式范围
1. 快速环境变量 NEXT_PUBLIC_BRAND_LOGO=url仅主 Logo
2. 源码branding.ts: BRANDING_NAME='米乐优品'全局名称 + Logo + 链接
3. 资源替换 public/ 下 favicon/PWA/OG 图浏览器图标 + 社交分享
4. 主题主色调 #FF8C00(米乐优品橙)CSS 变量 + Ant Design Token

12.5 全部物料预览

全部 43 个品牌资源(Logo / OG 图 / favicon ×14 / PWA 图标 ×8 / 启动画面):

横向 Logo

Logo 白色版

完整 Logo

深色背景

OG v2

OG v1

OG 宽版

欢迎 Banner

图标 512

图标 256

Maskable 512

PWA 512

PWA 384

PWA 192

启动画面

另有 favicon ×14 (.ico)、小尺寸图标 (16/32/48/64/72/96/128/144/152px)、Apple Touch Icon、竖版启动画面 1242×2688

全部 23 张 UI 物料 + 3 个视频动画 + CSS/JSON Token:

登录 Mockup

登录背景·浅

登录背景·深

侧边栏·折叠

侧边栏·浅

侧边栏·深

欢迎页·浅

欢迎页·深

色板

AI 头像

用户头像

空对话

无搜索结果

空知识库

空插件

错误页

网络错误

启动画面·首帧

启动画面·末帧

片头·首帧

片头·末帧

产品演示封面

培训视频封面

视频动画 (3)

启动画面动画

Logo 片头动画

AI 思考动画

配置文件:milejoy-theme-tokens.css + milejoy-design-tokens.json + milejoy-loading-animations.css

全部 12 个形态(3 基础 + 9 表情 + 6 场景):

正面

侧面

四分之三

欢迎

竖拇指

庆祝

思考

困惑

爱心

加载中

睡觉

错误

财务场景

物流场景

生图场景

双11场景

全部 25 套企业表情包:

hello

good-job

received

fighting

are-you-there

hahaha

ok

thumbsup

hug

speechless

shipped

orders-booming

rush-order

off-work

slacking

meeting

reconciling

five-star

thanks-boss

working-hard

goodbye

dont-rush

tea-time

666

goodnight

全部 12 张(功能海报 + 品牌海报 + 社交媒体图):

平台发布

品牌文化

AI 生图

视频生成

财务核对

物流追踪

团建

培训

招聘

朋友圈

公众号封面

小红书

3 个可用的 LobeChat 插件 manifest(物流/财务/生图),符合 chat-plugin-sdk 规范:

milejoy-logistics/manifest.json — 物流追踪插件
{
  "identifier": "milejoy-logistics",
  "api": [
    {
      "name": "queryLogistics",
      "description": "根据运单号查询跨境电商包裹的实时物流轨迹",
      "parameters": {
        "properties": {
          "trackingNumber": { "type": "string" },
          "carrier": { "enum": ["DHL","FedEx","UPS","SF-Express","4PX","auto"] }
        },
        "required": ["trackingNumber"]
      }
    },
    {
      "name": "batchQueryStatus",
      "description": "批量查询多个运单号的最新物流状态(最多50个)"
    },
    {
      "name": "queryAnomalyPackages",
      "description": "查询异常包裹(长时间未更新/清关异常/退件/投递失败)",
      "parameters": {
        "properties": {
          "anomalyType": { "enum": ["all","stalled","customs_hold","returned","delivery_failed"] },
          "daysThreshold": { "type": "number", "description": "未更新天数阈值,默认7天" }
        }
      }
    }
  ],
  "ui": { "url": "[部署地址已隐]", "height": 450 }
}

财务核对和图片生成插件 manifest 结构类似,在 milejoy_plugin_examples.zip (8KB)

插件架构图

开发流程图

Manus 全部产出清单(6 个 zip,共 400MB+)
大小内容
brand_assets.zip29MBfavicon ×14、PWA 图标 ×8、Apple Touch Icon、OG 图、横向 Logo
mascot_assets.zip95MB吉祥物 3 基础形态 + 9 表情(竖拇指/欢迎/庆祝/思考/困惑/爱心/加载/睡觉/错误)
stickers_pack.zip116MB企业表情包 25 套(你好/收到/加油/好的/爆单/摸鱼/下班/晚安/666...)
posters_pack.zip66MB海报 10 张(平台发布/AI 生图/视频/物流/财务/团建/培训/招聘/品牌/朋友圈)
ui_video_assets.zip96MB登录页(浅/深) + Mockup + 侧边栏 Logo(折叠/展开/浅/深) + 欢迎页(浅/深) + 主题 CSS/JSON
plugin_examples.zip8KB3 个插件 manifest.json(物流/财务/生图)+ plugin-index 索引

另有 6 篇方案文档 (.md)、6 篇研究笔记、design-tokens.json、loading-animations.css、3 个视频动画 (.mp4)。
原目录:~/.Trash/定制化开源项目以适配公司内部需求/

13 NotebookLM 知识库接入 MCP

把 Google NotebookLM 个人账号下的所有专业笔记和文档,通过 MCP(Model Context Protocol)暴露给 LobeChat 的 AI 助理,让公司全员共享同一份知识库 — 无需迁移文档、无需付费、AI 自动调用、答案带原文引用

📅 部署时间
2026-04-09 接入完成。基于 PleasePrompto/notebooklm-mcp v1.2.1 + punkpeye/mcp-proxy。

13.1 业务场景

需求

客户咨询时需要快速调用公司内部专业知识(产品手册、售后政策、技术文档等),手动查阅 NotebookLM 太慢。

约束

所有专业文件已经存在 个人 NotebookLM 账号 里,不想迁移;公司有 80 人需要访问,但只有一个 Google 账号;不想付 NotebookLM Enterprise 费用。

解决方案

用浏览器自动化工具(patchright + 真 Chrome)保持账号登录态,包成 MCP server,挂到 LobeChat 全员共享的助理上。

关键风险

免费版每日查询配额(估测 50-100 次/账号/天);Google 可能检测到自动化(建议长期用专用账号);登录态偶尔失效(需重新 OAuth)。

13.2 整体架构

公司员工 (80 人)
   │
   ├─→ LobeChat 网页 (https://lobehub.kang-kang.com)
   │      │
   │      ├─ 助理对话界面
   │      └─ MCP 工具调用 (16 个工具)
   │            │
   │            ▼
   │      tRPC 后端 (lobechat-prod 容器)
   │            │
   │            └── HTTP POST /mcp ──→  http://10.0.1.1:3289/mcp (host gateway)
   │                                              │
   │                                              ▼
   │                                    mcp-proxy (systemd 常驻)
   │                                    端口 3289 / API Key 鉴权
   │                                              │
   │                                              ▼ stdio
   │                                    notebooklm-mcp (npm CLI)
   │                                              │
   │                                              ▼
   │                                    Patchright + Google Chrome 147
   │                                    持久化 Chrome profile + 已登录态
   │                                              │
   │                                              ▼
   └────────────────────────→     https://notebooklm.google.com (个人账号)
                                            │
                                            ▼
                                  Gemini 2.5 + 用户上传的所有 sources
                                            │
                                            ▼
                                  带原文引用的回答
  

13.3 核心组件

组件位置作用
notebooklm-mcp 云端 VPS
npm i -g notebooklm-mcp@1.2.1
核心 MCP server。用 patchright + Chrome 模拟人类操作 NotebookLM 网页。暴露 16 个 MCP 工具:ask_question、list_notebooks、add_notebook、search_notebooks 等。
mcp-proxy 云端 VPS
npm i -g mcp-proxy
把 stdio MCP 包装成 HTTP/SSE。LobeChat 网页版只支持 HTTP MCP,不支持 stdio,所以必须套这一层。监听 0.0.0.0:3289,X-API-Key 鉴权。
Patchright + Chrome /root/.cache/ms-playwright/
Google Chrome 147
Patchright 是 Playwright 的 stealth fork,用真 Chrome 而非 chromium-headless-shell。带人性化打字延迟、鼠标移动模拟,降低 Google 反爬虫检测概率。
登录态 (browser_state) /root/.local/share/notebooklm-mcp/browser_state/state.json
/root/.local/share/notebooklm-mcp/chrome_profile/
Google OAuth 完成后保存的 cookies + localStorage + Chrome profile。从本地 Mac 完成首次 OAuth 后 scp 到云端,保证云端无头环境也能直接复用登录态。
systemd service /etc/systemd/system/notebooklm-mcp.service 常驻进程管理。开机自启,崩溃自动重启。日志写到 /var/log/notebooklm-mcp.log
LobeChat 集成 PostgreSQL user_installed_plugins 给每个 LobeChat 用户的 user_installed_plugins 表插入一条 customPlugin 记录,type=mcp,customParams.mcp.url 指向 http://10.0.1.1:3289/mcp,headers 带 X-API-Key。

13.4 部署步骤(已完成的全流程)

1️⃣  本地 Mac 完成 Google OAuth 登录
   - 直接调用 AuthManager.performSetup() 弹出真 Chrome 浏览器
   - 用户在浏览器里完成 Google 登录授权(30 秒)
   - Session 保存到 ~/Library/Application Support/notebooklm-mcp/

2️⃣  把登录态打包上传到云端
   - tar -czf nbm-data.tar.gz -C "~/Library/Application Support/notebooklm-mcp" .
   - scp nbm-data.tar.gz root@76.13.31.179:/tmp/
   - tar -xzf 到 /root/.local/share/notebooklm-mcp/
   - 清理 macOS 元数据 (._*) + chown root

3️⃣  云端 VPS 安装运行环境
   - npm install -g notebooklm-mcp@1.2.1
   - npx patchright install chrome  (Google Chrome 147)
   - npm install -g mcp-proxy

4️⃣  写 systemd service 让 mcp-proxy 常驻
   - ExecStart: mcp-proxy --host 0.0.0.0 --port 3289 \
       --apiKey nbm-secret-mile-2026 -- /usr/bin/notebooklm-mcp
   - systemctl enable + start notebooklm-mcp

5️⃣  LobeChat 容器内部访问云端 mcp-proxy
   - lobechat-prod 在 coolify 网络,gateway 是 10.0.1.1
   - 容器内 fetch http://10.0.1.1:3289/mcp 可达
   - 不需要公网域名,不需要 HTTPS

6️⃣  数据库注入 MCP 插件配置(绕过 UI 操作)
   - fetch tools/list 拿到 16 个工具的 schema
   - 构造 LobeChatPluginManifest(api[]、meta、mcpParams)
   - 构造 customParams(mcp.url、mcp.headers、mcp.auth)
   - INSERT 到 user_installed_plugins,给所有 3 个用户都注入
  

13.5 16 个 MCP 工具

分类工具说明
查询类 ask_question 核心工具。向 NotebookLM 提问,可选 notebook_id(已注册)或 notebook_url(即兴),支持 session_id 实现多轮上下文对话。
get_health检查认证状态、活跃 session 数。
list_notebooks列出已注册到 library 的 notebook 元数据(name/topics/use_cases/url)。
get_notebook获取单个 notebook 的详细信息。
select_notebook设置默认活跃 notebook,后续 ask_question 不传 notebook_id 时用此 notebook。
库管理 add_notebook 注册一个 notebook URL 到 library,需要提供 name、description、topics、use_cases,让 AI 知道何时调用它。
update_notebook更新已注册 notebook 的元数据。
remove_notebook从 library 移除 notebook(危险操作,需要用户确认)。
search_notebooks按关键词搜索 library,匹配 name/description/topics/tags。
get_library_statslibrary 统计(总数、各 notebook 的使用次数等)。
Session 管理 list_sessions 列出活跃 session(age、message count、last activity)。
close_session关闭指定 session。
reset_session重置 session 的对话历史(保留同一个 session_id)。
认证 & 维护 setup_auth 开启浏览器手动 Google 登录(仅在没有可显示器的环境之外可用)。
re_auth切换 Google 账号或重新认证。
cleanup_data深度清理所有数据。

13.6 AI 调用流程

用户在 LobeChat 输入:
  "查一下退货政策对超过 30 天的订单怎么处理"
        │
        ▼
LobeChat AI(启用了 NotebookLM 插件)
        │
        ├─ 看到 16 个工具可用
        ├─ 决定调用 ask_question
        │
        ▼
ask_question({
  question: "退货政策对超过 30 天的订单怎么处理?",
  notebook_url: "https://notebooklm.google.com/notebook/xxx"
})
        │
        ▼
HTTP POST → http://10.0.1.1:3289/mcp(带 X-API-Key 头)
        │
        ▼
mcp-proxy 转发到 stdio
        │
        ▼
notebooklm-mcp:
  1. 创建 session(或复用)
  2. Patchright 启动 Chrome(headless),加载已保存的 auth state
  3. 导航到 notebook URL
  4. 在输入框模拟人类打字输入问题
  5. 等待 NotebookLM 返回答案
  6. 解析答案 + 引用 + 提取
        │
        ▼
返回 JSON:{ status, question, answer, session_id, notebook_url }
        │
        ▼
LobeChat AI 接收答案,整理后发给用户
(带原文引用,便于追溯)
  

13.7 当前关键文件路径(云端 76.13.31.179)

用途路径
notebooklm-mcp 安装位置/usr/lib/node_modules/notebooklm-mcp/
登录态 + Chrome profile/root/.local/share/notebooklm-mcp/
library.json (notebook 索引)/root/.local/share/notebooklm-mcp/library.json
Patchright Chrome/root/.cache/ms-playwright/
systemd unit/etc/systemd/system/notebooklm-mcp.service
运行日志/var/log/notebooklm-mcp.log
HTTP MCP 端点http://10.0.1.1:3289/mcp (容器视角)
http://127.0.0.1:3289/mcp (host 视角)
API Key(X-API-Key 头)nbm-secret-mile-2026

13.8 待决策:如何"分库"

⚠ 关键决策点 — 还未做

当前 library 是空的(add_notebook 没注册任何 notebook)。AI 不知道你账号下有哪些 notebook,必须在对话里手动给它 URL 才能查询。

方案 A:批量注册所有重要 notebook(推荐)

给每个 notebook 调用一次 add_notebook,提供完整元数据:

add_notebook({
  url: "https://notebooklm.google.com/notebook/abc123",
  name: "TESERY 充电桩产品手册",
  description: "包含所有 TESERY 充电桩的安装、使用、故障排查文档",
  topics: ["充电桩", "TESERY", "安装", "故障"],
  use_cases: ["客户问充电桩问题", "技术支持咨询"],
  content_types: ["product_manual", "troubleshooting"]
})
  

注册后 AI 调用 list_notebooks 可看到所有可用 notebook,根据用户问题自动选最相关的。需要提前准备每个 notebook 的元数据

方案 B:每次对话手动指定 URL

不注册 library,用户在对话里告诉 AI:"用这个 notebook 查 ..." + 粘贴 URL。简单但不智能。

方案 C:在助理 system prompt 里硬编码索引

你有以下知识库可以查询,根据问题主题选择最匹配的 URL:
- 充电桩问题 → https://notebooklm.google.com/notebook/xxx1
- 售后政策 → https://notebooklm.google.com/notebook/xxx2
- 销售话术 → https://notebooklm.google.com/notebook/xxx3
当用户问相关问题时,调用 ask_question 工具,传对应的 notebook_url。
  

简单粗暴但有效,不需要 add_notebook。可以为不同部门(销售/技术/财务)做不同助理,每个助理只看到自己部门的 notebook。

方案 D:按助理隔离知识库(权限粒度最细)

未来可以做:

13.9 已知限制 & 风险

类别说明缓解措施
每日配额 NotebookLM 免费版估测 50-100 次问答/账号/天 升级 Google AI Pro($20/月,配额 5x);缓存重复问题;按部门分账号
Google 检测 自动化访问可能被风控(账号被锁、要求二次验证) 用专用账号(不要主账号);patchright stealth 模式已开启;不要短时间发太多请求
登录态失效 Cookies 会过期;Google 会要求定期重新验证 每 1-2 周重新跑一次 OAuth;脚本化重新登录流程;监控 get_health 状态
响应延迟 每次问答 3-15 秒(要启动 Chrome、加载页面、等 Gemini 响应) 常驻 Chrome 进程(已开启);session 复用减少重启开销;缓存常见问题
用户隔离 所有公司用户共享同一个 Google 账号 NotebookLM 账号级别没有隔离;通过 LobeChat 助理级别做权限控制
跨用户配额竞争 所有用户消耗同一个账号的配额 记录调用统计;高峰期降级(让 AI 优先回答常见问题)

13.10 维护命令

# 查看 service 状态
systemctl status notebooklm-mcp

# 看实时日志
tail -f /var/log/notebooklm-mcp.log

# 重启 service
systemctl restart notebooklm-mcp

# 测试 HTTP 端点是否健康
curl -s -X POST "http://127.0.0.1:3289/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "X-API-Key: nbm-secret-mile-2026" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}'

# 查看数据库里所有用户的 NotebookLM 插件
docker exec gis3jj74958vecvetgbq6e4u psql -U postgres -d lobechat \
  -c "SELECT user_id, identifier, jsonb_array_length(manifest->'api') AS tools FROM user_installed_plugins;"

# 重新登录 Google(在本地 Mac 跑)
node /tmp/notebooklm-login.mjs
# 然后 scp 新的 session 到云端
tar -czf /tmp/nbm-data.tar.gz -C "~/Library/Application Support/notebooklm-mcp" .
scp /tmp/nbm-data.tar.gz root@76.13.31.179:/tmp/
ssh root@76.13.31.179 "rm -rf /root/.local/share/notebooklm-mcp/* && tar -xzf /tmp/nbm-data.tar.gz -C /root/.local/share/notebooklm-mcp/ && find /root/.local/share/notebooklm-mcp -name '._*' -delete && chown -R root:root /root/.local/share/notebooklm-mcp && systemctl restart notebooklm-mcp"
  

13.11 给新员工配 NotebookLM 工具

当前是直接 INSERT 到 user_installed_plugins。新员工注册 LobeChat 后,他不会自动有这个插件 — 需要手动 INSERT 一条记录。

方案 1:手动 SQL(重复跑 inject-mcp.sql 加新 user_id)

方案 2:写一个 PostgreSQL trigger,每次 INSERT users 时自动给新用户注入 NotebookLM 插件

方案 3:把 NotebookLM 助理设置为公开市场助理,公司所有员工都能从市场添加

14 Q&A 问答区 持续更新

问答分布在对应位置
详细的源码级问答已内嵌到相关章节中(点击蓝色按钮展开)。以下是通用问题。
⚠ 实战踩坑 · 多模态文件 URL 公网穿透问题(2026-04-09)

"上传图片就报 407 / Error while downloading 192.168.2.221:19000/..." —— 部署在内网的 LobeChat 接云端 AI 时必撞,原因和方案见下方第一条 Q&A。

上传图片/文件后报 Error while downloading http://192.168.2.221:19000/... Upstream status code: 407,换模型也不行,怎么办?

根因一句话:LobeChat 把 MinIO 的内网地址当作图片 URL 发给云端 AI,云端 AI 服务器在海外/公网,下载不到 192.168.2.221 这个内网 IP,被中间代理拦截返回 407 Proxy Authentication Required。和你在哪台机器访问 LobeChat 完全无关,文字聊天没事是因为没带文件 URL。

为什么是"下载链接"而不是"上传图片"?—— 预签名 URL 直传机制

LobeChat 走的是 S3 标准最佳实践:浏览器直传 MinIO,对话里只放 URL。流程:

浏览器 ──①PUT 预签名 URL──> MinIO (192.168.2.221:19000)
浏览器 ──②对话+图片URL──> LobeChat 后端
LobeChat ──③转发对话+URL──> OpenAI / Claude / Poe (云端)
云端 AI ──④回头 GET 这个 URL──> ❌ 拉不到内网,407

三个原因这么设计:① 大文件不占 LobeChat 服务器带宽和内存;② 消息体只有几百字节 URL 不是几 MB base64,省 token 上下文;③ 多轮对话同一张图复用一个 URL。OpenAI 协议原生支持 image_url 字段,LobeChat 默认就走这条。

协议层面 image_url 也接受 data:image/png;base64,... 内嵌,但 LobeChat 没走这个分支。Claude/Gemini 原生 SDK 是 base64 内嵌,所以接原生端点不会撞这个问题;但如果接的是 api.xxx.com/v1 这种 OpenAI 兼容聚合站,100% 按 URL 下载解析,必撞。

三种部署形态对比

① 纯内网(你现在)LobeChat + MinIO 都在 192.168.2.221多模态必挂
② 内网部署 + 文件走公网主体不动,只把 MinIO 19000 通过公网域名暴露推荐
③ 全云端LobeChat + MinIO 都在 VPS对外服务才用
④ 本地 LLMOllama/vLLM 在同一内网不需要公网,但模型能力弱

推荐方案:MinIO 套公网域名(最小改动)

1. 加反代(Caddy 例子,或用 Cloudflare Tunnel 不开放路由器端口):

s3.kang-kang.com {
    reverse_proxy 192.168.2.221:19000
}

2. 改 LobeChat 环境变量

S3_PUBLIC_DOMAIN=https://s3.kang-kang.com
S3_ENDPOINT=https://s3.kang-kang.com
S3_ENABLE_PATH_STYLE=1

3. 改 MinIO 环境变量(必须,否则签名 host 不一致会 SignatureDoesNotMatch):

MINIO_SERVER_URL=https://s3.kang-kang.com
MINIO_BROWSER_REDIRECT_URL=https://console.kang-kang.com

4. 重启 LobeChat + MinIO,重新上传一张图测试,新 URL 应是 https://s3.kang-kang.com/lobe/files/...

注意事项
  • 旧的已上传文件 URL 是带内网 host 签名的,改完后旧文件会 404,需要重新上传或写脚本批量改 DB 里 host
  • Coolify env var 必须走 API/Eloquent 写入(自动加密),不能裸 SQL UPDATE
  • 不要直接把 19000 端口在路由器上暴露公网,优先 Cloudflare Tunnel / frp / WireGuard 反穿,安全得多
  • 这是所有云端 LLM 的通用限制,不是 LobeChat bug;只要你接的是云端 API,MinIO 就必须公网可达
Logto 和 Better Auth 是什么关系?为什么不直接用 Logto?

Better Auth 是 LobeChat 的认证框架(类似 Clerk/NextAuth 的开源替代),负责管理会话、密码哈希、Cookie、2FA、Passkey 等所有认证基础设施。Logto 只是作为一个 SSO Provider(OIDC 协议)接入 Better Auth。

简单说:Better Auth 是"认证引擎",Logto 是"其中一个登录入口"。这种设计的好处是 LobeChat 可以同时支持 11 种 SSO + 邮箱密码 + Magic Link + Passkey,而不被任何一个 IdP 绑死。

为什么用 RustFS 而不是直接用 MinIO?

RustFS 是 MinIO 的 Rust 重写版,API 完全兼容。LobeChat 的 docker-compose 默认用 RustFS 但代码里用的是标准 AWS SDK v3——所以可以无缝切换到 MinIO、AWS S3、Cloudflare R2 等任何 S3 兼容存储。配置只需改环境变量,代码零改动。

文件上传为什么不经过 LobeChat 服务器?

采用预签名 URL 直传模式:客户端先从服务端获取一个 1 小时有效的 PUT 预签名 URL,然后浏览器直接上传到 S3。这样大文件不会占用 LobeChat 服务器的带宽和内存,服务器只处理轻量的元数据。这是 S3 最佳实践。

内嵌问答索引

15 部署运维记录 2026-04-12

双端部署

生产(主)[部署地址已隐][内网 IP 已隐]
备份lobehub.kang-kang.com76.13.31.179
文档lobechat-docs.kang-kang.comCoolify

用户账号

mile[REDACTED]管理员
[账号已隐][REDACTED]Boss
[账号已隐][账号已隐]同事

服务器凭证

SSH:[SSH 已隐] 密码: [REDACTED]
宝塔:面板入口 [REDACTED] / [REDACTED]
域名:*.milejoy.com → [内网 IP 已隐]

数据库 & 存储

PG:postgres:[REDACTED] / lobechat
RustFS:[REDACTED] / [REDACTED]
备份:每日 3:00 自动 → 个人 VPS • 社区数据每 6h 增量同步(505 助理本地镜像)

变更记录

2026-04-16 Google Gemini API 替换 Poe API(额度耗尽);docker-compose 改 env_file 模式;JWKS_KEY 生成解决生图异步签名;RustFS 权限修复 + nginx 9200 SSL 代理解决 presigned URL 上传;bucket CORS 配置;文件上传与生图恢复正常
2026-04-12 公司 VPS 部署 + 数据迁移 + 社区本地镜像 + 自动备份
2026-04-11 个人 VPS 数据层重构:有状态服务迁出 Coolify
2026-04-09 多模态 URL 公网穿透问题修复
详细解释 可拖动