陌生推销电话里有这样一个瞬间:对方已经接起,正在说话,你在点头。但你心里一片空白。一个字也没听懂。
尤其是电话那头的英国人带着某种特别的口音,或者印度人说得又快又自信。这两种情况,都是我刚在迪拜做经纪人那几周的现实。
我是设计师。我会设计复杂系统,懂产品。但和陌生口音的人通电话是另一项技能,不会因为你想要就自动出现。
本可以只管练习、慢慢等。我决定做一个工具。
什么是 CallCoach
一个用 Swift 编写、结合 Apple Intelligence 和 Live Translation 的原生 iOS 应用。
通话时,它实时识别对方的语音,将文字直接显示在屏幕上。分析对方说了什么,提供提示——怎么回答、如何接着聊、该问什么。

这不是为了转写而转写,而是一位话术助手,恰好在需要时出现——在真实对话中,毫无延迟。
为什么选择 Swift 和 Apple Intelligence
我本可以用任何 Web 技术栈。但 Web 无法提供所需的速度,以及与麦克风、系统 API 的原生集成。
Apple Intelligence 是直接在设备上运行的本地语言引擎。无需请求云端,没有延迟,没有对话数据泄露。一切都在手机上。
Live Translation 能以所需精度处理带口音的英语。正适合迪拜这样的多国籍城市。

