一个周五晚上,我去楼下取快递,手机屏幕亮了好几次。不是消息,是我的 DeepSeek Harness 任务跑完了一轮,我本来想趁排队时间确认一下结果,结果发现手机里除了浏览器和聊天工具,根本没有一个能操作它的入口。那一刻挺泄气的——大模型驱动的任务链明明跑在家里那台机器上,我却被困在一个“只能看不能摸”的状态里。
后来我又试过用手机浏览器直接怼桌面版 Harness 的 Web 界面,结果在触屏上操作命令行工具和查看日志简直是一场灾难。于是我决定动手做一件事:用腾讯开源的 Kuikly 跨端框架,把一个“口袋版 DeepSeek Harness”真正跑起来,放在手机里。这篇文章就是我整个改造过程的完整记录,包括架构取舍、具体代码、三端实测结果,以及我在途中踩过的、官方文档里根本不会写的坑。
如果你手里也有一台跑着 Harness 或者同类 Agent 调试框架的机器,又希望在地铁、开会、摸鱼甚至出差时能随时瞄一眼任务状态、临时改个参数、重跑一次失败任务,这篇博文应该能帮你省下好几天的弯路。
1. 一个反常的需求:PC 上的 Harness 凭什么要搬进手机
1.1 我理解的 DeepSeek Harness,到底是个什么东西
先说清楚我改造的对象。DeepSeek Harness 是我在 OpenViking 社区里接触到的一套用于调试、编排和执行 DeepSeek 智能体任务链的框架,它本身不只是一个命令行工具,还提供桌面客户端和 VS Code 插件,可以把它理解成一个“大模型任务的驾驶舱”。
最常见的用法是:你用自然语言描述一个目标,Harness 把它拆解成多个步骤,动态调度模型工具,然后输出每一步的日志、中间结果和最终产物。和 Codex Harness 这类工具类似,它本质上是一个围绕大模型任务执行过程做可见性管理的系统。
我日常的使用习惯是:在 PC 上开着 Harness Desktop,调试一些 RAG 类任务、批量文本处理任务和定时复盘任务。但问题出在这些任务一旦跑起来,它就不需要我长时间盯屏幕了。可它结束的那一刻,往往恰好是我离开电脑的时候。
所以这个需求是真实存在的:我不是要在手机上重新实现一个 Harness,我需要一个能带着走的状态面板和遥控器。
1.2 为什么不直接在手机浏览器里访问桌面端
这是我第一个被否掉的方案。理论上,Harness Desktop 如果是带 Web 界面的,手机浏览器直接访问就能解决问题。但实际上在移动浏览器里做这种操作,体验真的非常糟糕:
- 桌面端 Web 界面是按宽屏鼠标交互设计的,手机竖屏状态下,侧边栏和日志面板互相挤压。
- 命令输入、参数编辑这种重键盘操作,在手机浏览器里每次都要拉起软键盘,切换输入框就像跳格子。
- 长日志在浏览器里滚动时,触屏的惯性滚动和桌面滚轮的精细控制完全不是一个感觉,看几百行日志眼睛都花了。
- 还有一个要命的点:手机息屏后,浏览器标签页经常被系统回收,再切回来界面就重新加载了,连状态都丢了。
我把这个方案用了一个周末就永久放弃了。结论很直接:移动端必须有一套原生级的交互,不能想着“把桌面工具直接塞进手机屏幕”。
1.3 为什么最终是腾讯 Kuikly,而不是 Flutter、RN 或原生两套
在选型阶段,我手边有几个候选:
| 方案 | 优点 | 我在意的问题 |
|---|---|---|
| Flutter | 生态大、组件丰富 | 团队技术栈以 Kotlin 为主,Dart 引入新语言成本 |
| React Native | JS 生态、社区成熟 | 跨端一致性和原生能力之间的胶水层比较厚 |
| 原生双端(Swift + Kotlin) | 性能极致 | 要维护两套 UI,对我这种主力 Kotlin 的人来说成本太高 |
| 腾讯 Kuikly | Kotlin 技术栈、Compose 风格声明式 UI、支持 Android/iOS/鸿蒙 | 社区相对较年轻,但这正是我想尝试的点 |
我选 Kuikly 的核心原因有三点,不是因为它比所有方案都强,而是在我这个具体场景里,它最对口。
第一,我整个服务端周边代码全是 JVM/Kotlin 写的,Kuikly 是 Kotlin Multiplatform 体系下的 UI 框架,业务逻辑和数据模型可以直接定义在 commonMain 里,两端共用,不用写两遍。第二,Harness 这类工具的输出是流式数据,而 Kotlin 协程 Flow 处理流式响应是天然优势,配合 Kuikly 的 Compose 风格状态管理,代码写起来非常顺手。第三,腾讯开源项目在国内落地时,对鸿蒙这类新平台的支持跟进比较积极,而我的测试机里刚好有一台鸿蒙平板。
用一句话总结我的选型思路:我不是在选一个“完美的跨端框架”,我是在选一个最能贴合我现有技术栈和场景的框架。Kuikly 对我这种 Kotlin 老用户来说,学习曲线是最平滑的。
2. 动手前先搭好三层:Harness 服务、API 网关、Kuikly 工程
2.1 Harness 侧:先让它“可以被远程请求”
既然手机要操作 Harness,那 Harness 这边首先得暴露一套可被程序调用的接口。我按 OpenViking 官方仓库的 README 走了一遍安装流程:拉取源码、安装依赖、启动本地服务。不同的版本启动方式不太一样,有的用 Python 启、有的用 Node 起,我这边实际采用的是容器化部署,把 Harness Core 跑在一个固定端口上。
这里强调三件事,都是我实际踩过的:
服务端不要直接暴露公网。我一开始图省事,把服务端口映射到了公网 IP,结果半天之后日志里就出现了扫描器的试探记录。后来我改成只在局域网内网段监听,并把服务端绑定到 Docker 网卡上,手机通过同一局域网访问,安全性才踏实。Harness 会持有模型 API Key 和任务执行权限,这不是闹着玩的。
提前确认端口和协议。移动端对接之前,先用 curl 在 PC 上验证一遍最基本的任务列表和日志接口。我见过太多人跳过这一步,结果写了好几百行移动端代码才发现服务端接口路径跟自己理解的不一样。
Harness 本身可能有桌面端、CLI 和服务三套模式,务必确认你启动的是服务模式。我当时就犯过这个错:启动 CLI 交互界面半天,以为它在等外部请求,其实那个进程根本没有监听任何端口。
下面这个命令是我这边的启动示意,具体参数以你所用版本的官方文档为准:
# 示例:启动 Harness 服务并监听 8080 端口 harness serve --port 8080 --host 0.0.0.0启动之后,我在 PC 上验证了一下:
curl http://127.0.0.1:8080/api/tasks curl http://127.0.0.1:8080/api/tasks/current能拿到 JSON 返回,说明这一层就通了。
2.2 API 网关层:移动端只需要六个接口
打通 Harness 服务之后,我没有让手机直接去调 Harness 的“全量接口”,而是自己加了一个很薄的中间层。为什么?因为 Harness 本身的接口是给桌面客户端用的,字段特别多,有的任务对象嵌套了五六层,在移动端网络状态不稳定的情况下,直接把全量数据拉到手机里,体验会很差。
我用 Ktor 写了个轻量网关,把 Harness 输出重新映射成移动端友好的结构。中间层不是一个重后端,只做三件事:字段裁剪、SSE 聚合、配置校验。最终手机端只需要跟六个接口打交道:
| 接口 | 方法 | 作用 |
|---|---|---|
/api/tasks | GET | 获取活跃任务列表 |
/api/tasks/{id} | GET | 获取单个任务详情 |
/api/tasks | POST | 发起一个新任务 |
/api/tasks/{id}/cancel | POST | 取消指定任务 |
/api/tasks/{id}/events | GET(SSE) | 订阅任务实时事件流 |
/api/configs | PATCH | 更新 Harness 运行参数 |
这里有一个关键设计决策:实时事件流我选了SSE(Server-Sent Events)而不是 WebSocket。因为 Harness 服务端的输出天然就是单向流——任务状态往客户端推,客户端并不需要频繁往服务端推消息。SSE 在移动端还有一个额外优势:它基于普通 HTTP,网络栈的兼容性好,配合 OkHttp 很容易做断线重连。
网关层还有一个职责是缓存。Harness 原始接口每次都会返回全部日志,而日志在移动端是一个高频读取的大字段。我在网关层加了简单的内存缓存,按任务 ID 维度缓存最近 200 条日志,让手机每次只拿增量。这个优化后面实测下来,对流量和数据解析耗时的改善非常明显。
2.3 Kuikly 工程创建与依赖配置
中间层就绪之后,我开始搭 Kuikly 客户端工程。这部分如果你有 Android 开发经验,上手会非常快。我在 Android Studio 里创建了一个 Kuikly 跨端工程,工程结构默认就有androidMain、iosMain、commonMain,鸿蒙那侧我另外通过 Kuikly 的构建脚本接入。
依赖方面,我加了这几个关键库:
// commonMain 中的依赖示意 implementation("io.ktor:ktor-client-core:2.3.x") implementation("io.ktor:ktor-client-okhttp:2.3.x") implementation("io.ktor:ktor-client-content-negotiation:2.3.x") implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.x") implementation("io.ktor:ktor-client-auth:2.3.x") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.x") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.x")这里踩了一个很隐蔽的坑:Kuikly 的版本和 Kotlin 编译器的版本捆绑非常紧。我第一次建工程时随手升级了 Kotlin 插件到最新版,结果编译直接报错,报错信息指向 Kuikly 内部一段不兼容的代码。后来我把 Kotlin 版本退回到 Kuikly 官方示例工程的同一版本,问题才消失。所以我的建议是:创建 Kuikly 工程时,别急着升 Kotlin 版本,先以官方模板自带的版本为准,跑通再动。
工程建好后,我在commonMain里先定义了一个数据模型池,把任务、日志、状态这些实体类全都放在这里。Kuikly 的这套跨端模型机制,让我在 Android 和鸿蒙两端都不用再重复定义这些类。
3. 核心改造:我给每个 Harness 任务做了移动端“驾驶舱”
3.1 任务列表页:LazyVerticalGrid 与活跃任务卡片
移动端 Harness 的首页,我没有做成传统列表,而是用 Kuikly 的 Compose 风格组件写了一个网格布局的任务卡片墙。每个卡片显示五个信息:任务名、当前状态、已耗时、最近一条日志摘要、操作按钮(取消/重跑)。
这里有个设计逻辑:桌面端 Harness 的任务列表是“表格视图”,信息密度高。但手机上屏幕就这么大,我必须做信息降维。我的取舍是——卡片只显示“要不要干预”的信息,不显示“完整执行过程”的信息。比如一个任务跑得好好的,我只看它跑了多久、当前进度就够;只有失败了,我才需要点进去看详细日志。
代码层面,页面加载时我先调一次GET /api/tasks拉全量快照,然后立刻订阅当前任务的事件流,这样既能快速渲染首屏,又能拿到后续的实时更新。关键是要在页面不可见时取消订阅,我用DisposableEffect处理了生命周期切换,避免页面在后台还持有网络连接。
DisposableEffect(Unit) { val job = scope.launch { api.getTasksSnapshot().collect { snapshot -> state.tasks = snapshot } } onDispose { job.cancel() } }这个设计的讽刺之处在于,我做这个“口袋版 Harness”,本质上就是为了让自己敢离开电脑。如果它本身还会让手机耗电跑到飞起,那就本末倒置了。所以连接生命周期管理是我最看重的一环。
3.2 流式输出面板:从 SSE 到协程 Flow 的完整管线
任务详情页是整个项目里技术含量最高的部分。Harness 在跑任务时,会产生大量的流式输出,包括模型中间决策、工具调用记录、步骤日志等。桌面端可以开一个终端窗口滚动刷屏,但手机端必须做一套“让流式数据可控”的机制。
我的管线是这样的:
- 进入详情页,先调
GET /api/tasks/{id}拿历史日志。 - 建立 SSE 连接,从
GET /api/tasks/{id}/events读取实时事件。 - 通过
callbackFlow把 OkHttp 的流式回调转成 Kotlin Flow。 - 在 ViewModel 层做事件分类,拆分为“状态变更事件”和“日志追加事件”。
- UI 层只订阅经过拆分的 StateFlow,用
collectLatest渲染。
以下是简化版的事件流转换示意:
fun sseEvents(taskId: String): Flow<HarnessEvent> = callbackFlow { val request = Request.Builder() .url("${baseUrl}/api/tasks/$taskId/events") .header("Accept", "text/event-stream") .build() val call = client.newCall(request) call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { val source = response.body?.source() ?: return while (!source.exhausted()) { val line = source.readUtf8Line() ?: break if (line.startsWith("data:")) { trySend(Json.decodeFromString<HarnessEvent>(line.drop(6))) } } } override fun onFailure(call: Call, e: IOException) { close(e) } }) awaitClose { call.cancel() } }写到这里必须强调一个我自己踩过的坑:Harness 的事件流并不是严格只有一种事件格式。它可能在一个连接里同时推送模型输出、步骤状态、心跳信息。我第一次对接时想当然地把所有data:消息都当成日志追加事件,结果 UI 上出现了“状态变成日志”“日志变成任务标题”的诡异现象。
后来我被迫给事件类型加了显式区分:状态事件、日志事件、心跳事件分开解析,然后在状态机里做约束。移动端这个任务状态机我定义了五个状态:
IDLE:任务排队中,还没开始跑RUNNING:任务执行中,持续有日志流入PAUSED:任务暂停,等待人工输入或外部资源SUCCEEDED:成功完成FAILED:执行失败
每个状态之间的转换规则,是我在事件流解析层硬编码的。虽然简陋,但它保证了一个最基本的原则:同一个事件不会同时出现在两个状态通道里。
3.3 配置中心:改模型参数不用再回电脑
Harness 的配置项其实挺多的,模型名称、温度、最大 token 数、top_p、任务并发数等等。PC 上改配置不算麻烦,但想象一个场景:我在外面发现今天跑的任务总是触发超时,想临时把超时阈值调大一点,这时候如果还要跑回电脑改配置文件再重启服务,那就太傻了。
所以我做了个配置中心页面,把常用的 Harness 配置项做成了表单。每个配置项在加载时从网关拉默认值,修改后点击保存就调用PATCH /api/configs接口。
真正的难点不是表单 UI,而是配置校验。Harness 有些配置之间是有关联约束的,比如并发数和模型推理 batch 大小相互影响,温度值范围是 0 到 2。我在前端只是做了一个基本的范围校验,但更严谨的做法是网关层再做一次基于当前运行状态的校验。为什么我后来还是决定在网关层补了?原因很现实:有一次我把并发数调到了 32,Harness 服务端直接内存溢出,整个服务崩掉了,所有在跑的任务全部断掉。那是我这个项目上线以来最惨的一次事故。
所以这里必须做一个保守策略:所有配置更新接口,网关层先尝试用临时配置启动一个极简测试任务,验证能跑通,再应用到全局。如果测试任务失败,接口返回错误信息,手机端显示“配置无效,已回滚”。
3.4 快捷指令与日志上报:移动端场景的增量价值
移动端改造不只是把桌面功能复刻一遍,它应该有一些桌面端没有的便利。我自己最常用的是“快捷指令”功能:把一些高频率的 Harness 任务预置成指令模板,比如“运行安全巡检”“生成今日汇总”“重跑上一轮失败任务”。在手机上一键触发,在 PC 上只需配置一次。
另一个增量功能是“日志上报”。Harness 任务经常在半夜跑完,早上起来手机上只看到了一个“失败”状态,但完全不知道失败在哪。我的做法是:在任务失败事件触发后,手机端自动拉取最近 200 行日志,并带上客户端时间戳存到本地数据库,就算 Harness 服务端那边日志被清理了,手机上也有一个最近失败记录的历史副本。
这个功能看起来不起眼,但我实际用了一周后,发现它对我的价值甚至超过任务列表和流式日志——因为它把“事后分析”这个高频需求真正搬到了口袋。
4. 真机实测:三端跑通之后,Harness 移动化的体验真相
4.1 性能数据:启动、滚动、流式渲染
整个客户端跑通后,我在一台 Android 旗舰机、一台 iPhone 和一台鸿蒙平板上做了基础性能测试。重点看四个指标:冷启动时间、列表滚动帧率、SSE 首字延迟、内存占用。
| 指标 | Android 设备 | iOS 设备 | 鸿蒙平板 |
|---|---|---|---|
| 冷启动到首页可用 | 约 1.2s | 约 1.1s | 约 1.4s |
| 任务网格滚动帧率 | 稳定 60 帧 | 稳定 60 帧 | 平均 55 帧 |
| 日志页面首屏可用耗时 | 约 300ms | 约 280ms | 约 350ms |
| 长任务日志页内存占用 | 约 85MB | 约 90MB | 约 110MB |
日志页内存占用最大,是因为我为了保留“回溯日志”的功能,在内存里缓存了大量已经滚过屏幕的日志行。鸿蒙平板上 110MB 其实也可以接受,但我后来还是做了分页回收,只保留当前可见区域前后各 200 行的完整文本。
相比之下,SSE 首字延迟让我最意外。我当时预估移动端走网关再走服务端,首字延迟至少 100ms。实测结果显示,网关层的字段裁剪和事件分类几乎没有带来额外开销,首字延迟在局域网环境下稳定在 8 到 15ms 之间。也就是说,手机上的流式日志和 PC 桌面端基本是同步的。
4.2 体验上的“三快”和“三慢”
真实的移动化体验不是性能数据能完全反映的,我用了一段时间之后,感受到明显的三快三慢。
三快,是指这三个场景明显比电脑操作更快:
- 离开电脑后查看任务结果:原来我下楼买菜都要惦记着任务跑没跑完,现在随时掏手机看一眼就行。
- 临时取消误发任务:有一次我配置错误发了一个超大任务,电脑又不在手边,手机上一键取消了,避免了浪费模型 API 额度。
- 失败后快速定位原因:手机直接看失败日志,比回电脑开桌面端再点好几层菜单快太多。
三慢,则是指这三个场景在手机上确实比电脑慢:
- 编辑复杂配置:手机软键盘输入 JSON 和长文本确实不舒服,所以我后来的配置中心尽量都用下拉框和滑块代替文本框。
- 浏览长日志定位关键字:手机没有快捷键和全选复制这一套组合操作,必须靠搜索框逐条跳转。
- 多任务并排对照:桌面端可以开一个终端窗口同时看两个任务的输出,手机上单页详情只能看一个任务。
这些发现也反过来影响了我后续的设计决策:凡是涉及“快速查看”和“一键操作”的功能都做进移动端;凡是涉及“沉浸式编辑”和“复杂比对”的功能,就默认留到 PC 端去做。
4.3 移动端与桌面端的边界划分
跑通之后,我对这套工具链的定位变得清晰了。桌面端仍然是我开发和调试 Harness 任务的主战场,它有全尺寸终端、鼠标键盘、多窗口,它是“工作台”。而移动端不是“精简版 Harness”,它是“驾驶舱状态牌+遥控器”。
我给自己定了一个边界原则:凡是无法在 30 秒内完成的操作,移动端就不刻意做。比如在手机上编辑一个完整的 RAG 知识库构建流程,这种事我不会做;但手机上一键触发“重跑上一轮失败任务”,这种 10 秒内闭环的操作,才是移动端的核心价值。
这个原则避免了移动端功能无限膨胀,也让我在后续迭代时能清楚地判断一个需求该不该做、该做到什么程度。
5. 踩坑记录:这些细节在官方示例里基本不会出现
5.1 时区导致的任务时间“穿越”
上线第一天我就发现一个诡异的问题:Android 手机上显示的任务开始时间比 PC 上早了 8 小时。查了一遍才发现,Harness 服务端返回的时间戳带+08:00时区信息,而我手机系统设置的是 UTC+9(我当时刚好在日本出差)。移动端代码里我直接用了Date.parse(),Kotlin 对它做了本地时区解释,导致显示混乱。
修复很简单:所有时间字段在网关层统一转成毫秒时间戳下发,客户端只负责展示,不做时区语义解释。但排查这个问题的过程还挺有代表性——你永远不知道一个看似简单的时间格式化会在什么奇奇怪怪的时区组合下出问题。
我把这个经验固定成了一条编码规范:跨端传输的时间一律用 epoch 毫秒,绝不用带时区的字符串。
5.2 SSE 断线重连:电梯和地铁是流式连接的天敌
SSE 最怕的不是服务端崩溃,而是移动网络切换。我坐地铁从地上进地下那一刻,Wi-Fi 断掉、蜂窝信号弱,SSE 连接会静默断开。糟糕的是 OkHttp 默认的 readTimeout 是 10 秒,但我遇到的场景往往超过 10 秒,等到触发超时再重连,中间已经丢了一大段日志。
我的解决方案有三层:
- 缩短 readTimeout,让失败更早暴露:从 10 秒改成 5 秒。
- 应用层心跳:服务端每 15 秒发一个
ping事件,客户端如果在 45 秒内没收到任何数据或心跳,就主动断开重连。 - 指数退避重连:第一次重连等 1 秒、第二次 2 秒、第三次 4 秒,最长时间不超过 30 秒,避免在信号不稳定的环境里疯狂重连。
重连之后,客户端会先调一次任务快照接口,把断线期间的遗漏补回来,再重新订阅事件流。这段逻辑是整个项目里最繁琐的部分,但也是稳定性提升最明显的一部分。
5.3 后台任务被系统杀死:移动端独特的生命周期问题
在 PC 上,一个进程只要不被用户手动杀掉,就能一直在后台跑。手机则完全不同,系统会在内存紧张时回收后台页面的进程。
最开始我的移动端 App 退到后台超过几分钟,再切回来时,任务流断了,界面还停留在断线前的状态,没有任何提示,必须手动下拉刷新才能恢复。后来我在生命周期回调里加了状态恢复逻辑:
override fun onResume() { super.onResume() if (lastEventTime < System.currentTimeMillis() - 10_000L) { viewModel.refreshAndResubscribe() } }这个逻辑很简单:App 从后台恢复时,如果发现距离最后一条事件超过 10 秒,就认为连接可能已断,先拉一次全量快照,再重新订阅事件流。
5.4 排查“乱冒字”的完整链路:从日志错乱到事件边界问题
热搜词里有个高频问题叫“DeepSeek Harness 胡乱冒字出来”,我遇到过两次,这里完整还原一下排查思路,因为它很有代表性。
第一次遇到,是 Harness 在跑一个长文本生成任务时,模型输出的正文里突然冒出一段完全无关的汉字片段,看起来既不是模型正常输出的内容,也不是日志注入。我当时第一反应是“模型幻觉了”,于是去 Harness 服务端看原始输出日志,发现服务端日志是正常的,没有这段“乱冒字”。
这就排除了模型本身的问题。接着我去看网关层收到的 SSE 原始报文,发现一个有意思的现象:有些 SSE 事件包的data字段里夹带了不属于它的事件 ID 前缀。原来是服务端在极少数高并发场景下,会连续输出两个事件,而网络缓冲区在移动网络环境下发生了粘包,导致两个事件的边界没有被正确切分。
排查到此,根因已经很清楚了:不是模型乱说话,而是事件流传输边界被破坏,两段不同位置的数据被拼成了一段,移动端解析后才产生了“乱冒字”的观感。
修复方式是两条:
- 在网关层用
\n\n严格切分 SSE 事件边界,避免粘包。 - 在客户端解析事件之前,增加一个简单的事件格式校验,如果事件内容不符合预期结构,丢弃并记录日志,而不是直接渲染到 UI 上。
这个问题给我的启发是:在移动端对接流式接口,不能把服务端返回的每一个事件都“无条件相信”。网络层可能发生任何魔幻的事情,接口消费方必须要有自己的容错机制。这也是我后来把“解析失败丢弃并告警”作为默认策略的原因。
6. 后续可以怎么扩展,以及我最后想说的
在不同设备上跑了一个多月之后,这个口袋版 DeepSeek Harness 已经成了我日常依赖的工具。我出门不用再担心家里的任务跑挂,也不会因为在外面错过了任务结果而焦虑。
这个项目后续我还打算做三个方向的扩展。第一个是桌面小组件:把活跃任务状态直接放到手机桌面,不用进 App 就能看到当前任务跑没跑完、成没成功,信息获取成本进一步降低。第二个是语音输入触发快捷指令:在外面不方便打字的时候,直接说一句话生成一个 Harness 任务,这跟快捷指令能力是一脉相承的。第三个是后台定时巡检模式:让 App 在指定时间段检查任务状态,仅在失败或异常时推送通知,其他时间保持静默,把“监控”和“打扰”的边界做对。
最后分享我自己总结的一条心得:工具链移动化的本质,不是把桌面功能搬一份到手机上,而是把最核心的“状态感知”和“紧急操作”提炼出来,让用户在任何地方都能掌握主动权。与其追求功能对齐,不如先想清楚手机这个场景下,哪些操作是真正高频、真正紧急的。想清楚了,技术选型、架构设计和交互细节都会顺势变得清晰。