# FFAI Agent — Phase 3 PRD（iOS / Android 原生 App）

> **状态**：🟡 v0.2 — 架构反推已填；时间 / 平台优先级 / 分发渠道 / SSO 方案待 Phase 2 GA 后定
>
> **本文档定位**：Phase 3（移动端原生 App）的**详细产品需求**。
>
> **触发条件**（与 + 关系，全满足）：
> 1. Phase 2 GA + Phase 2 north star 达标 ≥ 30 天
> 2. 移动端有清晰刚需信号（如 Phase 2 Teams 推送在移动设备上的转化率 > X%，或内测调研明确表达"通勤/现场"场景痛点）
> 3. 不为做而做：如果 Phase 2 后用户都在 Desktop 完成工作，移动端可推迟
>
> **上游**：[`01-prd.md`](./01-prd.md) §5.3
>
> **技术对应**：[`02-architecture.md`](./02-architecture.md) §8 PR16+（待补）+ §1.1.1 Phase 3 移动端壳设计

---

## 1. Phase 3 北极星与退出标准

> ❓ 阈值待 Phase 2 GA 数据 + 移动端调研出来后填。

| 指标 | MVP（单端 PR16）| GA（双端 + 完整功能）|
|---|---|---|
| **移动端 DAU / 总 DAU** | ≥ ❓ % | ≥ ❓ % |
| **审批 push 7 日响应率**（推送 → 用户处理动作）| ≥ ❓ % | ≥ ❓ % |
| **移动端独有场景使用次数 / 周**（拍照 OCR / 语音 / Live Activity）| ≥ ❓ | ≥ ❓ |
| App Store / Play Store 评分（如内部分发则 NPS）| ≥ 4.2 | ≥ 4.5 |
| 移动端崩溃率 | < 0.5% | < 0.2% |
| 首字延迟 p50（移动网络 4G+）| < 2s | < 1.5s |
| Phase 1 + Phase 2 north star 持续达标 | ≥ 各自 GA 阈值 | 同上 |
| 跨组织数据泄漏事件 | **0** | **0** |

---

## 2. 用户故事（按优先级 P0 / P1 / P2）

### 2.1 P0（MVP 必备 — 移动端最高频场景）

| # | Persona | 故事 | 平台 |
|---|---|---|---|
| **US-701** | 管理者 | 我想在手机收到审批 push（应用未打开也能推），点击 → 直接看摘要 → 点 "批准" 按钮 1 秒完成 | iOS + Android |
| **US-702** | 业务员工 | 我想出差路上用手机问 "我下周有几个会"，agent 回复像 Web 一样准确 | iOS + Android |
| **US-703** | 全部 | 我想跟 Web/Desktop 用同一个账号登录，会话和工件全同步 | iOS + Android |
| **US-704** | 业务员工 | 我想拍一张报销小票照片发给 agent，agent OCR 识别金额/类目 → 帮我提报销 | iOS + Android |
| **US-705** | 全部 | 我想 agent 长任务跑后台（关 App 也跑），完成时 push 通知唤回 | iOS（BackgroundTasks）+ Android（Foreground Service）|

### 2.2 P1（GA 前补齐 — 平台原生体验）

| # | Persona | 故事 | 平台 |
|---|---|---|---|
| **US-801** | 全部 | 我想用 Siri 说 "Hey Siri，FFAI 我下周有几个会" 直接跑 agent | iOS（Siri Shortcut + Intent）|
| **US-802** | 全部 | 我想 agent 长任务进度显示在 Live Activity / Dynamic Island（iPhone 14+）| iOS（ActivityKit）|
| **US-803** | 全部 | 我想从 Android 通知栏 Quick Settings tile 一键唤起 agent | Android（QSTile）|
| **US-804** | 项目经理 | 我想在手机上语音输入 "上季度延期项目"，agent 听懂后给表格 | iOS（Speech.framework）+ Android（SpeechRecognizer）|
| **US-805** | 业务员工 | 我想分享其他 App 内容（如邮件正文）到 FFAI，agent 帮我整理/归档 | iOS（Share Extension）+ Android（Share Intent）|
| **US-806** | 全部 | 我想离线时仍能看历史会话（只读），网络恢复后自动同步 | iOS + Android |

### 2.3 P2（GA 后再上）

- US-901: Apple Watch 复杂功能（审批快捷批/驳）
- US-902: Wear OS 同上
- US-903: iPad 适配（Split View / Stage Manager / Apple Pencil 注释）
- US-904: macOS Catalyst 复用 iPad 代码（如果 Phase 2 Electron 后想重做 native macOS）

