feat: 打通全栈标注闭环、异步拆帧与模型状态

后端能力:

- 新增 Celery app、worker task、ProcessingTask 模型、/api/tasks 查询接口和 media_task_runner,将 /api/media/parse 改为创建后台任务并由 worker 执行 FFmpeg/OpenCV/pydicom 拆帧。

- 新增 Redis 进度事件模块和 FastAPI Redis pub/sub 订阅,将 worker 任务进度广播到 /ws/progress;Dashboard 后端概览接口改为聚合 projects/frames/annotations/templates/processing_tasks。

- 统一项目状态为 pending/parsing/ready/error,新增共享 status 常量,并让前端兼容归一化旧状态值。

- 扩展 AI 后端:新增 SAM registry、SAM2 真实运行状态、SAM3 状态检测与文本语义推理适配入口,以及 /api/ai/models/status GPU/模型状态接口。

- 补齐标注保存/更新/删除、COCO/PNG mask 导出相关后端契约和模板 mapping_rules 打包/解包行为。

前端能力:

- 新增运行时 API/WS 地址推导配置,前端 API 封装对齐 FastAPI 路由、字段映射、任务轮询、标注归档、导出下载和 AI 预测响应转换。

- Dashboard 改为读取 /api/dashboard/overview,并订阅 WebSocket progress/complete/error/status 更新解析队列和实时流转记录。

- 项目库导入视频/DICOM 后创建项目、上传媒体、触发异步解析并刷新真实项目列表。

- 工作区加载真实帧、无帧时触发解析任务、回显已保存标注、保存未归档 mask、更新 dirty mask、清空当前帧后端标注、导出 COCO JSON。

- Canvas 支持当前帧点/框提示调用后端 AI、渲染推理/已保存 mask、应用模板分类并维护保存状态计数;时间轴按项目 fps 播放。

- AI 页面新增 SAM2/SAM3 模型选择,预测请求携带 model;侧边栏和工作区新增真实 GPU/SAM 状态徽标。

- 模板库和本体面板接入真实模板 CRUD、分类编辑、拖拽排序、JSON 导入、默认腹腔镜分类和本地自定义分类选择。

测试与文档:

- 新增 Vitest 配置、前端测试 setup、API/config/websocket/store/组件测试,覆盖登录、项目库、Dashboard、Canvas、工作区、模型状态、时间轴、本体和模板库。

- 新增 pytest 后端测试夹具和 auth/projects/templates/media/AI/export/dashboard/tasks/progress 测试,使用 SQLite、fake MinIO、fake SAM registry 和 Redis monkeypatch 隔离外部服务。

- 新增 doc/ 文档结构,冻结当前需求、设计、接口契约、测试计划、前端逐元素审计、实现地图和后续实施计划,并同步更新 README 与 AGENTS。

验证:

- conda run -n seg_server pytest backend/tests:27 passed。

- npm run test:run:54 passed。

- npm run lint、npm run build、compileall、git diff --check 均通过;Vite 仅提示大 chunk 警告。
This commit is contained in:
2026-05-01 13:29:14 +08:00
parent 4d65c37c73
commit f020ff3b4f
78 changed files with 7089 additions and 456 deletions

322
AGENTS.md
View File

