From 377e11946e28257a1c0b48ed0d3a6905b744b616 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Thu, 6 Aug 2026 14:47:42 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E9=A6=96=E9=A1=B5=E5=AF=B9=E5=A4=96=20?= =?UTF-8?q?Agent=20=E8=AE=BE=E8=AE=A1=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: CodeFree --- .../specs/2026-08-06-homepage-agent-design.md | 578 +++++++++++++++++++++ 1 file changed, 578 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-06-homepage-agent-design.md diff --git a/docs/superpowers/specs/2026-08-06-homepage-agent-design.md b/docs/superpowers/specs/2026-08-06-homepage-agent-design.md new file mode 100644 index 0000000..0afc1e3 --- /dev/null +++ b/docs/superpowers/specs/2026-08-06-homepage-agent-design.md @@ -0,0 +1,578 @@ +# 首页对外 Agent 设计 + +## 概述 + +将首页从空白欢迎页改造为完整的对外 agent 对话界面,支持 session 管理、工具调用、推理展示等功能。采用混合模式:未登录用户可体验(受限功能),登录后解锁完整功能并持久化历史。 + +## 用户分层 + +| 维度 | 未登录用户 | 已登录用户 | +|------|-----------|-----------| +| Session 存储 | 服务端临时 session(HttpOnly cookie token 关联),2h TTL | 持久化 session,软删除永久保留 | +| 模型 | 管理员配置的默认模型(不可切换) | 用户偏好模型(session 内可切换) | +| 推理 | 固定开启(不可切换) | session 级别开关,默认开启 | +| 工具 | 白名单工具自动启用(不可切换) | session 级别开关,默认开启 | +| 消息上限 | 单 session 20 条 assistant 回复 + IP 日 50 条 assistant 回复 | 无限制 | +| 反馈 | 不可用 | 可用 | +| System prompt 查看 | 不可见 | 工具栏按钮查看 | +| 工具栏 | 仅显示模型名 | 模型选择 + 推理开关 + 工具开关 + system prompt 按钮 | + +## 整体架构 + +### 布局 + +全屏占满,侧边栏(260px 固定)+ 主对话区。对话区消息限宽 800px 居中。移动端(< 768px)侧边栏抽屉化。 + +### 核心数据流 + +``` +用户输入 → POST /api/agent/chat (sessionId, content) + → 服务端校验身份(cookie token 或登录态) + → 限流检查(未登录:session 计数 + IP 计数) + → 保存 user message + → 从 DB 加载 session 历史 messages + → 注入 system prompt + 工具(按用户角色过滤) + → streamText (ai-sdk) → 流式响应 + → onFinish: 持久化 assistant message(含 parts JSON、modelId、token 用量) + → 更新 session lastActiveAt + → 首轮对话后异步生成标题 +``` + +### 新增 API 路由 + +``` +/api/agent/sessions GET — 分页列表(15/页) + POST — 新建 session +/api/agent/sessions/:id GET — session 详情 + 首页消息(30条) + PUT — 重命名 + DELETE — 软删除 +/api/agent/sessions/:id/config PUT — 更新 session 配置(modelId/enableThinking/enableTools) +/api/agent/sessions/:id/messages GET — 消息分页加载 +/api/agent/chat POST — 发送消息(流式响应) +/api/agent/chat/tool-approve POST — 工具审批(仅已登录用户) +/api/agent/feedback POST — 提交/修改反馈 +/api/agent/migrate POST — 登录后迁移临时 session + +/api/admin/agent-config GET — 读取所有配置 +/api/admin/agent-config/system-prompt PUT — 更新 system prompt +/api/admin/agent-config/default-model PUT — 更新默认模型 + 标题模型 +/api/admin/agent-config/public-tools PUT — 更新公开工具白名单 +``` + +## 数据库 Schema + +### `agent_sessions` 表 + +```typescript +export const agentSessions = sqliteTable("agent_sessions", { + id: text("id").primaryKey(), // UUID + userId: integer("user_id"), // null = 临时 session + tempToken: text("temp_token", { length: 64 }), // 临时 session 的 cookie token(正式 session 为 null) + title: text("title", { length: 100 }).notNull().default("新对话"), + modelId: integer("model_id"), // session 使用的模型(null = 用默认/偏好模型) + enableThinking: integer("enable_thinking").default(1).notNull(), // 0/1 + enableTools: integer("enable_tools").default(1).notNull(), // 0/1 + lastActiveAt: integer("last_active_at", { mode: "timestamp_ms" }).defaultNow().notNull(), + expiresAt: integer("expires_at", { mode: "timestamp_ms" }), // 临时 session 的过期时间 + deletedAt: integer("deleted_at", { mode: "timestamp_ms" }), // 软删除时间 + createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().$onUpdate(() => new Date()).notNull(), +}) +``` + +### `agent_messages` 表 + +```typescript +export const agentMessages = sqliteTable("agent_messages", { + id: text("id").primaryKey(), // UUID + sessionId: text("session_id").notNull(), + role: text("role", { length: 20 }).notNull(), // "user" | "assistant" + content: text("content").notNull().default(""), // 纯文本内容 + parts: text("parts"), // JSON string,存 MessagePart[] + modelId: integer("model_id"), // assistant 消息使用的模型 + inputTokens: integer("input_tokens"), // assistant 消息的输入 token + outputTokens: integer("output_tokens"), // assistant 消息的输出 token + sortOrder: integer("sort_order").notNull(), // 同 session 内顺序 + createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(), +}) +``` + +### `agent_message_feedback` 表 + +```typescript +export const agentMessageFeedback = sqliteTable("agent_message_feedback", { + id: integer("id").primaryKey({ autoIncrement: true }), + messageId: text("message_id").notNull(), + userId: integer("user_id").notNull(), + feedback: text("feedback", { length: 10 }).notNull(), // "like" | "dislike" + comment: text("comment"), // 可选文字说明 + createdAt: integer("created_at", { mode: "timestamp_ms" }).defaultNow().notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().$onUpdate(() => new Date()).notNull(), +}) +``` + +### `agent_config` 表 + +```typescript +export const agentConfig = sqliteTable("agent_config", { + key: text("key").primaryKey(), + value: text("value").notNull(), // JSON string + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).defaultNow().$onUpdate(() => new Date()).notNull(), +}) +``` + +**预设配置项(无默认值,管理员首次使用前需自行配置):** +- `system_prompt` — string,管理员配置的 system prompt +- `default_model_id` — number,未登录用户使用的默认模型 +- `title_model_id` — number,生成 session 标题的低成本模型 +- `public_tool_slugs` — string[],公开工具白名单 + +**注意:** 首次部署时 `agent_config` 表为空。管理员需在后台页面配置以上项后,agent 功能才能正常工作。未配置时: +- 未登录用户新建 session → 报错"默认模型未配置" +- 标题生成 → 跳过,保持"新对话"标题 +- 未登录用户工具 → 空列表(无白名单) + +### 索引 + +- `agent_sessions`: `userId` + `deletedAt`(列表查询)、`tempToken`(临时 session 查找) +- `agent_messages`: `sessionId` + `sortOrder`(消息分页) +- `agent_message_feedback`: `messageId` + `userId`(唯一约束) + +## 后端 API 设计 + +### Session 管理 + +#### `GET /api/agent/sessions` +分页列表,按 `lastActiveAt` 倒序,每页 15 条。 + +``` +请求: ?page=1 +响应: { + code: 0, + data: { + list: Session[], // id, title, lastActiveAt + total: number, + page: number, + pageSize: 15, + } +} +``` + +**身份识别逻辑:** +- 已登录:`WHERE userId = ? AND deletedAt IS NULL` +- 未登录:从 cookie 读 `tempToken`,`WHERE tempToken = ? AND deletedAt IS NULL AND expiresAt > now` +- 未登录且无 token:返回空列表 + +#### `POST /api/agent/sessions` +新建 session。 + +``` +请求: { modelId?: number } +响应: { code: 0, data: Session } +``` + +**逻辑:** +- 已登录:`userId` = 当前用户,`modelId` = 传入值 || 用户偏好模型 || 管理员默认模型 +- 未登录:生成 `tempToken`(64 位随机 hex),写入 HttpOnly cookie(`path=/; httpOnly; secure; sameSite=strict`),`modelId` = 管理员默认模型,`expiresAt` = now + 2h + +#### `GET /api/agent/sessions/:id` +Session 详情 + 首页消息(最近 30 条)。 + +``` +响应: { + code: 0, + data: { + session: Session, + messages: AgentMessage[], // 最近 30 条,按 sortOrder 正序 + hasMore: boolean, + } +} +``` + +**权限校验:** session 的 `userId` 或 `tempToken` 必须匹配当前身份。 + +#### `PUT /api/agent/sessions/:id` +重命名。 + +``` +请求: { title: string } +响应: { code: 0 } +``` + +#### `DELETE /api/agent/sessions/:id` +软删除。 + +``` +逻辑: deletedAt = now +``` + +#### `GET /api/agent/sessions/:id/messages` +消息分页加载(向上加载更早消息)。 + +``` +请求: ?before=&limit=30 +响应: { code: 0, data: { messages: AgentMessage[], hasMore: boolean } } +``` + +### 对话 + +#### `POST /api/agent/chat` +发送消息,流式响应。 + +``` +请求: { + sessionId: string, + content: string, // regenerate 时也必填,默认取最后一条 user message 内容(前端传入) + editMessageId?: string, // 编辑消息时传 + regenerate?: boolean, // 重新生成时传 +} +响应: SSE 流(ai-sdk toUIMessageStreamResponse) +响应头(未登录用户): + X-RateLimit-Session-Remaining: number — 当前 session 剩余 assistant 回复次数 + X-RateLimit-Ip-Remaining: number — 当前 IP 今日剩余 assistant 回复次数 +``` + +**核心流程:** +1. 身份校验 + session 权限校验 +2. 限流检查(未登录:session 计数 ≥ 20 或 IP 计数 ≥ 50 → 403) +3. 处理编辑/重新生成(截断 messages) +4. 保存 user message(`sortOrder` = 当前最大 + 1) +5. 读取 session 历史 messages +6. 读取 `agent_config` 的 system prompt +7. 工具注入: + - 已登录 + session.enableTools:全部 enabled 工具(需用户审批后执行) + - 已登录 + !session.enableTools:无工具 + - 未登录:`public_tool_slugs` 白名单工具(强制启用,自动执行,无需审批) +8. `streamText` 调用(复用现有 `resolveModel` 逻辑) +9. `onFinish` 回调:保存 assistant message(含 parts、modelId、token 用量),更新 session `lastActiveAt`,限流计数 +1 +10. 第一条 assistant 回复完成后,异步调用 `titleModelId` 生成标题 + +**编辑消息(截断 + 重新生成):** +- 前端传 `editMessageId` + `content` +- 服务端删除该 message 之后的所有 messages(硬删除,含 feedback) +- 用编辑后的内容更新该 message +- 继续正常对话流程 + +**重新生成:** +- 前端传 `regenerate: true` + `content`(取最后一条 user message 内容) +- 服务端删除最后一条 assistant message(含 feedback) +- 用传入的 content 重新生成 + +### Session 配置更新 + +#### `PUT /api/agent/sessions/:id/config` +更新 session 级别配置(仅已登录用户)。 + +``` +请求: { modelId?: number, enableThinking?: boolean, enableTools?: boolean } +响应: { code: 0 } +逻辑: 仅更新传入的字段,未登录用户 403 +``` + +### 工具审批 + +已登录用户的工具调用需要审批: +- LLM 发起 tool-call → 流式返回 tool-call part(状态"待审批") +- 前端展示审批按钮(同意/拒绝) +- 用户选择后调用审批 API → 服务端执行或拒绝 +- 未登录用户的白名单工具自动执行,无需审批 + +#### `POST /api/agent/chat/tool-approve` +工具审批(仅已登录用户)。 + +``` +请求: { sessionId: string, messageId: string, toolCallId: string, approved: boolean } +响应: { code: 0 } +逻辑: + - approved=true: 执行工具,返回 tool-result + - approved=false: 返回拒绝结果给 LLM,LLM 自行处理 +``` + +### 反馈 + +#### `POST /api/agent/feedback` +提交/修改反馈(仅登录用户)。 + +``` +请求: { messageId: string, feedback: "like" | "dislike", comment?: string } +响应: { code: 0 } +逻辑: UPSERT(messageId + userId 唯一约束) +``` + +### 迁移 + +#### `POST /api/agent/migrate` +登录后迁移临时 session(前端弹窗确认后调用)。 + +``` +请求: { tempToken: string } +响应: { code: 0, data: { migratedCount: number } } +逻辑: + 1. 查询 tempToken 关联的所有未删除、未过期临时 session + 2. 更新 userId = 当前用户,tempToken = null,expiresAt = null + 3. 迁移的 session 置底(lastActiveAt 保持原值,排序靠后) + 4. 清除 tempToken cookie +``` + +### 管理员配置 API + +``` +GET /api/admin/agent-config — 读取所有配置 +PUT /api/admin/agent-config/system-prompt — 更新 system prompt +PUT /api/admin/agent-config/default-model — 更新默认模型 + 标题模型 +PUT /api/admin/agent-config/public-tools — 更新公开工具白名单 +``` + +### 限流实现 + +内存缓存(Map),服务重启归零: + +```typescript +// 单 session 计数 +Map + +// IP 日计数 +Map +``` + +每日 0 点重置(日期字符串比对)。计数时机:assistant 回复完成后(`onFinish`)。 + +**前端获取剩余次数:** +- 未登录用户的 `/api/agent/chat` 响应头返回: + - `X-RateLimit-Session-Remaining` — 当前 session 剩余次数(20 - 已用) + - `X-RateLimit-Ip-Remaining` — 当前 IP 今日剩余次数(50 - 已用) +- 前端从响应头读取,更新 `useAgentLimit` 状态 +- 达到上限时禁用输入框 + 显示提示 + +### 定时任务 + +复用 `server/scheduler`,每 10 分钟扫描: +- 删除 `expiresAt < now AND deletedAt IS NULL` 的临时 session 及其消息(硬删除) + +## 前端架构 + +### 页面与组件结构 + +``` +app/pages/index.vue — 首页 agent 主页面(layout: 'home') +app/components/agent/ + ├── AgentSidebar.vue — 侧边栏(session 列表 + 新对话按钮 + 用户信息) + ├── AgentSidebarItem.vue — session 列表项(含操作菜单) + ├── AgentChatArea.vue — 主对话区(工具栏 + 消息列表 + 输入框) + ├── AgentToolbar.vue — 顶部工具栏(汉堡 + 模型选择 + 开关 + system prompt 查看) + ├── AgentMessageList.vue — 消息列表(含日期分隔线 + 滚动管理) + ├── AgentMessageBubble.vue — 单条消息气泡(左右分列) + ├── AgentMessageParts.vue — 消息 parts 渲染(text/reasoning/tool-call) + ├── AgentToolCall.vue — 工具调用折叠展示(含审批按钮) + ├── AgentReasoning.vue — 推理过程展示 + ├── AgentInputBox.vue — 底部输入框(多行自适应 + 附件预留 + 发送) + ├── AgentModelSelector.vue — 模型下拉选择器 + ├── AgentSystemPromptModal.vue — system prompt 查看弹窗 + ├── AgentFeedbackButtons.vue — 👍/👎 反馈按钮 + └── AgentEmptyState.vue — 空 session 状态 + +app/pages/admin/agent-config/ + ├── system-prompt.vue — system prompt 编辑 + ├── default-model.vue — 默认模型 + 标题模型选择 + └── public-tools.vue — 公开工具白名单多选 +``` + +### Composables + +``` +app/composables/useAgentSession.ts — session CRUD + 列表分页 + 切换 +app/composables/useAgentChat.ts — 对话核心逻辑(扩展自 useLlmChat) +app/composables/useAgentFeedback.ts — 反馈提交/修改 +app/composables/useAgentLimit.ts — 未登录限流状态(前端展示用) +``` + +### `useAgentChat` 核心设计 + +基于现有 `useLlmChat` 改造,关键差异: +1. 不再前端传完整 messages,只传当前消息内容 +2. sessionId 驱动,切换 session 时重新加载消息 +3. 支持编辑消息(editMessageId)和重新生成(regenerate) +4. 停止生成时保留 text parts,丢弃进行中的 tool-call parts +5. 流式结束后从响应获取 token 用量,更新 assistant message + +### 状态管理流程 + +``` +首页加载 + → useAgentSession.init() + → 检测登录态 + → 已登录:加载 session 列表(第 1 页),无 session 则自动新建 + → 未登录:检查 cookie token,有则加载临时 session 列表,无则自动新建(触发 token 生成) + → useAgentChat.loadMessages(currentSessionId) + → 加载最近 30 条消息 + +切换 session + → useAgentChat.clear() + → useAgentChat.loadMessages(newSessionId) + → 滚动到底部 + +发送消息 + → useAgentChat.send(content) + → 流式渲染 assistant 回复 + → 完成后前端乐观更新 session 列表顺序 + +编辑消息 + → 点击"编辑" → 内容填入输入框 + → 发送时带 editMessageId → 截断 + 重新生成 + +重新生成 + → 点击"重新生成" → 带 regenerate: true → 删除最后 assistant + 重新生成 + +停止生成 + → AbortController.abort() + → 保留 text parts,清理进行中 tool-call parts + → "重新生成"按钮出现在最后一条 assistant 消息下方 +``` + +### 滚动管理 + +- **新消息生成**:用户在底部 → 自动滚动;用户向上看历史 → 不自动滚动,显示"回到最新"按钮 +- **加载历史**:滚动到顶部触发加载,保持当前视口位置 +- **切换 session**:加载完成后滚动到底部 + +### 移动端适配 + +- `< 768px`:侧边栏变为抽屉,汉堡按钮在工具栏左侧,滑出时有遮罩 +- 消息限宽 800px 在移动端自动变为 `100% - padding` + +### 样式约定 + +- 消息气泡:用户右侧(主色调背景)、assistant 左侧(卡片背景) +- 工具栏高度 56px,底部固定 +- 输入框最小高度 64px,自适应增长,最大 200px +- 代码块:语法高亮 + 右上角复制按钮 +- 工具调用:折叠式,工具名 + 状态图标(✓/✗/调用中.../待审批) +- 推理过程:默认展开,显示在回复文本上方 +- 日期分隔线:跨天时插入居中分隔线 +- Markdown 渲染:基础 + 表格 + 代码高亮 +- 每条 assistant 回复下方显示模型名 + token 用量 + +### 交互细节 + +- 输入框:多行自适应 + Enter 发送 + Shift+Enter 换行 +- 附件按钮:UI 预留灰色禁用位置,后续迭代启用 +- Session 列表项:点击切换 + "..."操作菜单(重命名、删除) +- 新建 session:侧边栏顶部按钮,新 session 置顶 +- Session 切换:立即清空 + loading + 加载完成渲染 +- 重新生成按钮:仅最后一条 assistant 消息下方显示 +- 编辑按钮:仅最后一条用户消息下方显示,点击后内容填入底部输入框 +- 反馈:仅登录用户,每条 assistant 消息下方 👍/👎,可修改 +- System prompt 查看:仅登录用户,工具栏按钮 → 弹窗展示 + +## 边界情况与错误处理 + +### 身份与权限 + +| 场景 | 处理 | +|------|------| +| 未登录用户访问他人 session | 403 | +| 未登录用户 cookie token 过期(2h) | session 列表返回空,当前对话提示"session 已过期",自动新建临时 session | +| 未登录用户清除了 cookie | 旧临时 session 成为孤儿数据,定时任务 2h 后清理;用户获得新临时 session | +| 已登录用户访问他人 session | 403 | +| session 已软删除 | 404 | + +### 限流边界 + +| 场景 | 处理 | +|------|------| +| 未登录达到 session 上限(20 条 assistant) | 输入框禁用 + "已达对话上限,登录后继续" + 登录按钮 | +| 未登录达到 IP 日上限(50 条 assistant) | 输入框禁用 + "今日对话次数已达上限,登录后继续" + 登录按钮 | +| 限流计数时机 | assistant 回复完成后计数(`onFinish`) | +| 限流计数与停止生成 | 停止生成不产生完整 assistant 回复,不计入限流 | + +### 对话异常 + +| 场景 | 处理 | +|------|------| +| LLM 请求失败 | 报错提示,不保存 assistant message,用户可重新生成 | +| LLM 返回空内容 | 提示"模型未返回内容",删除空 assistant message | +| 工具调用失败 | tool-call part 标记"执行失败"(红色 ✗),LLM 自行处理错误 | +| 工具审批被拒(已登录用户) | tool-call part 标记"已拒绝"(红色 ✗),LLM 收到拒绝结果 | +| 流式中断(网络断开) | 保留已生成 text parts,清理进行中 tool-call parts,提示"网络中断" | +| 停止生成 | 保留 text parts,清理进行中 tool-call parts,显示"重新生成"按钮 | + +### Session 迁移 + +| 场景 | 处理 | +|------|------| +| 迁移时临时 session 已过期 | 跳过过期 session,只迁移未过期的 | +| 迁移时用户取消 | 不迁移,临时 session 继续按原 TTL 过期 | +| 迁移后 cookie token | 清除 `tempToken` cookie | +| 迁移的 session 位置 | 置底(`lastActiveAt` 保持原值,排序靠后) | + +### 消息操作 + +| 场景 | 处理 | +|------|------| +| 编辑消息后截断 | 硬删除被截断的 messages 及其 feedback | +| 重新生成 | 硬删除最后一条 assistant message 及其 feedback | +| 编辑最后一条 user message | 正常流程(截断 0 条 + 更新内容 + 重新生成) | +| 编辑中间某条 user message | 截断该消息之后所有 messages + 更新内容 + 重新生成 | + +### 数据一致性 + +| 场景 | 处理 | +|------|------| +| assistant message 保存失败 | 日志记录,消息仍返回给用户(不阻塞流式响应) | +| 标题生成失败 | session 保持"新对话"默认标题,日志记录 | +| 反馈的 message 被截断删除 | feedback 一并硬删除 | + +### 前端状态同步 + +| 场景 | 处理 | +|------|------| +| 多标签页同时操作 | 不做特殊处理(个人 agent 场景) | +| session 列表顺序 | 发送/接收完成后前端乐观更新 `lastActiveAt` 并重新排序列表 | +| 新建 session | 前端立即插入列表顶部,API 返回后替换为真实数据 | + +## 测试策略 + +### 后端测试 + +| 模块 | 测试点 | +|------|--------| +| Session CRUD | 创建/查询/重命名/软删除/权限校验 | +| 临时 session | token 生成、cookie 设置、过期判定、孤儿清理 | +| 限流 | session 计数、IP 计数、每日重置、达到上限拦截 | +| 对话流程 | 消息保存、历史加载、流式响应、token 用量记录 | +| 编辑/重新生成 | 截断逻辑、feedback 级联删除 | +| 工具注入 | 已登录全量(含审批)/已登录关闭/未登录白名单(自动执行) | +| 标题生成 | 首轮 assistant 回复后触发、失败兜底 | +| 迁移 | 全量迁移、过期跳过、cookie 清除、置底排序 | +| 反馈 | 提交/修改/撤销、未登录拦截 | +| 工具审批 | 同意执行/拒绝返回、未登录自动执行 | + +### 前端测试 + +| 模块 | 测试点 | +|------|--------| +| 消息渲染 | text/reasoning/tool-call/tool-result 各 part 类型 | +| 工具调用状态 | 调用中/成功/失败/拒绝/待审批 折叠展示 | +| 滚动管理 | 自动滚动/保持视口/回到最新/顶部加载 | +| 停止生成 | text 保留、tool-call 清理、重新生成按钮 | +| 编辑消息 | 输入框填充、截断、重新生成 | +| 限流 UI | 输入框禁用、提示文案、登录按钮、剩余次数显示 | +| 工具审批 | 审批按钮展示、同意/拒绝流程 | +| 移动端 | 抽屉化、遮罩、汉堡按钮 | + +## 不在本次范围内(后续迭代预留) + +- Session 分享功能(生成公开链接) +- 会话导出功能 +- 会话内搜索 +- 会话标签/分类 +- 收藏/置顶消息 +- 会话统计 +- 自定义 agent 人设 +- 会话模板 +- 全屏对话模式 +- 多语言支持 +- 语音功能 +- 快捷指令(`/` 弹出工具列表) +- 文件上传 +- 管理员反馈数据查看页面