RICHARDDAS

哎呀! 我不小心“感觉式编程”出了一个用于 Apple Watch 的 ChatGPT 客户端

2025年4月28日 — 作者: Richard Das 分类: Insights

一次意外的附加任务,涉及 SwiftUI, OpenAI’s API, CloudKit 和 Swift Data,以及我所了解的 AI 在现代产品开发中的局限性和可能性。

又一个“感觉式编程”故事

你可能在想:“哦不,又一个‘感觉式编程’的故事。”

我明白。我已经看过很多这样的故事了。有人让 AI 构建一些东西,它吐出代码,然后他们对它几乎可以工作感到震惊。

但这不是那样的故事。我花了 20 年时间设计产品、领导团队、为公司提供建议,并发布了无数应用——其中一些可能就在你的手机上。我曾在 Apple 工作过,并对将创新想法转化为实用的、以人为本的产品有着深刻的理解。 这不是一个关于 AI 将要取代什么的故事,而是关于 AI 能够真正为现代应用开发做出什么贡献,它的局限性在哪里,以及我们作为开发者和产品思考者,如何最好地利用它的优势。

例如,去年我使用 AI 帮助我将 AirPods 变成健身追踪器来计算俯卧撑,利用了它们内置的加速计。我喜欢探索现有技术中未被发掘的潜力。

这篇文章一部分是开发者的日志,一部分是产品探索,一部分是关于 AI 在产品开发中的能力的实用指南。因为在那一个周末(以及随后的一个月)发生的事情,比我过去六个月阅读关于 AI 的文章所学到的关于构建产品的未来还要多。

准备好了吗?

千里之行,始于一个 Prompt

一切都始于一个相当无辜的想法。

我脑海中有一个想法在盘旋,并且已经成为一种习惯,我喜欢在 GPT 中捕捉这些想法。写下来可以帮助我整理思绪,尤其是在我试图放松入睡的时候。 有时我会回头看它们,进一步开发它们,或者将它们存档——但首先,它们需要从脑海中 出来

在睡觉前,我迅速将以下内容输入到 Claude 中,以便我可以在早上重新审视它:

How feasible is it to make an Apple Watch app that allows the user to ask a question to chat gpt using the openAI API? I want to start with just a single conversation - so that every time the watch is used, it's just appending to the same thread. But optionally maybe down the line incorporate a way for the user to save a list of conversations and then choose between them. But for now, a single thread would be sufficient.典型的思维倾泻。

这是它的回复:

额… 我没料到会这样。

你会注意到我立刻注意到的事情。 Claude 不仅仅回答了我的问题——它回复的内容看起来非常像是 可运行的 代码。

如果我不至少尝试编译它,我就是一个傻瓜。

如果你是一名开发者,你可以猜到接下来会发生什么。 我创建了一个新的 Xcode 项目,打开了 OpenAI 仪表板,生成了一个 API 密钥,并将所有内容都插入进去。 然后我点击了 Build & Run。

令我完全惊讶的是,我正盯着一个运行在我的 Apple Watch 上的 ChatGPT 客户端:

什么鬼。

如果你感到难以置信,你不是一个人。 这也正是我的反应。 刚刚发生了什么? 我真的只是“感觉式编程”出了一个完整的应用吗?

好奇心编译了这个应用

我花了一分钟的时间才真正理解我已经从提出一个问题,到手腕上运行着一个应用的事实。 但不相信只是一种急性的好奇心。 我必须深入挖掘才能理解刚刚发生了什么。

根据它生成的代码,这是 Claude 从那个 prompt 有效构建的架构图。 它看起来相当不错,并且是一个良好的开端——这使得它更加有趣。

一个视图,一个控制器,一个硬编码的 API 密钥。

说实话,令人有点害怕的是,“感觉式编程”可以输出比我在职业生涯中见过的许多应用程序更好的结构化代码库。 也许是因为有太多关于关注点分离的书面建议,人类选择忽略,但 AI 只是被训练成认为这是正常的?

这看起来是一个非常体面和合理的模型(但是这个 isUser Bool 稍后会反咬我们一口。)

