docs: 开发文档面向AI精简重构,补充文档编撰规范
This commit is contained in:
@@ -1,92 +1,50 @@
|
||||
# 后端开发
|
||||
|
||||
本文档说明 alfred 后端的模块实现细节、API 索引和开发方式。开发规范(库优先级、类型规范、配置契约、API 路由规范、测试约定等)见 [开发规范文档](README.md#开发规范)。
|
||||
|
||||
适用场景:修改 `src/server/`、了解现有模块 API、查找可供复用的工具函数和数据访问接口。
|
||||
开发规范见 [开发规范文档](README.md)。
|
||||
|
||||
## 共享工具
|
||||
|
||||
`src/server/helpers/` 提供跨路由共用的工具模块:
|
||||
`src/server/helpers/`:
|
||||
|
||||
- `response.ts` — 响应格式化工具:
|
||||
- `createApiError(error, status)` — 构造 API 错误体
|
||||
- `createHeaders(mode, init)` — 创建响应 Headers(生产模式附加安全 header)
|
||||
- `createMetaResponse(version)` — 构造应用元信息响应
|
||||
- `formatDuration(ms)` — 格式化毫秒时长为可读字符串
|
||||
- `jsonResponse(body, options)` — JSON 响应构造
|
||||
- `url.ts` — URL 工具:
|
||||
- `parseIdFromUrl(url)` — 从 URL 解析路径中的 ID
|
||||
- `response.ts`:`createApiError(error, status)`、`createHeaders(mode, init)`、`createMetaResponse(version)`、`formatDuration(ms)`、`jsonResponse(body, options)`
|
||||
- `url.ts`:`parseIdFromUrl(url)`
|
||||
|
||||
`src/server/middleware/` 提供 API 参数校验和错误处理中间件:
|
||||
`src/server/middleware/`:
|
||||
|
||||
- `validate.ts` — 参数校验函数:
|
||||
- `validateIdParam(idStr, mode)` — 校验 ID 参数格式(字母数字 + `_` `-`)
|
||||
- `validatePagination(pageParam, pageSizeParam, mode)` — 校验分页参数(page 最小 1,pageSize 最大 200)
|
||||
- `validateTimeRange(from, to, mode)` — 校验时间范围参数
|
||||
- `error-handler.ts` — 错误处理中间件:
|
||||
- `AppError` — 业务异常类(含 statusCode)
|
||||
- `withErrorHandler(fn, mode, logger?)` — 包裹路由 handler,捕获 AppError 和未知异常
|
||||
- `validate.ts`:`validateIdParam(idStr, mode)` — 校验 ID 格式(字母数字 + `_-`);`validatePagination(pageParam, pageSizeParam, mode)` — page≥1, pageSize≤200;`validateTimeRange(from, to, mode)`
|
||||
- `error-handler.ts`:`AppError` — 业务异常类(含 statusCode);`withErrorHandler(fn, mode, logger?)` — 包裹 handler 捕获异常
|
||||
|
||||
## 数据库
|
||||
|
||||
项目使用 SQLite 作为存储后端,通过 bun:sqlite + Drizzle ORM 实现类型安全的数据访问。
|
||||
SQLite + bun:sqlite + Drizzle ORM。
|
||||
|
||||
### schema 定义
|
||||
- `src/server/db/schema.ts`:Drizzle 表结构,列名 snake_case,TS 类型 camelCase。
|
||||
- `src/server/db/connection.ts`:`createDatabase(dataDir, logger)` 打开 `alfred.db`,PRAGMA:foreign_keys=ON、journal_mode=WAL、busy_timeout=5000。`wrap(db)` 转为 Drizzle 实例。`paginateQuery()` 分页工具。
|
||||
- Migration:开发期 `drizzle-kit generate` 产出到 `drizzle/`;生产期嵌入可执行文件,启动时自动应用。备份到 `<dataDir>/backups/`,事务中执行,失败回滚。
|
||||
|
||||
`src/server/db/schema.ts` 使用 Drizzle ORM 定义表结构,列名使用 snake_case,TypeScript 类型使用 camelCase,Drizzle schema 负责映射。
|
||||
### 数据访问函数
|
||||
|
||||
### 数据库连接
|
||||
| 文件 | 函数 |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `projects.ts` | createProject、getProject、listProjects、updateProject、deleteProject、archiveProject、restoreProject |
|
||||
| `providers.ts` | createProvider、getProvider、listProviders、listProviderOptions、updateProvider、deleteProvider |
|
||||
| `models.ts` | createModel、getModel、listModels、getModelsByProviderId、updateModel、deleteModel |
|
||||
| `conversations.ts` | createConversation、getConversation、listConversations、updateConversation、updateConversationTimestamp、deleteConversation、createMessage、createMessages、listMessages |
|
||||
|
||||
`src/server/db/connection.ts` 的 `createDatabase(dataDir, logger)` 打开 `<dataDir>/alfred.db`,设置 PRAGMA(foreign_keys=ON、journal_mode=WAL、busy_timeout=5000)。
|
||||
|
||||
### migration 机制
|
||||
|
||||
- 开发期:使用 `drizzle-kit generate` 从 TS schema 生成 SQL migration 文件到 `drizzle/` 目录
|
||||
- 生产期:构建时将 `drizzle/*.sql` 嵌入可执行文件,启动时自动应用 pending migrations
|
||||
- 每次 migration 前自动备份现有 DB 到 `<dataDir>/backups/alfred-<timestamp>.db`
|
||||
- migration 在事务中执行,失败则回滚并停止启动
|
||||
|
||||
### 数据访问
|
||||
|
||||
`src/server/db/projects.ts` 提供项目数据访问函数(`createProject`、`getProject`、`listProjects`、`updateProject`、`deleteProject`、`archiveProject`、`restoreProject`)。`src/server/db/providers.ts` 提供供应商数据访问函数(`createProvider`、`getProvider`、`listProviders`、`listProviderOptions`、`updateProvider`、`deleteProvider`)。`src/server/db/models.ts` 提供模型数据访问函数(`createModel`、`getModel`、`listModels`、`getModelsByProviderId`、`updateModel`、`deleteModel`)。`src/server/db/conversations.ts` 提供会话和消息数据访问函数(`createConversation`、`getConversation`、`listConversations`、`updateConversation`、`updateConversationTimestamp`、`deleteConversation`、`createMessage`、`createMessages`、`listMessages`)。`src/server/db/connection.ts` 还提供 `paginateQuery` 和 `wrap` 工具函数。输入输出使用 `src/shared/api.ts` 的类型。函数内部使用 Drizzle query builder 包装 `bun:sqlite` Database。
|
||||
输入输出类型来自 `src/shared/api.ts`。
|
||||
|
||||
## AI 服务层
|
||||
|
||||
`src/server/ai/` 提供 AI Provider Registry 构建与连接测试能力。
|
||||
- `src/server/ai/types.ts`:`AIProviderConfig`(name、type、baseUrl、apiKey)、`AIModelConfig`(providerId、modelId、capabilities)。
|
||||
- `src/server/ai/registry.ts`:
|
||||
- `buildProviderRegistry(db)` — 从 DB 查询供应商构建 AI SDK Provider Registry,每次调用重建,不缓存。通过 `registry.languageModel('providerId:modelId')` 获取模型实例。
|
||||
- `testProviderConnection(config)` — 测试 Base URL 可达性 + `/models` 接口
|
||||
- `testModelConnection(config)` — 测试模型连通性
|
||||
- `countModels(db)` — 统计已配置模型数
|
||||
- `src/server/ai/agents/alfred-agent.ts`:`createAlfredAgent(model)` — ToolLoopAgent + `stepCountIs(20)` + `getCurrentTime` 工具。
|
||||
- `src/server/ai/tools/`:AI 工具定义。
|
||||
|
||||
### 类型定义
|
||||
|
||||
`src/server/ai/types.ts` 定义 AI 配置类型:
|
||||
|
||||
- `AIProviderConfig` — 供应商配置(name、type、baseUrl、apiKey)
|
||||
- `AIModelConfig` — 模型配置(providerId、modelId、capabilities)
|
||||
- `AIRegistryConfig` — Registry 构建配置(providers、models),供后续 AI 调用层组合使用
|
||||
|
||||
### Registry 构建
|
||||
|
||||
`src/server/ai/registry.ts` 提供:
|
||||
|
||||
- `buildProviderRegistry(db)` — 从 DB 查询所有供应商,构建 Vercel AI SDK Provider Registry
|
||||
- `testProviderConnection(config)` — 先测试 Base URL 可达性,再请求 `/models` 验证 API Key 和模型列表接口
|
||||
- `testModelConnection(config)` — 先通过 providerId 查询供应商(含 apiKey),再以 modelId 请求对应模型的 API 连通性
|
||||
- `countModels(db)` — 统计 DB 中已配置的模型总数,用于 Agent 调用前的可用性检查
|
||||
|
||||
每次 AI 调用时从 DB 查询 providers,构建 registry 后通过 `registry.languageModel('providerId:modelId')` 获取模型实例。不使用缓存层。模型是否存在以及业务能力标签由调用方基于 models 表先行校验,registry 只负责将 providerId/modelId 映射到 AI SDK 模型实例。
|
||||
|
||||
### Agent 流式调用
|
||||
|
||||
`src/server/ai/agents/alfred-agent.ts` 提供 `createAlfredAgent(model)` 工厂函数,返回 AI SDK `ToolLoopAgent` 实例。默认使用 `stepCountIs(20)` 限制最大迭代次数,配置 `getCurrentTime` 工具供 AI 调用。路由层通过 `createAgentUIStreamResponse()` 转为 SSE 响应,流结束通过 `onFinish` 回调可靠持久化 AI 回复(含完整 `parts`)。
|
||||
|
||||
### 供应商连通性测试
|
||||
|
||||
供应商连通性测试返回 `{ providerTestResponse: { ok, message } }`,前端根据 `ok` 展示成功或失败提示。
|
||||
|
||||
- `POST /api/providers/test` — 使用表单中尚未保存的供应商配置测试连接
|
||||
- `POST /api/models/test` — 使用模型关联供应商配置和 modelId 测试模型连接
|
||||
|
||||
测试连接不会写入数据库,也不会阻止保存。Base URL 不可达或 API Key 无效返回 `ok: false`;Base URL 可达但 `/models` 不支持、非标准或返回非鉴权错误时返回 `ok: true` 并在 `message` 中提示用户可检查 URL 或忽略提醒。
|
||||
|
||||
### 支持的供应商类型
|
||||
### 供应商类型
|
||||
|
||||
| type | AI SDK factory |
|
||||
| ------------------- | --------------------------------------------------- |
|
||||
@@ -94,50 +52,37 @@
|
||||
| `anthropic` | `createAnthropic({ apiKey, baseURL })` |
|
||||
| `openai-compatible` | `createOpenAICompatible({ name, apiKey, baseURL })` |
|
||||
|
||||
## 聊天 API
|
||||
### 连通性测试
|
||||
|
||||
聊天 API 按项目维度组织会话和消息:
|
||||
- `POST /api/providers/test` — 用未保存配置测试,不写入 DB,不阻止保存。Base URL 不可达或 API Key 无效返回 `ok: false`;`/models` 不支持返回 `ok: true` + 提示。
|
||||
- `POST /api/models/test` — 用模型关联供应商 + modelId 测试。
|
||||
|
||||
## 聊天 API
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ------ | ----------------------------------------------- | ------------------ |
|
||||
| GET | `/api/projects/:id/conversations` | 列出项目下所有会话 |
|
||||
| POST | `/api/projects/:id/conversations` | 创建新会话 |
|
||||
| GET | `/api/projects/:id/conversations/:cid` | 获取会话详情 |
|
||||
| PATCH | `/api/projects/:id/conversations/:cid` | 更新会话标题或模型 |
|
||||
| DELETE | `/api/projects/:id/conversations/:cid` | 删除会话及其消息 |
|
||||
| GET | `/api/projects/:id/conversations/:cid/messages` | 获取会话消息列表 |
|
||||
| POST | `/api/projects/:id/chat` | 发送消息并流式回复 |
|
||||
| PATCH | `/api/projects/:id/conversations/:cid` | 更新会话 |
|
||||
| DELETE | `/api/projects/:id/conversations/:cid` | 删除会话及消息 |
|
||||
| GET | `/api/projects/:id/conversations/:cid/messages` | 获取消息列表 |
|
||||
| POST | `/api/projects/:id/chat` | 发送消息,SSE 回复 |
|
||||
|
||||
聊天路由处理器位于 `src/server/routes/chat/`,遵循统一的 handler 模式。`send.ts` 处理发送消息:验证会话归属后只保存最后一条用户消息到 DB,通过 `createAlfredAgent` 创建 agent,调用 `createAgentUIStreamResponse` 返回 SSE UI 消息流,流结束通过 `onFinish` 回调保存 AI 回复到 DB。
|
||||
`send.ts`:验证会话归属 → 保存用户消息 → `createAlfredAgent` → `createAgentUIStreamResponse` → `onFinish` 持久化 AI 回复。
|
||||
|
||||
## 日志模块
|
||||
## 日志
|
||||
|
||||
后端运行时代码统一通过 Logger 接口输出日志。
|
||||
|
||||
| 实现 | 用途 |
|
||||
| --------------------- | ------------------------ |
|
||||
| PinoLoggerWrapper | 生产运行时 |
|
||||
| ConsoleFallbackLogger | 配置加载失败前的降级日志 |
|
||||
| NoopLogger | 静默丢弃日志 |
|
||||
| MemoryLogger | 测试替身 |
|
||||
| 实现 | 用途 |
|
||||
| --------------------- | -------------- |
|
||||
| PinoLoggerWrapper | 生产运行时 |
|
||||
| ConsoleFallbackLogger | 配置加载前降级 |
|
||||
| NoopLogger | 静默丢弃 |
|
||||
| MemoryLogger | 测试替身 |
|
||||
|
||||
## 版本管理
|
||||
|
||||
版本号以 `package.json.version` 为唯一来源。
|
||||
|
||||
版本获取方式:
|
||||
|
||||
- 开发模式:`src/server/version.ts` 运行时从 package.json 读取
|
||||
- 生产模式:`scripts/build.ts` 在构建时将版本号烘焙为字面量注入
|
||||
|
||||
版本升迁命令:
|
||||
|
||||
```bash
|
||||
bun run version:patch # 升迁 patch 版本
|
||||
bun run version:minor # 升迁 minor 版本
|
||||
bun run version:major # 升迁 major 版本
|
||||
bun run version:set # 显式设置版本号
|
||||
```
|
||||
唯一来源:`package.json`。开发模式从 package.json 运行时读取;生产模式构建时烘焙为字面量。
|
||||
|
||||
## 更新触发条件
|
||||
|
||||
|
||||
Reference in New Issue
Block a user