# ai 对接契约

> 负责人：ai12
> 关联：[matrix.md](./matrix.md)、[ai.proto](../../../packages/shared-proto/proto/ai.proto)、[events.proto](../../../packages/shared-proto/proto/events.proto)

---

## §1 我提供什么（对外接口）

### 1.1 gRPC 接口（如有）

| Service   | RPC                    | 请求                          | 响应                     | 端口  |
| --------- | ---------------------- | ----------------------------- | ------------------------ | ----- |
| AiService | Chat                   | ChatRequest                   | ChatResponse             | 50057 |
| AiService | StreamChat             | ChatRequest                   | stream ChatChunk         | 50057 |
| AiService | GenerateQuestion       | GenerateQuestionRequest       | GeneratedQuestion        | 50057 |
| AiService | OptimizeExpression     | OptimizeExpressionRequest     | OptimizedExpression      | 50057 |
| AiService | GenerateLessonPlan     | GenerateLessonPlanRequest     | LessonPlan               | 50057 |
| AiService | StreamGenerateQuestion | StreamGenerateQuestionRequest | stream GeneratedQuestion | 50057 |

### 1.2 HTTP 端点（如有）

无对外 HTTP 端点，仅 gRPC（含 2 个 Server Streaming RPC：StreamChat / StreamGenerateQuestion）。

### 1.3 GraphQL schema（如 BFF）

不适用。

### 1.4 Kafka 事件发布（如有）

| Topic               | Event                                                                                    | 消费方   |
| ------------------- | ---------------------------------------------------------------------------------------- | -------- |
| edu.ai.usage.events | AIUsageEvent（operation: chat/generate_question/optimize_expression/lesson_preparation） | data-ana |

> 注：AIUsageEvent 豁免 Outbox 模式（派生数据事件，见 004 §12.2 + §15.3 #6）。

### 1.5 错误码前缀

`AI_`（如 AI_PROVIDER_UNAVAILABLE、AI_TOKEN_LIMIT_EXCEEDED、AI_CONTENT_FILTERED）

---

## §2 我消费什么（依赖上游）

### 2.1 gRPC 调用（同步）

| 被调用方        | Service.RPC                            | 用途                         | mock 策略                                                   |
| --------------- | -------------------------------------- | ---------------------------- | ----------------------------------------------------------- |
| content (ai09)  | KnowledgeGraphService.GetPrerequisites | 生成题目时获取知识点前置依赖 | content 就绪前使用本地知识点 stub（固定 3 个前置知识点）    |
| content (ai09)  | QuestionService.SearchQuestions        | 备课时检索同类题目参考       | content 就绪前返回空列表                                    |
| data-ana (ai11) | AnalyticsService.GetStudentWeakness    | 个性化出题时获取学生薄弱点   | data-ana 就绪前使用本地薄弱点 stub（固定 2 个 weak_points） |

### 2.2 Kafka 事件订阅（异步）

| Topic                              | Event               | 发布方         | mock 策略                              |
| ---------------------------------- | ------------------- | -------------- | -------------------------------------- |
| edu.content.knowledge_point.events | KnowledgePointEvent | content (ai09) | content 就绪前不订阅，使用内置知识点表 |
| edu.content.question.events        | QuestionEvent       | content (ai09) | content 就绪前忽略                     |

### 2.3 HTTP 调用（如有）

| 被调用方                         | Method.Path               | 用途               | mock 策略                                                          |
| -------------------------------- | ------------------------- | ------------------ | ------------------------------------------------------------------ |
| LLM Provider（OpenAI/百川/本地） | POST /v1/chat/completions | 调用大模型生成内容 | 开发期使用本地 mock server 返回固定 ChatResponse，不消耗真实 token |

---

## §3 就绪信号

### 3.1 我依赖的上游就绪标志

- [ ] content gRPC 50054 启用（ai09）—— 知识点维度 + 题库检索
- [ ] edu.content.knowledge_point.events topic 有事件发布（ai09）
- [ ] data-ana gRPC 50055 启用（ai11）—— 学生薄弱点（可选，ai 可先独立运行）

### 3.2 我的就绪标志（供下游消费）

- [ ] ai gRPC 50057 启用（HealthService.Check 返回 SERVING）
- [ ] AiService.Chat / StreamChat 可调用（含流式响应）
- [ ] AiService.GenerateQuestion / StreamGenerateQuestion 可调用
- [ ] AiService.GenerateLessonPlan 可调用（P5 补全）
- [ ] AiService.OptimizeExpression 可调用
- [ ] edu.ai.usage.events topic 可发布（供 data-ana 统计 AI 用量）

---

## §4 Mock 策略

### 4.1 我提供的 mock

在 ai 真实服务就绪前，为下游（teacher-bff）提供以下 mock：

- **gRPC mock**：使用 grpc-mock 拦截 50057 端口
  - AiService.Chat 返回固定 ChatResponse（content="这是 AI 助手的模拟回复"）
  - AiService.StreamChat 返回固定流（3 个 ChatChunk，最后一个 done=true）
  - AiService.GenerateQuestion 返回固定 GeneratedQuestion（question/answer/explanation）
  - AiService.GenerateLessonPlan 返回固定 LessonPlan（3 个 LessonSection）
  - AiService.StreamGenerateQuestion 返回固定流（2 个 GeneratedQuestion）
- **Kafka mock**：ai 就绪前不发布真实 AIUsageEvent，data-ana 仪表盘 AI 用量显示"暂无数据"

### 4.2 我消费的 mock

在真实上游就绪前，ai 使用以下 mock：

- **LLM Provider mock**：本地启动 mock server，POST /v1/chat/completions 返回固定 JSON（不消耗真实 token，不产生费用）
- **content 知识点**：内置固定知识点表（数学 20 个知识点 + 前置依赖关系），不依赖 content gRPC
- **data-ana 薄弱点**：内置固定学生薄弱点（2 个 weak_points），不依赖 data-ana gRPC
- **事件订阅**：不订阅 content 事件，知识点维度表静态
