模块架构设计文档 — ai
AI 标识:ai12(复核与长期演进接手)← ai06(初稿,2026-07-09)
负责模块:ai(P5)
阶段:架构设计外包 · 阶段 2(模块架构设计,ai06 初稿 + ai12 长期演进复核)
日期:2026-07-09(ai06 初稿)/ 2026-07-09(ai12 复核与补充)
关联文档:阶段 1 理解确认书、ai-allocation.md、004 架构影响地图、pending-features.md、coord 交叉审查报告
审查请求:本设计文档待 coord 按 ai-allocation.md §8 交叉审查(接口一致性 / 端口冲突 / Topic 重复 / 错误码重叠 / 黄金模板对齐)
ai12 接手说明:本设计文档初稿由 ai06 在 ai-allocation v0.x 阶段撰写(涵盖 8 节标准模板)。ai12 在 ai06 初稿基础上执行:作者归属更正、长期架构演进补充(新增 §9-§17 共 9 节)、P5 交付物覆盖度核对、向前兼容性铺垫。所有 ai12 新增内容用「ai12 增补」标注,ai06 原文保留并标注「ai06 初稿」。
设计原则与全局约束
本设计遵循以下强制约束(来自 project_rules.md + coding-standards.md + 004):
- 契约先行:proto 已定义(analytics.proto / ai.proto),实现前不修改 proto,如需修改走 coord 流程
- CQRS 读写分离:ai 是无状态服务(无 DB 写),用量计费通过事件外发由 data-ana 落 ClickHouse
- 事件驱动:ai 不消费事件;仅发布
AIUsageRecorded 派生数据事件(004 §12.2 豁免 Outbox,允许直接 producer)
- gRPC 优先:004 §4.1 + §4.2 明确 P5 ai 启用 gRPC server(含 StreamChat 流式 RPC),HTTP 保留作 Gateway 直连降级
- 三支柱可观测:structlog + prometheus-client + OpenTelemetry(已具备,需补业务指标 + gRPC interceptor)
- 降级模式:外部依赖(LLM / 下游 gRPC / Redis)不可用时返回骨架数据 + ActionState
details: {degraded: true}
- Python 规范:pydantic-settings 配置 / Pydantic 模型校验 / async 优先 / 类型注解强制 / ruff 零错误
- 响应信封(ai12 增补):004 §11.5 强制 ActionState,禁止
{success, data, degraded} 偏离结构
- 端口规范(ai12 增补):HTTP 3008 + gRPC 50058(HTTP+10050 规则),提请 coord 在 004 §1.2 +
infra/port-allocation.md 补登
- 错误码前缀(ai12 增补):
AI_*(004 §11.4 错误码前缀矩阵已确认 ai 前缀为 AI_)
1. 模块内部分层图
1.1 ai06 初稿:分层图
1.2 ai12 增补:完整分层图(含安全 / 评估 / 缓存 / 工作流持久化)
ai12 增补:分层规则
- 入口层:HTTP(保留作 Gateway 直连降级 + 前端 SSE 流式)+ gRPC(主入口,BFF 调用,含
StreamChat / StreamGenerateQuestion 流式 RPC)
- 中间件层:RequestContextMiddleware(X-Request-Id)→ AuthDepends(JWT claims 解析)→ RateLimitDepends(多维度限流)→ OTel 自动埋点
- 安全层(ai12 增补):PIIRedactor → InputSanitizer → OutputModerator,所有进出 LLM 的数据必须经过安全层
- 应用服务层:6 个 Service,每个对应一类 AI 能力
- Provider 适配层:抽象
LLMProvider 接口 + 多适配器实现(策略模式)+ ProviderFailoverChain 故障切换(resilience 模式)
- Prompt 模板:模板注册 + Jinja2 渲染 + 版本化(P5 YAML,P6+ DB + A/B)
- 评估层(ai12 增补):规则校验 + LLM-as-judge + 质量门禁,构成生成质量保障三道防线
- 工作流持久化(ai12 增补):Redis 短期(P5)+ Temporal(P6+ 评估),保证长运行工作流状态不丢失
- 缓存层(ai12 增补):Prompt hash 缓存(P5)+ 语义缓存(P6+ 评估)+ 模板缓存
- 下游 client:gRPC 调 content / data-ana / iam(DataScope),均带 client interceptor(trace + retry + circuit breaker)
- 用量计费:token 消耗统计 + Kafka 事件外发 + 配额校验
2. 领域模型
ai 是无状态服务,不持有持久化聚合根。领域模型为请求/响应模型 + 工作流编排:
2.1 聚合根(请求型,无持久化)(ai06 初稿)
| 聚合根 |
含义 |
生命周期 |
ChatConversation |
单次聊天请求 |
单次请求 |
QuestionGenerationTask |
出题任务 |
单次请求(备课工作流中多步) |
ExpressionOptimizationTask |
表达优化任务 |
单次请求 |
LessonPreparationWorkflow |
备课工作流 |
跨多步(分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库) |
2.2 ai12 增补:扩展聚合根与值对象
| 聚合根 |
含义 |
生命周期 |
持久化 |
PromptTemplate |
Prompt 模板 |
持久化(YAML/DB) |
YAML 文件 P5 / DB P6+ |
UsageRecord |
用量记录 |
单次 LLM 调用 |
不持久化,Kafka 事件外发 |
EvaluationResult |
生成结果质量评估 |
单次生成调用 |
不持久化(P6+ 评估落 ClickHouse 供分析) |
WorkflowState |
工作流状态 |
跨多步(数小时/天) |
Redis 短期 P5 / Temporal P6+ |
2.3 值对象
ChatMessage:role + content
Usage:prompt_tokens + completion_tokens + total_tokens + latency_ms
GeneratedQuestion:question + answer + explanation + question_type + difficulty + knowledge_point_ids
PromptTemplate:name + system_prompt + user_template + variables + version
- ai12 增补:
UserContext:user_id + roles + data_scope + school_id + request_id
- ai12 增补:
ProviderConfig:provider_name + model + api_key + base_url + timeout + max_retries
- ai12 增补:
EvaluationReport:rule_score + judge_score + overall_score + reasons + pass
- ai12 增补:
WorkflowStep:step_name + status + started_at + completed_at + result + error
- ai12 增补:
QuotaContext:school_id + grade_id + teacher_id + budget + used + remaining
2.4 ai12 增补:备课工作流状态机
备课工作流是 ai 服务最复杂的工作流,明确状态机避免实现混乱:
ai12 增补:状态持久化策略
| 状态 |
P5 持久化 |
P6+ 持久化 |
| Pending-Analyzing-Generated |
Redis(key: ai:workflow:{id},TTL 24h) |
Temporal workflow history |
| PendingReview |
Redis + 通知 BFF(前端轮询/SSE) |
Temporal signal(教师审核事件) |
| 终态(Persisted/Rejected/Expired) |
Redis 标记 + 事件日志 |
Temporal workflow 完成 + ClickHouse |
ai12 增补:工作流事件日志(供审计 + RLHF 数据收集)
2.5 ai12 增补:备课工作流序列图(细化 004 §9.3)
3. 数据模型
3.1 ai06 初稿:无 DB 设计
ai 服务无独占数据库,无 MySQL schema。所有数据通过 Kafka 事件外发(用量计费)或 gRPC 查询下游。
3.2 ai12 增补:Redis schema 详细设计
虽然 ai 无 DB,但 Redis 承载多种数据结构,需明确 schema:
| Key 模式 |
数据类型 |
TTL |
用途 |
ai:ratelimit:user:{user_id} |
Hash |
滑动窗口 |
用户级令牌桶(每分钟 10 次) |
ai:ratelimit:ip:{ip} |
Hash |
滑动窗口 |
IP 级令牌桶(每分钟 30 次) |
ai:ratelimit:school:{school_id} |
Hash |
滑动窗口 |
学校级令牌桶(每分钟 100 次) |
ai:quota:school:{school_id} |
Hash |
月度重置 |
学校月度 token 预算 + 已用 |
ai:quota:teacher:{user_id} |
Hash |
月度重置 |
教师月度 token 预算 + 已用 |
ai:cache:prompt:{hash} |
String |
30 分钟 |
LLM 响应缓存(hash 缓存) |
ai:cache:template:{template_name} |
String |
1 小时 |
Prompt 模板缓存 |
ai:cache:datascope:{user_id} |
String |
5 分钟 |
DataScope 缓存(避免重复查 iam) |
ai:workflow:{workflow_id} |
Hash |
24 小时 |
备课工作流状态 |
ai:workflow:user:{user_id} |
Set |
24 小时 |
用户的活跃工作流 ID 集合 |
ai:dedup:event:{event_id} |
String |
7 天 |
事件去重(SETNX) |
ai:circuit:provider:{provider_name} |
String |
60 秒 |
Provider 熔断状态 |
3.3 ai12 增补:用量计费 Kafka 事件 payload(建议 events.proto 新增 AIUsageEvent)
3.4 ai12 增补:缓存策略
| 数据 |
存储 |
TTL |
失效策略 |
| Prompt 模板 |
Redis(template cache) |
1 小时 |
文件/DB 变更时主动失效(pub/sub 通知) |
| LLM 响应(相同 prompt hash) |
Redis(prompt cache) |
30 分钟 |
短 TTL,避免陈旧;用户反馈差时主动失效 |
| DataScope(避免重复查 iam) |
Redis(datascope cache) |
5 分钟 |
短 TTL,权限变更时由 iam 主动失效(pub/sub) |
| 限流计数 |
Redis(ratelimit hash) |
滑动窗口 |
自动过期 |
| 配额计数 |
Redis(quota hash) |
月度重置 |
月初定时任务重置 |
| 工作流状态 |
Redis(workflow hash) |
24 小时 |
工作流终态后保留 1h 再过期(供审计) |
| P6+ 语义缓存 |
Redis + 向量索引 |
30 分钟 |
embedding 相似度 > 0.95 命中 |
4. API 设计
4.1 HTTP 端点(保留作 Gateway 直连降级,ai06 初稿 + ai12 增补)
ai12 增补:所有路径加 /v1 版本前缀(P5 起强制),便于未来破坏性变更;响应统一为 ActionState
| method |
path |
权限 |
请求体 |
响应 |
说明 |
| GET |
/healthz |
— |
— |
{status, service} |
liveness |
| GET |
/readyz |
— |
— |
{status, llm_configured, ...} |
readiness |
| GET |
/metrics |
— |
— |
Prometheus |
指标 |
| POST |
/ai/v1/chat |
AI_CHAT |
ChatRequest |
ActionState<ChatData> |
LLM 聊天 |
| POST |
/ai/v1/chat/stream |
AI_CHAT |
ChatRequest |
SSE stream |
流式聊天 |
| POST |
/ai/v1/generate/question |
AI_QUESTION_GENERATE |
GenerateQuestionRequest |
ActionState<GeneratedQuestionData> |
生成题目 |
| POST |
/ai/v1/generate/question/stream |
AI_QUESTION_GENERATE |
GenerateQuestionRequest |
SSE stream(题目逐字生成) |
ai12 增补:题目逐字流式 |
| POST |
/ai/v1/optimize/expression |
AI_EXPRESSION_OPTIMIZE |
OptimizeExpressionRequest |
ActionState<OptimizedExpressionData> |
优化表达 |
| POST |
/ai/v1/lesson/preparation |
AI_LESSON_PREPARE |
LessonPreparationRequest |
ActionState<LessonPreparationData> |
ai12 增补:备课工作流启动 |
| GET |
/ai/v1/lesson/preparation/{workflow_id} |
AI_LESSON_PREPARE |
— |
ActionState<WorkflowState> |
ai12 增补:查询工作流状态 |
| POST |
/ai/v1/lesson/preparation/{workflow_id}/confirm |
AI_LESSON_PREPARE |
ConfirmRequest |
ActionState<PersistResult> |
ai12 增补:教师确认入库 |
| GET |
/ai/v1/prompts |
AI_PROMPT_READ |
query: ?page&size |
ActionState<Page<TemplateSummary>> |
ai12 增补:模板列表 |
| POST |
/ai/v1/prompts |
AI_PROMPT_CREATE |
CreatePromptRequest |
ActionState<PromptTemplate> |
ai12 增补:创建模板 |
| GET |
/ai/v1/prompts/{id} |
AI_PROMPT_READ |
— |
ActionState<PromptTemplate> |
ai12 增补:获取模板 |
| PUT |
/ai/v1/prompts/{id} |
AI_PROMPT_UPDATE |
UpdatePromptRequest |
ActionState<PromptTemplate> |
ai12 增补:更新模板(版本化) |
| GET |
/ai/v1/usage/me |
AI_USAGE_READ |
query: ?start_date&end_date |
ActionState<UsageSummary> |
ai12 增补:当前用户用量 |
| GET |
/ai/v1/usage/school/{school_id} |
AI_USAGE_READ_ALL |
query: ?start_date&end_date |
ActionState<UsageSummary> |
ai12 增补:学校用量(管理员) |
4.2 gRPC 契约(ai.proto,待实现 server,ai06 初稿 + ai12 增补)
| RPC |
请求 |
响应 |
权限 |
说明 |
Chat |
ChatRequest{messages, model, temperature, user_id?, session_id?, data_scope?} |
ChatResponse{content, model, usage} |
AI_CHAT |
非流式聊天 |
StreamChat |
ChatRequest |
stream ChatChunk |
AI_CHAT |
流式聊天(SSE over gRPC) |
GenerateQuestion |
GenerateQuestionRequest{prompt, subject, difficulty, grade?, knowledge_point_ids?, question_type?, count?} |
GeneratedQuestion |
AI_QUESTION_GENERATE |
生成题目 |
StreamGenerateQuestion |
GenerateQuestionRequest |
stream GeneratedQuestionChunk |
AI_QUESTION_GENERATE |
ai12 增补:题目逐字流式生成 |
OptimizeExpression |
OptimizeExpressionRequest{text, context} |
OptimizedExpression |
AI_EXPRESSION_OPTIMIZE |
优化表达 |
GenerateLessonPlan |
GenerateLessonPlanRequest{class_id, subject_id, topic, user_id, data_scope} |
LessonPlanResponse{workflow_id, status, questions?} |
AI_LESSON_PREPARE |
ai12 增补:备课工作流启动 |
GetLessonPlanStatus |
GetLessonPlanStatusRequest{workflow_id} |
LessonPlanStatus{workflow_id, status, questions?, error?} |
AI_LESSON_PREPARE |
ai12 增补:查询工作流状态 |
ConfirmLessonPlan |
ConfirmLessonPlanRequest{workflow_id, modifications?} |
ConfirmResult{success, persisted_question_ids?} |
AI_LESSON_PREPARE |
ai12 增补:教师确认入库 |
4.3 ai12 增补:Pydantic 请求/响应模型(ActionState 对齐)
4.4 ai12 增补:Prompt 模板 YAML 格式
5. 事件设计
5.1 消费的事件
ai 服务不消费任何事件(无状态,纯请求-响应)。
ai12 增补:长期可考虑消费的事件(P6+ 评估)
edu.content.question.published:题库新增题目事件,可用于"题目查重"——避免 AI 生成与已有题目重复
edu.identity.user.role_changed:用户角色变更,主动失效 DataScope 缓存(替代当前定时 5min TTL 失效)
5.2 发布的事件
| 事件 |
Topic |
触发时机 |
消费者 |
Payload |
AIUsageRecorded |
edu.insight.ai.usage(004 §7.2 已确认) |
每次 LLM 调用完成 |
data-ana(落 ClickHouse ai_usage_log) |
{event_id, user_id, school_id, request_id, provider, model, operation, prompt_tokens, completion_tokens, total_tokens, latency_ms, success, degraded, metadata} |
ai12 增补:长期可发布事件(P6+ 评估,待 coord 仲裁)
| 事件 |
Topic |
触发时机 |
消费者 |
用途 |
AIContentGenerated |
edu.insight.ai.generated |
生成内容审计 |
data-ana |
落 ClickHouse 供质量分析 |
AIFeedbackRecorded |
edu.insight.ai.feedback |
教师对生成结果反馈 |
data-ana |
RLHF 数据集构建 |
AIWorkflowEvent |
edu.insight.ai.workflow |
工作流步骤状态变更 |
data-ana + msg |
工作流监控 + 失败告警 |
发布实现:
- 每次 LLM 调用后异步发布(不阻塞响应)
aiokafka.AIOKafkaProducer + acks=all + idempotent=true + transactional_id
- 失败重试 3 次(指数退避:1s/2s/4s),仍失败记录日志(不影响主流程,用量计费为派生数据可丢失)
- 幂等性:
event_id UUID + Redis SETNX 去重(TTL 7 天),防止 producer 重试导致重复投递
6. 横切关注点对齐清单
6.1 权限装饰器等价物(ai06 初稿 + ai12 增补)
6.2 错误码清单(前缀 AI_*,ai06 初稿 + ai12 增补)
| 错误码 |
触发条件 |
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 + degraded:true |
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 |
工作流状态不允许此操作(如已 Persisted 再次 Confirm) |
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 |
6.3 Logger 初始化
- 位置:
main.py(已具备)
- 配置:
structlog.make_filtering_bound_logger(level) + TimeStamper(fmt="iso") + ConsoleRenderer
- 改进:生产环境改用
structlog.processors.JSONRenderer()(当前 ConsoleRenderer 适合开发)
- ai12 增补:日志必须包含
request_id(从 RequestContextMiddleware 注入)、user_id、workflow_id(如适用),便于全链路追踪
6.4 Metrics 指标清单(ai06 初稿 + ai12 增补)
| 指标名 |
类型 |
标签 |
描述 |
ai_http_requests_total |
Counter |
method, path, status |
HTTP 请求总数 |
ai_http_request_duration_seconds |
Histogram |
method, path |
HTTP 请求延迟 |
ai_grpc_requests_total |
Counter |
method, status |
gRPC 请求总数(ai12 增补) |
ai_grpc_request_duration_seconds |
Histogram |
method |
gRPC 请求延迟(ai12 增补) |
ai_llm_calls_total |
Counter |
provider, model, operation, status |
LLM 调用总数 |
ai_llm_call_duration_seconds |
Histogram |
provider, model, operation |
LLM 调用延迟 |
ai_llm_tokens_total |
Counter |
provider, model, type (prompt/completion) |
token 消耗总数 |
ai_llm_stream_chunks_total |
Counter |
provider, model |
流式 chunk 总数 |
ai_llm_first_token_seconds |
Histogram |
provider, model |
流式首字延迟(ai12 增补,SLO) |
ai_provider_failover_total |
Counter |
from_provider, to_provider, reason |
Provider 故障切换次数(ai12 增补) |
ai_grpc_downstream_calls_total |
Counter |
downstream, method, status |
下游 gRPC 调用总数 |
ai_rate_limit_hits_total |
Counter |
dimension (user/ip/school) |
限流命中次数 |
ai_quota_used_ratio |
Gauge |
school_id, dimension (school/teacher) |
配额使用率(ai12 增补) |
ai_usage_events_published_total |
Counter |
status |
用量事件发布数 |
ai_prompt_template_renders_total |
Counter |
template_name, status |
模板渲染次数 |
ai_prompt_cache_hits_total |
Counter |
— |
Prompt 缓存命中(ai12 增补) |
ai_workflow_active_count |
Gauge |
status |
活跃工作流数(ai12 增补) |
ai_workflow_duration_seconds |
Histogram |
status |
工作流总耗时(ai12 增补) |
ai_evaluation_pass_rate |
Gauge |
operation |
评估通过率(ai12 增补) |
ai_circuit_breaker_state |
Gauge |
provider, state (closed/open/half_open) |
熔断器状态(ai12 增补) |
6.5 Tracer 初始化
- 位置:
main.py init_tracer()(已具备,dev_mode 跳过)
- 改进:
- gRPC server 注册
grpc.aio.ServerInterceptor 透传 W3C trace context(从 metadata 提取 traceparent)
- 下游 gRPC client 注册
grpc.aio.ClientInterceptor 注入 traceparent 到 metadata
- LLM 调用创建子 span(
ai_llm_call),记录 provider/model/tokens/latency
- 备课工作流每步创建子 span(
ai_workflow_step),记录 step_name/status/duration
6.6 /healthz 检查逻辑
6.7 /readyz 检查逻辑(ai06 初稿 + ai12 增补)
6.8 优雅关闭顺序(ai06 初稿 + ai12 增补)
- HTTP server stop accepting new requests(uvicorn graceful shutdown)
- ai12 增补:拒绝新工作流启动(返回
AI_SERVICE_SHUTTING_DOWN 503)
- gRPC server graceful stop(关键:等待在途
StreamChat / StreamGenerateQuestion 流式 RPC 完成,60s 超时,避免截断用户响应)
- LLM 流式请求 drain(等待 httpx stream 完成)
- ai12 增补:在途工作流状态保存(Redis 已持久化,无需额外操作;Temporal 模式下需 complete workflow)
- Kafka producer flush + close(确保用量事件已投递)
- 下游 gRPC channels close(content / data-ana / iam)
- Redis connection close
信号处理:注册 signal.SIGTERM handler,触发上述顺序,总超时 90s(gRPC 60s + 其他 30s)。
7. 与其他模块的交互点(契约清单)
| 方向 |
对方服务 |
协议 |
接口/事件 |
用途 |
| 被调用 |
api-gateway |
HTTP |
/ai/* |
Gateway 代理 |
| 被调用 |
teacher-bff |
gRPC |
AiService.* |
BFF 聚合 AI 能力 |
| 调用 |
content |
gRPC |
KnowledgeGraphService.GetPrerequisites / GetLearningPath |
出题上下文查询 |
| 调用 |
content |
gRPC |
TextbookService.ListTextbooks |
出题教材关联(ai12 增补) |
| 调用 |
content |
gRPC |
QuestionService.CreateQuestions(待 coord 补全 proto) |
备课工作流入库(ai12 增补) |
| 调用 |
data-ana |
gRPC |
AnalyticsService.GetStudentWeakness / GetLearningTrend |
个性化出题学情查询 |
| 调用 |
data-ana |
gRPC |
AnalyticsService.GetClassPerformance |
备课工作流班级学情(ai12 增补) |
| 调用 |
iam |
gRPC |
IamService.GetEffectiveDataScope(待 coord P4 补全) |
DataScope 解析(ai12 增补) |
| 调用 |
LLM Provider |
HTTP |
OpenAI 兼容 REST /chat/completions |
LLM 推理 |
| 发布 |
— |
Kafka |
edu.insight.ai.usage(004 §7.2 已确认) |
用量计费外发 |
| 发布 |
— |
Kafka |
edu.insight.ai.generated(P6+ 待 coord 仲裁) |
生成内容审计(ai12 增补) |
| 发布 |
— |
Kafka |
edu.insight.ai.feedback(P6+ 待 coord 仲裁) |
RLHF 反馈数据(ai12 增补) |
8. 风险与假设
8.1 假设
- 假设 1:content 服务实现了
KnowledgeGraphService gRPC server。当前 content.proto 已定义但未实现 gRPC server(ai05 阶段 2 设计中)。ai 调用前需确认 content gRPC 可用
- 假设 2:data-ana 实现了
AnalyticsService gRPC server(ai11 设计文档已设计)。ai 调用前需确认 data-ana gRPC 可用
- 假设 3:coord 同意新增
edu.insight.ai.usage topic + AIUsageEvent proto message。004 §7.2 已确认 topic,events.proto message 待 coord 补全
- 假设 4:Redis 可用(限流 + 缓存 + 工作流状态依赖)。若 Redis 不可用:
- 限流降级为"无限制"(风险:LLM 成本失控),或降级为内存令牌桶(单实例有效,多实例不一致)
- 缓存失效,所有 LLM 调用直达 Provider(成本增加)
- 工作流状态丢失,在途工作流失效(影响教师审核流程)
- 假设 5(ai12 增补):iam 提供
GetEffectiveDataScope gRPC API。coord 已仲裁 P4 补全(§15.3 #5)
- 假设 6(ai12 增补):LLM Provider API 稳定性符合 SLA(OpenAI 99.5%+)。多 Provider 故障切换可覆盖单 Provider 故障
- 假设 7(ai12 增补):教师审核备课结果在 24h 内完成。超过 24h 工作流过期,需重新发起
8.2 技术风险
| 风险 |
影响 |
缓解措施 |
| LLM 调用延迟高(>30s) |
用户体验差 |
超时 30s + 降级骨架响应 + 流式优先(用户感知首字延迟) |
| LLM 成本失控 |
财务风险 |
Redis 令牌桶限流(user/IP/school 三维度)+ 月度配额 + 用量计费监控 + 告警阈值 |
| LLM Provider 单点故障 |
服务不可用 |
多 Provider 适配器 + ProviderFailoverChain 自动 failover(OpenAI 失败切 Anthropic,再切本地 Ollama) |
| 流式 RPC 中断 |
用户响应截断 |
gRPC server graceful shutdown 60s drain + 客户端重连机制 + 工作流状态持久化支持重试 |
| Prompt 注入攻击 |
LLM 输出恶意内容 |
InputSanitizer 过滤 + system prompt 加安全约束 + OutputModerator 过滤 |
| 下游 gRPC 不可达 |
备课工作流失败 |
降级:跳过学情查询,仅基于 prompt 生成题目 + degraded: true |
| PII 数据泄露给 LLM |
合规风险 |
PIIRedactor 在写入 prompt 前脱敏(学生姓名→"学生A",手机号→"***") |
| 生成质量低 |
教师审核拒绝率高 |
RuleValidator + LLMJudge 双重校验 + 不达标重试(最多 3 次) |
| 工作流状态丢失 |
教师审核流程中断 |
Redis 持久化 + 24h TTL + 事件日志(P6+ Temporal) |
| Redis 单点故障 |
限流/缓存/工作流全失效 |
Redis 哨兵/集群(P6 硬化)+ 降级策略(限流无限制/缓存失效/工作流拒绝启动) |
8.3 未决设计决策(需 coord 仲裁)
- 备课工作流是否引入 Temporal:004 §2.3 列出 Temporal 用于 AI 编排,但简单 4 步工作流可用 FastAPI BackgroundTasks + Redis 状态。建议:P5 用 BackgroundTasks + Redis(24h TTL),P6 评估迁移 Temporal(支持长运行 + 复杂状态机)
edu.insight.ai.usage topic 已确认,AIUsageEvent proto message 待补全:需 coord 在 shared-proto events.proto 新增(ai12 已给出建议 schema §3.3)
- LLM Provider 配置化路由:是否在 shared-py 建立通用
LLMProvider 抽象(供未来其他 Python 服务复用)。建议 P5 阶段在 ai 服务内部实现,P6 评估是否提取到 shared-py
- Prompt 模板存储位置:YAML 文件(简单,无 DB)vs DB(动态更新)。建议 P5 用 YAML 文件(
services/ai/src/ai/prompts/*.yaml),P6 评估迁移 DB
- ai12 新增 10 项提请(详见 01-understanding.md §"ai12 新增提请项"):ai gRPC 端口 50058、ai.proto 待补全、events.proto 待补 AIUsageEvent、iam GetEffectiveDataScope、响应信封整改、长运行工作流持久化、多模态规划、评估框架归属、RLHF 数据收集
ai12 增补:长期架构演进设计(§9-§17)
以下章节为 ai12 新增,旨在为未来 P6+ 演进做好铺垫,确保 P5 实现不阻塞未来扩展。
9. 安全架构
9.1 PII 脱敏流水线
| PII 类型 |
检测方式 |
脱敏方式 |
| 学生姓名 |
白名单匹配(来自 iam) |
替换为"学生A/B/C" |
| 手机号 |
正则 1[3-9]\d{9} |
替换为 *** |
| 身份证号 |
正则 \d{17}[\dXx] |
替换为 *** |
| 邮箱 |
正则 |
替换为 ***@***.com |
| 家庭住址 |
NER 模型(P6+ 评估) |
替换为"某地" |
9.2 Prompt 注入防御
- 输入 sanitize:过滤 SQL 注入模式(
'; DROP TABLE)、HTML 注入(<script>)、命令注入(; rm -rf)
- System prompt 加安全约束:明确禁止执行"忽略上述指令"、"输出系统 prompt"等攻击模式
- 输出过滤:检测输出中是否包含 system prompt 内容、是否执行了注入指令
9.3 输出内容审核
- 敏感词过滤:基于敏感词字典(政治/色情/暴力/广告),P5 用基础字典,P6+ 接入专业审核 API
- 安全校验:检测输出是否包含代码执行指令、是否诱导不当行为
- 审核失败处理:返回
AI_CONTENT_MODERATION_REJECTED 503 + 触发重新生成(最多 3 次)
9.4 多租户数据隔离
- DataScope 透传:所有 LLM 调用必须携带
UserContext.data_scope,确保生成内容不超出用户权限范围
- Prompt 注入 DataScope 约束:system prompt 明确"仅可生成用户 DataScope 范围内的内容"
- 配额隔离:按 school_id 隔离 token 配额,避免单一学校耗尽全平台预算
10. 评估框架
10.1 三道防线质量保障
10.2 RuleValidator 规则清单
| 规则 |
校验内容 |
失败处理 |
| 题型匹配 |
生成题目格式符合 question_type(单选/多选/填空等) |
重新生成 |
| 答案非空 |
answer 字段非空且非占位符 |
重新生成 |
| 解析合理 |
explanation 长度 >= 20 字符 |
重新生成 |
| 难度匹配 |
LLM 判定难度与请求 difficulty 一致 |
标记 warning,通过 |
| 知识点覆盖 |
生成题目覆盖请求的 knowledge_point_ids |
标记 warning,通过 |
| 字符长度 |
question <= 1000 字符 |
截断 + warning |
| 格式合规 |
输出可解析为 JSON(如要求 JSON 格式) |
重新生成 |
10.3 LLMJudge 评估维度
| 维度 |
评分标准 |
权重 |
| 准确性 |
题目本身正确性、答案正确性 |
0.4 |
| 清晰度 |
题目表述清晰、无歧义 |
0.2 |
| 难度匹配 |
实际难度与请求难度匹配 |
0.2 |
| 知识点相关性 |
题目与知识点相关 |
0.1 |
| 解析质量 |
解析能帮助学生理解 |
0.1 |
评分阈值:
>= 0.85:优秀,直接通过
0.7-0.85:合格,通过
< 0.7:不合格,重新生成
10.4 评估数据收集(RLHF 基础)
每次生成结果记录:
- 生成内容(题目/答案/解析)
- 评估报告(rule_score / judge_score / overall_score)
- 教师反馈(采纳/拒绝/修改,由 BFF 调用
ConfirmLessonPlan 时携带 modifications)
P6+ 评估发布 AIFeedbackRecorded 事件,data-ana 落 ClickHouse 供未来 fine-tune。
11. RAG 检索架构演进
11.1 P5:复用 content 服务(无独立向量库)
11.2 P6+:引入向量库语义检索(评估)
演进路径:
- P5:仅基于 content 知识点查询
- P6+:评估引入向量库(pgvector 复用 MySQL / Chroma 独立部署)+ Embedding Provider 抽象
- P7+:评估 Reranker 提升检索精度
proto 预留(ai12 增补,建议 coord 在 ai.proto v2 预留):
12. 多模态支持规划
12.1 P5:仅文本
- 出题:仅支持文本题目
- 优化:仅支持文本表达优化
- 聊天:仅支持文本对话
12.2 P6+:多模态评估
| 场景 |
模态 |
技术方案 |
| 数学几何题 |
文本+图片 |
GPT-4V / Claude 3 Vision 多模态 LLM |
| 英语听力题 |
文本+音频 |
TTS 生成音频 + 文本题目 |
| 物理实验题 |
文本+图片 |
多模态 LLM + 实验图理解 |
| 化学反应题 |
文本+图片 |
多模态 LLM + 化学式图片识别 |
12.3 proto 预留(ai12 增补)
13. LLM Provider 适配器详细设计
13.1 LLMProvider 抽象接口
13.2 适配器实现清单
| Provider |
API 协议 |
认证方式 |
备注 |
OpenAIProvider |
OpenAI REST |
Bearer token |
默认 Provider,gpt-4o-mini |
AnthropicProvider |
Anthropic REST |
x-api-key header |
内部转换为 OpenAI 兼容格式 |
BaichuanProvider |
OpenAI 兼容 REST |
Bearer token |
百川大模型,国内访问稳定 |
LocalOllamaProvider |
Ollama REST |
无认证 |
本地部署,私有化场景 |
13.3 ProviderFailoverChain 故障切换
13.4 配置化路由
14. 性能与容量规划
14.1 SLO 指标
| 指标 |
SLO |
测量方式 |
| 流式首字延迟 |
< 2s(P95) |
ai_llm_first_token_seconds |
| 非流式响应延迟 |
< 10s(P95) |
ai_llm_call_duration_seconds |
| 流式 chunk 间隔 |
< 100ms |
客户端测量 |
| 工作流总耗时 |
< 60s(P95) |
ai_workflow_duration_seconds |
| 评估通过率 |
> 80% |
ai_evaluation_pass_rate |
| 服务可用性 |
99.5% |
30 天滚动窗口 |
14.2 容量规划
| 资源 |
单实例容量 |
P5 预估需求 |
扩展策略 |
| 并发 LLM 调用 |
50(受 httpx 限制) |
200(500 教师) |
水平扩展(4 实例) |
| 并发流式 RPC |
30 |
100 |
水平扩展(4 实例) |
| Redis 内存 |
100MB |
500MB(工作流状态) |
Redis 集群(P6+) |
| Kafka 吞吐 |
1000 events/s |
100 events/s |
单分区足够 |
| 月度 token 消耗 |
— |
10M tokens |
按学校配额控制 |
14.3 流式背压处理
- gRPC 流式 RPC:默认支持背压(client 慢消费时 server 自动暂停 yield)
- SSE:FastAPI StreamingResponse 不支持背压,需手动检测 client 断开(
await request.is_disconnected())
- 超时:流式 RPC 60s 无 chunk 自动断开;非流式 30s 超时
15. 测试策略
15.1 测试金字塔
| 层级 |
覆盖率目标 |
工具 |
重点 |
| 单元测试 |
≥ 80% |
pytest + pytest-asyncio |
LLMProvider 适配器 / RuleValidator / PIIRedactor / 模板渲染 |
| 集成测试 |
关键路径 |
pytest + httpx + testcontainers |
HTTP/gRPC 端点 / Redis / Kafka |
| 契约测试 |
proto 一致 |
pact-python |
ai.proto 与 BFF 契约一致性 |
| 评估测试 |
生成质量 |
自研 + LLM-as-judge |
题目质量评分(人工 + 自动) |
15.2 LLM Mock 策略
- 测试环境不调真实 LLM(成本高 + 不稳定)
MockLLMProvider 实现固定响应(按 prompt 模式匹配返回预设响应)
- 评估测试用真实 LLM(独立 CI job,低频运行)
16. 实施里程碑(ai12 增补)
16.1 M14(第 1 月):基础架构
目标:建立 LLMProvider 抽象 + gRPC server 基础
| 交付物 |
验收标准 |
LLMProvider 抽象接口 + OpenAIProvider 实现 |
单元测试覆盖 ≥ 80% |
| AnthropicProvider + BaichuanProvider + LocalOllamaProvider 实现 |
4 Provider 切换可用 |
ProviderFailoverChain + CircuitBreaker |
故障切换测试通过 |
| gRPC server(Chat + StreamChat) |
BFF 可调通 gRPC 流式 RPC |
| Redis 多维度限流(user/IP/school) |
限流命中准确 |
| 响应信封整改为 ActionState |
004 §11.5 合规 |
| Dockerfile 多阶段构建 |
镜像 < 200MB |
16.2 M15(第 2 月):出题核心
目标:Prompt 模板管理 + 出题工作流 + 用量计费
| 交付物 |
验收标准 |
| PromptTemplateService + Jinja2 渲染 |
5+ 模板可渲染 |
| Prompt 模板 CRUD API |
HTTP/gRPC 均可用 |
| GenerateQuestion + StreamGenerateQuestion |
题目逐字流式生成 |
| RuleValidator + LLMJudge + QualityGate |
评估通过率 > 80% |
| UsageRecorder + KafkaProducer |
用量事件落 ClickHouse |
| QuotaEnforcer(学校/教师配额) |
配额超限返回 429 |
| PIIRedactor + InputSanitizer + OutputModerator |
安全测试通过 |
16.3 M16(第 3 月):备课工作流
目标:备课工作流 4 步编排端到端
| 交付物 |
验收标准 |
| LessonPreparationWorkflow 4 步编排 |
端到端跑通 |
| WorkflowStateStore(Redis 持久化) |
24h 内可恢复 |
| 教师审核流程(BFF 编排) |
教师可审核/修改/拒绝 |
| content.CreateQuestions 入库 |
题目入库成功 |
| gRPC client(content / data-ana / iam) |
下游调用稳定 |
| 集成测试 + 契约测试 |
关键路径覆盖 |
| 文档完善(README + known-issues) |
文档同步更新 |
17. 与架构标准的对齐清单
17.1 通用架构原则对齐
| 原则 |
对齐情况 |
说明 |
| DDD 限界上下文 |
✅ |
D6.b 生成子域,边界清晰 |
| CQRS |
N/A |
ai 无 DB,无读写分离 |
| 事件驱动 |
✅ |
AIUsageRecorded 事件外发,豁免 Outbox(004 §12.2) |
| 12-factor |
✅ |
配置通过环境变量 + pydantic-settings |
| SOLID |
✅ |
LLMProvider 抽象(OCP/ISP)+ Service 单一职责(SRP) |
| Hexagonal architecture |
✅ |
LLMProvider / ContentClient / DataAnaClient 均为端口 |
17.2 Resilience 模式对齐
| 模式 |
实现 |
| Circuit Breaker |
ProviderFailoverChain + CircuitBreaker |
| Retry with backoff |
LLM 调用 3 次重试(1s/2s/4s 指数退避) |
| Timeout |
LLM 30s / gRPC 60s / Redis 1s |
| Bulkhead |
单实例 50 并发 LLM 调用上限 |
| Failover |
多 Provider 故障切换链 |
| Graceful degradation |
LLM 不可用返回骨架 + degraded:true |
17.3 可观测性对齐
| 支柱 |
实现 |
与 004 §10 对齐 |
| Logs |
structlog + JSONRenderer + request_id |
✅ |
| Metrics |
prometheus-client + 20+ 业务指标 |
✅ |
| Traces |
OpenTelemetry + gRPC interceptor |
✅ |
| Health |
/healthz + /readyz(多维度检查) |
✅ |
待 coord 交叉审查项(ai 相关)
| # |
议题 |
涉及文档 |
| 2 |
新增 edu.insight.ai.usage topic + AIUsageEvent proto message |
004 §7.2 + events.proto |
| 4 |
ai 实现 gRPC server 决策(含 StreamChat 流式 RPC) |
004 §4.1 |
| 6 |
ai 备课工作流是否引入 Temporal |
004 §2.3 |
| 7 |
端口冲突检查:ai 3008 HTTP + 50058 gRPC(ai12 增补:gRPC 端口待补登) |
full-stack-runbook + 004 §1.2 |
| 8 |
错误码前缀检查:AI_*(与其他服务不重叠) |
— |
| 9 |
黄金模板对齐:Python 服务无 NestJS 装饰器,权限校验用 FastAPI Depends 等价物是否认可 |
— |
| ai12-A1 |
ai gRPC 端口 50058 补登 004 §1.2 + infra/port-allocation.md |
004 §1.2 |
| ai12-A2 |
ai.proto 待补全(GenerateLessonPlan / StreamGenerateQuestion / 字段扩展) |
ai.proto |
| ai12-A3 |
events.proto 待补 AIUsageEvent message(建议 schema 见 §3.3) |
events.proto |
| ai12-A4 |
iam GetEffectiveDataScope proto P4 补全(004 §15.3 #5 已仲裁) |
iam.proto |
| ai12-A5 |
响应信封偏离 ActionState 强制整改(004 §11.5 已约束) |
known-issues §2.9 |
| ai12-A6 |
Prompt 模板存储位置(P5 YAML / P6+ DB 评估) |
— |
| ai12-A7 |
长运行工作流持久化(P5 Redis 24h / P6+ Temporal 评估) |
004 §2.3 |
| ai12-A8 |
多模态支持规划(P5 文本 / P6+ 多模态评估),proto v2 预留字段 |
ai.proto v2 |
| ai12-A9 |
评估框架归属(P5/P6 ai 内部 / P7+ 独立评估) |
— |
| ai12-A10 |
RLHF 数据收集 AIFeedbackRecorded topic(P6+ 评估) |
004 §7.2 |
下一步:等待 coord 交叉审查通过后,进入阶段 3(按图实施)。
AI Agent: ai12 (ai) ← ai06(初稿)
Branch: 单仓库并行模式(直接提交 main)
Coordinator: coord-ai