191 lines
13 KiB
Markdown
191 lines
13 KiB
Markdown
# 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 <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 替代 |
|