--- id: ai-recognize name: AI 照片识别填充表单 area: rn-app status: planned --- ## 概述 用户在物品登记表单中,选取当前物品的一张或多张照片,可附加文字补充描述,发送给通义千问 Qwen-VL-Max 进行多模态识别。AI 返回富文本描述后,用户在回复文本中框选任意片段,点击字段注入按钮将其填入表单对应字段(名称、规格、单价、单位、备注)。 ## 技术选型 - **API**:通义千问 Qwen-VL-Max(阿里云灵积 DashScope,OpenAI 兼容格式) - **多图策略**:多轮对话叠加(每轮可追加图片或文字) - **选区注入**:TextInput selectable + lastSelectionRef(blur 安全) - **图片压缩**:expo-image-manipulator(600px / 0.65 质量,约 60KB/张) - **密钥存储**:expo-secure-store ## 新增依赖 ``` expo-secure-store # API Key / access_token 加密存储 expo-image-manipulator # 识别前压缩图片 ``` ## 任务卡 ### 📋 通义千问 API 服务层 - status: todo - complexity: S - files: apps/property-survey/src/services/qianfanAI.ts - acceptance: 封装 DashScope OpenAI 兼容接口;getApiKey() / saveApiKey() 读写 SecureStore;chat(messages) 发送多轮对话(支持 image_url + text content 混合);token 不存在时抛出带中文提示的 Error;网络/鉴权失败统一抛出可读错误;导出 QianfanMessage 类型 ### 📋 API Key 设置 UI - status: todo - complexity: S - files: apps/property-survey/src/features/settings/QianfanApiSettings.tsx - depends: ai-recognize - acceptance: 含 API Key 输入框(secureTextEntry)和保存按钮;「测试连接」按钮调 qianfanAI.chat 发一条纯文字 ping,成功显示绿色"连通 ✓",失败显示红色错误原因;组件可独立渲染,供后续接入设置入口 ### 📋 useAiRecognize hook - status: todo - complexity: M - files: apps/property-survey/src/features/recognize/useAiRecognize.ts - depends: ai-recognize - acceptance: 接收 photos: Photo[] 参数;管理 selectedIds(togglePhoto 切换)、hint 文字、messages 历史、loading、response、error 状态;lastSelectionRef 仅在 end>start 时更新(blur 不清空);getSelectedText() 从 lastSelectionRef 提取 response 子串;recognize() 将选中照片 filePath 用 expo-image-manipulator 压缩后转 base64,拼入 messages,调 qianfanAI.chat,追加 AI 回复到 messages 并更新 response;未配置 API Key 时 throw 并在 error 中显示引导文字;支持多轮追加(再次调 recognize() 时保留 messages 历史) ### 📋 AI 识别面板 UI - status: todo - complexity: M - files: apps/property-survey/src/features/recognize/AiRecognizePanel.tsx, apps/property-survey/src/features/recognize/PhotoSelector.tsx, apps/property-survey/src/features/recognize/InjectionToolbar.tsx - depends: ai-recognize - acceptance: AiRecognizePanel 为全屏 Modal;顶部 PhotoSelector 横向滚动展示照片缩略图,选中项右上角绿色勾;hint TextInput 单行(placeholder:"补充描述,如品牌型号…");「识别」按钮 loading 时显示 ActivityIndicator;response 区域用 multiline TextInput(editable=false,selectable=true,onSelectionChange 更新 hook);底部 InjectionToolbar 固定显示「→名称」「→规格」「→单价」「→单位」「→备注」5个按钮,点击时调 onInject(field, selectedText);selectedText 为空时按钮 disabled;注入成功后对应按钮短暂变绿(300ms);error 非空时在 response 区上方显示红色提示条 ### 📋 表单接线 - status: todo - complexity: S - files: apps/property-survey/src/features/item/useItemForm.ts, apps/property-survey/src/features/item/ItemFields.tsx - depends: ai-recognize - acceptance: useItemForm 新增 injectField(key: keyof FormState, value: string) 方法(调 setField 并标记 isDirty);ItemFields 照片列顶部「照片」标题行右侧增加「✨ AI」文字按钮(仅 photoItemId 非空时显示);点击打开 AiRecognizePanel Modal,传入当前物品照片列表和 injectField 回调;注入后 Modal 保持打开,用户可继续框选其他字段 ## 决策记录 - 选用通义千问 Qwen-VL-Max:阿里电商生态训练数据对家具/家电/品牌商品识别最准,且价格为同档位最低(约 0.003元/千token) - 多轮对话而非一次性多图:允许用户逐步追加照片和追问,比一次提交更灵活 - TextInput selectable 方案而非分句 Chip:原生文本选择体验与系统一致,且支持任意粒度框选(如只选价格数字);用 lastSelectionRef 解决 blur 丢选区问题 - 图片压缩到 600px/0.65:实测约 55-70KB/张,3张约 200KB,在 4MB API 限制内且不明显影响识别准确度