# AI 模块 nextstep > 模块:ai(ai12 负责)| gRPC 50058 | HTTP 3008 | Python (FastAPI) > 更新时间:2026-07-13 --- ## §1 当前状态 P5 实现已完成。8 RPC 全部实现,gRPC 拦截器已修复为异步兼容,下游 gRPC 客户端已从 Mock 切换为真实 gRPC 调用。377 个测试通过,覆盖率 88%。 ### 已完成 - [x] ai.proto 8 RPC 完整版(含 GetLessonPlanStatus / ConfirmLessonPlan) - [x] events.proto AIUsageEvent 补全 - [x] gRPC server 端口 50058,异步拦截器(Logging + Auth + Error) - [x] HTTP 10 端点(/v1/ai 前缀,ActionState 信封) - [x] LLM Provider FailoverChain(OpenAI / Anthropic / 百川 / Ollama + 熔断 + 故障切换) - [x] 评估三道防线(RuleValidator + LLMJudge + QualityGate) - [x] 用量记录(Redis)+ Kafka 事件发布 + 配额管理 - [x] 安全层(PII + 输入清洗 + 输出审核) - [x] 下游 gRPC 客户端真实调用(content / data-ana / iam,不再使用 Mock) - [x] 备课工作流(4 步编排 + Redis 状态存储) - [x] Dockerfile 多阶段构建 + docker-compose.deploy.yml 环境变量补全 --- ## §2 上游依赖(ai 依赖谁) ### §2.1 gRPC 同步调用 | 被调用方 | 端口 | Service.RPC | 用途 | 状态 | | --------------- | ----- | -------------------------------------- | --------------------------------------- | --------- | | content (ai09) | 50054 | KnowledgeGraphService.GetPrerequisites | 查询知识点前置依赖(备课工作流 Step 2) | ✅ 已实现 | | content (ai09) | 50054 | KnowledgeGraphService.GetLearningPath | 查询学习路径(备课工作流 Step 2) | ✅ 已实现 | | content (ai09) | 50054 | QuestionService.BatchCreateQuestions | 批量创建题目入库(备课工作流 Confirm) | ✅ 已实现 | | data-ana (ai11) | 50055 | AnalyticsService.GetClassPerformance | 查询班级学情(备课工作流 Step 1) | ✅ 已实现 | | data-ana (ai11) | 50055 | AnalyticsService.GetStudentWeakness | 查询学生薄弱点(备课工作流 Step 1) | ✅ 已实现 | | data-ana (ai11) | 50055 | AnalyticsService.GetLearningTrend | 查询学习趋势(备课工作流 Step 1) | ✅ 已实现 | | iam (ai06) | 50052 | IamService.GetEffectiveDataScope | 查询用户数据范围(多租户配额) | ✅ 已实现 | ### §2.2 基础设施依赖 | 依赖 | 用途 | 状态 | | ----------------------- | ----------------------------------------------- | --------- | | Redis | 限流(三维度令牌桶)+ 工作流状态存储 + 用量记录 | ✅ 已实现 | | Kafka | AIUsageEvent 事件发布(topic: `edu.ai.usage`) | ✅ 已实现 | | OpenTelemetry Collector | 链路追踪 + 指标导出 | ✅ 已实现 | ### §2.3 LLM Provider 依赖 | Provider | 环境变量 | 用途 | 状态 | | --------- | ------------------------------------ | ------------- | --------- | | OpenAI | `OPENAI_API_KEY` / `OPENAI_BASE_URL` | 首选 LLM | ✅ 已实现 | | Anthropic | `ANTHROPIC_API_KEY` | Failover 第二 | ✅ 已实现 | | 百川 | `BAICHUAN_API_KEY` | Failover 第三 | ✅ 已实现 | | Ollama | `OLLAMA_BASE_URL` | 本地降级 | ✅ 已实现 | > 未配置任何 API key 时进入降级模式,返回 `degraded=true` + 空内容。 --- ## §3 下游就绪信号(谁依赖 ai) ### §3.1 teacher-bff (ai03) — P1 | 就绪标志 | 消费方式 | 状态 | | -------------------------------------------------------- | --------------------- | --------- | | AiService.Chat / StreamChat 可调用 | gRPC 50058 + SSE 3008 | ✅ 已就绪 | | AiService.GenerateQuestion 可调用 | gRPC 50058 | ✅ 已就绪 | | AiService.GenerateLessonPlan 可调用 | gRPC 50058 | ✅ 已就绪 | | AiService.GetLessonPlanStatus / ConfirmLessonPlan 可调用 | gRPC 50058 | ✅ 已就绪 | | AiService.OptimizeExpression 可调用 | gRPC 50058 | ✅ 已就绪 | > teacher-bff 通过 `AI_GRPC_TARGET=ai:50058` 连接,留空时走降级模式 B。 ### §3.2 student-bff (ai04) — P1 | 就绪标志 | 消费方式 | 状态 | | --------------------------------------- | ---------- | --------- | | AiService.StreamChat 可调用(流式对话) | gRPC 50058 | ✅ 已就绪 | > student-bff 需要的 StreamAIChat 对应 ai 的 StreamChat RPC。 ### §3.3 api-gateway (ai01) — P2 | 就绪标志 | 消费方式 | 状态 | | -------------------------- | ---------------------------------------- | --------- | | ai HTTP 3008 /healthz 可达 | HTTP 反向代理 `/api/v1/ai/*` → `ai:3008` | ✅ 已就绪 | ### §3.4 data-ana (ai11) — P2 | 就绪标志 | 消费方式 | 状态 | | ------------------------------------- | -------------------------------------- | --------- | | Kafka topic `edu.ai.usage` 有事件发布 | CDC 消费者 → ClickHouse `ai_usage_log` | ✅ 已就绪 | ### §3.5 parent-bff / push-gateway — 无直接依赖 parent-bff 和 push-gateway 不直接依赖 ai 模块。 --- ## §4 联调待办 | # | 联调项 | 联调方 | 阻塞条件 | 状态 | | --- | ------------------------------------- | ------------------ | ----------------------------- | ------------------------------------ | | 1 | ai gRPC + teacher-bff SSE 联调 | teacher-bff (ai03) | ai 服务容器启动 | ✅ ai 侧就绪(待 teacher-bff 接入) | | 2 | ai gRPC StreamChat + student-bff 联调 | student-bff (ai04) | ai 服务容器启动 | ✅ ai 侧就绪(待 student-bff 接入) | | 3 | ai /healthz + api-gateway 联调 | api-gateway (ai01) | ai 服务容器启动 | ✅ ai 侧就绪(待 api-gateway 路由) | | 4 | AIUsageEvent + data-ana CDC 消费联调 | data-ana (ai11) | ai Kafka 生产 + data-ana 消费 | ✅ ai 生产就绪(待 data-ana 消费) | | 5 | ai ↔ content gRPC 联调 | content (ai09) | 双方容器启动 | ✅ ai 客户端就绪(待 content 启动) | | 6 | ai ↔ data-ana gRPC 联调 | data-ana (ai11) | 双方容器启动 | ✅ ai 客户端就绪(待 data-ana 启动) | | 7 | ai ↔ iam gRPC 联调 | iam (ai06) | 双方容器启动 | ✅ ai 客户端就绪(待 iam 启动) | > ai 侧 Docker 容器已启动并验证通过,下游 gRPC 客户端连接循环已在 lifespan 中执行成功。剩余联调项等待对端模块接入。 --- ## §5 Docker 测试环境 ### §5.1 构建与启动 ```bash # 构建镜像 docker compose -f infra/docker-compose.deploy.yml build ai # 启动 ai 服务(依赖 Redis + Kafka + 基础设施) docker compose -f infra/docker-compose.yml up -d redis kafka docker compose -f infra/docker-compose.deploy.yml up -d ai ``` ### §5.2 健康检查 ```bash curl http://localhost:3008/healthz # {"status":"ok","service":"ai"} curl http://localhost:3008/readyz # {"status":"ok","llm_configured":...,"grpc_running":true} ``` ### §5.3 环境变量(docker-compose.deploy.yml 已配置) | 变量 | 默认值 | 说明 | | ------------------------- | -------------------- | -------------------- | | `HTTP_PORT` | 3008 | HTTP 端口 | | `GRPC_PORT` | 50058 | gRPC 端口 | | `REDIS_URL` | redis://redis:6379/0 | Redis 连接 | | `KAFKA_BOOTSTRAP_SERVERS` | kafka:29092 | Kafka 连接(容器内) | | `CONTENT_GRPC_ENDPOINT` | content:50054 | content gRPC 端点 | | `DATA_ANA_GRPC_ENDPOINT` | data-ana:50055 | data-ana gRPC 端点 | | `IAM_GRPC_ENDPOINT` | iam:50052 | iam gRPC 端点 | | `OPENAI_API_KEY` | (空) | OpenAI API Key | | `DEV_MODE` | true | 开发模式(本地测试) | ### §5.4 本地 Docker 测试结果(2026-07-13) **测试环境**:`infra/docker-compose.test.yml` + `edu-full_default` 外部网络 | # | 验证项 | 结果 | 证据 | | --- | -------------------- | ------- | ------------------------------------------------------------------------------------- | | 1 | Docker 镜像构建 | ✅ 通过 | 多阶段构建成功(builder + runtime),Dockerfile curl 版本 pinning 已移除 | | 2 | 容器启动 | ✅ 通过 | ai 服务容器成功启动并加入 `edu-full_default` 网络 | | 3 | 下游 gRPC 客户端连接 | ✅ 通过 | 日志:`grpc_client_connected endpoint=content:50054` / `data-ana:50055` / `iam:50052` | | 4 | Redis 连接 | ✅ 通过 | 日志:限流器 + 工作流状态存储初始化成功 | | 5 | Kafka producer 启动 | ✅ 通过 | 日志:`kafka_producer_started bootstrap_servers=kafka:29092 topic=edu.ai.usage` | | 6 | gRPC server 启动 | ✅ 通过 | 日志:`grpc_server_started port=50058`,端口 50058 监听成功 | | 7 | gRPC 拦截器加载 | ✅ 通过 | 异步拦截器(Logging + Auth + Error)正确加载,无 ValueError 降级警告 | | 8 | HTTP /healthz | ✅ 通过 | `curl http://localhost:3008/healthz` → `{"status":"ok","service":"ai"}` | | 9 | HTTP /readyz | ✅ 通过 | `curl http://localhost:3008/readyz` → `{"grpc_running":true,...}` | | 10 | LLM 降级模式 | ✅ 预期 | 未配置 API key,`llm_configured:false, degraded:true`(预期行为) | | 11 | 单元测试 | ✅ 通过 | 377 个测试通过,覆盖率 88% | | 12 | ruff lint | ✅ 通过 | 零警告 | **关键修复记录**: | 问题 | 根因 | 修复 | | ----------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | gRPC 拦截器 ValueError | 同步 `grpc.ServerInterceptor` 与 `grpc.aio.server()` 不兼容,服务器静默降级为无拦截器 | 重写所有 3 个拦截器继承 `grpc.aio.ServerInterceptor`,`intercept_service` 改为 `async` | | Dockerfile 构建失败 | `curl=7.88.*` 版本 pinning 在 Debian Trixie 中不存在 | 移除版本 pinning,改为 `curl` | | 端口 50058 占用 | 本地 Python 进程占用 | `Stop-Process -Id -Force` | | proto_gen 导入失败 | 生成的 `*_pb2_grpc.py` 使用绝对导入 | `proto_gen/__init__.py` 添加 `sys.path.insert(0, _PB_DIR)` | | 类型注解 AttributeError | `grpc.aio.HandlerCallDetails` 不存在 | 类型注解改用 `grpc.HandlerCallDetails` 和 `grpc.RpcMethodHandler`(同步版本,用于注解) | --- ## §6 P6+ 待评估 | # | 待评估项 | 说明 | | --- | ----------------- | ------------------------------------------------------------------ | | 1 | Temporal 引入评估 | 备课工作流 P5 用 BackgroundTasks + Redis,P6 评估是否引入 Temporal | | 2 | content 事件订阅 | P5 不订阅 content 事件,P6+ 评估是否需要知识点变更事件驱动 | | 3 | MockLLMProvider | 02 文档提到但未实现,测试环境用 httpx Mock 替代 |