Files
Edu/services/ai/docs/nextstep.md

191 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 模块 nextstep
> 模块aiai12 负责)| 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 FailoverChainOpenAI / 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 + runtimeDockerfile 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 + RedisP6 评估是否引入 Temporal |
| 2 | content 事件订阅 | P5 不订阅 content 事件P6+ 评估是否需要知识点变更事件驱动 |
| 3 | MockLLMProvider | 02 文档提到但未实现,测试环境用 httpx Mock 替代 |