Kiora Companion 是基于 Kiora SDK 构建的 Android 宿主应用。它把本机模型、OCR、相册检索、工具授权与 A2A 连接放进同一会话,用两条实际流程检验移动端智能体能否完成从采集到交付的任务。SDK 提供运行机制和可选适配器,Companion 负责交互界面、Android 权限、文件绑定与本地数据管理。
本文围绕文档扫描和相册搜索两条链路,说明数据如何从手机进入智能体、何时成为真正的文件,以及何时交由其他 Agent 处理。这样可以区分界面展示、模型生成的文字和已经执行的操作。
应用承担的职责
Companion 支持本机 LiteRT-LM 和 MNN 模型,也可以连接兼容 Chat Completions 的 API。聊天模型负责理解用户意图、规划工具和整理文本;OCR 与向量模型分别完成识别和检索;Android 宿主则负责权限、文件读写、任务状态和界面。多个模型并存不意味着同一任务总会调用全部模型,具体路径取决于用户请求和当前配置。
text
Android 相机/相册/文件选择器
↓
Companion:权限、预览、校对、附件绑定
↓
Kiora SDK:会话、模型、工具选择与授权
├── 本机模型、OCR、向量检索
├── 本机工作区工具
└── A2A → 可访问的其他 Agent
对端侧智能体而言,这个分工具有实际意义。模型可以提出"写入 Markdown"或"发送已选照片"的动作,但文件是否写成、照片是否仍可读取、远端是否收到任务,都必须由程序返回的结果证实。尤其在长会话中,"刚才那个文件"是语义指代,需要会话上下文辅助理解;最终用于上传的文件对象仍须由宿主按受控 ID 查找。
从纸质资料到 Markdown 文件
用户可以连续拍摄文档页面,也可以一次导入多张图片。扫描界面支持调整页序、查看原图、逐页修改 OCR 结果及保存草稿。PP-OCRv6 适配器在手机本机识别扫描内容,识别结果先交给用户校对,再进入聊天会话。
扫描入口不是一个简单的"拍照并立即提问"按钮。多页材料中,拍摄顺序不一定等于阅读顺序;透视和光照会影响识别;公式、姓名、年份、参考文献等又不能只凭语言模型补全。Companion 因此把页面选择、OCR 结果和人工校对放在模型整理之前。用户可以先确认每一页的文字,再让智能体根据这份可核对的原文组织标题与段落。
单页与多页在整理阶段采用同一原则:页面顺序由用户调整,OCR 文本以页为单位保留,最后再合并为文档输入。多页界面提供采集与校对能力,但不能把其存在等同于所有类型的多页文档均已完成准确率验证。对于模糊、缺页或明显识别错误的文本,合理的行为是保留疑点并请求校对,而不是让模型编造缺失事实。
整理阶段遵循"先生成成品,再交付成品"的顺序。用户要求智能体把校对后的原文整理为 Markdown 时,本机模型调用工作区 write 工具,实际写出 .md 文件。宿主确认写入成功,并将该文件绑定为受控附件;随后,模型才能调用目标 Agent Card 中声明支持文件输入的 A2A skill。上传前仍需核验文件 ID,并按宿主授权策略请求用户确认。
用一条明确的状态链描述该过程更准确:
text
扫描页面 → 本机 OCR → 人工校对 → 模型整理 Markdown
→ write 写入工作区 → 宿主确认文件存在
→ 绑定附件 ID → 用户授权 → A2A 上传
→ 远端任务完成 → 展示远端结果
其中,模型给出一段 Markdown 文字不等于磁盘上已经有 .md 文件。只有 write 返回成功、宿主在工作区查到文件,文件才有资格进入"可上传附件"列表。同理,聊天界面展示了"已调用 DSH"也不等于目标桌面已有文件,远端仍可能因为授权、路径或任务执行失败而返回错误。界面应分别表达本机写入、A2A 发送和远端落盘这三个阶段。
这一设计还避免了一个任务边界错误:用户要求"手机把扫描原文整理为文档,再通过 DSH 发送到桌面",如果手机把扫描原图直接交给 DSH,并要求 DSH 重新 OCR 与整理,虽然桌面可能最终得到文档,但任务并非按照用户指定的分工完成。Companion 应把本机整理出的文档作为交付物;除非用户明确要求远端分析原图,否则扫描图片不应被默认替换为交付附件。
这一顺序避免了两类常见偏差:仅在回复中宣称"已生成文件",实际没有落盘;或者跳过本机整理,把扫描原图直接发送给远端,让远端重新完成用户本来交给手机的任务。真实演示使用一页扫描素材验证了完整交付链路;多页采集与逐页校对是扫描界面提供的能力,不应把单页演示描述为多页实测。
演示中的目标 Agent 是电脑端 DSH。它接收手机交付的 Markdown 附件,返回桌面文件路径、大小和 SHA-256 校验信息。用户继续提出"刚才的文件"相关要求时,Companion 复用同一手机会话与 A2A 连接保存的 contextId,使后续远端任务进入既有上下文。这个过程展示的是 A2A 协作机制,目标设备并不限于电脑。
SHA-256 校验在这里用于确认目标文件与发送的附件字节一致,不用于评价 OCR 内容是否正确。扫描内容是否准确,仍取决于拍摄质量、OCR 结果及人工校对;结构是否合理,则取决于整理要求和模型输出。将这几种质量问题分开,有助于定位失败:识别错误需要回到扫描文本,文件不一致需要检查传输与远端保存,会话断裂则应检查 contextId 的保存与复用。
相册索引如何避免"全部图片都是结果"
相册流程使用 Kiora SDK 的 EmbeddingGemma 2 适配器,为手机照片建立文字和图像向量索引。索引存放在应用私有 SQLite 数据库,记录照片标识、版本信息与向量;原图仍由 Android 系统相册管理。索引过程不会自动上传照片。搜索前,应用会剔除已删除、已修改或当前不可访问照片对应的失效向量。
索引与搜索是两个不同阶段。索引阶段从 Android 媒体库读取有权限访问的照片,将图片转换为向量,并保存媒体 ID、修改时间、大小、模型指纹和预处理版本等元数据。EmbeddingGemma 2 的适配器使用 MediaPipe UniversalEmbedder;文本查询和图片向量进入同一相似度比较空间。数据库保存的是检索所需的数字表示,而不是原图副本。批量处理可控制一次索引的工作量,后续遇到新增、删除、修改或权限变化时,再按元数据识别需要新增、更新或移除的记录。
这类检索返回的是相似度排序,不是"识别到某物"的确定性判断。分数高只代表在当前模型与索引中更接近查询语义;同一张照片可能因为背景或构图被排到意料之外的位置。因此,聊天场景先展示靠前的少量候选供用户判断,而设置中的测试搜索可展示更多结果以检查排序。已索引总数与本次命中候选数必须分开显示,不能把 59 张库存照片表述为"找到了 59 张杯子"。
从自然语言查询到两张照片交接
用户在聊天中输入"搜索相册中的杯子"后,智能体调用只读的相册搜索工具。聊天界面展示排序靠前的照片,而不是将已索引照片总数当作匹配结果,也不默认要求视觉模型逐张读取图片内容。用户可以查看、展开和放大结果,再勾选希望交付的照片。仅在用户要求分析某张照片的内容时,应用才需要将对应原图提供给具备视觉能力的当前模型。
返回照片本身,而不是返回一串文件名、内部 ID 和小数分数,能够让用户直接判断检索是否符合意图。多张候选在会话中以可展开的图片组呈现,单张可进入预览并双指缩放。分数仍可用于程序排序和测试界面诊断,不必成为聊天回答的主体。这样,搜索工具完成的是"找图",视觉模型的逐图分析则留给明确提出的后续任务。
这里还存在端侧模型输入约束与附件传输的区别。某个本机聊天模型在一次推理中只接受一张图片,并不意味着 A2A 一次只能发送一张文件。相册检索展示的多张候选也不需要全部作为视觉输入交给聊天模型。模型可以只收到搜索工具返回的结构化候选,界面从媒体库加载缩略图;最终发送几张,取决于用户实际勾选及目标 Agent 的文件能力、大小限制和授权结果。
当用户要求把选中的照片交给其他 Agent 时,Companion 将选中项绑定为本轮附件,核验实际可读取的照片,再按目标 skill 的文件输入声明与授权策略发送。照片以文件附件传输,不占用本机视觉模型的单图输入名额。真实演示中,两张选中的照片分别由电脑端落盘,并完成 SHA-256 校验。
附件绑定需要比自然语言更严格。搜索结果中的 gallery:<photoId> 是供本机照片读取工具解析的标识,selected:<index> 指向当前轮次用户勾选的附件,照片文件名只用于展示和目标端命名。这些值不可互换。模型如果把缩略图的名称当成本机工作区路径,或把搜索结果 ID 当作本轮已选附件,宿主应在发起网络请求前拒绝并返回可理解的错误。最终上传列表应来自界面选择状态,而不是让模型自行猜测有几张图。
Android content:// URI 同样不能原样交给远端。Companion 取得系统授权后在本机读取文件字节,SDK 再按 A2A 附件格式构造消息。发送前需要重新检查用户是否仍有照片读取权限、原图是否存在、总大小是否超出当前内联附件上限,以及目标 skill 是否声明接受文件。若用户选了两张而发送栏只显示一张,这属于本机选择或绑定阶段的问题;如果发送栏显示两张但远端只落盘一张,则需要沿 A2A 消息和远端处理结果继续排查。
连接状态与会话连续性
Companion 可以配置多个 A2A 连接,并给连接设置便于用户称呼的别名。连接设置页周期性检查 Agent Card 和任务端点的可达性,帮助发现地址错误或网络阻断。但"已连通"只说明检测时这两个端点可访问,不保证指定 skill 的一次任务会成功,更不保证远端有权写入用户期望的目录。真实调用仍需保留错误详情与任务状态。
连续多轮委托需要稳定保存"本机聊天会话 + 目标连接"对应的 contextId。完成一项任务后,下次再说"让 DSH 改刚才那个文件",新请求应带着该上下文标识;未完成任务的恢复则使用 taskId。如果每次都按新连接创建远端上下文,远端会产生多个无关会话,"刚才的文件"也就失去指向。Companion 将这些标识与本机会话一起持久化,而不是寄希望于提示词让远端猜测前一项工作。
离线处理与跨 Agent 协作的边界
本机模型推理、OCR 和相册索引可以在手机上完成,适合网络受限时的采集、检索与初步整理。A2A 委托需要目标 Agent 在当前网络可达;若使用在线聊天模型,查询文字及工具返回的照片名称可能进入该模型请求。因此,"端侧能力可离线运行"不等于应用的所有模式和跨设备操作都始终离线。
附件上传发生在宿主核验和用户授权之后。授权约束的是发送前的操作,并不让目标 Agent 在收到附件后无法读取其内容。当前 A2A 附件总大小上限为 10 MiB,远端服务也必须具备相应的文件输入和执行能力。上述边界由 SDK 与 Companion 共同落实,不能仅通过提示词保证。
本地数据也有明确边界:扫描草稿、聊天记录与相册向量索引放在应用私有空间;系统相册原图仍由系统媒体库管理。应用的访问能力随 Android 权限变化。如果选择在线聊天模型,查询文字以及工具返回的部分元数据可能进入服务请求;如果向 A2A 目标上传附件,目标 Agent 在接收后可读取内容。不能把"附件字节没有放入手机聊天模型提示词"误写成"远端 Agent 无法看到附件"。
在网络受限的地点,手机可以先完成允许离线执行的扫描、OCR、相册搜索及文档整理;要把结果交给其他 Agent,仍需目标服务在当前网络路径可达。A2A 放大的是可连接时的执行范围,不改变远端操作对网络和目标权限的依赖。这样的表述也更适合工程评估:本机链路、传输链路和目标执行链路可以分别测试。
如何验证这两条链路
演示证明了具体链路能够完成,但不能代替覆盖所有设备、文档版式和相册内容的系统性评测。对扫描流程,可分别检查校对后文本、工作区 Markdown 文件、A2A 附件、目标文件字节;对相册流程,可分别检查索引有效性、候选排序、勾选数量、发送列表和远端落盘数量。每一步都应保留失败状态,避免只看聊天中的一句"已完成"。
| 检查点 | 主要问题 | 可观察结果 |
|---|---|---|
| OCR 与校对 | 原文是否正确、页序是否合理 | 校对界面的逐页文本 |
| 文档生成 | 是否真的写出了 .md 文件 |
工作区文件与 write 结果 |
| 附件绑定 | 是否发送了用户选定的文件 | 发送栏、受控文件 ID、大小 |
| A2A 任务 | 是否延续了预期上下文 | 连接、contextId、任务状态 |
| 目标交付 | 文件是否完整落盘 | 路径、字节数与 SHA-256 |
目前公开材料中的示例能够说明设计与可复现的操作顺序;检索准确率、耗时和能耗等量化结论还需要统一数据集与设备条件支撑,不能从一次演示外推。
几类实际失败说明了什么
开发过程中的失败案例比单一成功截图更能说明边界应放在哪里。一次相册交付中,模型把照片名称当成工作区文件名提交,目标工具返回"工作区文件不存在"。名称确实出现在聊天记录中,但它并不指向本机可读取文件。修正方向是让宿主维护照片 ID 与媒体库对象的映射,并在模型调用远端工具之前解析当前用户的选择。
另一次调用把已选照片写成了错误的 ID 形式,工具报告无效文件 ID。此时如果继续修改提示词,让模型"下次记住格式",仍无法保证复杂会话中每一次都正确。更可靠的做法是由程序绑定当前轮次选中项,参数校验失败时立即返回明确错误,并避免把不存在的附件交给授权或远端传输环节。
扫描流程则暴露过"跳过本机写文档,直接让远端根据原图生成文档"的偏差。用户意图中包含两个有顺序的动作:整理本机已校对的原文、交付整理后的文件。改进点是把 write 的成功结果作为后续上传的前置条件,同时让交付工具只接收本轮生成的 Markdown 附件。这里既有模型规划质量问题,也有宿主缺少任务产物约束的问题;单靠更长的系统提示词无法提供文件存在性保证。
这些案例共同指向一个原则:自然语言负责表达目标,模型负责选择与组织动作,而文件存在性、ID 映射、用户授权、上传数量和远端任务状态由程序判定。模型的灵活性与程序的确定性需要同时存在,二者不应互相替代。
Kiora Companion 展示了端侧 AI 与 A2A 的分工:手机先处理适合本地完成的感知和文件生成任务;在网络可达且用户授权后,其他 Agent 接续处理后续工作。后续仍需围绕不同设备、目标 Agent 与任务类型继续验证兼容性和实际效果。