# AI 模块审计报告 > 审计日期:2026-06-25 > 审计范围:`src/modules/ai/` 全部代码 + `src/app/api/ai/` 路由 + app 层接入点 > 审计依据:`docs/architecture/004_architecture_impact_map.md`、`docs/architecture/005_architecture_data.json`、`docs/standards/coding-standards.md`、项目硬约束 > 审计方法:逐文件源码审阅 + 架构图一致性比对 + 角色-权限映射核对 + 行业标杆对标(Khanmigo / Duolingo Max / Squirrel AI / Century Tech) --- ## 一、现有实现概要 ### 1.1 文件分布(共 32 个文件) ``` src/modules/ai/ ├─ types.ts 330 行 AiService / AiClientService 接口 + 业务类型 ├─ schema.ts 248 行 Zod 校验(输入 + AI 输出) ├─ actions.ts 415 行 10 个 Server Action(含权限校验) ├─ data-access.ts 138 行 内存事件存储 + 使用统计聚合 ├─ services/ │ ├─ ai-service.ts 478 行 DefaultAiService 实现(封装 shared/lib/ai) │ ├─ prompt-templates.ts 300 行 9 套 System Prompt 常量 │ ├─ usage-tracker.ts 100 行 trackAiUsage + withAiTracking │ └─ content-safety.ts 291 行 输入/输出过滤 + 每日限额(原子操作) ├─ context/ │ ├─ ai-client-provider.tsx 62 行 React Context 注入 AiClientService │ └─ create-ai-client-service.ts 54 行 createFullAiClientService / createCoreAiClientService ├─ hooks/ │ ├─ use-ai-chat-stream.ts 155 行 SSE 流式聊天 + localStorage 持久化 │ ├─ use-ai-chat.ts 57 行 非流式聊天(⚠ 死代码,未被引用) │ ├─ use-ai-suggestion.ts 72 行 相似题 / 批改建议 │ ├─ stream-utils.ts 135 行 SSE 解析纯函数 │ ├─ use-floating-ball.ts 160 行 悬浮球组合 hook │ ├─ use-drag-position.ts 130 行 拖拽 hook │ └─ use-position-persistence.ts 99 行 位置 localStorage ├─ components/ │ ├─ ai-assistant-widget.tsx 329 行 全局悬浮球 + 上下文感知 + Sheet │ ├─ ai-chat-panel.tsx 417 行 聊天面板(card / widget 双变体) │ ├─ ai-error-boundary.tsx 31 行 SectionErrorBoundary 包装 │ ├─ ai-skeleton.tsx 47 行 AiSuggestionSkeleton / AiChatSkeleton │ ├─ ai-suggestion-card.tsx 178 行 相似题卡片(⚠ 死代码,未被引用) │ ├─ ai-provider-selector.tsx 89 行 表单字段(react-hook-form) │ ├─ ai-markdown-renderer.tsx 162 行 Markdown + 图表代码块渲染 │ ├─ ai-chart-renderer.tsx 351 行 Recharts 4 图表(bar/line/pie/radar) │ ├─ ai-grading-assist.tsx 173 行 教师批改辅助 │ ├─ ai-error-book-analysis.tsx 246 行 学生错题本 AI 分析 │ ├─ ai-lesson-content-generator.tsx 180 行 教师备课内容生成 │ ├─ ai-question-variant-generator.tsx 218 行 题目变体生成 │ ├─ ai-usage-dashboard.tsx 221 行 管理员使用统计 │ ├─ ai-child-summary.tsx 186 行 家长学情摘要(⚠ 未接入页面) │ └─ ai-study-path.tsx 200 行 学生学习路径(⚠ 未接入页面) └─ src/app/api/ai/ ├─ chat/route.ts 196 行 非流式聊天端点 └─ chat/stream/route.ts 237 行 SSE 流式端点 ``` ### 1.2 数据流 ``` app/(dashboard)/layout.tsx │ 模块级 const aiClientService = createFullAiClientService() ▼ ← React Context │ ├─ ← 全局悬浮球 │ └─ useAiClientOptional() / useFloatingBall() │ └─ │ └─ useAiChatStream() → fetch('/api/ai/chat/stream') │ │ │ ▼ │ route.ts: requirePermission(AI_CHAT) │ + tryConsumeDailyQuota │ + filterUserInput / filterAiOutput │ + createAiChatCompletionStream (shared/lib/ai) │ └─ 各业务页面(teacher/homework/submissions、student/error-book、teacher/exams/build 等) └─ └─ / / ... └─ useAiClient().suggestGrading(...) → suggestGradingAction → requirePermission(AI_CHAT, HOMEWORK_GRADE) → createAiService(userId).suggestGrading(input) → withAiTracking(...) → createAiChatCompletion (shared/lib/ai) ``` ### 1.3 架构图记录情况 `005_architecture_data.json` 中 `modules.ai` 节点记录了完整的依赖矩阵、exports 清单、集成点、权限点、安全策略、流式特性、i18n 命名空间。 **但比对发现两处与实际实现不一致**(详见 §二 P0-2): - `ai.integrations.parent-dashboard` 声称 `AiChildSummary` 接入 `parent/dashboard` 页面 → 实际未接入 - `ai.integrations.student-learning` 声称 `AiStudyPath` 接入 `student/learning/study-path` 页面 → 实际该路由不存在 --- ## 二、现存问题与原因分析 ### P0 — 紧急且阻断使用 #### P0-1:家长角色完全缺失 `AI_CHAT` 权限 - **位置**:[permissions.ts](file:///e:/Desktop/CICD/src/shared/lib/permissions.ts#L161-L176) `ROLE_PERMISSIONS_SEED.parent` 数组 - **问题**:parent 角色权限清单中**没有任何** `Permissions.AI_CHAT`,但: - [actions.ts](file:///e:/Desktop/CICD/src/modules/ai/actions.ts#L267) `generateChildSummaryAction` 第一行调用 `requirePermission(Permissions.AI_CHAT)` → 家长调用必返回 403 - [actions.ts](file:///e:/Desktop/CICD/src/modules/ai/actions.ts#L296) `recommendStudyPathAction` 同上 - `/api/ai/chat/route.ts` L49 同上 - `AiChildSummary` / `AiStudyPath` 组件存在但家长/学生路径下完全无法使用 - **违反规则**: - 项目硬约束「所有 Server Action 必须调用 `requirePermission()` 进行权限校验」—— 校验逻辑本身正确,但权限未授予 - 项目硬约束「家长需要的功能应被授权」(K12 系统家长是关键角色) - **后果**:家长角色付费的 AI 学情摘要功能在生产环境 100% 失败;学生使用学习路径推荐时若依赖家长代调也会失败 #### P0-2:架构图虚构集成(AiChildSummary / AiStudyPath 完全未接入) - **位置**: - [005_architecture_data.json](file:///e:/Desktop/CICD/docs/architecture/005_architecture_data.json#L19725-L19744) `ai.integrations.parent-dashboard` / `ai.integrations.student-learning` - 004 文档同步描述 - **问题**: - 声称 `AiChildSummary` 集成于 `parent/dashboard` —— grep 全仓 `AiChildSummary` 仅在自身文件、actions、context 出现,**app/ 下零引用** - 声称 `AiStudyPath` 集成于 `student/learning/study-path` —— 该路由**不存在**(`app/(dashboard)/student/learning/` 下只有 `textbooks/`、`assignments/`、`courses/`、`page.tsx`) - 声称 `AiUsageDashboard` 集成于 `admin/ai-usage` —— 实际接入在 `admin/ai-settings/page.tsx`(路径不一致) - **违反规则**: - 项目硬约束「如果发现项目中存在架构图未记录的模块、函数、表、路由等,必须优先完善架构图信息」 - 项目硬约束「改码必同步图」—— 反向也成立:图中记录的集成必须真实存在 - **后果**:依赖架构图做影响分析的开发者会误以为功能已上线,跳过实现;测试用例遗漏;产线功能缺失 #### P0-3:`getAiUsageStatsAction` 错误消息 i18n 键错误 - **位置**:[actions.ts](file:///e:/Desktop/CICD/src/modules/ai/actions.ts#L381) - **问题**:管理员查询使用统计失败时返回 `t("error.chatFailed")`("AI 请求失败"/"AI request failed"),与场景不符 - **后果**:管理员看到"AI 请求失败"误以为是 AI 调用失败,实为统计查询失败 ### P1 — 高优先级 #### P1-1:数据访问层使用内存存储,多实例部署不可用 - **位置**: - [data-access.ts](file:///e:/Desktop/CICD/src/modules/ai/data-access.ts#L34) `const eventStore: StoredAiEvent[] = []` 单实例内存 - [content-safety.ts](file:///e:/Desktop/CICD/src/modules/ai/services/content-safety.ts#L137) `const dailyUsageMap = new Map<...>()` 单实例内存 - **问题**:注释自承认"生产环境应替换为 Redis",但当前实现: - 多实例部署下,`getAiUsageStats` 聚合的统计仅包含当前实例数据 - `tryConsumeDailyQuota` 在多实例下,每个实例独立计数,实际可用次数 = 限额 × 实例数 - 进程重启后所有统计归零 - **违反规则**:项目硬约束「企业级补充」「可扩展性:采用配置驱动设计」 - **后果**:K8s 多 Pod 部署后限额失效、统计失真 #### P1-2:`AiUsageDashboard` 违反 React Hooks 规范 - **位置**:[ai-usage-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-usage-dashboard.tsx#L53-L57) - **问题**: ```tsx useEffect(() => { void loadStats() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) ``` - `loadStats` 依赖 `aiClient` 但被 disable 抑制 - `aiClient` 变化时不会重新加载 - **后果**:eslint-disable 掩盖真实 bug;aiClient 引用变更时不刷新 #### P1-3:`AiClientProvider` 在 layout 模块级创建 service - **位置**:[layout.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/layout.tsx#L10) - **问题**:`const aiClientService = createFullAiClientService()` 在模块加载时执行(module scope),service 对象被所有用户共享 - **当前可工作原因**:Server Action 内部 `requirePermission()` 会从 session 动态解析用户 - **风险**:未来若 service 需要请求级状态(如缓存当前用户权限),模块级单例会泄露 - **建议**:移入 Server Component 函数体内创建 #### P1-4:`AiAssistantWidget` 内嵌英文 prompt 硬编码 - **位置**:[ai-assistant-widget.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-assistant-widget.tsx#L228-L326) `inferContextFromPath` - **问题**:6 个角色的 `systemPrompt` 为英文硬编码字符串,未走 i18n - **缓解**:API 端点会强制覆盖(学生侧 SOCRATIC_TUTOR_SYSTEM_PROMPT),客户端 prompt 仅作为上下文提示 - **后果**:维护 prompt 需改代码;多语言场景下非英语用户的提示词不一致 #### P1-5:死代码 `useAiChat` Hook - **位置**:[use-ai-chat.ts](file:///e:/Desktop/CICD/src/modules/ai/hooks/use-ai-chat.ts) 57 行 - **问题**:grep 全仓 `useAiChat` 仅在自身文件 + 005 架构数据中引用,**实际无任何组件使用** - **原因**:早期非流式实现被 `useAiChatStream` 取代,但文件未删除 - **后果**:架构图 exports 中仍记录 `useAiChat`,误导调用方 #### P1-6:死代码 `AiSuggestionCard` 组件 - **位置**:[ai-suggestion-card.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-suggestion-card.tsx) 178 行 - **问题**:grep 全仓 `AiSuggestionCard` 仅在自身文件 + 架构数据中引用,**实际无任何页面使用** - **原因**:`AiErrorBookAnalysis` 已包含相似题功能,`AiSuggestionCard` 是早期独立实现 - **后果**:维护成本;架构图 exports 仍记录该组件 #### P1-7:API 路由与非流式路由大量重复代码 - **位置**: - [chat/route.ts](file:///e:/Desktop/CICD/src/app/api/ai/chat/route.ts) 196 行 - [chat/stream/route.ts](file:///e:/Desktop/CICD/src/app/api/ai/chat/stream/route.ts) 237 行 - **问题**:权限校验 / 限流 / Zod 校验 / 配额消费 / 输入过滤 / 系统提示构建 / 配额退款 7 段逻辑几乎逐行复制 - **后果**:修一处漏一处易出 bug;测试需双倍 ### P2 — 中等优先级 #### P2-1:`ai-chart-renderer.tsx` 使用 `as` 断言 - **位置**:[ai-chart-renderer.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-chart-renderer.tsx#L122-L132) - **问题**: ```ts data: obj.data as Array>, // as 断言 series: obj.series as AiChartSeries[], // as 断言 yDomain: Array.isArray(obj.yDomain) ? obj.yDomain as [number, number] : undefined, // as 断言 ``` - **违反规则**:项目硬约束「禁止 `as` 断言(除非从 `unknown` 转换)」—— 严格说此处从 `unknown` 转,但应使用类型守卫或 Zod parse - **建议**:用 `z.array(AiChartSeriesSchema).parse(obj.series)` 校验 #### P2-2:`AiChatPanel` 单文件 417 行接近上限 - **位置**:[ai-chat-panel.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-chat-panel.tsx) 417 行 - **问题**:card / widget 两个变体有 ~60% 重复 JSX(消息列表、空状态、输入框、流式指示器各写两遍) - **建议**:抽取 `` / `` / `` / `` 子组件 #### P2-3:`AiAssistantWidget.inferContextFromPath` 配置硬编码 - **位置**:[ai-assistant-widget.tsx](file:///e:/Desktop/CICD/src/modules/ai/components/ai-assistant-widget.tsx#L223-L328) - **问题**:100+ 行 if-else 路由匹配,新增角色/路由需改代码 - **建议**:改为配置驱动 ```ts const CONTEXT_MAP: Array<{ match: RegExp; config: AiContextConfig }> = [...] ``` #### P2-4:`content-safety.ts` 关键词仅英文 - **位置**:[content-safety.ts](file:///e:/Desktop/CICD/src/modules/ai/services/content-safety.ts#L20-L43) - **问题**:`BLOCKED_INPUT_PATTERNS` / `STUDENT_BLOCKED_PATTERNS` 正则仅匹配英文关键词 - **后果**:中文"自杀/暴力/色情"等不当内容无法识别,K12 中国场景下安全防线不足 #### P2-5:`AiService.chat` 的 `usage` 字段始终返回 `null` - **位置**:[ai-service.ts](file:///e:/Desktop/CICD/src/modules/ai/services/ai-service.ts#L177) - **问题**:`return { result: { content, usage: null }, tokenUsage }` —— `AiChatResult.usage: unknown` 类型但实际始终 null - **建议**:将 `tokenUsage` 包入 `usage` 字段,或修改类型为 `usage: null` #### P2-6:架构图遗漏 `/admin/ai-settings` 路由 - **位置**:[005_architecture_data.json](file:///e:/Desktop/CICD/docs/architecture/005_architecture_data.json) `routes` 节点 - **问题**:仅在 `ai.integrations.admin-dashboard.page` 字段提及 `admin/ai-usage`,但 `routes` 表中无 `/admin/ai-settings` 条目(实际页面位于 `/admin/ai-settings`) - **后果**:路由审计遗漏 --- ## 三、行业差距对比 ### 3.1 与 Khanmigo(Khan Academy)差距 | 维度 | Khanmigo | 我们 | 差距影响 | |------|----------|------|---------| | 教师可见学生 AI 对话 | ✓ 教师后台可审阅 | ✗ 对话仅存 localStorage | 教师无法了解学生提问习惯,无法干预 Socratic 失败场景 | | 多模态输入 | ✓ 支持图片 | ✗ 仅文本 | 数学几何题无法拍照上传 | | Activity 难度自适应 | ✓ 根据学生水平动态调整 | ✗ 固定 difficulty 参数 | 同一题目对快慢学生无差异 | | Teacher copilot 模式 | ✓ 教师侧 AI 提示教学策略 | ✗ 仅 widget 通用助手 | 教师备课缺专业引导 | ### 3.2 与 Duolingo Max 差距 | 维维 | Duolingo Max | 我们 | 差距影响 | |------|--------------|------|---------| | Explain My Answer | ✓ 错题后一键解释 | ✓ `explainError` 已实现但未接入页面 | 功能闲置 | | Roleplay | ✓ 情景对话练习 | ✗ 无 | 英语口语训练缺失 | | "立即练习"按钮 | ✓ 相似题后直接进入练习流 | ✗ 仅"选择" | 学生看到相似题但无法作答,流程断裂 | ### 3.3 与 Squirrel AI(松鼠 AI)差距 | 维度 | Squirrel AI | 我们 | 差距影响 | |------|-------------|------|---------| | 纳米级知识图谱 | ✓ 700+ 知识点拆分 | ⚠ `recommendStudyPath` 已支持 knowledgeGraph 注入,但未接入页面 | 功能已实现但未上线 | | 自适应路径 | ✓ 实时根据答题调整 | ✗ 一次性生成路径,无反馈循环 | 路径在学习过程中不更新 | | 学习目标对齐 | ✓ 与课标 / 升学目标对齐 | ✗ studyPathInput.learningGoal 仅文本 | 缺少课标映射 | ### 3.4 与 Century Tech 差距 | 维度 | Century Tech | 我们 | 差距影响 | |------|--------------|------|---------| | 全校 AI 成本看板 | ✓ token 消耗 / 预算预警 | ⚠ `AiUsageDashboard` 无 token 字段 | 管理员无法评估成本 | | 多 Provider 对比 | ✓ A/B 测试 | ✗ 单次调用单 provider | 无法评估哪家性价比高 | | 课程标准映射 | ✓ AI 推荐与课标对齐 | ✗ 无 | 学习路径与课标脱节 | ### 3.5 K12 通用缺失 - **a11y**:`AiAssistantWidget` 悬浮球无键盘焦点;`AiChatPanel` 流式 token 更新未限流(屏幕阅读器频繁打断) - **空状态**:`AiUsageDashboard` 无数据时仅显示文案,无引导管理员"先发起一次 AI 对话" - **错误恢复**:`AiErrorBoundary` 透传 `SectionErrorBoundary`,无 AI 专属重试策略(如降级到非流式) --- ## 四、改进优先级建议 ### P0(必须立即修复 — 阻断核心功能) | 编号 | 改进项 | 方向说明 | |------|--------|---------| | P0-1 | parent 角色补齐 `AI_CHAT` 权限 | 在 `ROLE_PERMISSIONS_SEED.parent` 数组追加 `Permissions.AI_CHAT` | | P0-2 | 修复架构图虚构集成 | 二选一:(A) 在 parent/dashboard 接入 `AiChildSummary`,新建 `student/learning/study-path` 路由接入 `AiStudyPath`;(B) 从架构图 integrations 中移除两条虚构集成。**本次采用方案 A**:实际接入组件,让功能上线 | | P0-3 | `getAiUsageStatsAction` 错误 i18n 修复 | 新增 `ai.error.statsFailed` 翻译键,替换 `chatFailed` | ### P1(高优先级 — 影响可维护性与正确性) | 编号 | 改进项 | 方向说明 | |------|--------|---------| | P1-1 | 内存存储抽象化 | 提取 `AiUsageStore` 接口,当前内存实现作为 `InMemoryAiUsageStore`,未来可替换 `RedisAiUsageStore`;不阻塞当前发布 | | P1-2 | `AiUsageDashboard` useEffect 修复 | 抽取 `loadStats` 为 `useCallback`,依赖数组加入 `aiClient` | | P1-3 | layout service 创建移入 Server Component | 改为 `function DashboardLayout() { const service = createFullAiClientService(); ... }` | | P1-4 | `inferContextFromPath` 配置化 + i18n 化 | 改为 `CONTEXT_MAP` 数组,prompt 走 i18n key | | P1-5 | 删除 `use-ai-chat.ts` 死代码 | 文件 + 架构图 exports 同步移除 | | P1-6 | 删除 `ai-suggestion-card.tsx` 死代码 | 同上 | | P1-7 | API 路由共享逻辑抽取 | 抽取 `prepareAiChatRequest(req)` 返回 `{ body, isStudent, quota, limitResult }` | ### P2(中等优先级 — 代码质量与扩展性) | 编号 | 改进项 | 方向说明 | |------|--------|---------| | P2-1 | `ai-chart-renderer` `as` 断言替换为 Zod parse | 用 `AiChartSpecSchema.parse()` 校验 | | P2-2 | `AiChatPanel` 拆分子组件 | 抽取 `ChatMessages` / `ChatInput` / `ChatEmptyState` | | P2-3 | `content-safety` 增加中文关键词 | 扩展正则至中文场景 | | P2-4 | `AiService.chat.usage` 修正 | 返回实际 tokenUsage 或改类型为 `null` | | P2-5 | 架构图补齐 `/admin/ai-settings` 路由 | 005 routes 节点新增 | ### 中长期方向(不在本次实施范围) - 接入 Redis 替换内存存储(需运维配合) - 多模态输入(需 OCR/视觉模型) - 教师 AI 对话审阅后台(需新增 DB 表) - 多 Provider A/B 测试(需扩展 Provider 模型) - 课程标准映射(需课标数据源) --- ## 五、架构图同步说明 本次审计发现架构图需更新如下节点: ### 5.1 `005_architecture_data.json` 同步项 1. **`modules.ai.exports.hooks`** 移除 `useAiChat`(死代码已删除) 2. **`modules.ai.exports.components`** 移除 `AiSuggestionCard`(死代码已删除) 3. **`modules.ai.integrations.parent-dashboard`** 更新 `page` 字段为真实接入路径 4. **`modules.ai.integrations.student-learning`** 更新 `page` 字段为真实接入路径 `student/learning/study-path` 5. **`modules.ai.integrations.admin-dashboard`** 更正 `page` 为 `admin/ai-settings`(原误记为 `admin/ai-usage`) 6. **`routes./admin/ai-settings`** 新增节点(若 routes 表中确实缺失) 7. **`rolePermissionsSeed.parent`** 追加 `ai:chat` 权限 8. **`modules.ai.i18n.v2Keys`** 追加 `error.statsFailed` 翻译键 ### 5.2 `004_architecture_impact_map.md` 同步项 1. AI 模块章节的「集成点」表格更新实际接入路径 2. 移除已删除组件的引用 --- ## 六、本次实施清单 ### 已实施(本次审计直接修复) | 编号 | 类型 | 改动 | |------|------|------| | P0-1 | 代码 | `permissions.ts` parent 角色追加 `AI_CHAT` | | P0-2 | 代码 + 架构图 | 接入 `AiChildSummary` 到 parent/dashboard(`ParentDashboard` 新增 `aiSummarySlot`,page.tsx 为每个子女渲染 `AiChildSummary`);新建 `student/learning/study-path` 路由(page.tsx + loading.tsx + error.tsx)接入 `AiStudyPath`;同步 004/005 文档集成点与路由表 | | P0-3 | 代码 + i18n | `actions.ts` 修复错误键;`ai.json` (zh/en) 新增 `error.statsFailed` | | P1-2 | 代码 | `ai-usage-dashboard.tsx` 修复 useEffect | | P1-3 | 代码 | `layout.tsx` service 移入函数体 | | P1-5 | 代码 | 删除 `use-ai-chat.ts`;架构图 exports.hooks 移除 `useAiChat` | | P1-6 | 代码 | 删除 `ai-suggestion-card.tsx`;架构图 exports.components 移除 `AiSuggestionCard` | | P2-1 | 代码 | `ai-chart-renderer.tsx` 替换 `as` 为 Zod parse(新增 `AiChartSpecSchema`) | | P2-4 | 代码 | `ai-service.ts` 修正 `usage` 字段返回实际 tokenUsage | | 架构图 | 文档 | 004/005 同步上述改动;新增 `student.studyPath` i18n 块(zh/en) | | 权限 | 文档 | `005` 的 `rolePermissionsSeed.parent` 追加 `AI_CHAT` | | i18n | 文档 | `005` 的 `modules.ai.i18n.v2Keys` 追加 `error.statsFailed` | | 路由 | 文档 | `005` 的 `routes` 表新增 `/student/learning/study-path`;更新 `/parent/dashboard` 描述含 AI 摘要集成;`/admin/ai-settings` 校正为 admin 集成路径(原误记为 `admin/ai-usage`) | ### 未实施(中长期,需独立任务) | 编号 | 原因 | |------|------| | P1-1 Redis 替换内存 | 需运维提供 Redis 实例 | | P1-4 prompt 完全 i18n 化 | 客户端 prompt 已被服务端覆盖,影响小 | | P1-7 API 路由共享逻辑抽取 | 涉及测试回归,单独 PR | | P2-2 AiChatPanel 拆分 | 涉及大量回归,单独 PR | | P2-3 中文关键词扩展 | 需安全策略评审 |