19 KiB
ai 对接契约
负责人:ai12 关联:matrix.md、port-allocation.md、ai.proto、events.proto、02-architecture-design.md、objections/ai_issue.md 端口权威源:port-allocation.md §3/§5 —— ai = HTTP 3008 / gRPC 50058
本契约已对齐 02-architecture-design.md 设计文档,P5 实现已完成。9 个 ISSUE 全部已裁决(见 objections/ai_issue.md):端口 50058、topic
edu.ai.usage、8 RPC、events.proto 补 AIUsageEvent、备课工作流 P5 用 BackgroundTasks+Redis、iam P4 补全、proto 包名next_edu_cloud.<domain>.v1、ActionState 方案 B 降级。所有 ⏳ 标记已更新为 ✅。
§1 我提供什么(对外接口)
1.1 gRPC 接口
| Service | RPC | 请求 | 响应 | 端口 | 状态 |
|---|---|---|---|---|---|
| AiService | Chat | ChatRequest{messages, model, temperature, user_id?, session_id?, data_scope?} |
ChatResponse{content, model, usage, degraded, degraded_reason} |
50058 | ✅ 已实现 |
| AiService | StreamChat | ChatRequest |
stream ChatChunk |
50058 | ✅ 已实现 |
| AiService | GenerateQuestion | GenerateQuestionRequest{prompt, subject, difficulty, grade?, knowledge_point_ids?, question_type?, count?} |
GeneratedQuestion |
50058 | ✅ 已实现 |
| AiService | StreamGenerateQuestion | GenerateQuestionRequest |
stream GeneratedQuestionChunk |
50058 | ✅ 已实现 |
| AiService | OptimizeExpression | OptimizeExpressionRequest{text, context} |
OptimizedExpression |
50058 | ✅ 已实现 |
| AiService | GenerateLessonPlan | GenerateLessonPlanRequest{class_id, subject_id, topic, target_difficulty, question_count, user_id, data_scope} |
LessonPlanResponse{workflow_id, status, estimated_completion_seconds, degraded, degraded_reason} |
50058 | ✅ 已实现 |
| AiService | GetLessonPlanStatus | GetLessonPlanStatusRequest{workflow_id} |
LessonPlanStatus{workflow_id, status, questions, error?, degraded, degraded_reason} |
50058 | ✅ 已实现 |
| AiService | ConfirmLessonPlan | ConfirmLessonPlanRequest{workflow_id, modifications} |
ConfirmResult{success, persisted_question_ids, error?} |
50058 | ✅ 已实现 |
RPC 总数:8 RPC(coord 裁决 ISSUE-03,备课工作流查询/确认也走 gRPC)。 proto 现状:ai.proto 已升级到 8 RPC 完整版(含 degraded/degraded_reason 字段)。 proto package:
next_edu_cloud.ai.v1(coord G17 裁决保持现状,覆盖 project_rules §5)。
1.2 HTTP 端点
HTTP 保留作 api-gateway 直连降级 + SSE 流式。api-gateway 代理
/api/v1/ai/*→ ai/v1/ai/*。
| Method | Path | 权限 | 响应 | 说明 | 状态 |
|---|---|---|---|---|---|
| GET | /healthz |
— | {status, service} |
liveness | ✅ 已实现 |
| GET | /readyz |
— | {status, llm_configured, degraded, grpc_running, providers} |
readiness(多维度检查) | ✅ 已实现 |
| GET | /metrics |
— | Prometheus | 指标 | ✅ 已实现 |
| POST | /v1/ai/chat |
ai:chat |
ActionState<ChatData> |
LLM 聊天 | ✅ 已实现 |
| POST | /v1/ai/chat/stream |
ai:chat |
SSE stream | 流式聊天 | ✅ 已实现 |
| POST | /v1/ai/generate/question |
ai:question:generate |
ActionState<GeneratedQuestionData> |
生成题目 | ✅ 已实现 |
| POST | /v1/ai/generate/question/stream |
ai:question:generate |
SSE stream(题目逐字生成) | 题目逐字流式 | ✅ 已实现 |
| POST | /v1/ai/optimize/expression |
ai:expression:optimize |
ActionState<OptimizedExpressionData> |
优化表达 | ✅ 已实现 |
| POST | /v1/ai/lesson-plan/generate |
ai:lesson:generate |
ActionState<LessonPreparationData> |
备课工作流启动 | ✅ 已实现 |
| GET | /v1/ai/lesson-plan/status/{workflow_id} |
— | ActionState<WorkflowStatusData> |
查询工作流状态 | ✅ 已实现 |
| POST | /v1/ai/lesson-plan/confirm/{workflow_id} |
ai:lesson:confirm |
ActionState<ConfirmResultData> |
教师确认入库 | ✅ 已实现 |
响应信封:所有响应使用 ActionState(004 §11.5 强制)。降级采用方案 B(总裁 §2.6):success=true + error=null + data 内 degraded=true。 路径:业务路由统一
/v1/ai/*前缀。
1.3 GraphQL schema(如 BFF)
不适用。ai 是业务服务,不暴露 GraphQL;由 teacher-bff 聚合 ai gRPC 能力为 GraphQL。
1.4 Kafka 事件发布
| Topic | Event | 触发时机 | 消费方 | Payload | 状态 |
|---|---|---|---|---|---|
edu.ai.usage |
AIUsageEvent |
每次 LLM 调用完成 | data-ana | {event_id, aggregate_id, event_type, occurred_at, user_id, school_id, request_id, provider, model, operation, prompt_tokens, completion_tokens, total_tokens, latency_ms, success, degraded, metadata} |
✅ 已实现 |
topic 命名裁决:ISSUE-02 已裁决,采用
edu.ai.usage(matrix.md §4 确认,最简短)。 events.proto 已补全:ISSUE-04 已裁决,AIUsageEventmessage 已补入 events.proto,schema 见 02-architecture-design.md §3.3。 Outbox 豁免:AIUsageEvent 为派生数据事件,004 §12.2 + §15.3 #6 仲裁豁免 Outbox,允许直接 producer(aiokafka+ acks=all + idempotent + transactional_id)。 长期可发布事件(P6+ 评估,非 P5 范围):AIContentGenerated(topicedu.ai.generated,生成内容审计)、AIFeedbackRecorded(topicedu.ai.feedback,RLHF 数据)、AIWorkflowEvent(topicedu.ai.workflow,工作流监控)。
1.5 错误码前缀
AI_*(对齐 02-architecture-design.md §6.2 完整清单,matrix.md §6 已确认 ai 前缀为 AI_)
| 错误码 | 触发条件 | HTTP | gRPC status |
|---|---|---|---|
AI_UNAUTHORIZED |
缺失 x-user-id 或 token 无效 | 401 | UNAUTHENTICATED |
AI_FORBIDDEN |
角色无对应权限 | 403 | PERMISSION_DENIED |
AI_RATE_LIMITED |
触发限流(user/IP/school) | 429 | RESOURCE_EXHAUSTED |
AI_QUOTA_EXCEEDED |
学校/教师月度 token 配额耗尽 | 429 | RESOURCE_EXHAUSTED |
AI_LLM_UNAVAILABLE |
LLM Provider 不可达(降级骨架) | 200 | OK + degraded flag |
AI_LLM_TIMEOUT |
LLM 调用超时(30s) | 504 | DEADLINE_EXCEEDED |
AI_LLM_ALL_PROVIDERS_FAILED |
所有 Provider 故障切换链均失败 | 503 | UNAVAILABLE |
AI_INVALID_MODEL |
model 名不支持 | 400 | INVALID_ARGUMENT |
AI_INVALID_DIFFICULTY |
difficulty 不在 easy/medium/hard | 400 | INVALID_ARGUMENT |
AI_INVALID_QUESTION_TYPE |
question_type 不在枚举内 | 400 | INVALID_ARGUMENT |
AI_DOWNSTREAM_UNAVAILABLE |
content / data-ana gRPC 不可达 | 502 | UNAVAILABLE |
AI_PROMPT_RENDER_FAILED |
Prompt 模板渲染失败 | 500 | INTERNAL |
AI_PROMPT_TEMPLATE_NOT_FOUND |
Prompt 模板不存在 | 404 | NOT_FOUND |
AI_WORKFLOW_NOT_FOUND |
备课工作流 ID 不存在 | 404 | NOT_FOUND |
AI_WORKFLOW_EXPIRED |
工作流已过期(24h 未审核) | 410 | FAILED_PRECONDITION |
AI_WORKFLOW_STATE_INVALID |
工作流状态不允许此操作 | 409 | FAILED_PRECONDITION |
AI_EVALUATION_FAILED |
生成质量评估未通过且重试耗尽 | 503 | UNAVAILABLE |
AI_PII_DETECTED |
输入包含未脱敏 PII | 400 | INVALID_ARGUMENT |
AI_PROMPT_INJECTION_DETECTED |
输入疑似 Prompt 注入攻击 | 400 | INVALID_ARGUMENT |
AI_CONTENT_MODERATION_REJECTED |
输出内容审核不通过(敏感词) | 503 | UNAVAILABLE |
AI_INTERNAL_ERROR |
未捕获异常 | 500 | INTERNAL |
§2 我消费什么(依赖上游)
2.1 gRPC 调用(同步)
| 被调用方 | Service.RPC | 用途 | mock 策略 |
|---|---|---|---|
| content (ai09) | KnowledgeGraphService.GetPrerequisites |
出题上下文:查询知识点前置依赖 | content 就绪前使用本地知识点 stub(固定 3 个前置知识点) |
| content (ai09) | KnowledgeGraphService.GetLearningPath |
个性化出题:查询学生学习路径 | content 就绪前返回空路径 |
| content (ai09) | TextbookService.ListTextbooks |
备课工作流:查询教材章节关联知识点 | content 就绪前返回空列表 |
| content (ai09) | QuestionService.CreateQuestions(待 coord 补 proto) |
备课工作流:生成的题目入库 | content 就绪前跳过入库,工作流标记 PersistFailed |
| data-ana (ai11) | AnalyticsService.GetStudentWeakness |
靶向出题:查询学生薄弱知识点 | data-ana 就绪前使用本地薄弱点 stub(固定 2 个 weak_points) |
| data-ana (ai11) | AnalyticsService.GetLearningTrend |
难度调节:查询学习趋势 | data-ana 就绪前返回默认趋势 |
| data-ana (ai11) | AnalyticsService.GetClassPerformance |
备课工作流:班级整体学情 | data-ana 就绪前降级跳过学情查询(degraded:true) |
| iam (ai06) | IamService.GetEffectiveDataScope(待 P4 补全,ISSUE-07) |
多租户配额:查询用户 DataScope | iam RPC 未就绪时降级为"仅按 user_id 配额,不按 school_id" |
端口:content gRPC 50054 / data-ana 50055 / iam 50052(port-allocation.md §5)。注意 02-architecture-design.md §1.1/§1.2 mermaid 图误标为 3005/3006/3002(HTTP 端口),应以 50054/50055/50052 为准。
2.2 Kafka 事件订阅(异步)
ai 是无状态服务,P5 不消费任何 Kafka 事件(01/02 §5.1 明确)。原 contract 列出的 content 事件订阅已删除(与设计文档矛盾,见 ISSUE-05.5)。
P6+ 评估可消费事件(待 coord 仲裁,非 P5 范围):
| Topic | Event | 发布方 | 用途 | 评估阶段 |
|---|---|---|---|---|
edu.content.question.events |
QuestionEvent | content (ai09) | 题目查重:避免 AI 生成与已有重复 | P6+ |
edu.iam.user.events |
UserEvent | iam (ai06) | 角色变更:主动失效 DataScope 缓存 | P6+ |
2.3 HTTP 调用(外部)
| 被调用方 | Method.Path | 用途 | mock 策略 |
|---|---|---|---|
| LLM Provider(OpenAI/Anthropic/百川/Ollama) | POST /v1/chat/completions | 调用大模型生成内容 | 开发期使用本地 mock server 返回固定 ChatResponse,不消耗真实 token |
多 Provider 通过
ProviderFailoverChain故障切换(OpenAI → Anthropic → 百川 → 本地 Ollama),熔断器连续 3 次失败触发 60s 熔断。
§3 就绪信号
3.1 我依赖的上游就绪标志
- ai.proto 升级 v1 完整版(8 RPC + 字段扩展)—— coord(ISSUE-03 已裁决,已实现)
- events.proto 补
AIUsageEventmessage —— coord(ISSUE-04 已裁决,已补全) - ai 用量事件 topic 命名裁决 —— coord(ISSUE-02 已裁决,
edu.ai.usage) - content gRPC 50054 启用(ai09)—— 知识点维度 + 题库检索 + 入库(全并行模式用 Mock)
- data-ana gRPC 50055 启用(ai11,可选)—— 学生薄弱点(可降级独立运行,全并行模式用 Mock)
- iam
GetEffectiveDataScopeRPC P4 补全(ai06 + coord,ISSUE-07,可降级,全并行模式用 Mock) - LLM Provider API key 配置(人类决策者)
3.2 我的就绪标志(供下游消费)
- ai gRPC 50058 启用(HealthService.Check 返回 SERVING)
- AiService.Chat / StreamChat 可调用(含流式响应)
- AiService.GenerateQuestion / StreamGenerateQuestion 可调用
- AiService.GenerateLessonPlan 可调用(P5 补全)
- AiService.GetLessonPlanStatus / ConfirmLessonPlan 可调用
- AiService.OptimizeExpression 可调用
- ai 用量事件 topic 可发布(供 data-ana 统计 AI 用量)
端口:50058(port-allocation.md §3/§5/§7 权威源,ISSUE-01 已裁决,50058 让给 ai)。
§4 Mock 策略
4.1 我提供的 mock
在 ai 真实服务就绪前,为下游(teacher-bff)提供以下 mock:
- gRPC mock:使用 grpc-mock 拦截 50058 端口
- AiService.Chat 返回固定 ChatResponse(content="这是 AI 助手的模拟回复")
- AiService.StreamChat 返回固定流(3 个 ChatChunk,最后一个 done=true)
- AiService.GenerateQuestion 返回固定 GeneratedQuestion(question/answer/explanation)
- AiService.GenerateLessonPlan 返回固定 LessonPlanResponse(workflow_id + 3 个题目)
- AiService.StreamGenerateQuestion 返回固定流(2 个题目逐字 chunk)
- AiService.OptimizeExpression 返回固定 OptimizedExpression
- 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
- iam DataScope:内置固定 DataScope(SCHOOL 级),不依赖 iam GetEffectiveDataScope
- 事件订阅:P5 不订阅任何事件,无 mock 需要
§5 契约裁决项汇总
以下 9 个 ISSUE 全部已裁决(见 objections/ai_issue.md 与 president-final-rulings.md、coord-final-decisions.md),P5 实现已按裁决结论落地。
| 裁决项 | ISSUE | 裁决结论 | 裁决依据 | 影响章节 |
|---|---|---|---|---|
| ai gRPC 端口 | ISSUE-01 | 50058 | port-allocation.md §3/§5/§7 权威源 | §1.1 / §3.2 |
| ai 用量事件 topic 命名 | ISSUE-02 | edu.ai.usage |
matrix.md §4 | §1.4 |
| ai.proto P5 目标 RPC 数 | ISSUE-03 | 8 RPC(查询/确认走 gRPC) | 设计文档 §4.2 + coord B2 裁决 | §1.1 |
| events.proto 补 AIUsageEvent | ISSUE-04 | ai12 补全,schema 见设计文档 §3.3 | ai12 已补入 events.proto | §1.4 |
| contract 对齐设计文档 | ISSUE-05 | ai12 自纠完成 | ai_contract.md 已重写对齐 | 全文 |
| 备课工作流 Temporal | ISSUE-06 | P5 用 BackgroundTasks + Redis(24h TTL),P6 评估 Temporal | 总裁 §7.12 + coord A7 | §2.1(无影响) |
| iam GetEffectiveDataScope P4 补全 | ISSUE-07 | P4 补全,ai 用 Mock | 全并行模式 | §2.1 |
| proto package 命名(全局) | ISSUE-08 | 保持 next_edu_cloud.<domain>.v1 |
coord G17 裁决覆盖 project_rules §5 | §1.1 |
| 响应信封 ActionState 整改 | ISSUE-09 | ActionState + 方案 B 降级(success=true + error=null + degraded) | 总裁 §2.6 | §1.2 |