api-server/docs/ai-runtime-api-reference.md

192 lines
7.0 KiB
Markdown
Raw Normal View History

# 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.02026-06
**新增**
- Heartbeat 响应新增 `cancelRequested` 字段HTTP 200 替代 204
- `getSnapshot` 自动重建过期/缺失快照(不再返回 SNAPSHOT_EXPIRED
- `submitFailure` 支持 `JOB_CANCELLED` 错误码
- 用户 API`POST /ai/jobs` 创建分析 Job11 步流程含幂等、配额、熔断)
- 用户 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 种 jobTypelearning_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.02026-05
**初始版本**
- Internal APIPoll / Lock / Heartbeat / GetSnapshot / ResolveCredential / SubmitResult / SubmitFailure / SubmitInvocationLogs
- User APIProfile / Settings / Learning Profile CRUD
- 鉴权InternalAuthGuardx-internal-api-key+ JWT用户端点
- 加密CredentialEncryptionServiceAES-256-GCM
- 配额UserAiQuotaServicecheckQuota + 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)