---

## 3. 功能清单（架构 §8 PR16+ 待补，下表为 PRD 反推的 PR 拆分建议）

| PR | 功能模块 | 用户可见行为 | 优先级 | 验收 |
|---|---|---|---|---|
| **PR16** | iOS 原生壳 + 独立 A2UI Swift renderer + Swift HostBridge + SSO 登录 + 会话 / 消息流 + **iOS Data Protection Class A**（设备锁屏后磁盘加密）+ **远程失效信号**（启动时 attestation：服务端撤销 session → 本地清缓存 + 强制重新登录）+ **MDM remote wipe 兼容** | iOS App 装包跑通三件套 + 设备遗失场景验证 | P0 | US-702/703 + remote wipe 5 分钟内本地数据不可读 |
| **PR16.5** | Android 原生壳 + Compose A2UI renderer + Kotlin HostBridge + 同 PR16 功能 + **EncryptedSharedPreferences + Keystore**（敏感数据加密）+ 同 remote wipe + attestation 流程 | Android App 装包跑通三件套 | P0 | US-702/703（Android 端）+ remote wipe 同 PR16 |
| **PR17** | iOS APNs + Android FCM 推送链路 + 后端 push service + **payload 安全**（仅 deep link + 无敏感数据；锁屏不显金额/姓名/项目，仅"你有新审批"通用文案；明细必须 App 启动后拉）+ **i18n 双语 push 文案** | 审批 push 端到端，点击拉起对应会话 | P0 | US-701 + 锁屏 payload 不含 PII 测试 |
| **PR17.5** | 后台长任务（iOS BackgroundTasks / Android Foreground Service）| 关 App 也能跑长任务，完成 push 唤回 | P0 | US-705 |
| **PR18** | 拍照 OCR 工具（client:camera + client:vision_local）+ 报销/小票场景模板 | US-704 | P0 | US-704 |
| **PR19** | iOS Live Activity / Dynamic Island + Android QSTile | 系统级一等公民集成 | P1 | US-802/803 |
| **PR20** | Siri Shortcut（App Intent） + Android Assistant Action | 语音唤起 agent | P1 | US-801 |
| **PR21** | iOS Speech.framework + Android SpeechRecognizer | 输入框语音转文字 | P1 | US-804 |
| **PR22** | Share Extension（iOS）+ Share Intent（Android）| 系统分享菜单接入 FFAI | P1 | US-805 |
| **PR23** | 离线读模式 + 增量同步（CoreData / Room 本地存储 + 后端 lastMessageId 增量拉）+ **跨 org evict / 用户登出强清**（INV-1 在移动端本地缓存场景的延伸：设备遗失 + 多 org 切换是真实威胁）| US-806 | P1 | US-806 + 跨 org 切换缓存清除测试 |

> ⚠️ Phase 3 PR 数量比 Phase 1/2 少很多——大部分功能复用 Phase 1 backend；移动端只新增"端原生壳 + push + 平台特性集成"。

---

## 4. UI 与交互（详见 05-ui-interaction-spec.md，Phase 3 增量）

### 4.1 平台原生设计语言（不沿用 Lark DS）

| 平台 | 设计语言 | 理由 |
|---|---|---|
| **iOS** | Apple HIG（Human Interface Guidelines）+ SwiftUI 标准组件 | 用户习惯 / App Store 审核 / 系统集成（Live Activity / Siri）|
| **Android** | Material You（Material Design 3）+ Jetpack Compose 标准组件 | 同上 |

**为什么不复用 Lark DS**：移动端 Lark DS 投影成本高 + 跟系统其他 App 体验割裂；G3/G9 已锁全原生路线（02-architecture §1.1.1 决策依据）。

### 4.2 关键页面（与 Web 三栏架构的对应）

| Web 三栏 | iOS 实现 | Android 实现 |
|---|---|---|
| 左栏（会话/项目）| Tab Bar 第一个 tab → 会话列表（NavigationStack）| Bottom Navigation 第一个 → RecyclerView 会话列表 |
| 中间（对话流）| 点击会话进入详情页 → SwiftUI ScrollView + LazyVStack | 同上 → Compose LazyColumn |
| 右栏（Display Panel）| 详情页底部 sheet（PresentationDetents）/ 横屏拆 SplitView | BottomSheet / 横屏 PaneScaffold |

### 4.2.1 移动端 a11y（review 反馈，原 PRD 零提及）