它如何改变了工作
当你看见对方所说的话的文字,焦虑就消退了。不再把精力花在“他说了什么”,而是开始想“我该怎么回答”。
从求生模式,进入对话模式。根本性的转变。
我还在继续开发。下一步是针对不同场景建立话术库:首次通话、异议、澄清预算、约定见面。提示不只是转写,而是针对具体话语给出智能的上下文回应。
我为什么讲这件事
CallCoach 是我思考产品方式的一个例子。
不是“试什么技术”,而是“解决什么问题”。问题具体而切身。解决方案原生、快速,没有多余的依赖。
会写代码的设计师并不等于开发者。但有时正是这一点,让想法不至于死在等待实现的队列里。
技术实现
从技术角度,我把 CallCoach 当作真正的工作工具来做,而不是概念稿。
首先,我和 ChatGPT 一起梳理想法与架构:明确产品任务、用户场景,为 Codex 准备一份很长的初始提示词,让它创建基础 Swift 项目,配置依赖,搭出第一个可运行的应用骨架。
Codex 的 token 窗口用完后,我把项目交给 Claude Code。接下来开发中真正的“硬菜”都在那边完成,使用 Opus 4.7 模型。
它尤其擅长长线工程:思考很多,消耗很多 token,但能很好地守住架构,认真编写彼此衔接的代码,帮助把应用打磨到可用。
之后我开始定期切到 Xcode:构建、跑测试、捕捉崩溃,把错误交回 Claude Code,一起修复问题。有些修改直接在 Xcode 里完成,我也在那里接入 Codex 和 Claude,作为随处可用的助手。
应用逻辑如何组织
应用围绕设备上的本地语音处理构建。
普通模式下,CallCoach 可以通过麦克风监听音频流,显示实时转写。但最关键的技术决定是分离双方声音。
主要问题是,使用扬声器通话时,麦克风会同时听到你和对方。结果转写混入双方话语,无法再有效用于辅导。
为了解决这个问题,我做了两种模式。
模式 1——System Audio
这是主要的推荐模式。
应用通过 ScreenCaptureKit 捕获系统音频输出,也就是只捕获送往扬声器的声音。这样就能只转写对方的声音,不会录入我通过麦克风发出的声音。
此外还使用标志 excludesCurrentProcessAudio = true,以排除应用自身的声音。
首次启动时,macOS 会请求屏幕录制权限——没有这个权限,就无法捕获系统音频。
模式 2——Microphone + Push-to-Mute
这是备用方案。
它使用普通麦克风采集,但我自己说话时,可以按住按钮,暂时停止向转写器发送音频缓冲区。
逻辑很简单:我说话时按住按钮,说完松开,应用重新开始听对方。
代码里实现了什么
从架构上,应用由几个独立管理器组成,每个负责处理流水线的一部分。
AudioCaptureManager
负责通过以下组件采集音频: AVAudioEngine.
- 使用
installTap作用于inputNode startRecording()返回AsyncStream<AVAudioPCMBuffer>- 缓冲区流继续向下传递,不会遇到以下问题:
跨 actor 传递(actor crossing) - 同时通过 RMS 和 dB 计量计算信号电平
- UI 更新被发送到
@MainActor通过Task
SystemAudioCaptureManager
这是我为捕获系统音频单独添加的类,通过 SCStream.
正是它让转写只接收扬声器里对方的声音,而不是周围全部的声学噪声。
TranscriptionManager
这一层接收音频缓冲区流,并将其传给 SpeechAnalyzer ,来自 Speech 框架。
随后遍历 AsyncThrowingStream<SpeechTranscriptionResult>,区分临时与最终转写,并显式管理切换到 @MainActor.
也就是说,已经不只是“识别声音”,而是把流式转写稳妥地整合进实时界面。
CoachingEngine
这是一个独立逻辑层,将普通转写变成有用的对话工具。
processNewTranscript(_:)使用 1.5 秒防抖- 上一个
Task在收到新的语音片段时取消 LanguageModelSession每次会话只创建一次,之后复用- 保留上下文历史
- 解析器按以下格式解析回答:
[类型]:文本,回退到.phrase - 通过关键词确定通话阶段
- 向模型发送最后约 300 个单词,而非整份转写
我还额外加入了迪拜市场的上下文: Emaar, DAMAC, Binghatti, ROI、预订、见面、Zoom,以及其他对话中常见的信号。
ContentView
所有部分在这里汇合成用户界面。
.task在启动时请求麦克风权限.onChange(segments.count)触发coach.processNewTranscripttoggleSession()将流和格式传给startTranscription- 各管理器的错误通过独立的以下处理器显示:
.onChange
正是这一层让整个应用有了实时运作的感觉:切换音源、开始和停止会话、静音逻辑、错误处理,以及实时更新提示。
我如何训练提示
我还额外给系统补充了实际业务上下文。
针对陌生推销电话,我使用真实销售话术:开场问候、保持对话、初步筛选客户、澄清预算、处理疑虑,最后约定见面或一次深入的 Zoom 沟通。
这些并非网上抽象的模板,而是 SPI Dubai 中介公司教给我们的实际逻辑。
因此,提示变得更实用。不是简单地“礼貌地回点什么”,而是按正确的结构、在正确的阶段继续对话。
第二个场景——产品设计师面试
同时,我为同一种方法做了第二个使用方向:参加产品设计师面试。
这里我用自己的项目、案例和作品集训练系统,让它帮助的不是陌生推销,而是另一类对话——需要快速、自信、条理清晰地回答关于自身经验的问题。
因此,CallCoach 实质上已经不只是语音转文字应用,而是一个通用对话助手,可以适配具体的交流场景。
真实的开发过程是什么样的
坦白说,对我来说,这也是一个重要例子,说明今天真实的产品开发是什么样。
想法源于自己的痛点。
借助语言模型快速搭建架构。
与智能体合作编写复杂代码。
最后的打磨在 Xcode 里亲手完成——构建、崩溃、调试、反复迭代。
不是“神经网络的魔法”,而是正常的工程流程,只是被新工具加速了。
更新日志 / CallCoach 是如何做出来的
1. 明确想法
- 确认了具体问题:打陌生推销电话时,面对英国和印度口音,有些话靠耳朵根本来不及识别。
- 明确产品任务:不只是转写对话,还要帮助继续聊下去。
- 与 ChatGPT 一起梳理概念、用户流程和未来应用的结构。
- 为 Codex 生成一份很长的初始提示词,快速搭起 Swift 项目骨架。
2. 项目初次搭建
- Codex 创建了基础 Swift 项目。
- 配置了主要依赖。
- 准备了基础架构。
- 搭出了第一个可运行原型。
3. 主要工程实现
- Codex 的 token 窗口耗尽后,项目转交给 Claude Code。
- 主要开发继续使用 Opus 4.7。
- 通过 Claude Code 完成应用的核心部分:音频采集、转写、辅导逻辑、与界面的连接。
4. 解决核心产品问题——分离声音
- 发现关键问题:麦克风同时听到用户和对方。
- 实现了两种模式:
- System Audio ——仅捕获系统音频输出,使用
ScreenCaptureKit - Microphone + Push-to-Mute ——备用模式,手动暂停向转写器发送缓冲区
- System Audio ——仅捕获系统音频输出,使用
- 已添加
excludesCurrentProcessAudio = true,以排除应用自身的声音。
5. 已实现的组件
SystemAudioCaptureManager.swift——新增的系统音频采集类,通过SCStreamAudioCaptureManager.swift——音频采集,通过AVAudioEngine,已添加isMutedTranscriptionManager——流式转写,通过SpeechAnalyzerCoachingEngine——防抖、复用模型会话、解析短语、通话阶段逻辑ContentView.swift——会话管理、音源选择、错误、按钮与界面Info.plist——系统捕获权限project.pbxproj——接入ScreenCaptureKit.framework
6. 调试与测试
- 通过 Xcode 构建和运行。
- 每次应用崩溃,都把错误交回 Claude Code。
- 迭代修复:Xcode → 崩溃/错误 → Claude Code → 修复 → 新构建。
- 部分小改动直接在接入 Codex 和 Claude 的 Xcode 中完成。
7. 针对真实场景训练
- 针对陌生推销,在系统中加入真实销售话术:
- 问候
- 普通寒暄
- 客户资格筛选
- 了解兴趣
- 约定见面或 Zoom
- 话术以 SPI Dubai 的实际工作与培训为基础。
- 同时创建第二个使用场景:辅助产品设计师岗位面试。
- 为此,用我的项目、案例和作品集训练模型。
8. 最终成果
- 本地转写,不向云端发送数据
- 对话过程中实时提供提示
- 销售与面试各有独立逻辑
- 原生技术栈,无需 Web 补丁
- 用 ChatGPT + Codex + Claude Code + Xcode 协同做出的可用产品




@nevsky_alexander
在 Instagram,看明信片之外的迪拜:家人、城市、新发现,还有项目之间的日常。在 Threads,聊聊设计与 AI 的随想;在 YouTube,分享一条限时动态装不下的视频。选你习惯的平台关注,常来聊聊