257 lines
6.4 KiB
Markdown
257 lines
6.4 KiB
Markdown
# LobeChat 项目分析报告
|
||
|
||
基于对 lobe-chat 项目的深入探索,以下是值得 aitu 借鉴的关键模式和架构决策。
|
||
|
||
---
|
||
|
||
## 1. 状态管理 - Zustand Slice 模式
|
||
|
||
**文件路径:** `src/store/chat/store.ts`
|
||
|
||
LobeChat 使用了非常优雅的 **Zustand 切片组合模式**:
|
||
|
||
```typescript
|
||
// 每个功能是独立的 slice
|
||
export interface ChatStoreAction extends
|
||
ChatMessageAction,
|
||
ChatThreadAction,
|
||
ChatAIChatAction,
|
||
ChatTopicAction {}
|
||
|
||
// 组合 slices 创建 store
|
||
const createStore: StateCreator<ChatStore> = (...params) => ({
|
||
...initialState,
|
||
...chatMessage(...params),
|
||
...chatAiChat(...params),
|
||
...chatTopic(...params),
|
||
});
|
||
|
||
export const useChatStore = createWithEqualityFn<ChatStore>()(
|
||
subscribeWithSelector(devtools(createStore)),
|
||
shallow,
|
||
);
|
||
```
|
||
|
||
**借鉴价值:** aitu 的 `DrawnixContext` 可以重构为类似的切片模式,提高可维护性和测试性。
|
||
|
||
---
|
||
|
||
## 2. AI 流式响应 - 平滑动画系统
|
||
|
||
**文件路径:** `packages/fetch-sse/src/fetchSSE.ts`
|
||
|
||
独立的 `@lobehub/fetch-sse` 包处理 SSE 流,特别是 **平滑文本动画**:
|
||
|
||
```typescript
|
||
const createSmoothMessage = (params) => {
|
||
let buffer = '';
|
||
let outputQueue: string[] = [];
|
||
|
||
const startAnimation = (speed) => {
|
||
// 使用 requestAnimationFrame 实现平滑更新
|
||
// 动态速度调整基于队列长度
|
||
const targetSpeed = Math.max(speed, outputQueue.length);
|
||
currentSpeed += (targetSpeed - currentSpeed) * speedChangeRate;
|
||
};
|
||
};
|
||
```
|
||
|
||
**借鉴价值:** 可提取到独立包用于 aitu 的 AI 聊天功能,避免 UI 卡顿。
|
||
|
||
---
|
||
|
||
## 3. 模块化包结构
|
||
|
||
```
|
||
packages/
|
||
├── agent-runtime/ # AI 代理执行引擎
|
||
├── database/ # Drizzle ORM 模型
|
||
├── fetch-sse/ # SSE 流处理
|
||
├── model-runtime/ # LLM 提供商集成
|
||
├── types/ # 共享类型
|
||
├── utils/ # 通用工具
|
||
└── builtin-tool-*/ # 模块化工具包
|
||
```
|
||
|
||
**借鉴价值:** aitu 可以提取为:
|
||
- `@aitu/generation-service` - AI 图片/视频生成
|
||
- `@aitu/task-queue` - 异步任务管理
|
||
- `@aitu/media-cache` - IndexedDB 缓存
|
||
|
||
---
|
||
|
||
## 4. 数据库架构 - Drizzle ORM
|
||
|
||
**文件路径:** `packages/database/src/schemas/`
|
||
|
||
Schema-first 的设计,自动生成 Zod 验证:
|
||
|
||
```typescript
|
||
export const sessions = pgTable('sessions', {
|
||
id: text('id').$defaultFn(() => idGenerator('sessions')).primaryKey(),
|
||
slug: varchar('slug', { length: 100 }).notNull(),
|
||
title: text('title'),
|
||
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
||
}, (t) => [
|
||
uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
|
||
]);
|
||
|
||
export const insertSessionSchema = createInsertSchema(sessions);
|
||
```
|
||
|
||
**借鉴价值:** 如果 aitu 需要服务端持久化,Drizzle 是很好的选择。
|
||
|
||
---
|
||
|
||
## 5. 环境变量验证 - Zod + @t3-oss/env
|
||
|
||
**文件路径:** `src/envs/auth.ts`
|
||
|
||
类型安全的环境变量配置:
|
||
|
||
```typescript
|
||
export const getAuthConfig = () => createEnv({
|
||
client: {
|
||
NEXT_PUBLIC_ENABLE_CLERK_AUTH: z.boolean().optional().default(false),
|
||
},
|
||
server: {
|
||
AUTH_SECRET: z.string().optional(),
|
||
},
|
||
runtimeEnv: {
|
||
AUTH_SECRET: process.env.AUTH_SECRET || process.env.NEXT_AUTH_SECRET,
|
||
},
|
||
});
|
||
```
|
||
|
||
**借鉴价值:** aitu 的 API Key 配置可以采用类似模式,提供更好的类型安全和默认值处理。
|
||
|
||
---
|
||
|
||
## 6. SWR 数据获取模式
|
||
|
||
**文件路径:** `src/libs/swr/index.ts`
|
||
|
||
针对不同场景的 SWR 封装:
|
||
|
||
```typescript
|
||
// 灵活数据 - 支持焦点刷新
|
||
export const useClientDataSWR = (key, fetch, config) =>
|
||
useSWR(key, fetch, {
|
||
focusThrottleInterval: 5 * 60 * 1000,
|
||
revalidateOnFocus: true,
|
||
});
|
||
|
||
// 一次性获取 - 用于初始化
|
||
export const useOnlyFetchOnceSWR = (key, fetch, config) =>
|
||
useSWR(key, fetch, {
|
||
revalidateOnFocus: false,
|
||
revalidateOnReconnect: false,
|
||
});
|
||
```
|
||
|
||
**借鉴价值:** aitu 可以创建类似的 hooks 区分不同数据刷新策略。
|
||
|
||
---
|
||
|
||
## 7. 国际化 - 扁平化 key 结构
|
||
|
||
**文件路径:** `locales/en-US/common.json`
|
||
|
||
```json
|
||
{
|
||
"about": "About",
|
||
"alert.cloud.action": "Try now",
|
||
"alert.cloud.desc": "All users get {{credit}} free credits...",
|
||
"cmdk.about": "About"
|
||
}
|
||
```
|
||
|
||
配合懒加载:
|
||
|
||
```typescript
|
||
resourcesToBackend(async (lng, ns) => {
|
||
return loadI18nNamespaceModule({ lng, ns });
|
||
})
|
||
```
|
||
|
||
**借鉴价值:** 比嵌套结构更易维护,支持按需加载减少包体积。
|
||
|
||
---
|
||
|
||
## 8. 测试配置
|
||
|
||
**文件路径:** `tests/setup.ts`
|
||
|
||
```typescript
|
||
import 'fake-indexeddb/auto'; // 模拟 IndexedDB
|
||
import { vi } from 'vitest';
|
||
|
||
// 集中 mock 外部服务
|
||
vi.mock('@lobehub/analytics/react', () => ({
|
||
useAnalytics: () => ({ analytics: { track: vi.fn() } }),
|
||
}));
|
||
```
|
||
|
||
**借鉴价值:** aitu 使用 localforage,可以用 `fake-indexeddb` 进行测试。
|
||
|
||
---
|
||
|
||
## 9. tRPC API 层
|
||
|
||
**文件路径:** `src/libs/trpc/lambda/`
|
||
|
||
分层的 procedure 设计:
|
||
|
||
```typescript
|
||
const baseProcedure = trpc.procedure.use(openTelemetry);
|
||
export const publicProcedure = baseProcedure;
|
||
export const authedProcedure = baseProcedure.use(oidcAuth).use(userAuth);
|
||
```
|
||
|
||
**借鉴价值:** 如果 aitu 需要后端 API,tRPC 提供端到端类型安全。
|
||
|
||
---
|
||
|
||
## 10. 关键建议总结
|
||
|
||
| 领域 | 当前 aitu 状态 | 可借鉴改进 |
|
||
|------|---------------|-----------|
|
||
| 状态管理 | DrawnixContext + RxJS | Zustand slice 模式 |
|
||
| AI 流式 | 自定义实现 | 提取 fetchSSE 包 + 平滑动画 |
|
||
| 包结构 | Nx monorepo | 提取服务为独立包 |
|
||
| 环境配置 | 直接读取 | Zod 验证 + 类型安全 |
|
||
| 测试 | 基础配置 | fake-indexeddb + 集中 mock |
|
||
| i18n | 简单实现 | 扁平 key + 懒加载 |
|
||
|
||
---
|
||
|
||
## 11. 优先级建议
|
||
|
||
### 高优先级(立即可用)
|
||
|
||
1. **Zustand Slice 模式** - 重构状态管理,提高可维护性
|
||
2. **环境变量验证** - 使用 Zod 确保配置正确性
|
||
3. **SWR 封装** - 统一数据获取策略
|
||
|
||
### 中优先级(下一阶段)
|
||
|
||
4. **fetchSSE 包** - 提取流式响应处理
|
||
5. **fake-indexeddb 测试** - 改进测试覆盖率
|
||
6. **扁平化 i18n** - 简化国际化维护
|
||
|
||
### 低优先级(长期规划)
|
||
|
||
7. **模块化包结构** - 大规模重构
|
||
8. **Drizzle ORM** - 需要后端时考虑
|
||
9. **tRPC** - 需要后端 API 时考虑
|
||
|
||
---
|
||
|
||
## 参考链接
|
||
|
||
- LobeChat GitHub: https://github.com/lobehub/lobe-chat
|
||
- Zustand 文档: https://docs.pmnd.rs/zustand
|
||
- Drizzle ORM: https://orm.drizzle.team
|
||
- tRPC: https://trpc.io
|
||
- @t3-oss/env: https://env.t3.gg
|