# 第八阶段:AI 底座与教师端对话 第八阶段从“只预留 AI”进入 AI 底座建设。AI 当前只面向租户教师和后台运营,不开放给学生端。 ## 当前 AI 使用场景 ### 1. 租户教师 AI 对话 第一批先做基础对话能力: - 教师在租户后台发起对话。 - AI 根据当前租户配置调用模型。 - 对话历史按租户和教师隔离保存。 - 响应不暴露模型 API Key、Provider 原始响应密钥、内部租户 ID 或对象存储细节。 - 后续可追加 function calling,用于读取或操作受控业务能力。 预留 function call 的原则: - Controller 不直接暴露任意 tool/function 名称给客户端调用。 - Application 层定义可用业务函数目录,例如题目检索、题目草稿生成、班级学习概览、学生跟进建议。 - Infrastructure 用 Semantic Kernel 把受审计的业务函数注册为 plugin。 - 每个 function call 都必须记录租户、教师、会话、函数名、输入摘要、结果状态和耗时。 - 有写操作的 function 必须复用现有 RBAC、DataScope、Tenant Scope 和 AuditLog,不允许 AI 绕过后台权限。 ### 对话存储模型 不要把 Semantic Kernel 的 `ChatHistory`、`ChatMessageContent`、`KernelContent`、tool call object graph 或 provider 原始 response 直接作为 EF Core 持久化模型。原因: - SK 的对象模型适合运行时编排,不适合作为长期数据库 schema。 - OpenAI-compatible provider 的消息格式并不完全等价;DeepSeek 这类接口对 `role`、`content`、`tool_calls`、`tool_call_id` 的结构要求更严格。 - 如果把 SK metadata、内部 content item 或历史 tool 结构原样回放给 DeepSeek,容易触发请求参数错误。 - 后续换 provider、增加 function call 或做消息压缩时,直接持久化 SK 对象会变成强耦合。 数据库只保存 provider-neutral 的规范化消息: - `AiConversation` - `TenantId` - `TeacherUserId` - `Title` - `Scenario`:`teacher_chat`、后续可扩展。 - `ProviderCode` - `Model` - `Status` - `Metadata` - `AiConversationMessage` - `TenantId` - `ConversationId` - `Sequence` - `Role`:固定为 `system`、`user`、`assistant`、`tool`。 - `ContentText` - `ToolCallId` - `ToolName` - `ToolArguments` - `ToolResultSummary` - `ProviderMessageId` - `TokenInput` - `TokenOutput` - `Metadata` - `AiToolCallLog` - `TenantId` - `ConversationId` - `MessageId` - `ToolCallId` - `ToolName` - `InputSummary` - `ResultStatus` - `DurationMs` - `ErrorCode` 运行时转换规则: 1. Application 层读取规范化消息,不产生 SK 类型。 2. Infrastructure adapter 把规范化消息转换成 Semantic Kernel `ChatHistory`。 3. DeepSeek/OpenAI-compatible adapter 只发送 provider 接受的字段: - 普通消息:`role + content`。 - assistant tool call:`role=assistant + tool_calls`。 - tool 结果:`role=tool + tool_call_id + content`。 4. Provider 原始响应只保存必要摘要和可审计 ID,不作为下一轮请求的直接输入。 5. 任何无法被目标 provider 表达的 SK metadata 都必须丢弃或写入内部 `Metadata`,不得回放给模型 API。 ### 2. AI 审核题目反馈 这个场景保持简单,不做复杂扩展: - 输入:题目反馈内容、题目基本信息、反馈类型、提交用户上下文摘要。 - 输出:审核建议、风险等级、归类标签、是否建议人工复核。 - 不做 function calling。 - 不直接修改题目、反馈状态或用户数据。 - 只生成建议结果,最终状态变更仍由教师或运营人员确认。 ## Provider 与密钥边界 - AI Provider 使用 `TenantExternalProvider(capability=ai)`。 - 租户自己的模型 API Key 存入 `TenantSecret`,通过 `SecretRef` 关联。 - `ConfigPublic` 只允许保存公开配置,例如 provider、model、endpoint、deployment、temperature 默认值、max token 限制。 - `ConfigPublic` 禁止出现 `secret`、`token`、`apiKey`、`key`、`privateKey` 等敏感字段。 - `Microsoft.SemanticKernel` NuGet 包只引用在 `Tiku.Infrastructure`。 - `Tiku.Api`、`Tiku.Application`、`Tiku.Domain` 不直接引用 Semantic Kernel namespace。 ## 建议模块边界 Application 层后续只放业务抽象: - `IAiConversationService` - `IAiFeedbackReviewService` - `IAiKernelFactory` - `IAiProviderConfigService` Infrastructure 层负责: - 根据当前租户 Provider 配置和 `TenantSecret` 创建 Kernel。 - 注册受审计 plugin。 - 调用 chat completion。 - 处理 provider 错误、超时、重试和调用日志。 Domain 层可增加持久化模型: - `AiConversation` - `AiConversationMessage` - `AiToolCallLog` - `AiFeedbackReview` ## 第一批实施顺序 1. 增加 AI Provider capability、Semantic Kernel 包和架构测试边界。 2. 增加 AI 配置服务测试:租户 A/B 不能互读模型配置和密钥。 3. 增加教师对话数据模型和最小 API: - `POST /api/tenant-admin/ai/conversations` - `GET /api/tenant-admin/ai/conversations` - `GET /api/tenant-admin/ai/conversations/{conversationId}` - `POST /api/tenant-admin/ai/conversations/{conversationId}/messages` 4. 增加 fake AI provider,先跑通对话和日志,不接真实模型。 5. 增加题目反馈审核最小 API: - `POST /api/tenant-admin/ai/question-feedback/review` 6. 最后再接真实模型 Provider。 ## 暂不做 - 学生端 AI。 - 复杂 RAG。 - 自动改题、自动发布题目。 - 自动处理反馈状态。 - 让客户端指定任意 function call。 - 生产环境使用明文 API Key 配置。