struct Message: Identifiable, Codable {
    let id: UUID
    let content: String
    let isUser: Bool
    let timestamp: Date

    init(content: String, isUser: Bool) {
        self.id = UUID()
        self.content = content
        self.isUser = isUser
        self.timestamp = Date()
    }
}

那么操作的真正大脑,Conversation Manager 呢? 它会结构良好吗? 剧透:既是又不是。

这里发生了很多事情,我不会详细分享(随意自己“感觉式编程”一个)——但简化的整体类看起来像这样:

class ConversationManager: ObservableObject {
    @Published var messages: [Message] = []
    private let apiKey: String = "YOUR_API_KEY" // Store securely in practice

    init() {
        loadMessages()
    }

    func sendMessage(_ content: String) {
        // save message to User Defaults
        // Call OpenAI API in a Task
        Task {
            do {
                // call OpenAIAPI
            } catch {
                // dispatch an error on the main thread
            }
        }
    }

    private func callOpenAIAPI(with prompt: String) async throws -> String {
        // compose a URL request (with the hardcoded tokens!)
        // Add the latest user prompt
        // send it
        // return the response as a String, or throw an Error
    }

    private func saveMessages() {
        // persist to User Defaults
    }

    private func loadMessages() {
        // read messages from User Defaults
    }
}

在某些方面,它比预期的要好。 结构清晰,Swift 习惯用法基本遵循,代码可读。 但在其他方面,它立即揭示了 AI 生成代码的缺陷,如果你没有真正尝试构建和发布产品,你就不会注意到这些缺陷。

有效的部分(和无效的部分)

代码的某些部分出乎意料地坚实:

但一些差距立刻显现出来。 首先,什么是 Speech Recognizer?

AI 似乎也不知道,它只是认为我们需要一个。 这是未编辑的,逐字生成的精确代码:

class SpeechRecognizer {
    private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    private var recognitionTask: SFSpeechRecognitionTask?
    private let audioEngine = AVAudioEngine()

    func startRecording(completion: @escaping (String) -> Void) {
        // Request permissions and start recording
        // Implementation details omitted for brevity
        // In a real app, you would implement the speech recognition logic here
    }

    func stopRecording() {
        audioEngine.stop()
        recognitionRequest?.endAudio()
    }
}

正如你可能,嗯,认识到的 – SFSpeechRecognizer 是一个实际存在的框架,你可以使用它来检查设备上语音识别服务的可用性,并启动语音识别过程。

你(和 AI)可能 知道的是,虽然它是在 iOS10 左右推出的,但 它在 watchOS 上不可用。

这是一个关键限制的小例子:今天的 AI 仍然主要停留在表面层面。 它可以模仿它以前见过的模式,但在像 watchOS 这样的平台上,约定很具体,并且文档生态系统更加分散,模型很容易错过最简单的路径。

这并不是对 AI 的打击,而只是 Apple 开发往往是孤立的事实。 当我在 gptsfordevs.com 工作时,我花时间解决了这个问题——这是一种弥合这种差距的工具(目前处于离线状态,但我正在努力尽快将其恢复!)

工程化一个 MVP

手腕上有了可用的应用程序后,下一步是使其不那么脆弱。

我决定使用 SwiftData 添加持久性,引入两个简单的模型类型:ConversationMessage。 有了这个,我终于可以跨启动恢复对话历史记录。 架构现在有一些骨架:一个适当的持久层,一个简单的对话协调器来管理流程,以及一个基本的网络层来调用 OpenAI API。

关注点分离 FTW!

Keynote 中可供选择的颜色快用完了。

此时 API 密钥仍然是硬编码的,这并不理想,但我暂时忽略了这一点,以便继续推动这个小家伙前进。

最初的 UI 在功能上可用,但很糟糕。 麦克风按钮占据了屏幕的一半,响应被截断且几乎无法读取。 它证明了这个概念,但它远非可用,并且不像原生的 watchOS 界面。

显然,四个单词太多而无法显示。