INV-6 + Phase 1 §4 a11y 约束在移动端必须有等价投影：

| iOS | Android | 范围 |
|---|---|---|
| **VoiceOver** 完整支持 | **TalkBack** 完整支持 | 所有交互元素 accessibility label + traits/role 标注 |
| **Dynamic Type** 字号缩放（最大 AX5）| **Font scale**（最大 1.3×）| 所有文本响应式，Display Panel 表格 / 卡片不溢出 |
| **Reduce Motion** 关闭 Live Activity 动画 | **Reduce Motion** 同 | 流式打字、增量推送、Live Activity 入场动画都可关 |
| **Increase Contrast** ≥ WCAG AA（4.5:1 / 3:1 大字）| 同 | 默认主题 + 高对比主题双套 |
| **Voice Control** 说"批准"等同点按钮 | **Voice Access** 同 | 审批 push 卡片按钮可语音操作 |

**i18n 范围**（CI 门禁，跟 Phase 1 同等强约束）：
- iOS：`Localizable.strings` zh-Hans + en；`InfoPlist.strings` 双语；Siri Intent phrases 双语
- Android：`strings.xml` 默认 + `values-en/strings.xml`；Quick Settings tile 名 + 描述双语
- Push 通知文案：APNs `localized-key` + Android `localizationKey`，server 不下发 raw text 仅 key
- Live Activity / Dynamic Island：所有可见字段双语 key
- CI gate：每个 PR16-PR23 启动前跑 i18n missing key linter，任一缺失 = block release

### 4.3 移动端独有交互

- **拍照 OCR**：相机权限 → 拍 → on-device 预 OCR（Vision.framework / ML Kit）→ 结果送 LLM 二次结构化
- **Push 唤起**：APNs/FCM 携带 deep link（`ffai://session/<id>`）→ App 启动直达
- **Live Activity（iOS 16+）**：长任务在锁屏 / Dynamic Island 显示进度
- **语音输入**：长按麦克风按钮 → on-device 转文字 → 用户编辑后发送

### 4.4 移动端 vs Web 体验差异（明确取舍）

| 功能 | Web/Desktop | Mobile | 理由 |
|---|---|---|---|
| Plan / Permission mode 切换 | UI toggle | 设置页（不放主路径）| 移动端用户少改 mode，藏深 |
| 工件库（AgentArtifact）| 独立 tab | 详情页底部 sheet | 移动端屏幕小 |
| Sub-agent 后台任务列表 | 右栏 Task 卡片 | Tab Bar 单独 tab "任务" | 移动端 push 是主入口 |
| Slash command | 输入框 `/` 触发 | 长按 + 工具栏菜单 | 移动键盘体验 |
| Cron 配置 | admin Web 后台 | **不做**（Web 改）| 移动端不是 admin 场景 |

---

## 5. 数据模型（详见 02-architecture §5）

### 5.1 Phase 3 新增表

| 表 | 用途 | PR |
|---|---|---|
| `MobileDevice` | 用户设备注册（platform / push_token / app_version / last_active）| PR17 |
| `MobilePushSubscription` | 用户推送偏好（哪些事件类型 push）| PR17 |
| `MobileSyncCheckpoint` | 离线增量同步 checkpoint（lastMessageId per device）| PR23 |

### 5.2 Phase 1/2 表的 Phase 3 字段扩展

| 表 | 新字段 | PR |
|---|---|---|
| `AgentSession` | `surface` enum 增 `'mobile-ios' \| 'mobile-android'` | PR16/16.5 |
| `AgentArtifact` | `mobileOcrSource: bool`（标记由移动端 OCR 入库）| PR18 |

---

## 6. API 契约（Phase 3 增量）

| Endpoint | Method | 用途 | PR |
|---|---|---|---|
| `/api/agent/mobile/devices` | POST | 设备注册（启动时上报）| PR17 |
| `/api/agent/mobile/devices/:id` | DELETE | 注销设备（用户登出）| PR17 |
| `/api/agent/mobile/push/subscriptions` | GET / POST | 用户 push 偏好管理 | PR17 |
| `/api/agent/mobile/sync/incremental` | GET | 增量同步（query: `since=<lastMessageId>`）| PR23 |
| `/api/agent/mobile/ocr/extract` | POST | 服务端 OCR 后处理（结构化）| PR18 |

**push 协议**：APNs（Apple Push Notification service） + FCM（Firebase Cloud Messaging）。

