.cursorrules 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. # Tolan AI 英语语音助手 - 开发规则
  2. ## 项目概述
  3. - **后端**: FastAPI + Python 3.11+ + WebSocket
  4. - **前端**: React 18 + TypeScript + Vite + Ant Design
  5. - **AI服务**: 阿里云 DashScope (STT/LLM/TTS)
  6. - **架构**: 实时语音对话流水线 (STT → LLM → TTS)
  7. ---
  8. ## 代码生成原则
  9. ### 文档规则
  10. - ❌ 避免生成冗长的 Markdown 文档
  11. - ✅ 代码优先,注释简洁
  12. - ✅ 必要文档:配置说明、API定义(列表形式)
  13. - ✅ 文档格式:表格 > 列表 > 段落
  14. - ✅ **文档生成路径规则**:
  15. - 项目文档 → `项目文档/` 目录
  16. - 后端文档 → `backend/文档/` 目录
  17. - 前端文档 → `frontend/docs/` 目录(如需要)
  18. - 配置文档 → 对应模块根目录(如 `backend/ENV_CONFIG.md`)
  19. ### 代码风格
  20. - **Python**: 类型提示、async/await、异常处理必需
  21. - **TypeScript**: 严格类型、接口定义、错误边界
  22. - **命名**: 英文、语义化、统一风格(snake_case/camelCase)
  23. - **注释**: 仅对复杂逻辑、性能优化、临时方案添加
  24. ---
  25. ## 后端开发规则
  26. ### FastAPI 结构
  27. ```python
  28. # 路由定义
  29. @app.websocket("/ws")
  30. async def endpoint(websocket: WebSocket): ...
  31. # 类型定义(必需)
  32. from pydantic import BaseModel
  33. class Message(BaseModel):
  34. type: str
  35. data: dict
  36. ```
  37. ### 异步编程
  38. - 所有 I/O 操作使用 `async/await`
  39. - 使用 `asyncio.to_thread()` 包装同步库
  40. - WebSocket 消息处理异步化
  41. ### 配置管理
  42. - 所有配置从 `.env` 读取
  43. - 使用 `pydantic-settings` 管理
  44. - 必需项无默认值,可选项提供默认值
  45. ### AI 服务集成
  46. ```python
  47. # DashScope API 调用模式
  48. import dashscope
  49. dashscope.api_key = settings.DASHSCOPE_API_KEY
  50. # 流式处理
  51. async for chunk in ai_service.generate_stream(text):
  52. await websocket.send_json(chunk)
  53. ```
  54. ### 错误处理
  55. ```python
  56. try:
  57. result = await api_call()
  58. except Exception as e:
  59. logger.error(f"Error: {e}")
  60. await send_error(request_id, str(e))
  61. ```
  62. ---
  63. ## 前端开发规则
  64. ### React + TypeScript
  65. ```typescript
  66. // 类型定义(必需)
  67. interface Message {
  68. type: string;
  69. requestId: string;
  70. data: unknown;
  71. }
  72. // 组件定义
  73. export const Component: React.FC<Props> = ({ ... }) => {
  74. // Hooks
  75. const [state, setState] = useState<Type>(initial);
  76. // 处理函数
  77. const handleEvent = useCallback(() => { ... }, [deps]);
  78. return <UI />;
  79. };
  80. ```
  81. ### WebSocket 通信
  82. ```typescript
  83. // 发送消息
  84. ws.send(JSON.stringify({ type, requestId, data }));
  85. // 接收消息
  86. ws.onmessage = (event) => {
  87. const message: Message = JSON.parse(event.data);
  88. handleMessage(message);
  89. };
  90. ```
  91. ### 状态管理 (Zustand)
  92. ```typescript
  93. export const useStore = create<State>((set) => ({
  94. value: initial,
  95. update: (newValue) => set({ value: newValue }),
  96. }));
  97. ```
  98. ### 音频处理
  99. ```typescript
  100. // 录音
  101. const recorder = new Recorder({ sampleRate: 16000 });
  102. recorder.start();
  103. // Base64 编码
  104. const base64 = btoa(String.fromCharCode(...audioData));
  105. ```
  106. ### 移动端适配 (postcss-px-to-viewport)
  107. #### 配置信息
  108. - **设计稿基准**: 375px × 812px (iPhone X/11/12/13/14)
  109. - **转换单位**: vw (viewport width)
  110. - **自动转换**: CSS 文件中的 px 自动转换为 vw
  111. - **排除目录**: node_modules(第三方组件不转换)
  112. #### 核心规则
  113. ##### ✅ CSS 文件样式(自动转换)
  114. ```css
  115. /* 直接写 px,插件自动转换为 vw */
  116. .box {
  117. width: 100px; /* → 26.66667vw */
  118. padding: 16px; /* → 4.26667vw */
  119. font-size: 14px; /* → 3.73333vw */
  120. }
  121. ```
  122. ##### ✅ 内联样式(手动转换)
  123. ```typescript
  124. import { px2vw } from '@/utils/viewport';
  125. // 必须使用 px2vw() 函数
  126. <div style={{
  127. width: px2vw(100), // ✅ 正确
  128. padding: px2vw(16), // ✅ 正确
  129. }}>
  130. // ❌ 错误:内联样式不会自动转换
  131. <div style={{ width: '100px' }}> // 保持 100px
  132. ```
  133. ##### ✅ 固定尺寸元素(不转换)
  134. ```css
  135. /* 使用大写 PX 避免转换 */
  136. .icon {
  137. width: 20PX; /* 保持 20px */
  138. height: 20PX; /* 保持 20px */
  139. }
  140. /* 或添加 .ignore 类 */
  141. .ignore {
  142. width: 20px; /* 不会转换 */
  143. }
  144. ```
  145. ##### ❌ 1px 边框问题
  146. ```css
  147. /* 当前配置:1px 会转换为 vw */
  148. /* 如需真实 1px(如分隔线),使用大写 PX */
  149. .divider {
  150. border: 1PX solid #ddd; /* 保持 1px */
  151. }
  152. /* 或使用 transform 方案 */
  153. .hairline::after {
  154. height: 1px;
  155. transform: scaleY(0.5);
  156. }
  157. ```
  158. #### 使用场景
  159. ##### 场景 1: 动态计算的内联样式
  160. ```typescript
  161. // ❌ 错误
  162. const width = 100;
  163. <div style={{ width: `${width}px` }}>
  164. // ✅ 正确
  165. import { px2vw } from '@/utils/viewport';
  166. const width = 100;
  167. <div style={{ width: px2vw(width) }}>
  168. ```
  169. ##### 场景 2: 响应式最大/最小值
  170. ```css
  171. /* 结合使用 vw 和固定值 */
  172. .container {
  173. width: 100vw;
  174. max-width: 430PX; /* 使用 PX 限制最大宽度 */
  175. }
  176. ```
  177. ##### 场景 3: 媒体查询
  178. ```css
  179. /* 媒体查询中的 px 不会转换(已配置 mediaQuery: false) */
  180. @media (min-width: 768px) {
  181. .content {
  182. max-width: 430px; /* 保持 px */
  183. }
  184. }
  185. ```
  186. ##### 场景 4: TouchTarget 最小尺寸
  187. ```typescript
  188. // Apple HIG 标准:最小 44px
  189. // 这些 px 会被转换为 vw
  190. <button style={{
  191. minWidth: px2vw(44),
  192. minHeight: px2vw(44),
  193. }}>
  194. ```
  195. ##### 场景 5: iOS 安全区域
  196. ```css
  197. /* env() 函数不受影响,正常工作 */
  198. .safe-area {
  199. padding-top: env(safe-area-inset-top);
  200. padding-bottom: env(safe-area-inset-bottom);
  201. }
  202. ```
  203. #### 禁止事项
  204. - ❌ 在 JS 中硬编码 vw 值
  205. - ❌ 混用多种适配方案(rem + vw)
  206. - ❌ 在媒体查询中依赖 px 自动转换
  207. - ❌ 内联样式忘记使用 px2vw()
  208. - ❌ 修改第三方组件样式(使用其原生方案)
  209. #### 调试技巧
  210. ```bash
  211. # 1. 查看转换后的 CSS
  212. npm run build
  213. # 检查 dist/assets/*.css
  214. # 2. 开发模式实时查看
  215. npm run dev
  216. # 浏览器开发者工具 → 检查元素 → Computed 样式
  217. # 3. 临时禁用转换
  218. /* px-to-viewport-ignore-next */
  219. .special {
  220. width: 100px; /* 这行不会转换 */
  221. }
  222. ```
  223. #### 工具函数
  224. ```typescript
  225. // frontend/src/utils/viewport.ts
  226. import { px2vw, px2vh } from '@/utils/viewport';
  227. px2vw(100) // → '26.66667vw' (基于 375px)
  228. px2vh(100) // → '12.31527vh' (基于 812px)
  229. ```
  230. ---
  231. ## API 集成规则
  232. ### DashScope STT
  233. - 模型: `qwen3-asr-flash-realtime`
  234. - 格式: PCM 16kHz mono
  235. - 流式识别: WebSocket API
  236. ### DashScope LLM
  237. - 模型: `qwen-turbo/qwen-plus`
  238. - 模式: 流式输出 (stream=True)
  239. - 提示词: 简化版优先
  240. ### DashScope TTS
  241. - 模型: `qwen3-tts-flash`
  242. - 音色: `Cherry` (女声)
  243. - 输出: PCM 24kHz
  244. - 速率控制: `TTS_MIN_INTERVAL` 避免限流
  245. ---
  246. ## WebSocket 消息协议
  247. ### 客户端 → 服务端
  248. ```typescript
  249. { type: "audio_chunk", requestId, data: base64, chunkIndex, sampleRate }
  250. { type: "audio_end", requestId, totalChunks }
  251. { type: "cancel", requestId }
  252. ```
  253. ### 服务端 → 客户端
  254. ```typescript
  255. { type: "text_result", requestId, text, isFinal }
  256. { type: "emotion", requestId, emotion }
  257. { type: "audio_response", requestId, data: base64, chunkIndex, isLast }
  258. { type: "error", requestId, code, message }
  259. ```
  260. ---
  261. ## 性能优化规则
  262. ### 后端优化
  263. - TTS 连接预热(启动时)
  264. - HTTP/2 连接池(httpx)
  265. - 流式处理(LLM+TTS 并行)
  266. - 动态超时(根据 STT 更新时间)
  267. ### 前端优化
  268. - 音频流式播放(边接收边播放)
  269. - WebSocket 断线重连
  270. - 组件懒加载 (React.lazy)
  271. - 防抖/节流(用户交互)
  272. ---
  273. ## 文档生成规范
  274. ### 文档类型与路径
  275. | 文档类型 | 生成路径 | 示例 |
  276. |---------|---------|------|
  277. | 项目架构/设计文档 | `项目文档/` | `项目文档/系统架构设计.md` |
  278. | API 文档 | `项目文档/` | `项目文档/前后端交互API文档.md` |
  279. | 需求文档 | `需求/` | `需求/用户需求.md` |
  280. | 后端技术文档 | `backend/文档/` | `backend/文档/数据库设计.md` |
  281. | 后端配置说明 | `backend/` | `backend/ENV_CONFIG.md` |
  282. | 前端技术文档 | `frontend/docs/` | `frontend/docs/组件设计.md` |
  283. | 部署运维文档 | `项目文档/` | `项目文档/部署指南.md` |
  284. ### 文档命名规则
  285. - 使用中文文件名(项目主要使用中文)
  286. - 描述清晰、简洁
  287. - 格式:`功能描述.md`
  288. ---
  289. ## 常见任务模板
  290. ### 添加新的 WebSocket 消息类型
  291. 1. `backend/models.py`: 定义 Pydantic 模型
  292. 2. `backend/websocket_handler.py`: 添加处理函数
  293. 3. `frontend/src/types/index.ts`: 定义 TypeScript 类型
  294. 4. `frontend/src/utils/websocket.ts`: 添加发送/接收逻辑
  295. ### 添加新的环境变量
  296. 1. `backend/.env`: 添加配置项
  297. 2. `backend/config.py`: 定义 Settings 字段
  298. 3. `backend/ENV_CONFIG.md`: 更新文档(仅列表)
  299. ### 添加新的 UI 页面
  300. 1. `frontend/src/pages/`: 创建页面组件
  301. 2. `frontend/src/router/`: 添加路由
  302. 3. 使用 Ant Design 组件,保持风格统一
  303. ### 生成技术文档
  304. 1. 确定文档类型(架构/API/配置/需求等)
  305. 2. 根据上表选择正确的生成路径
  306. 3. 使用表格格式(优先)
  307. 4. 内容控制在 100 行以内
  308. ---
  309. ## 禁止事项
  310. - ❌ 生成超过 100 行的 Markdown 文档
  311. - ❌ 在代码中使用 `any` 类型(TypeScript)
  312. - ❌ 同步阻塞 I/O 操作(后端)
  313. - ❌ 硬编码 API 密钥或敏感信息
  314. - ❌ 未处理的 Promise rejection
  315. - ❌ 未添加类型提示的函数(Python/TS)
  316. ---
  317. ## 调试技巧
  318. ```python
  319. # 后端日志
  320. print(f"[{request_id}] 🔍 Debug: {variable}")
  321. # 性能测试
  322. import time
  323. start = time.time()
  324. # ... code ...
  325. print(f"⏱️ Duration: {(time.time() - start)*1000:.1f}ms")
  326. ```
  327. ```typescript
  328. // 前端日志
  329. console.log('[WebSocket] Received:', message.type);
  330. console.time('Processing');
  331. // ... code ...
  332. console.timeEnd('Processing');
  333. ```