所以我回到 Apple Watch 的设计学校。 我浏览了 Human Interface Guidelines 中关于 Designing for watchOS 的部分 以刷新我的记忆,并启动了 Developer 应用程序,提示 WWDC 中相关的会话,其中涵盖了最近对 watchOS 10 的重新设计。

从那里,很明显我需要进行一些更改:

区别是显着的:该应用程序从“概念验证”变成了“我实际上可以使用它”。 切换到我实际使用它:

“我们在这里,我们正在等待”——变形金刚,2007 年由 Michael Bay 执导。 不客气。

这些都是小的调整,但 AI 没有,可能也无法自行做出这些调整。 它们需要平台熟悉度,设计敏感性和关于我希望应用程序如何工作的上下文。 换句话说:产品决策。

性能考虑:细节很重要

当我开始改进 UI 时,我发现了一些性能问题。 首先,我注意到单元格在每次刷新时都会对整个对话列表进行排序。 对于处理能力有限的小型设备来说,这并不理想。

Section(header: Text(“Recent Conversations”)) {
    ForEach(store.conversations.sorted(by: { $0.lastUpdated > $1.lastUpdated })) { conversation in

不要这样做。

解决这些看似很小的优化,会在像 Apple Watch 这样的受限设备上的用户体验产生巨大的影响。

创新始于痛点。

获得这样的原型通常需要一个团队至少一周的专注工作。 借助 AI,我可以在一个晚上在手腕上运行一些东西。

我从想知道某件事是否可能,到塑造一个真实的产品——有一个可用的版本摆在我面前,可以尝试、测试和迭代。

这就是 AI 在早期产品发现中如此强大的原因。 对于新技术,尤其是这种不熟悉的技术,你不能总是通过思考来获得洞察力。 有时,你需要构建它,尝试它,看看它的感觉如何。 拥有真实的东西可以让你了解什么可能真正有用。

一旦我体验到了这一点,我便开始思考它如何也能帮助他人。 这种关注点的转变是真正的价值。

我们无法预测未来,但我们可以创造未来。 Dennis Gabor

AI 加快了反馈循环。 它可以帮助你快速测试想法,从错误中学习,并瞥见新的机会。 但将这些见解转化为人们想要的东西仍然需要产品判断、周到的设计和迭代。

在几个小时内,我得到了一些技术上可行,外观过得去,并且可以在启动时恢复状态的东西。 诱惑是继续进行 hacking。 但是时候放大并提出更大的问题了:

我开始在自己的日常生活中测试该应用程序。 很快就清楚了,在遛狗,走进厨房,开会之间,拿出手机感觉太多了,但抬起手腕很容易。 并且我开始考虑更多可以用到它的地方和情况。

这不再是周末的 hacking。 这是一个产品的开始。

如果这听起来很有趣,请在第 2 部分发布时在下方注册。 并告诉我你想听更多关于什么的信息!

但首先…

自己尝试一下

我很想让你亲自尝试该应用程序,并分享你对以下方面的想法:

这几周非常忙碌,有很多工作要做,才能将其变成你可以每天使用的真正产品。

我很自豪地说,WristGPT 现在已在 App Store 上提供!

立即在 App Store 上下载 WristGPT

第 2 部分 中,我将深入探讨接下来发生的事情:添加具有 Complications 的一键访问,使用 AIProxy 保护 API 密钥,使用 CloudKit 和 App Groups 同步数据,以及为移交到 iPhone 设计。 所有这些都只是基础!

学习,创新,睡觉,重复。

我每月一次分享见解和实用指南,以释放您产品中的创新。 向行业专家学习,并改变您进行产品设计和创新的方法。

注册!

我绝不会与任何人分享您的电子邮件。 随时取消订阅。

Built with Kit

评论

发表评论 取消回复

您的电子邮件地址不会被公开。 必填字段标记为 *

评论 * 名称 * 电子邮件 * 网站 保存我的姓名,电子邮件和网站在此浏览器中,以便下次我发表评论。 Δ

Previous: Turning AirPods into a Fitness Tracker to Fight Cancer

RICHARDDAS

ⓒ 2024 Cleverbit AI Ltd.