直接回答
流式输出是一串事件,不意味着每个片段都完整或适合直接显示为最终答案。LiteRT-LM Kotlin API 文档展示了从对话收集生成输出的方式,但应用仍负责线程、请求标识、界面状态、取消、解析和资源生命周期。初始化与推理应远离主线程;把有限事件传给 ViewModel 或 Repository;并区分部分文本、完成、错误和取消。 流式界面应有明确状态机:空闲、初始化、生成、收尾、完成、取消和失败。开始新请求时分配唯一请求 ID,并有意清空或保留旧结果。每个事件都要与当前 ID 比对,避免旧流的延迟片段覆盖新请求。重组只负责绘制状态,不应重新创建对话或重复订阅原生输出从而重复推理。 部分输出可能是未完成的句子、格式错误的 JSON、不完整工具提案,或后来被用户取消的敏感文字。应决定是立即展示片段、标作草稿还是等安全边界后再显示。若输出是结构化内容,不要对任意中间片段运行解析器或工具处理器;应等协议边界结束,严格解析并验证。停止操作在运行时支持时应传到实际协程和引擎取消路径。仅移除屏幕订阅不能证明原生生成停止。 各层应有明确所有权:推理协调器按库契约拥有引擎和对话;ViewModel 管理任务状态与活动 Job;UI 只负责渲染,不拥有原生资源。离开页面时,要决定生成继续还是取消。进程死亡后,进程内流已经消失,不能把加载转圈恢复成仍在运行。若需要恢复任务,应持久化任务输入与阶段标记,再重新初始化运行时,而不是序列化原生对话。 错误处理要保留阶段:模型初始化、对话创建、流式输出、结果校验、取消或关闭。流以错误结束时,不得把部分答复显示成成功。模型没有输出时应明确表示无结果,不要编造回退答案。除非用户主动选择支持导出,否则不要把敏感提示和响应写入日志。 测试固定提示样例,覆盖短输出、多事件输出、空输出、结构错误、事件间取消、首个请求未完成又开始第二个、旋转、导航、进程重建和关闭。记录事件顺序与最终状态。测试只证明准确模型、运行时和 UI 策略,不构成生成速度或质量基准。实施时使用应用自有状态机和请求 ID;在后台协程收集不可变事件;让 Compose 只渲染状态;将停止操作传给引擎所有者;只在完成边界校验最终结果;最后用确定样例验证每条事件序列。流式输出是生成之上的 UI 和生命周期契约,不证明 Cove 某个公开版本支持它,也不表示所有运行时的取消行为相同。
首个输出流未结束时用户又提交第二个提示
Android 用户提交问题并看到部分文本,完成前更改请求并再次点击生成。此时旧流又发出文本,新请求已经开始。
决策
为请求分配唯一身份,决定旧任务是否取消,并丢弃旧事件,不能把两个流合并成一个答案。
预期结果
页面保持单一连贯状态,不会把中断或过期的部分响应标为新请求的最终结果。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- 请求 ID 与事件顺序
- 部分、完成、错误和取消状态
- 停止与关闭后的资源记录
只有记录了模型身份、构建配置、实际后端、任务样例和失败情况,运行时比较才可复核。
实施流程
- 定义事件与请求身份. 将初始化、部分输出、完成、取消和失败表示成不同不可变事件,并绑定一个请求 ID。新请求到达时明确决定旧流的处理策略。
- 不要在 UI 重组中收集推理. 在应用协调器或 ViewModel 的任务作用域启动收集,发出状态更新,并确保 Compose 只绘制当前状态。重组不得重复创建引擎或再发起请求。
- 安全处理部分输出. 选择草稿展示策略;结构化数据要等待完整边界后再解析;运行时支持时传递取消;所有不属于当前请求 ID 的事件都应忽略。
- 覆盖生命周期和失败场景. 测试空流、解析错误、片段间停止、重叠请求、旋转、导航、进程死亡和关闭。确认 UI 最终状态与真实终止事件一致。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 每个事件绑定单个请求 ID,旧片段无法更改当前结果。
- 取消操作到达实际执行所有者,部分输出不会被报告为完成。
- 初始化、生成、校验、取消和关闭错误可以分别识别。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 等到完整后再展示 | 只显示最终校验结果 | 边界更简单,但不提供增量显示。 |
| 将部分输出标作草稿 | 增量 UI 加明确草稿状态 | 更快反馈,但需要处理旧事件和取消。 |
| 把每个片段当成最终结果 | 没有边界或请求跟踪 | 可能展示格式错误或过期文本并触发下游动作。 |
需要避免的失败模式
- 把部分 JSON 当最终动作
- 让 Composable 重组启动第二个流
- 仅移除订阅就认为原生推理已停止
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
- LiteRT-LM repository
- Google AI Edge LiteRT-LM Kotlin API 入门
- Android Developers — 进程和线程
- Android Developers — Activity 生命周期
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
离开页面会停止 LiteRT-LM 输出流吗?
不一定。移除 UI 订阅后,运行时任务仍可能继续。应将取消传到任务所有者,并按 API 契约验证原生工作确实结束,同时忽略过期请求片段。
可以从部分片段解析并执行函数调用吗?
不要执行中间片段。应等待文档定义的完整边界,解析完整结构,完成校验和宿主授权后再运行;不完整流要作为未完成状态处理。
流式输出会让模型回答质量更好吗?
流式只改变输出传递方式,并不代表答案正确。质量或速度结论需要独立任务样例、验收规则和可复核记录支持。