feat: 初始化 Android 客户端(M0 骨架 + M1 登录 + M2 基础模块)
M0:Gradle 9.7.1 / AGP 9.2.1 / Kotlin 2.4 / Miuix 0.9.3,minSdk 33,compileSdk 37。 M1:登录页(服务器地址可配、服务端/本地错误分流、EncryptedSharedPreferences、自动登录与静默重登、401 登出)。 M2 基础模块:消息内容解析器(<p>/<action>/<narration> + 流式增量)、WebSocket 客户端、REST 数据层与分页去重。 45 个单元测试全绿,真机验证通过(登录页 + 启动图标)。 devdocs/ 收录设计文档、前后端协同事项与细化方案。
This commit is contained in:
commit
84de51c7b7
67 files changed
+3960
No files matched your search
@@ -0,0 +1,207 @@
|
||||
# 02 — 前后端协同事项(Cyrene-For-Android ⇄ Cyrene v2)
|
||||
|
||||
> **用途**:Android 端「要做/想做」,但**后端当前不支持、或契约未明确**的事项统一登记在这里。主人(AskaEth)按此去后端做适配与开发。
|
||||
> **状态机**:`待确认` → `已确认` → `已排期` → `已完成` / `不采纳`
|
||||
> **依据**:`Cyrene_v2/Docs/api-reference.md`、`Docs/api-client.md`、`DevDocs/design/12,14,15,16,21`(调研中发现多处文档与实现不一致,见 §E2)
|
||||
> **最后更新**:2026-09-08
|
||||
|
||||
---
|
||||
|
||||
## 登记规范(开发过程中持续维护)
|
||||
|
||||
**触发条件**——开发 / 联调中遇到以下任一情况,**当场**追加到本文并立即向主人反馈,不攒到最后:
|
||||
|
||||
1. 调用后端**直接失败**(501 / 404 / 500 / 字段缺失导致功能做不了);
|
||||
2. **契约或设计文档写了、但实现没做**(例如 `mode` 不落库、`voice_input` 不落库)——即「理论上应该支持但没有」;
|
||||
3. 后端行为与文档不符,导致客户端必须绕行。
|
||||
|
||||
**每条要写清**:
|
||||
|
||||
- **现象**:可复现的请求 / 响应或代码位置(`文件:行`);
|
||||
- **客户端为什么要**:不补会怎样;
|
||||
- **后端建议方案**:客户端期望的接口 / 字段 / 语义;
|
||||
- **当前绕行**:客户端现在怎么扛过去(没有就写「无,功能阻塞」);
|
||||
- **优先级**:阻塞 P0 / 影响体验 / P1 / P2 / 文档治理。
|
||||
|
||||
**流程**:`待确认` → 主人确认 → `已排期` → 后端完成 → `已完成`(注明后端版本 / commit);不做的标 `不采纳` 并写明理由。
|
||||
|
||||
**反馈节奏**:每轮开发结束时,在对话里列「本轮新增协同事项」(编号 + 一句话),主人直接照着修后端。
|
||||
|
||||
---
|
||||
|
||||
## A. 阻塞 P0 的(不解决就得改客户端设计)
|
||||
|
||||
### A1. 结构化流帧不带 `sessionId` → 多会话流式无法归属
|
||||
- **现象**:`message.start/delta/end`、`thinking.*`、`tool_call.*`、`tool_execution.update` 的 payload 只有 `{index}` / `{id,index,delta}`,**没有 `sessionId`**(`model.StreamEvent` 无会话上下文)。
|
||||
- **客户端为什么要**:WS 是全局一条连接,多会话同时流式时无法判断 delta 属于哪个会话,切会话会串台。
|
||||
- **后端建议**:这些帧补 `sessionId`,或至少补 `turnId`(客户端用 turnId 反查会话)。
|
||||
- **当前绕行**:客户端硬约束「同一时刻只有一个会话在流式」(已拍板接受)。
|
||||
- **优先级**:中高(有绕行,不阻塞 P0)
|
||||
|
||||
### A2. `interrupt` 清空 pending → 「打断」模式连发丢消息
|
||||
- **现象**:`interrupt {sessionId}` 取消在跑 turn **并清空 pending**,下行 `session_interrupted{dropped}`。
|
||||
- **客户端为什么要**:S1.4 定了「追加队列 / 打断」两种模式;用户选「打断」时连发多条会被静默丢弃。
|
||||
- **后端建议**:`interrupt` 增加 `scope`(`current` = 只取消当前 turn / `all` = 连 pending 一起清),默认维持现状。
|
||||
- **当前绕行**:打断模式下 UI 限制一次只能发一条 + 明确提示。
|
||||
- **优先级**:中
|
||||
|
||||
### A3. 消息 `mode` 不落库 → 渲染形态丢失
|
||||
- **现象**:design/16 §5.1 写「消息体带 `mode`」,但 Web 路径(`session_persist.go`)与 IM 路径(`channelbridge/persist.go`)**都不写 `Mode`**,字段 `omitempty` → 实际恒缺。
|
||||
- **客户端为什么要**:9 值渲染 mode(chat/code/home/work/docs/voice/vision/agents/settings)决定气泡/卡片形态,且「只有 chat 才解析 `<p>`」这条规则依赖它。
|
||||
- **后端建议**:持久化时写入 `mode`(design/16 已约定,属实现缺失)。
|
||||
- **当前绕行**:客户端一律回落 `chat`。
|
||||
- **优先级**:中
|
||||
|
||||
### A4. 会话 `title` 恒空 → 列表没名字
|
||||
- **现象**:`historySessionSummary.title` 有字段,但 `store.go` 恒写 `""`,无生成规则/接口。
|
||||
- **后端建议**:后端生成(截首条用户消息),或提供 `PATCH /sessions/{id}` 让客户端改名。
|
||||
- **当前绕行**:客户端本地生成「会话 N」+ 序号持久化。
|
||||
- **优先级**:低
|
||||
|
||||
### A5. WS 断线无补发 / 无 resume cursor
|
||||
- **现象**:无 `since` / `lastEventId`;`seq` 只用于排序,未规定可续传;重连仅有 `history` 回放主会话最近 50 条。
|
||||
- **客户端为什么要**:断线期间超过 50 条就永久丢失,只能整页重拉。
|
||||
- **后端建议**:`/ws/chat?since=<seq 或 createdAt>` 补发,或提供 `GET /sessions/{id}/messages?after=<ms>` 正序增量接口。
|
||||
- **当前绕行**:重连后 REST 按 `before` 补差 + 按 `id` 去重。
|
||||
- **优先级**:中
|
||||
|
||||
---
|
||||
|
||||
## B. 影响体验 / 客户端需绕行
|
||||
|
||||
### B1. 无图片上传端点 → 大图只能 base64 塞进 `chat.image`
|
||||
- **现象**:无 `/upload`;`image` 只接受 base64 data URI 或 http(s) URL;WS 上行帧限 **4096 字节**。
|
||||
- **客户端为什么要**:主人已定「发图先做着」,但没有上传端点就只能把压缩后的 base64 走 REST,大图会撑大请求体(WS 完全走不通)。
|
||||
- **后端建议**:`POST /api/v1/upload`(multipart)→ 返回 URL;`chat.image` 收 URL。
|
||||
- **当前绕行**:客户端压缩后再 base64,限制单图尺寸。
|
||||
- **优先级**:中高(主人已要求做发图)
|
||||
|
||||
### B2. token 24h 且无 refresh 端点
|
||||
- **现象**:`tokenTTL=24h`,无 refresh/renew;`rotate-jwt` 让全部 token 立即失效。
|
||||
- **客户端为什么要**:主人选了「静默重登」,但静默重登需要长期保存账号密码。
|
||||
- **后端建议**:提供 refresh token,或拉长 TTL + 支持「记住登录」的长期凭据。
|
||||
- **当前绕行**:存密码做静默重登(安全上不理想)。
|
||||
- **优先级**:中高
|
||||
|
||||
### B3. `GET /sessions` 信息太少(无未读、无最后一条预览)
|
||||
- **现象**:只有 `{id,type,title?,createdAt}`。
|
||||
- **客户端为什么要**:会话列表做不了预览和未读红点;多端同步时无法标记已读。
|
||||
- **后端建议**:补 `lastMessage{content,role,createdAt}`、`unreadCount`,或提供已读游标接口。
|
||||
- **优先级**:低(P0 按序号展示即可)
|
||||
|
||||
### B4. `platform` / `mood` / `mode` 取值未穷举
|
||||
- **现象**:文档只给示例(`web`/`telegram`/`weixin-ilink`/`qq-obv11`/`kook`;`calm`;`normal`)。
|
||||
- **客户端为什么要**:来源角标和情绪标签需要**稳定映射**(显示「来自微信」而不是原始字符串)。
|
||||
- **后端建议**:给出权威枚举表 + 未知值回落约定。
|
||||
- **当前绕行**:客户端维护映射表 + 未知值原样显示。
|
||||
- **优先级**:低
|
||||
|
||||
### B5. 管理员端点 5 个返回 501
|
||||
- **清单**:`POST /admin/users`、`POST /admin/iot/scenarios`、`POST /admin/iot/rules`、`POST /admin/plugins/install`、`POST /admin/rag/delete`。
|
||||
- **客户端为什么要**:管理员页面要决定这些入口做不做、做了怎么降级。
|
||||
- **当前绕行**:客户端硬编码「已知未实现」清单,入口置灰。
|
||||
- **优先级**:低
|
||||
|
||||
### B6. `proactive_message` 只在 `api-reference.md` 有,`api-client.md` 摘要缺失
|
||||
- **后端建议**:补进 `Docs/api-client.md`。
|
||||
- **优先级**:低(文档治理)
|
||||
|
||||
---
|
||||
|
||||
## C. P1 语音相关
|
||||
|
||||
### C1. `voice_input` 轮次不落库、不过编排器
|
||||
- **现象**:`runVoiceTurn` 直调 `runner.Run`,无 `history.AppendFull`、不经 `session_orchestrator` → **无 `message_persisted`、无 busy/pending、不跨端同步**;与 `api-client.md` §3.1「随语音轮次落库」不符。
|
||||
- **客户端为什么要**:语音问过的事必须进历史、跨端可见,否则在 Web 上查不到。
|
||||
- **后端建议**:把语音轮次接入编排器与持久化(与文本轮次同一路径),或在文档里明确「语音不留痕」。
|
||||
- **当前绕行(客户端已定)**:**不用 `voice_input` 提交对话**,改为 `REST /audio/transcribe` 转写 → **WS `chat`**(落库 + 流式 + 编排)→ `REST /audio/speech` 播放。
|
||||
- **优先级**:中
|
||||
|
||||
### C2. 语音降级到 silent 时客户端无法感知
|
||||
- **现状**:cloud 档已接阿里百炼(STT `qwen-audio-3.0-asr-flash` / TTS `cosyvoice-v3-flash`,需 `DASHSCOPE_API_KEY`);`edge-tts`/`espeak` 未实现;`local` 默认不注入;`silent` 兜底返回空音频。
|
||||
- **客户端为什么要**:没有 key 时语音会「静默成功但没声音」,客户端无法提示。
|
||||
- **后端建议**:降级到 silent 时给出可识别信号(响应头 `X-Voice-Provider: silent` 或专门错误码)。
|
||||
- **优先级**:中
|
||||
|
||||
### C3. `voice_output` 是否带 `sessionId` / `deviceId` 未规定
|
||||
- **客户端为什么要**:多端同时说话时无法区分归属。
|
||||
- **优先级**:低
|
||||
|
||||
---
|
||||
|
||||
## D. P2 / 路线缺失(客户端想做,后端尚无能力)
|
||||
|
||||
### D1. 移动端推送通道(系统通知)—— 最大缺口
|
||||
- **现象**:主动推送只有 WS `proactive_message`(仅 owner/admin 连接收到),**App 不在前台就收不到**。
|
||||
- **客户端为什么要**:design/21 §5 的 P2 明确要「主动推送 → 系统通知」,目前没有任何移动推送通道。
|
||||
- **后端建议**:接 FCM / 个推 / 自建长连接保活,并在 WS 之外提供推送投递。
|
||||
- **优先级**:P2,但属**路线级缺失**,建议早登记。
|
||||
|
||||
### D2. 消息搜索 / 会话搜索
|
||||
- **现象**:无搜索接口(`/admin/memories/search` 是记忆,不是消息)。
|
||||
- **优先级**:P2
|
||||
|
||||
### D3. 消息编辑 / 删除 / 重发
|
||||
- **现象**:无接口。
|
||||
- **优先级**:P2
|
||||
|
||||
### D4. 未读状态 / 已读游标
|
||||
- 见 B3。
|
||||
- **优先级**:P2
|
||||
|
||||
### D5. 会话改名 / 置顶 / 排序
|
||||
- 见 A4;`GET /sessions` 次级按 `created_at ASC`,改名/置顶需要新接口。
|
||||
- **优先级**:P2
|
||||
|
||||
---
|
||||
|
||||
## E. 文档与契约治理
|
||||
|
||||
### E1. `X-API-Version` 形同虚设
|
||||
- **现象**:服务端不校验该头;CORS `Allow-Headers` 只有 `Authorization, Content-Type`(未放开 `X-API-Version`)。
|
||||
- **建议**:要么真正校验,要么从契约里去掉,避免客户端误以为存在版本协商。
|
||||
|
||||
### E2. 文档与实现不一致(调研发现)
|
||||
| 事项 | 文档说法 | 实现 |
|
||||
|---|---|---|
|
||||
| 真实 ASR/TTS | design/21 §4「未接通」 | 已接百炼并实测 |
|
||||
| `voice_input.deviceId` | design/21「待补」 | 已有 |
|
||||
| REST 语音端点 | design/21「待新增」 | 已有 |
|
||||
| 语音轮次落库 | api-client「随轮次落库」 | 未落库(见 C1) |
|
||||
| 消息 `mode` | design/16 §5.1「消息体带 mode」 | 不落库(见 A3) |
|
||||
| 来源标识注入 | design/16 §4.6.1「文本前缀」 | 独立 system 消息 + 结构化 `SenderName` |
|
||||
- **建议**:以 `api-reference.md` + 代码为准,`api-client.md` 随实现同步(AGENTS.md 已有此红线)。
|
||||
|
||||
### E3. `thinking` 可达性确认
|
||||
- **现状**:WS 实时/回放已剥离,只有 `GET /sessions/{id}/messages`(owner/admin)返回。
|
||||
- **客户端影响**:历史消息的思维链需要额外请求。
|
||||
- **待确认**:这是有意为之(隐私)还是可优化?
|
||||
|
||||
---
|
||||
|
||||
## 附:状态一览
|
||||
|
||||
| 编号 | 事项 | 状态 | 优先级 |
|
||||
|---|---|---|---|
|
||||
| A1 | 流帧缺 `sessionId` | 待确认 | 中高 |
|
||||
| A2 | `interrupt` 清 pending | 待确认 | 中 |
|
||||
| A3 | `mode` 不落库 | 待确认 | 中 |
|
||||
| A4 | `title` 恒空 | 待确认 | 低 |
|
||||
| A5 | 无 resume cursor | 待确认 | 中 |
|
||||
| B1 | 无图片上传端点 | 待确认 | 中高 |
|
||||
| B2 | 无 token refresh | 待确认 | 中高 |
|
||||
| B3 | `sessions` 信息少 | 待确认 | 低 |
|
||||
| B4 | 枚举未穷举 | 待确认 | 低 |
|
||||
| B5 | admin 501 清单 | 待确认 | 低 |
|
||||
| B6 | `proactive_message` 文档缺失 | 待确认 | 低 |
|
||||
| C1 | 语音轮次不落库 | 待确认 | 中 |
|
||||
| C2 | 语音降级无信号 | 待确认 | 中 |
|
||||
| C3 | `voice_output` 字段 | 待确认 | 低 |
|
||||
| D1 | 无移动推送通道 | 待确认 | P2(路线缺失) |
|
||||
| D2 | 无消息搜索 | 待确认 | P2 |
|
||||
| D3 | 无编辑/删除/重发 | 待确认 | P2 |
|
||||
| D4 | 无未读状态 | 待确认 | P2 |
|
||||
| D5 | 会话改名/置顶 | 待确认 | P2 |
|
||||
| E1 | `X-API-Version` 虚设 | 待确认 | 低 |
|
||||
| E2 | 文档与实现不一致 | 待确认 | 中 |
|
||||
| E3 | `thinking` 可达性 | 待确认 | 低 |
|
||||
Reference in new issue
Block a user