@@ -1,17 +1,21 @@
# AGENTS.md — AI 编码助手项目指南
> 本文件面向 AI 编码助手。阅读者应假设对该项目一无所知。以下所有信息基于项目实际内容,不做假设性推断
> 本文件面向 AI 编码助手。阅读者应假设对该项目一无所知。以下信息基于当前仓库实际文件、脚本和源码不要把早期设计目标当作已实现事实。任何代码和功能修改都要落实到文档和测试上如果生成git commit信息要逐个列点把所有修改都列上重要的、大的修改放前面不重要的、小的修改列在后面
---
## 项目概述
本项目是一个**语义分割系统**Semantic Segmentation System Web 前端应用,用于 AI 驱动的图像/视频分割与标注。它提供了一个深色主题Dark Mode的单页应用SPA包含项目管理、分割工作区、AI 智能分割引擎和模板库四大核心模块
本项目是一个**语义分割系统**Semantic Segmentation System,当前形态是 React 前端 + FastAPI 后端的全栈 Web 应用,用于视频/DICOM 医学影像上传、服务器端拆帧、交互式 Canvas 标注、SAM 2/SAM 3 可选辅助分割、模板分类管理和标注导出
- **项目名称**: `react-example`package.json 中的 name
- **部署目标**: Google AI StudioCloud Run
- **AI Studio 应用链接**: https://ai.studio/apps/2707f0e1-d453-4594-a618-fba53cb937c4
- **业务文档**: `语义分割系统构建方案.docx`(项目根目录,未解析内容
- **项目名称**: `react-example``package.json` 中的 `name`
- **前端入口**: `src/main.tsx``src/App.tsx`
- **前端服务入口**: `server.ts`Express + Vite 中间件 / 生产静态服务,并保留少量旧版 mock API
- **后端入口**: `backend/main.py`FastAPI
- **默认前端地址**: `http://localhost:3000`
- **默认后端地址**: `http://localhost:8000`
- **前端 API 配置**: `src/lib/config.ts`,优先读取 `VITE_API_BASE_URL`,未配置时按当前浏览器 hostname 推导 `http://<host>:8000`
- **业务文档**: `语义分割系统构建方案.docx`(项目根目录)
---
@@ -21,15 +25,22 @@
|------|------|
| 前端框架 | React 19 + TypeScript 5.8 |
| 构建工具 | Vite 6 |
| 样式方案 | TailwindCSS 4 + 自定义深色主题 |
| 状态管理 | React `useState`(无外部状态库 |
| 路由 | 无路由库,基于 React 状态切换模块 |
| Canvas 渲染 | Konva + react-konva |
| 前端样式 | TailwindCSS 4 + 自定义深色主题 |
| 前端状态 | Zustand`src/store/useStore.ts` |
| 前端请求 | Axios`src/lib/api.ts` |
| 实时通信 | WebSocket 客户端(`src/lib/websocket.ts` |
| Canvas 渲染 | Konva + react-konva + use-image |
| 图标库 | lucide-react |
| 动画 | motion |
| AI SDK | @google/genaiGemini API |
| 后端/服务器 | Express 4单文件 `server.ts` |
| 运行时 | Node.jsES Modules`"type": "module"` |
| 动画依赖 | motion(在 `package.json` 中声明) |
| AI SDK 依赖 | `@google/genai``package.json` 中声明;当前业务源码未直接调用 |
| 后端框架 | FastAPI + Uvicorn |
| ORM / 数据库 | SQLAlchemy + PostgreSQL |
| 缓存 / 队列 Broker | Redis |
| 后台任务 | Celery worker |
| 对象存储 | MinIO |
| AI 推理 | SAM 2 / SAM 3 可选模型 + PyTorch`GET /api/ai/models/status` 返回真实 GPU/模型状态 |
| 视频 / 影像处理 | FFmpeg / OpenCV / pydicom |
| 运行时 | Node.js ES ModulesPython 3.11 后端环境 |
---
@@ -37,159 +48,244 @@
```
Seg_Server/
├── server.ts # Express 服务端入口(开发服务器 + 生产静态文件服务)
├── index.html # SPA HTML 入口
├── vite.config.ts # Vite 构建配置
├── tsconfig.json # TypeScript 配置
├── package.json # 依赖与脚本
├── .env.example # 环境变量模板
├── metadata.json # AI Studio 元数据(目前为空)
├── src/
── main.tsx # React 应用挂载点StrictMode
│ ├── App.tsx # 根组件:模块路由 + 登录鉴权
│ ├── index.css # TailwindCSS 导入 + 自定义工具类
│ ├── lib/
│ └── utils.ts # `cn()` 工具函数clsx + tailwind-merge
── components/
├── auth/
│ └── Login.tsx # 登录页
├── layout/
│ └── Sidebar.tsx # 左侧导航栏w-16
├── dashboard/
└── Dashboard.tsx # 总体概况仪表盘
├── projects/
└── ProjectLibrary.tsx # 项目库列表
├── workspace/
│ ├── VideoWorkspace.tsx # 核心分割工作区布局
│ ├── CanvasArea.tsx # Konva 画布(缩放/平移/选点)
│ ├── ToolsPalette.tsx # 左侧工具栏
│ ├── OntologyInspector.tsx # 右侧本体/属性检查面板
── FrameTimeline.tsx # 底部时间轴
├── ai/
│ └── AISegmentation.tsx # AI 智能分割引擎界面
└── templates/
└── TemplateRegistry.tsx # 模板库管理
├── server.ts # Express + Vite 前端入口;保留 /api/login、/api/projects、/api/templates mock
├── index.html # SPA HTML 入口
├── vite.config.ts # Vite 配置;含 @/* 路径别名与 DISABLE_HMR 逻辑
├── tsconfig.json # TypeScript 配置@/* 映射到项目根目录
├── package.json # npm 依赖与脚本
├── .env.example # AI Studio/Gemini 前端环境变量模板
├── metadata.json # AI Studio 元数据
├── public/
── logo.png # Sidebar 使用的 /logo.png
├── doc/ # 当前实现审计、接口契约和后续实施文档
├── start_services.sh # 本地一键启动 PostgreSQL/Redis/MinIO/FastAPI/Celery/前端
├── backend/ # FastAPI 后端
├── main.py # 应用入口、lifespan、CORS、路由注册、WebSocket
── config.py # Pydantic Settings读取 backend/.env
├── database.py # SQLAlchemy Engine / Session
├── models.py # Project/Frame/Template/Annotation/Mask/ProcessingTask ORM
├── schemas.py # Pydantic 请求/响应模型
├── minio_client.py # MinIO 上传、下载、预签名 URL
├── redis_client.py # Redis 连接封装
├── celery_app.py # Celery app 配置
├── worker_tasks.py # Celery 任务入口
├── download_sam2.py # SAM 2 权重下载脚本
├── requirements.txt # Python 依赖
├── routers/
│ │ ├── auth.py # /api/auth/login
│ │ ├── projects.py # /api/projects 与 /api/projects/{id}/frames
│ │ ├── templates.py # /api/templates
│ │ ── media.py # /api/media/upload、/upload/dicom、/parse
├── ai.py # /api/ai/predict、/models/status、/auto、/annotate
│ │ └── export.py # /api/export/{project_id}/coco、/masks
└── services/
├── frame_parser.py # FFmpeg/OpenCV 拆帧、pydicom 读片、帧上传
│ ├── sam2_engine.py # SAM 2 懒加载推理封装和 fallback
│ ├── sam3_engine.py # SAM 3 状态检测与文本语义推理适配器
│ └── sam_registry.py # SAM 模型选择、GPU 状态与推理分发
└── src/ # React 前端
├── main.tsx # React StrictMode 挂载
├── App.tsx # 登录拦截 + 模块切换
├── index.css # TailwindCSS 导入 + 全局样式
├── store/useStore.ts # Zustand 全局状态
├── lib/api.ts # Axios API 封装
├── lib/websocket.ts # 解析进度 WebSocket 客户端
├── lib/utils.ts # cn() 工具函数
└── components/ # 扁平化组件目录
├── Login.tsx
├── Sidebar.tsx
├── Dashboard.tsx
├── ProjectLibrary.tsx
├── VideoWorkspace.tsx
├── CanvasArea.tsx
├── ToolsPalette.tsx
├── OntologyInspector.tsx
├── FrameTimeline.tsx
├── AISegmentation.tsx
└── TemplateRegistry.tsx
```
以下目录/文件通常是运行产物或本地数据,已在 `.gitignore` 中忽略:`node_modules/``dist/``models/``uploads/``frames/``Data_*/``*.mp4``*.dcm``*.7z``backend/.env`、日志文件等。
`doc/` 目录是当前项目的事实文档入口。修改功能前优先查看:
- `doc/03-frontend-element-audit.md`:哪些前端元素是真功能,哪些是 Mock/UI-only。
- `doc/04-api-contracts.md`:前后端接口契约,以及当前不一致点。
- `doc/05-implementation-plan.md`:建议的后续实施顺序。
---
## 构建与运行命令
### 前端 / Node 入口
```bash
# 安装依赖
npm install
# 开发模式(启动 Express + Vite 中间件,端口 3000
# 开发模式:运行 tsx server.tsExpress 集成 Vite middleware,端口 3000
npm run dev
# 生产构建输出 dist/
# 生产构建输出 dist/
npm run build
# 预览生产构建
# Vite 预览
npm run preview
# 生产模式启动Node 直接运行 server.ts需先 build
# 生产模式运行 server.ts服务 dist/;仍保留 server.ts 中的旧版 mock API
npm start
# 类型检查(不输出文件)
# TypeScript 类型检查
npm run lint
# 清理构建产物
# 删除 dist/
npm run clean
```
**开发服务器地址**: `http://localhost:3000`
### FastAPI 后端
**环境变量**(复制 `.env.example``.env.local`
- `GEMINI_API_KEY` — Gemini AI API 密钥AI Studio 会自动注入)
- `APP_URL` — 应用托管 URLAI Studio 自动注入 Cloud Run 地址)
```bash
cd backend
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
```
### 一键启动
```bash
./start_services.sh
```
该脚本会依次检查/启动 PostgreSQL、Redis、MinIO、FastAPI 后端、Celery worker 和前端。
---
## 运行时架构
### 前端
- 单页应用React 19 `StrictMode` 挂载。
- 模块切换通过 `App.tsx` 中的 `activeModule` 状态控制,可选值:
`'dashboard' | 'projects' | 'ai' | 'workspace' | 'templates'`
- 默认进入 `workspace`(分割工作区)。
- 未登录时全局拦截,显示 `Login` 组件。
### 后端 (`server.ts`)
- Express 服务器,端口 `3000`
- **开发模式**: 集成 Vite 中间件(`middlewareMode: true`
- **生产模式**: 静态文件服务 `dist/`,所有路由回退到 `index.html`
- **API 端点**(内存数据存储,无数据库):
- `POST /api/login` — 认证(固定用户名 `admin`,密码 `123456`
- `GET /api/projects` — 返回项目列表
- `GET /api/templates` — 返回模板列表
- 单页应用,无路由库;模块切换由 `useStore().activeModule` 控制。
- 模块值包括:`dashboard``projects``ai``workspace``templates`
- 默认模块是 `workspace`
- 未登录时渲染 `Login`
- 登录成功后 token 写入 `localStorage`Axios request interceptor 会附加 `Authorization: Bearer <token>`
- `App.tsx` 在登录后调用 `getProjects()` 初始化项目列表。
### 部署
- 面向 **Google AI Studio** / **Cloud Run** 部署。
- `metadata.json` 用于 AI Studio 元数据配置(当前为空)
- `vite.config.ts` 中 HMR 可通过环境变量 `DISABLE_HMR=true` 关闭AI Studio 环境下文件监听被禁用以防止 agent 编辑时闪烁)。
### 后端
- 主后端是 `backend/main.py` 的 FastAPI 服务
- `lifespan` 启动时会:
- 创建数据库表;
- 检查/创建 MinIO bucket `seg-media`
- 测试 Redis 连接;
- 后台 seed 默认模板;
- 如果本地存在 `Data_MyVideo_1.mp4`,后台 seed 默认演示项目并拆前 100 帧。
- API 路由包括:
- `POST /api/auth/login`
- `GET/POST/PATCH/DELETE /api/projects`
- `GET/POST /api/projects/{project_id}/frames`
- `GET/POST/PATCH/DELETE /api/templates`
- `POST /api/media/upload`
- `POST /api/media/upload/dicom`
- `POST /api/media/parse`
- `GET /api/tasks`
- `GET /api/tasks/{task_id}`
- `POST /api/ai/predict`
- `GET /api/ai/models/status`
- `POST /api/ai/auto`
- `POST /api/ai/annotate`
- `GET /api/ai/annotations`
- `PATCH/DELETE /api/ai/annotations/{annotation_id}`
- `GET /api/dashboard/overview`
- `GET /api/export/{project_id}/coco`
- `GET /api/export/{project_id}/masks`
- `GET /health`
- `WS /ws/progress`
### 存储
- PostgreSQL 存储项目、帧、模板、标注、mask 和后台任务元数据。
- MinIO 存储上传视频、DICOM、拆出的帧、缩略图等对象前端展示使用预签名 URL。
- Redis 当前作为 Celery broker/result backend并用于连接检查。
---
## 主要业务流程
1. 登录:`Login.tsx` 调用 `POST /api/auth/login`,默认开发凭证为 `admin / 123456`
2. 项目管理:`ProjectLibrary.tsx` 调用项目 API 创建项目、拉取列表。
3. 上传资源:视频走 `/api/media/upload`DICOM 批量走 `/api/media/upload/dicom`
4. 拆帧入队:前端调用 `/api/media/parse`;后端创建 `ProcessingTask` 并投递 Celery。
5. worker 执行Celery worker 用 FFmpeg 优先拆视频帧,失败后用 OpenCV fallbackDICOM 使用 pydicom并持续更新任务进度。
6. 帧展示:`VideoWorkspace.tsx` 调用 `/api/projects/{id}/frames``CanvasArea.tsx``FrameTimeline.tsx` 显示当前帧与时间轴缩略图。
7. AI 分割:前端工具包括正向点、反向点和框选;后端 `ai.py` 期望按 `image_id``prompt_type``prompt_data``model` 调用 SAM registry。SAM 2 支持点/框/自动分割SAM 3 入口支持文本语义推理,运行时不满足官方要求时会在状态接口中标为不可用。
8. 模板管理:`TemplateRegistry.tsx` 管理分类、颜色和 z-index`OntologyInspector.tsx` 在工作区显示当前模板分类树。
9. 导出:后端支持 COCO JSON 和 PNG mask ZIP 导出。
---
## 当前实现注意事项
- `src/lib/config.ts` 会优先读取 `VITE_API_BASE_URL``VITE_WS_PROGRESS_URL`;未配置时按当前浏览器 hostname 推导后端 `:8000` 地址。
- 前端 `predictMask()` 已按后端 `PredictRequest` 发送 `image_id``prompt_type``prompt_data``model`,并将后端 `polygons` 转成 Konva 可渲染的 `pathData`
- 前端 `exportCoco()` 已对齐后端 `/api/export/{project_id}/coco`;工作区“导出 JSON 标注集”按钮已绑定下载流程,导出前会先保存当前待归档 mask。
- 工作区“结构化归档保存”按钮已接入 `POST /api/ai/annotate``PATCH /api/ai/annotations/{id}`;加载工作区时会通过 `GET /api/ai/annotations` 回显已保存标注。
- 工作区“清空遮罩”会调用 `DELETE /api/ai/annotations/{id}` 删除当前帧已保存标注,并清空当前帧本地 mask。
- 项目状态已统一为 `pending``parsing``ready``error`;前端 `src/lib/api.ts` 会兼容归一化旧库中可能存在的 `Ready``Parsing``Error`
- `server.ts` 仍有旧版 `/api/login``/api/projects``/api/templates` mock当前前端真实 API 调用主要走 FastAPI 的 `/api/auth/*``/api/projects``/api/templates` 等接口。
- `Dashboard.tsx` 初始统计、队列和活动日志来自 `GET /api/dashboard/overview`;解析队列来自 `processing_tasks`Celery worker 通过 Redis pub/sub 的 `seg:progress` 频道推送细粒度进度,再由 FastAPI 广播到 `/ws/progress`
---
## 代码风格与约定
### 样式规范
- **深色主题**: 全局背景色以 `#0a0a0a``#111``#0d0d0d``#151515``#1e1e1e` 为主。
- **强调色**: 青色(`cyan-400`/`cyan-500`)用于激活状态、按钮和关键指示器
- **工具类优先**: 全面使用 TailwindCSS 工具类,通过 `cn()` 合并条件类名
- **自定义工具类**: `index.css` 中定义 `.no-scrollbar` 用于隐藏滚动条
- 深色主题为主,常见背景色包括 `#0a0a0a``#111``#0d0d0d``#151515``#1e1e1e`
- 青色(如 `cyan-400` / `cyan-500`)用于激活状态、主按钮和关键指标
- 前端样式主要使用 TailwindCSS 工具类,通过 `cn()` 合并条件类名
- `src/index.css` 使用 TailwindCSS 4 的 `@import "tailwindcss";`
### 组件规范
- 所有组件使用 **函数组件 + Hooks**,无类组件。
- 组件按功能模块分目录存放在 `src/components/{module}/`
- Props 类型使用 TypeScript `interface` 定义
- 导入排序React → 第三方库 → 内部模块 → 类型
- 组件使用函数组件 + Hooks
- 当前组件目录是扁平结构:`src/components/*.tsx`,不是按模块子目录分层
- Props 类型优先使用 TypeScript `interface`
- UI 文本保持中文。
- 代码与注释优先使用英文。
### 命名规范
- 组件文件使用 **PascalCase**(如 `AISegmentation.tsx`)。
- 工具文件使用 **camelCase**(如 `utils.ts`)。
- 类型/接口使用 **PascalCase**
### 语言约定
- **界面文本**: 全部使用 **中文**(如 "核心分割工作区"、"AI智能分割引擎"、"导出 JSON 标注集"
- **代码与注释**: 使用英文
- 添加新 UI 文本时,**必须保持中文**。
- 组件文件使用 PascalCase例如 `AISegmentation.tsx`
- 工具文件使用 camelCase例如 `utils.ts`
- 类型和接口使用 PascalCase
---
## 测试策略
**当前状态:无测试文件。**
当前仓库已配置前端 Vitest 测试和后端 pytest 测试。测试依据 `doc/07-current-requirements-freeze.md``doc/08-current-design-freeze.md``doc/09-test-plan.md`
- 项目中不存在 `.test.``.spec.` 文件
- 无测试框架配置(如 Jest、Vitest、Playwright
- 若需添加测试,建议在前端引入 Vitest与 Vite 同生态)进行单元测试,或使用 Playwright 进行 E2E 测试
- 前端测试配置:`vitest.config.ts`,共享 setup 在 `src/test/setup.tsx`
- 前端测试命令:`npm run test:run`
- 后端测试依赖:`backend/requirements-dev.txt`
- 后端测试命令:`pytest backend/tests`,或在 `backend/` 目录执行 `pytest tests`
- 基础静态校验:`npm run lint``npm run build``python -m py_compile backend/routers/ai.py backend/routers/templates.py backend/schemas.py`
- 后端测试使用内存 SQLite、fake MinIO 和 fake SAM registry不依赖真实 PostgreSQL、MinIO、Redis 或模型权重。
---
## 安全注意事项
- **硬编码凭证**: `server.ts` 中登录验证使用硬编码凭据(`admin` / `123456`),生产环境必须替换为真实的身份验证机制
- **Mock JWT**: 登录成功返回固定 `fake-jwt-token-for-admin`无实际的 JWT 签名验
- **内存数据存储**: 所有项目/模板数据存储在内存中,服务重启后数据丢失。无持久化层
- **环境变量**: `GEMINI_API_KEY` 通过 `.env.local` 管理,已加入 `.gitignore`,不会误提交
- **CORS / 安全头**: Express 服务器目前未配置 CORS 策略或安全响应头(如 Helmet
- FastAPI 登录是开发用硬编码凭证:`admin / 123456`
- 登录成功返回固定 token`fake-jwt-token-for-admin`没有真实 JWT 签名验。
- Axios 会附加 Bearer token但后端大多数业务路由当前没有鉴权依赖
- `backend/.env``.gitignore` 忽略不要提交真实数据库、MinIO、Redis、模型路径等敏感配置
- `start_services.sh` 中包含本机路径和 sudo 启动逻辑,迁移机器时要审查
- Express `server.ts` 的旧版 mock API 只适合开发/兼容场景,不能当生产鉴权或持久化方案。
---
## 关键依赖与注意事项
## AI Studio / Vite 特定配置
- **React 19**: 使用 `createRoot` API注意与 React 18 的部分差异
- **TailwindCSS 4**: 使用 `@import "tailwindcss"` 语法(非 v3 的 `@tailwind` 指令)
- **react-konva**: Canvas 交互核心,所有画布相关操作(缩放、选点、遮罩)均依赖此库。
- **use-image**: 用于异步加载图片到 Konva 画布。
- **路径别名**: `@/*` 映射到项目根目录(由 `vite.config.ts``tsconfig.json` 共同配置)。
- **缺失资源**: `Sidebar.tsx` 引用了 `/Logo.png`,但项目根目录无此文件,运行时会 404。
---
## AI Studio 特定配置
- `vite.config.ts` 中通过 `loadEnv` 加载环境变量,并将 `GEMINI_API_KEY` 注入到 `process.env.GEMINI_API_KEY`
- AI Studio 会在部署时自动注入 `GEMINI_API_KEY``APP_URL`
- `DISABLE_HMR` 环境变量用于在 AI Studio agent 编辑模式下关闭 HMR避免界面闪烁。**请勿修改此逻辑。**
- `.env.example` 包含 `GEMINI_API_KEY``APP_URL`,说明这些值由 AI Studio 注入
- `vite.config.ts` 通过 `loadEnv``GEMINI_API_KEY` 注入到 `process.env.GEMINI_API_KEY`
- `vite.config.ts` 中的 `DISABLE_HMR` 逻辑用于关闭 HMR避免 AI Studio agent 编辑时闪烁。**不要随意修改该逻辑。**