AI 模块 nextstep
模块:ai(ai12 负责)| gRPC 50058 | HTTP 3008 | Python (FastAPI)
更新时间:2026-07-13
§1 当前状态
P5 实现已完成。8 RPC 全部实现,gRPC 拦截器已修复为异步兼容,下游 gRPC 客户端已从 Mock 切换为真实 gRPC 调用。377 个测试通过,覆盖率 88%。
已完成
§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 构建与启动
§5.2 健康检查
§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 <PID> -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 替代 |