**`packages/agent-protocol/` Swift / Kotlin codegen**：
- TypeScript 定义为源
- CI 跑 codegen → Swift Package + Kotlin Multiplatform 模块
- 协议版本不一致 = build 失败

---

## 7. 验收标准（按 US 编号，BDD）

### US-701 验收
```gherkin
Given 我是审批人，已安装 FFAI iOS App + 启用 push 通知
And 组织 push 偏好开启 "审批"
When 有人提交一个差旅审批
Then 5 秒内我手机收到 APNs push（含审批人 / 金额 / 项目摘要）
When 我点 push
Then App 启动直达该审批详情页（deep link 生效）
And 我点 "批准" 按钮，1 秒内完成 Approval.act 调用
And 卡片更新为 "已批准"
```

### US-702 验收
```gherkin
Given 我有 iOS App + 在 4G 网络
When 我说 "上季度延期项目按延期天数排"
Then 2 秒内首字到（p50 < 2s）
And 完整表格在底部 sheet 渲染（移动端版 Display Panel）
And 点击项目名跳到项目详情（移动端导航栈）
And 跟 Web 看到的内容一致（INV-1 + INV-8）
```

### US-704 验收
```gherkin
Given 我有 iOS App + 已绑定 OneDrive backend
When 我点 "+" → 拍照 → 拍一张报销小票
Then on-device Vision 框架预提取金额/日期/商家
And 结果送 LLM 二次结构化（类目 / 项目归属推断）
And agent 生成报销表单预览，我点 "提交" 调 Approval.submit
And 原始照片 + OCR 结果存到我 OneDrive (PR10.5 复用)
```

### US-705 验收
```gherkin
Given 我让 agent 跑 "汇总下属本周工作"（sub-agent 长任务）
When 我切到别的 App / 锁屏
Then iOS BackgroundTasks 继续跑（≤ 30 秒后转后端 sub-agent 接管）
When 任务完成（10 分钟后）
Then 我收到 push "汇总完成"
When 我点 push
Then App 启动直达汇总结果工件
```

剩余 US 验收待对应 PR 启动时补。

---

## 8. 测试策略（Phase 3 增量）

| 层 | Phase 3 重点 |
|---|---|
| **L0a/L0b** | Swift / Kotlin codegen 后类型与 TypeScript 源一致 |
| **L1** | push 端到端（mock APNs/FCM）+ OCR 后处理 + 增量同步边界 |
| **L2 设备测试** | iOS：iPhone 13 / 14 / 15 / iPad；Android：Pixel 6+ / Samsung S22+ / 折叠屏（如 Z Fold）|
| **L3 真机灰度** | 内部 50 人 TestFlight / Google Internal Testing 跑 2 周 |
| **平台特性测试** | Live Activity / Siri / QSTile / Share Extension 各自有手动验收脚本 |

**新增专项**：
- 离线 → 在线同步压测（10k 消息差量同步不卡顿）
- push 在不同 OS 版本（iOS 16/17/18 + Android 13/14/15）通知样式一致性
- on-device OCR 准确率 ≥ 90%（10 类典型小票样本）

---

## 9. 上线计划与里程碑

> ❓ 时间窗待 Phase 2 GA 数据 + 平台优先级决策后填。

| 里程碑 | 范围 | 用户 | 退出标准 |
|---|---|---|---|
| **M9 平台优先级 + 调研** | 不写代码 | 内部 | 决策"先 iOS 还是先 Android" + 内测部门移动端使用习惯调研 |
| **M10 单端 Alpha** | PR16 或 PR16.5（看 M9 决策）+ PR17 | 内部 ~10 人 | 三件套 + push 跑通 |
| **M11 单端 Beta** | + PR17.5 + PR18 | 内测部门 ~50 人 | 拍照 OCR + 后台任务稳定 |
| **M12 单端 GA** | + PR19-PR23 | 全员（看分发渠道）| Phase 3 north star 单端达标 ≥ 30 天 |
| **M13 第二端 Alpha** | PR16 或 PR16.5（另一端，复用后端）| 内部 ~10 人 | 同 M10 标准 |
| **M14 双端 GA** | 同 M12 | 全员 | 双端 north star 达标 |

---

## 10. 关键决策待定（M9 阶段必须回答）

