ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

语音输入补进 DSH Mobile,TaoToken 继续给 Harness 供模型调用

语音输入补进 DSH Mobile,TaoToken 继续给 Harness 供模型调用 1. 从 session.prompt 的图片数组到语音输入DSH Mobile 的下一步DSH Mobile 下一步要补的不是一个孤立的话筒按钮而是把语音输入接进session.prompt的内容链路。电脑端 Harness 继续负责 Agent 循环、工具执行和插件运行模型 Key 统一去 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentvoice_intro获取Base URL 填https://taotoken.net/api。手机端要做的是拿到语音、转成文本、可选附带图片引用、调用session.prompt、处理断线恢复并把模型请求日志留在电脑侧可查。session.prompt已经支持图片内容数组这件事对移动端交互开发者很有参考价值prompt 的 content 并不是一段死字符串而是可以承载多模态片段的结构。语音最稳妥的接入方式不是把音频硬塞进模型而是先在客户端或电脑侧完成 ASR把识别文本作为text片段发送如果用户同时拍了一张报错截图再把图片引用作为image片段追加进去。这样既不破坏现有 RPC 结构也能让 Harness 继续用同一套模型供应商配置。本文的可复现产出有两份一份语音输入链路草案一份 Harness 模型请求日志字段规范。前者决定 Android、iOS、鸿蒙三端怎么共享录音与识别状态后者决定当语音发送失败、模型调用超时或请求重复时你能不能快速定位是客户端、隧道、Harness 还是 TaoToken 侧的问题。2. 语音输入链路草案从按住说话到 session.prompt 的可复现路径移动端语音输入最怕做成“录完再想”。用户按一下、说半句、发现识别错了又得重来。对 DSH Mobile 这种远程控制面板来说语音输入应该围绕短指令和补充信息设计例如“继续跑刚才的任务”“允许这次审批”“把错误日志里的 502 改成 504 再试一次”。链路可以拆成七步入口交互按住说话、上滑取消、松手进入识别预览。不要松手立刻发送先给用户一次确认或修改机会。权限申请Android 需要RECORD_AUDIOiOS 需要NSMicrophoneUsageDescription鸿蒙侧也要在模块配置中声明麦克风权限。权限被拒绝时给出可操作提示而不是只弹一个“失败”。录音参数统一为 16 kHz、单声道、16 bit PCM按 100 ms 左右分片。三端底层 API 不同但写入共享层的 chunk 结构必须一致。端点检测用 RMS 或 VAD 判断用户是否说完。短指令场景下静音超过 800 ms 可以自动停止长语音则保留手动停止。ASR 转换优先调用系统识别能力拿到 partial text 和 final text。系统识别不可用时把音频文件写入本地缓存通过已经建立的 SSH 或 Relay 隧道交给电脑侧自建 ASR 适配器。没有适配器时只保留录音草稿不上传到不明服务。文本确认把 final text 回填到输入框允许用户手动改错别字。如果带图片把图片引用加到 content 数组里。调用session.prompt用 HTTP RPC 发送同时生成clientMsgId做幂等。重连后只恢复观察和控制不重新执行已经发过的 Prompt。共享层接口可以长得像下面这样三端分别实现页面只依赖接口// commonMain interface DshVoiceInputModule { suspend fun ensurePermission(): Boolean fun start(): kotlinx.coroutines.flow.FlowVoiceChunk suspend fun stop(): VoiceDraft suspend fun cancel() } data class VoiceChunk( val sequence: Long, val pcm: ByteArray, val rms: Int, val timestampMs: Long ) data class VoiceDraft( val audioPath: String, val durationMs: Long, val partialText: String, val finalText: String? )发送时不要为语音另造一套 RPC。session.prompt本来就是发消息入口语音只是多了一步“先转文本”。下面是一个 Kotlin 侧发送示例clientMsgId推荐由客户端生成并持久化到当前草稿中suspend fun sendVoicePrompt( sessionId: String, text: String, imageRefs: ListString emptyList() ) { val clientMsgId generateClientMsgId() val content buildList { add(mapOf(type to text, text to text)) imageRefs.forEach { ref - add(mapOf(type to image, ref to ref)) } } dshHostProtocol.rpc( method session.prompt, body mapOf( sessionId to sessionId, clientMsgId to clientMsgId, content to content ) ) }这里有两个细节值得移动端开发者注意。第一clientMsgId不能每次重试都变否则断线重发会产生两条用户消息。第二语音识别结果如果在录音结束后才返回页面状态要区分“录音中”“识别中”“待确认”“已发送”否则用户会以为按钮没反应。3. 在 Kuikly commonMain 里下沉语音 Module三端差异只留在桥接层DSH Mobile 用 Kuikly 做跨端原生核心思路是共享业务语义把平台差异压到最底层。语音输入也应该沿用这套拆法在commonMain定义DshVoiceInputModuleAndroid 用AudioRecordiOS 用AVAudioEngine鸿蒙用AudioCapturer但上层页面只看到start()、stop()、cancel()和统一的数据结构。这样做的收益在协议变化时会放大。今天语音只转文本明天如果session.prompt的内容数组新增了音频附件类型或者你要把语音草稿同步到电脑侧做二次识别只需要改共享层的状态机和适配接口不需要在三个宿主工程里各写一遍。Android、iOS、鸿蒙宿主代码仍然会有三四千行量级但那些代码主要处理权限、前后台、音频焦点、蓝牙耳机切换、系统中断等平台能力不承载业务状态机。语音模块还要和已有的连接层配合。DSH Mobile 直接连接电脑侧 Harness 暴露的 Host 协议手机不重新实现 Agent 循环。连接方式可能是 SSH 隧道也可能是扫码 Relay。语音识别如果走电脑侧适配器音频上传必须复用现有隧道不能再开一条公网通道。录音文件放在本地缓存发送成功后按策略清理发送失败则保留草稿等重连后再让用户决定是否重发。页面层可以按 16 ms 合帧更新识别文本。系统 ASR 的 partial result 可能来得很密如果每个字都触发一次重组长语音会明显掉帧。做法是录音波形按 chunk 更新识别文本按固定时间窗口合并最终文本只在finalText到达时写入输入框。4. 电脑端 Harness 改走 TaoTokenBase URL、API Key 与模型请求日志手机端语音链路能不能跑通最终还要看电脑端 Harness 的模型调用是否稳定。在电脑侧配置模型供应商时Key 去 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentharness_provider创建或获取Base URL 填https://taotoken.net/api。注意 Base URL 是给工具和 SDK 用的不要在后面拼接 UTM 参数UTM 只用于官网访问统计。如果 Harness 通过环境变量读取模型配置可以先用下面这组方式验证通道。Key 用占位符YOUR_API_KEY实际值不要提交到仓库。# 电脑端 Harness 运行时环境 export TAOTOKEN_API_KEYYOUR_API_KEY export OPENAI_BASE_URLhttps://taotoken.net/api export OPENAI_API_KEY$TAOTOKEN_API_KEY # 如果当前适配器走 Anthropic 兼容通道 export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKEN$TAOTOKEN_API_KEY配置完成后不要只依赖界面上的“成功”提示要在 Harness 侧打一份模型请求日志。建议用 JSONL每行一个请求字段至少包含{ts:2026-01-15T10:00:00.123Z,request_id:req_01J...,session_id:sess_abc,source:mobile_voice,provider:taotoken,base_url:https://taotoken.net/api,model:claude-sonnet-4-20250514,prompt_chars:42,image_count:0,latency_ms:812,status:200,input_tokens:128,output_tokens:56,event_seq:9012}这份日志能回答几个高频问题请求有没有真正发出、走的是不是 TaoToken、Base URL 有没有写错、模型名是否被供应商拒绝、语音转文本后的 prompt 长度是否异常、响应延迟是否集中在 ASR 之后。日志里不要记录完整 Authorization Header也不要记录用户原始音频。需要排查时用request_id和session_id关联客户端事件流即可。如果日志里完全没有请求先检查 Harness 进程是否加载了新环境变量如果status是 401 或 403检查 Key 是否复制完整、是否在正确项目下创建如果status是 404优先检查 Base URL 是否误写成了带路径或带查询参数的形式。调试入口可以放在 TaoToken 官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentrequest_log的控制台侧但请求日志主体仍然留在电脑本地避免把敏感上下文传到无关位置。5. 用 Claude Code、Codex 与 CC Switch 验证 TaoToken 通道在正式把 Harness 切到语音工作流之前可以先用 Claude Code 和 Codex 验证 TaoToken 的模型通道。这样做的好处是把“客户端语音问题”和“模型供应商配置问题”分开。Claude Code 用settings.json变量是ANTHROPIC_*Codex 用config.toml不要把ANTHROPIC_*套到 Codex 上。Claude Code 的settings.json可以这样写{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-20250514, ANTHROPIC_SMALL_FAST_MODEL: claude-3-5-haiku-20241022 } }Codex 的config.toml单独配置不要混用 Anthropic 变量model gpt-5-codex model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat # 若控制台模型要求 Responses API再改为 responses对应环境变量export TAOTOKEN_API_KEYYOUR_API_KEY如果你使用 CC Switch 管理多套配置可以把它当成“三件套”来理解Claude Code、Codex、OpenAI 兼容客户端各维护一套 Profile切换时只换 Base URL 和 Key不交叉污染变量名。Profile用途Base URLKey 变量注意Claude Code验证 Anthropic 兼容通道https://taotoken.net/apiANTHROPIC_AUTH_TOKEN与settings.json保持一致Codex验证 OpenAI 兼容通道https://taotoken.net/apiTAOTOKEN_API_KEY不要写ANTHROPIC_*OpenAI 兼容客户端调试 Chat Completionshttps://taotoken.net/apiTAOTOKEN_API_KEY按客户端字段填写验证时让 Claude Code 或 Codex 跑一个最小请求然后回到 Harness 请求日志确认provider是taotoken、base_url是https://taotoken.net/api、status是 200。如果这里都正常再回到手机端语音链路排查录音、ASR 和session.prompt。6. 锁屏、切后台、重连与重复发送移动端语音交互排障清单语音输入比纯文本输入多了一层系统能力排障时不要只盯着模型。下面这份清单按移动端真实场景排列适合作为 DSH Mobile 语音功能的自测表。第一权限与音频焦点。Android 切到后台后可能被回收录音权限或音频焦点iOS 来电、闹钟、其他 App 抢占麦克风都会导致录音中断。处理方式是监听中断事件保存已经录到的音频并把状态改为“待确认”或“草稿”不要让页面卡在“录音中”。第二VAD 阈值。短指令场景下用户说“允许”可能只有几百毫秒VAD 阈值过高会直接截掉。可以先固定一个保守阈值再把录音前 300 ms 和后 500 ms 保留下来避免首尾字丢失。第三ASR 空结果。先检查采样率、声道数和编码格式是否符合识别引擎要求。系统识别不接受 16 kHz 单声道时需要在平台层重采样。识别失败时不要把空字符串发给session.prompt否则用户会看到一条没有内容的 Prompt。第四session.prompt超时。移动网络切换、Relay 断线、电脑休眠都会导致 RPC 超时。客户端要用clientMsgId标记这条语音 Prompt超时后进入待重试状态。重连成功后先补session/event再拉session.history对齐聊天记录最后用最新快照覆盖 queue 和 jobs。重连是恢复观察和控制不是重新执行任务。第五重复发送。用户看到超时提示后可能手动再点一次发送。如果客户端没有幂等键Harness 会收到两条相同内容。建议在草稿创建时生成clientMsgId重试时复用同一个值服务端按clientMsgId去重。第六图片与语音混合发送。session.prompt支持图片内容数组但语音转文本后应该以text为主图片作为可选补充。不要把大段音频直接塞进 content 数组除非 Harness 协议已经明确支持音频类型。长音频和长文本更适合拆成多次短 Prompt移动端只做决策入口。第七日志关联。客户端日志里记录clientMsgId、录音时长、ASR 耗时、RPC 耗时和最终request_id。Harness 日志里记录模型请求字段。两端通过session_id和clientMsgId关联。这样当用户说“我刚才那条语音发出去没反应”时你能在几分钟内判断是没录上、没识别、没发出还是模型调用失败。7. 落地顺序与 CTA模型对话 → Coding Plan → 创建 Key → Claude Code 文档语音输入补进 DSH Mobile 的落地顺序可以很务实先在 KuiklycommonMain定义语音接口和状态机再补 Android、iOS、鸿蒙三端录音实现接着把 ASR 结果回填到输入框用clientMsgId调session.prompt最后在电脑端 Harness 配置 TaoToken 模型通道并打开请求日志验证。整个过程不需要把 Agent 循环搬到手机手机仍然是短而高频交互的决策节点。如果你还没有配置模型 Key可以按下面路径走一遍先打开模型对话看可用模型与返回格式再根据使用量选择 Coding Plan然后创建 API Key 并填入 Harness 或本地工具最后用 Claude Code 文档核对settings.json和ANTHROPIC_*配置。Base URL 始终填https://taotoken.net/apiKey 用YOUR_API_KEY占位实际值不要提交到 Git。模型对话https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentcta_chatCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcta_coding_plan创建 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentcta_api_keysClaude Code 文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentcta_claude_code_doc官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentfinal_step当语音输入、session.prompt、Harness 模型请求日志和 TaoToken 通道都跑通后DSH Mobile 能接住的不只是文字追问还包括通勤路上的一句“继续跑”、会议间隙的一次审批、排队时对 Agent 追问的即时补充。移动端开发者要盯住的仍然是那条链路录音是否可靠、识别是否可改、发送是否幂等、重连是否补事件、模型请求是否有日志。把这些做扎实语音输入才不是演示功能而是远程 Agent 工作流里真正可用的一环。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进