192 lines
7.0 KiB
Markdown
192 lines
7.0 KiB
Markdown
|
|
# AI Runtime 接入文档
|
|||
|
|
|
|||
|
|
面向 Runtime 开发者和 iOS/Admin 前端开发者的 API 参考。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 错误码枚举
|
|||
|
|
|
|||
|
|
### 1.1 Internal API 错误码(Runtime ↔ API)
|
|||
|
|
|
|||
|
|
| 错误码 | HTTP | 说明 | retryable |
|
|||
|
|
|--------|------|------|:---------:|
|
|||
|
|
| `JOB_NOT_FOUND` | 404 | Job 不存在 | false |
|
|||
|
|
| `JOB_NOT_ACTIVE` | 409 | Job 状态不是 locked/running | false |
|
|||
|
|
| `JOB_ALREADY_LOCKED` | 409 | 已被其他 Runtime 锁定 | true |
|
|||
|
|
| `SNAPSHOT_NOT_FOUND` | 404 | 快照不存在(已废弃:自动重建) | false |
|
|||
|
|
| `CREDENTIAL_NOT_FOUND` | 404 | 凭证不存在或已删除 | false |
|
|||
|
|
| `CREDENTIAL_REQUIRED` | 400 | user_deepseek_key 模式缺少 credentialId | false |
|
|||
|
|
| `RESULT_ALREADY_EXISTS` | 409 | 重复提交(幂等 key 冲突) | false |
|
|||
|
|
| `RESULT_SCHEMA_UNSUPPORTED` | 422 | schema 版本不支持 | false |
|
|||
|
|
| `INTERNAL_ERROR` | 500 | 服务器内部错误 | true |
|
|||
|
|
|
|||
|
|
### 1.2 User API 错误码(iOS / Admin → API)
|
|||
|
|
|
|||
|
|
| 错误码 | HTTP | 说明 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| `AI_ANALYSIS_DISABLED` | 400 | 用户未开启 AI 分析 |
|
|||
|
|
| `INVALID_JOB_TYPE` | 400 | jobType 不在支持列表中 |
|
|||
|
|
| `INVALID_TARGET_TYPE` | 400 | quiz/flashcard 必须 targetType=knowledge_base |
|
|||
|
|
| `CREDENTIAL_REQUIRED` | 400 | user_deepseek_key 模式需要绑定凭证 |
|
|||
|
|
| `CREDENTIAL_NOT_FOUND` | 404 | 凭证不存在或无权访问 |
|
|||
|
|
| `DAILY_JOB_LIMIT_EXCEEDED` | 400 | 超过每日 AI Job 数量上限 |
|
|||
|
|
| `DAILY_TOKEN_BUDGET_EXCEEDED` | 400 | 超过每日 Token 预算上限 |
|
|||
|
|
| `PLATFORM_CIRCUIT_OPEN` | 503 | 平台熔断器开启,拒绝新请求 |
|
|||
|
|
| `PLATFORM_CIRCUIT_HALF_OPEN` | 503 | 熔断器半开,仅允许少量请求 |
|
|||
|
|
| `PLATFORM_TOKEN_BUDGET_EXCEEDED` | 400 | 平台日 Token 预算耗尽 |
|
|||
|
|
| `PLATFORM_COST_BUDGET_EXCEEDED` | 400 | 平台日成本预算耗尽 |
|
|||
|
|
|
|||
|
|
### 1.3 Runtime 上报的错误码
|
|||
|
|
|
|||
|
|
Runtime 通过 `POST /internal/runtime/jobs/{jobId}/fail` 上报的错误码:
|
|||
|
|
|
|||
|
|
| 错误码 | 说明 | retryable |
|
|||
|
|
|--------|------|:---------:|
|
|||
|
|
| `JOB_CANCELLED` | 用户取消 Job | false |
|
|||
|
|
| `JOB_TIMEOUT` | Job 执行超时(默认 120s) | false |
|
|||
|
|
| `MODEL_TIMEOUT` | DeepSeek API 超时/503/502/500 | true |
|
|||
|
|
| `MODEL_RATE_LIMIT` | DeepSeek 429 限流 | true |
|
|||
|
|
| `MODEL_NETWORK_ERROR` | 网络连接失败 | true |
|
|||
|
|
| `SNAPSHOT_FETCH_FAILED` | 快照拉取失败 | true |
|
|||
|
|
| `INVALID_SNAPSHOT` | 快照版本不兼容 | false |
|
|||
|
|
| `CREDENTIAL_RESOLVE_FAILED` | 凭证解析失败 | false |
|
|||
|
|
| `UNSUPPORTED_JOB_TYPE` | jobType 不支持 | false |
|
|||
|
|
| `MODEL_RESPONSE_INVALID` | 模型返回无效 JSON | false |
|
|||
|
|
| `MODEL_JSON_PARSE_FAILED` | 模型输出 JSON 解析失败 | false |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 限流说明
|
|||
|
|
|
|||
|
|
### 2.1 用户级限流
|
|||
|
|
|
|||
|
|
通过 `UserAiSettings` 控制,默认值:
|
|||
|
|
|
|||
|
|
| 参数 | 默认值 | 说明 |
|
|||
|
|
|------|--------|------|
|
|||
|
|
| `maxDailyAiJobs` | 20 | 每日 AI Job 创建上限 |
|
|||
|
|
| `maxDailyTokenBudget` | 100,000 | 每日 Token 消耗上限 |
|
|||
|
|
|
|||
|
|
超出限制返回 `DAILY_JOB_LIMIT_EXCEEDED` 或 `DAILY_TOKEN_BUDGET_EXCEEDED`。
|
|||
|
|
|
|||
|
|
限流粒度:`(userId, localDate, apiKeyMode)`,platform_key 和 user_deepseek_key 独立计数。
|
|||
|
|
|
|||
|
|
### 2.2 平台级熔断
|
|||
|
|
|
|||
|
|
平台密钥(platform_key)模式启用的保护机制:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
├── 日 Token 预算:10,000,000 tokens
|
|||
|
|
├── 日成本预算:$500 (50000 cents)
|
|||
|
|
├── 熔断阈值:连续 10 次失败 → open
|
|||
|
|
├── 半开限制:最多 2 个活跃 platform_key Job
|
|||
|
|
└── 恢复:单次成功 → closed(重置失败计数)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
熔断状态流转:
|
|||
|
|
```
|
|||
|
|
closed ──[10次连续失败]──> open ──[手动半开]──> half_open ──[成功]──> closed
|
|||
|
|
│ │
|
|||
|
|
└───[超限]───<───────────────[超限]───┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.3 API 全局限流
|
|||
|
|
|
|||
|
|
通过 NestJS ThrottlerModule 控制,默认限制:
|
|||
|
|
|
|||
|
|
| 端点 | 限制 | 窗口 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `/ai/*` 用户端点 | 60 次/分钟 | 60s |
|
|||
|
|
| `/internal/runtime/*` | 300 次/分钟 | 60s |
|
|||
|
|
|
|||
|
|
超限返回 HTTP 429。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 变更日志
|
|||
|
|
|
|||
|
|
### v1.1.0(2026-06)
|
|||
|
|
|
|||
|
|
**新增**:
|
|||
|
|
- Heartbeat 响应新增 `cancelRequested` 字段(HTTP 200 替代 204)
|
|||
|
|
- `getSnapshot` 自动重建过期/缺失快照(不再返回 SNAPSHOT_EXPIRED)
|
|||
|
|
- `submitFailure` 支持 `JOB_CANCELLED` 错误码
|
|||
|
|
- 用户 API:`POST /ai/jobs` 创建分析 Job(11 步流程含幂等、配额、熔断)
|
|||
|
|
- 用户 API:`GET/POST/PUT/DELETE /ai/model-credentials` 凭证管理
|
|||
|
|
- 用户 API:`GET /ai/analyses`, `/ai/recommendations`, `/ai/weak-points` 分析结果查询
|
|||
|
|
- 用户 API:`GET /ai/quizzes`, `/ai/flashcards` 已发布内容查询
|
|||
|
|
- 平台预算熔断:`PlatformBudgetService` 日 Token/成本限制 + 熔断器
|
|||
|
|
- 结果落库:`persistResult` 支持 5 种 jobType(learning_state_analysis / weak_point_analysis / next_action_planning / quiz_generation / flashcard_generation)
|
|||
|
|
- 候选转换:`convertQuizCandidates` / `convertFlashcardCandidates` 去重 + 正式记录创建
|
|||
|
|
- Job 完成通知:`notifyJobComplete` 推送通知给用户
|
|||
|
|
|
|||
|
|
**修复**:
|
|||
|
|
- `getProfile` 的 `?? {}` 修复(之前 Promise 永远 truthy)
|
|||
|
|
- `HeartbeatResponse.cancel_requested: bool` 客户端适配
|
|||
|
|
- N+1 去重查询 → 批量预取(Quiz/Flashcard convertCandidates)
|
|||
|
|
- `persistResult` / `notifyJobComplete` fire-and-forget 增加日志 + 计数器
|
|||
|
|
- `job-reaper` take:500 上限 → 游标批处理循环
|
|||
|
|
- `checkAndReserve` → `checkQuota` 重命名(澄清只读语义)
|
|||
|
|
- `testCredential` 使用 ConfigService 获取 DeepSeek baseUrl
|
|||
|
|
- Quota 测试拆分 `mockSettingsFindUnique` / `mockUsageFindUnique`
|
|||
|
|
|
|||
|
|
### v1.0.0(2026-05)
|
|||
|
|
|
|||
|
|
**初始版本**:
|
|||
|
|
- Internal API:Poll / Lock / Heartbeat / GetSnapshot / ResolveCredential / SubmitResult / SubmitFailure / SubmitInvocationLogs
|
|||
|
|
- User API:Profile / Settings / Learning Profile CRUD
|
|||
|
|
- 鉴权:InternalAuthGuard(x-internal-api-key)+ JWT(用户端点)
|
|||
|
|
- 加密:CredentialEncryptionService(AES-256-GCM)
|
|||
|
|
- 配额:UserAiQuotaService(checkQuota + incrementJobCount + recordTokenUsage)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 接入示例
|
|||
|
|
|
|||
|
|
### 4.1 Runtime 心跳 + 取消检测(v1.1)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /internal/runtime/jobs/{jobId}/heartbeat
|
|||
|
|
Authorization: Bearer <RUNTIME_SERVICE_TOKEN>
|
|||
|
|
x-runtime-instance-id: runtime-001
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
响应 200:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"jobId": "job-abc123",
|
|||
|
|
"lockUntil": 1700000000123,
|
|||
|
|
"cancelRequested": false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`cancelRequested=true` 时,Runtime 应在下一个 I/O 检查点中止执行并提交 `JOB_CANCELLED` fail。
|
|||
|
|
|
|||
|
|
### 4.2 前端创建分析 Job
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /ai/jobs
|
|||
|
|
Authorization: Bearer <USER_JWT>
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"jobType": "quiz_generation",
|
|||
|
|
"targetType": "knowledge_base",
|
|||
|
|
"targetId": "kb-001",
|
|||
|
|
"apiKeyMode": "platform_key",
|
|||
|
|
"idempotencyKey": "client-uuid-xxx",
|
|||
|
|
"questionCount": 10
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
幂等:相同 `idempotencyKey` 重复请求返回已有 Job ID(不重复创建)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 相关资源
|
|||
|
|
|
|||
|
|
- [Internal API 通信协议](./ai-runtime-internal-api-protocol.md)
|
|||
|
|
- [AI Runtime 架构](./ai-runtime-architecture.md)
|
|||
|
|
- [Job 状态机](./ai-job-state-machine.md)
|
|||
|
|
- [数据权限与隐私](./ai-data-permission-privacy.md)
|
|||
|
|
- [用户 AI API 文档](./ai-runtime-user-api.md)
|