| # | 问题 | 候选 | 影响 |
|---|---|---|---|
| **MD-1** | **iOS / Android 谁先做**？双端并行还是单端验证后再做第二端？ | (a) iOS 先（公司高管 iPhone 多）/ (b) Android 先（一线员工 Android 多）/ (c) 双端同步 | 决定 PR16 vs PR16.5 顺序 + 团队人力 |
| **MD-2** | **公司分发渠道** | iOS：Enterprise Cert / TestFlight / Apple Business Manager / 公司 MDM；Android：APK 自分发 / 公司 MDM / Play Store 内部测试 | 决定签名/审核流程 + 上线节奏 |
| **MD-3** | **SSO 接入方式** | OIDC / SAML / 公司自有 SSO SDK | 决定登录链路 + 用户体验 |
| **MD-4** | **是否走中国大陆原生推送服务**（国内 Android push 需要厂商通道：华为/小米/OPPO/vivo）— **建议 Phase 1 GA 时就启动调研 + 到达率 PoC**（不是 M9 才决；Phase 3 north star 直接依赖此） | 接 / 不接（仅 FCM）/ 通过聚合 SDK（如个推/极光）| 决定 push 到达率 + Phase 3 north star 是否能达标 |
| **MD-5** | **OCR 是 on-device 还是云端** | (a) on-device（Vision/ML Kit，隐私好）/ (b) 云端（更准）/ (c) 混合（先 on-device，复杂走云端）| 决定 PR18 实现 + 隐私合规 |

---

## 11. 风险与依赖

### 11.1 Phase 3 特有依赖

| 依赖 | 状态 | 缺则影响 | 责任方 |
|---|---|---|---|
| Apple Developer 企业账号 / App Store 账号 | 待申请 | 无法分发 iOS App | 公司 IT / 法务 |
| Google Play Console / Android 签名 | 待申请 | 无法分发 Android App | 同上 |
| APNs 推送证书 / FCM 项目 | 待申请 | push 完全不通 | DevOps |
| 公司 MDM 系统 | 现状未知 | 影响企业分发模式选择 | IT |
| iOS / Android 原生工程师 | 待招/外包 | 无法实施 | HR |
| 跟 Phase 1 + Phase 2 backend 完全兼容（API 不破坏）| 既有约束 | 移动端跟桌面/Web 行为不一致 | Phase 1/2 owner |

### 11.2 Phase 3 特有风险

| 风险 | 影响 | 缓解 |
|---|---|---|
| App Store / Play Store 审核被拒 | 上线延后数周 | 提前准备隐私清单（Privacy Manifest）+ 数据使用声明 |
| 国内 Android push 到达率低（默认 FCM 在国内不可达）| 核心 push 场景失效 | MD-4 评估走厂商通道或聚合 SDK |
| 移动端跟 Web/Desktop 体验不一致（INV-8 受挑战）| 用户感知割裂 | A2UI 协议强制；packages/agent-protocol Swift/Kotlin codegen CI 卡死 |
| 原生开发周期长 / 双端工作量翻倍 | 上线慢 | MD-1 单端验证后再做第二端，不双端并行 |
| OCR 准确率不够 → 用户失望 | 报销场景差评 | day-1 选高识别率场景（小票/会议名片）+ 留兜底人工编辑 |
| iOS BackgroundTasks 限额收紧 / Android Foreground Service 通知必现 | UX 受限 | 长任务以 push 为主，本地后台仅短窗 hand-off |

---

## 12. 文档版本

| 版本 | 日期 | 变更 | 作者 |
|---|---|---|---|
| v0.0 (占位) | 2026-05-15 | 已知方向 + 待办 outline | (slot-2 session) |
| v0.2 (架构反推完整版) | 2026-05-15 | 按 Phase 1 PRD 同等深度重写：US 16 条 / PR 8 个建议 / 数据模型 / API / BDD 验收 / MD-1~5 关键决策 | (slot-2 session) |
| v0.3 (8 角色 review) | 2026-05-15 | PR17 push payload 安全约束 / PR23 跨 org 缓存 evict / MD-4 国内 push 调研提前到 Phase 1 GA / i18n 双语 push 文案 | (slot-2 session + 8 角色 review) |
| v0.4 (Round 2 review) | 2026-05-16 | PR16/16.5 加 Data Protection + remote wipe + attestation（设备遗失场景）| (slot-2 session + 8 角色 round 2) |
| v0.5 (Round 3+4 review) | 2026-05-16 | 新增 §4.2.1 移动端 a11y 段（VoiceOver / TalkBack / Dynamic Type / Reduce Motion / Voice Control）+ i18n CI 门禁覆盖 PR16-PR23（Localizable.strings / strings.xml / Siri Intent / Live Activity / push localized-key）| (slot-2 session + 8 角色 round 3+4) |
