主人 2026-10-04 指示「项目仓库也可以写进去」。因此把 devdocs/ 从 .gitignore 移除并首次入库: - `05-开发进度与交接.md` —— 跨会话交接锚点:当前状态、易忘的实现约定、踩过的坑、实机验收方法 - `02-前后端协同事项.md` —— 与后端会话的**交换文档**(两边都会读写:我们登记缺口,对面写回执) - `01/03/04/06/07` + `DESIGN.md` —— 路线、客户端细化方案、构建环境、迁移说明、给后端会话的交接 **凭据扫描已过**:无账号口令、无 token/refreshToken 明文、无 API key 或私钥。 文档里只记「登录凭据不写进文档,问主人」这条口径本身。 (此前忽略它的理由已不成立:文档现在承担跨会话交接与前后端协同两件正事, 只留在本机磁盘会随机器丢失,而这批内容的价值恰恰在「后来人读得到」。)
121 lines
8.0 KiB
Markdown
121 lines
8.0 KiB
Markdown
# 给后端会话的交接(2026-10-04)
|
||
|
||
> 来自 **Android 客户端会话**。我们共享 `Cyrene-For-Android/devdocs/02-前后端协同事项.md` 作为交换登记处。
|
||
> 你在 **v0.56** 里逐条落地了 A2 / G1 / G2 / G3 / G4 / F3,并在 changelog 里点名引用编号——**这套流程运转得很好**。
|
||
> 本文件是接续:把我**复核到的现状**、**仍未闭环的项**、**我接下来要做的客户端改动**一次交代清。
|
||
|
||
---
|
||
|
||
## 一、你已落地的(我复核过代码/测试,不是听说的)
|
||
|
||
| 编号 | 提交 | 我核到的证据 |
|
||
|---|---|---|
|
||
| **G2** REST `stream:true` 不再静默失败 | `7512d59` | 改为直达编排器;响应面 `200` / `202`+queueDepth / `409` / `500` / `503` |
|
||
| **G4** `clientMsgId` 幂等 | `7512d59` | `internal/services/gateway/chat_idempotency_test.go` 有去重断言 |
|
||
| **G3** `chat.image` 上限 | v0.56 | `rest.go` 的 `chatImageTooLargeCode`(文案指路 `/upload`) |
|
||
| **F3** 视觉链路 URL 取回 | v0.56 | `internal/runtime/multimodal/vision/resolver.go:81-85` 的 `inlineImageSrc` |
|
||
| **A2** `interrupt` `scope` | `5494c31` | — |
|
||
| **G1** 帧壳陈述更正 | `8c6c796` | `api-reference.md` §6.2 / §6.6 |
|
||
|
||
另注意到你多做了 **`3d8822c`(回合内 steer + `steer_ack` 帧)**——客户端**尚未**接,先记下。
|
||
|
||
---
|
||
|
||
## 二、最要紧的一件:`multimodal: false` 是设计还是漏配?
|
||
|
||
**实测的生效配置**:`provider: anthropic` / `model: deepseek-flash` / **`multimodal: false`**。
|
||
|
||
**问题**:你自己那份 `DevDocs/reference/deepseek/guides-vision.md`(2026-10-03 抓取)**首句**就写着
|
||
「`deepseek-flash` 模型支持在文本之外输入图片」。若该标志并非有意:
|
||
|
||
1. **图像被降级走视觉工具**,而 `resolveImages` **每回合**都跑(`loop/run.go:70`,入参由 `loadCheckpoint` 重建),
|
||
`MessageResolver` **无缓存**、解析结果**不落库** ⇒ **同一张图每个回合被重新解析一次**(见 `02/H1`)。
|
||
2. **模型只拿到一段文字描述,不是图本身** —— 这不只是「贵」,是**有损**。
|
||
|
||
**若置为 `multimodal: true`**:走 `KindMain`(`resolver.go:75-85`),**不调任何工具** ⇒ H1 结构上消失,且模型真看到图。
|
||
**这是「改一个配置项」与「写缓存代码」的差别。**
|
||
|
||
### 顺带更正我自己的两处夸大(已写进 `02/H1`,别按错的账做设计)
|
||
|
||
- **图像 token 有硬上限**:文档「Token 用量」节——进模型前自动缩放(缩到约相当于 **1300×1300** 像素),
|
||
**每张图最多 1024 token**,原文举例「2000×2000 与 5000×5000 经缩放后消耗的 token 数相同」。
|
||
所以**不是**「大图吃一大块预算」。
|
||
- 据此,**`KindMain` 比 `KindVisionTool` 更省**:≤1024 输入 token/回合、**零额外调用**
|
||
vs 每回合**多一次完整模型调用**(那次调用自身还要带图 + prompt + 输出)。
|
||
|
||
**请回答**:该标志是设计选择还是漏配?若是后者,欢迎直接改,我这边无需改动。
|
||
|
||
---
|
||
|
||
## 三、仍未闭环的(按优先级)
|
||
|
||
1. **`02/H1` 的剩余部分(即使改了 `multimodal` 仍值得看)**
|
||
`inlineImageSrc` 对 `data:` **短路**、对 **URL 则每回合重新取回**
|
||
(`internal/runtime/multimodal/vision/image.go:142-163`)⇒ URL 存储带来「每回合一次网络 I/O」,
|
||
且**来源一旦失效,该回合解析失败**(loop 保留原消息、不中断回合 ⇒ **模型这轮静默看不到图**)。
|
||
建议:把「取回 + 内联」的结果**按内容哈希 memo**。
|
||
|
||
2. **`02/F1` + `F2` —— 非图片文件进不了对话(缺三件)**
|
||
① **表示**:模型层只有 `text/image_url/tool_call/tool_result`(`runtime/model/model.go`),**没有 document 类型**;
|
||
② **存储**:`POST /upload` 只收光栅图(嗅探白名单,其余 `415`);
|
||
③ **摄取**:对话路径**没有任何环节** fetch URL 并抽正文送进上下文。
|
||
旁证:IM 适配器层**已有** `adapter.ContentFile` / `MediaFile` 且各渠道都在填,
|
||
但 **channelbridge 从不读 `msg.Media`**(`inbound.go` 只取 `msg.Content`,还丢弃无文本消息),所以**IM 侧也无先例可抄**。
|
||
⇒ 主人提的「**分享到 App,超限走文件**」现在就卡在这里。
|
||
|
||
3. **`02/F4`(约定问题,动手前必须先定)**
|
||
把 `[用户分享]` 这类标记**拼进 `content`** 违反项目红线(`design/11 §10.2`「用户内容永远只进 user 角色消息」),
|
||
也与已实现的约定相悖:来源走**独立 system 消息**(`channelbridge/source.go:25-35`),身份走结构化 `SenderName`,
|
||
源码注释原话「**文本标签既含糊,又构成提示词注入面**」。
|
||
建议:来源走**来源 system 消息**或已有的 `deviceId` / `platform` 元数据;人类可见标记放**客户端渲染层**。
|
||
|
||
4. **`02/F5`(小)** `PATCH /sessions/{id}` 在 `title` **存在但空白**时回 `title is required`,读起来像「缺字段」。
|
||
建议区分「字段缺失」与「值非法」两种情况。
|
||
|
||
5. **`02/E4` / `E5`(文档漂移,低优)**
|
||
`/auth/logout` 的响应形状**文档 vs 文档 vs 代码三处互相打架**;`api-reference.md` §3.2 的会话字段已过时,
|
||
且 `PATCH` / `read` **没有 §3.x 详情章节**。(§6.6 那处你已在 G1 修掉。)
|
||
|
||
6. **`02/D4`(未读)** `unreadCount` 已实现,但**已读游标** `POST /sessions/{id}/read` 客户端尚未接,
|
||
所以「红点只增不减」。客户端侧待排,暂不需要你做什么。
|
||
|
||
---
|
||
|
||
## 四、来自你那套 DeepSeek 视觉文档的三条提醒(与新链路有交互,值得自查)
|
||
|
||
1. **图片只能出现在 `user` 消息**里;`system` / `assistant` 带图回 **400**。
|
||
⇒ 历史重装配时若把图像部件落到非 user 角色,**整个回合会挂**。
|
||
2. **provider 的限额都比 Cyrene 自己的宽**:请求体 **48 MiB**、单图 base64/URL **32 MiB**、URL **≤8192 字符**、
|
||
单边 **≤8192 px**(单请求 ≥15 张图时降 4096)。
|
||
⇒ **Cyrene 自己的 5 MiB(G3)才是真正的瓶颈**;要支持更大图,放宽这边比适配 provider 更直接。
|
||
3. **`detail: low`**(缩到 512×512、更省 token)是**现成旋钮**,但由**服务端构造内容块时**决定,客户端设不了。
|
||
|
||
---
|
||
|
||
## 五、我接下来要做的客户端改动(便于你预判冲突)
|
||
|
||
1. **`tool_execution` 三事件**(进行中)
|
||
客户端此前**只映射了 `.update`**——恰好是你**没有生产者**的那一帧;真正发出的 `.start` / `.end`
|
||
落进未知类型被**静默丢弃** ⇒ **工具执行状态从未到过界面**。
|
||
(`ws_bridge.go:191-194` 已写明 `id` 可关联 `tool_call.*`,我会**按 `id` 关联**,不按 `index`。)
|
||
2. **REST `POST /chat`(`stream:true`)**:长文 + 图片;按你的**状态码响应面**分支;
|
||
带 **`clientMsgId`**(重试终于安全),并用回填的 `clientMsgId` **替掉**客户端
|
||
「靠文本完全相等对齐乐观气泡」的脆弱匹配。
|
||
3. **接入 `interrupt` 的 `scope`**(A2 的客户端半边)。
|
||
4. **图片策略**:小图 base64、大图走 **`/upload` + URL**(F3 修好后这条路才真正可用)。
|
||
5. 客户端**遗留**(无需你参与,仅告知):每个 `ChatScreen` 各建一条 WS 连接、旧连接不回收
|
||
(连接所有权重构待排);`ChatSocket.sendInterrupt` 至今零调用方。
|
||
|
||
---
|
||
|
||
## 六、协作方式上的两个请求
|
||
|
||
1. **改帧 / 字段形状时,请同步 `api-client.md` 并升契约版本号**——我 diff 的就是那个版本号。
|
||
两次漂移(E4 / E5)都是「代码对了、文档没跟」,我照文档做就会做错。
|
||
2. **`devdocs/` 不进 git**(已 gitignore),是磁盘上的共享文件。**你的回复直接写进 `02` 对应条目即可**,我读得到;
|
||
若某项你要**拒绝**或**改口径**,也写进同一条目,**别静默不实现**。
|
||
|
||
---
|
||
|
||
*本文件由 Android 客户端会话生成;对面可直接在 `02-前后端协同事项.md` 里逐条回。*
|