共计 2080 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点分析
最近在开发 macOS 应用集成 ChatGPT API 时,遇到了几个让我头疼的问题。这些问题可能也是其他开发者正在面对的,所以我想分享一下我的解决方案。

- 响应延迟问题 :直接调用 API 时,等待完整响应经常需要 3-5 秒,用户体验很差
- Token 消耗不可控 :长对话时 token 使用量会爆炸式增长,成本难以控制
- 沙盒环境限制 :macOS 的沙盒机制导致文件存储和网络访问都受到严格限制
特别是当用户网络环境不稳定时,这些问题会被进一步放大。我尝试过简单的重试机制,但效果并不理想。
技术方案选择
经过多次实验,我总结出了一套相对完善的解决方案。
- 通信协议选择
- 对比了传统的 REST API 和 WebSocket
-
最终选择 REST + 流式传输,因为:
- 更简单的实现
- 更好的兼容性
- 足够的性能
-
异步处理架构
- 使用 Swift Concurrency(async/await)
- 结合 Grand Central Dispatch (GCD) 进行任务调度
-
实现了响应式更新 UI
-
本地缓存策略
- 采用 Core Data 存储对话历史
- 设计了一套智能清理机制
- 实现了离线浏览功能
代码实现细节
基础 API 调用封装
struct ChatGPTAPI {
private let apiKey: String
private let session: URLSession
init(apiKey: String) {
self.apiKey = apiKey
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
self.session = URLSession(configuration: config)
}
func sendMessage(_ message: String) async throws -> String {// 实现代码...}
}
带指数退避的重试机制
func sendWithRetry(_ request: URLRequest, maxRetries: Int = 3) async throws -> Data {
var retryCount = 0
var delay = 1.0
while retryCount < maxRetries {
do {return try await session.data(for: request).0
} catch {
retryCount += 1
if retryCount == maxRetries {throw error}
let delayTime = min(delay * pow(2, Double(retryCount)), 10)
try await Task.sleep(nanoseconds: UInt64(delayTime * 1_000_000_000))
}
}
throw NSError(domain: "", code: -1)
}
流式响应处理
func handleStreamResponse(_ response: URLResponse) async throws -> String {
guard let httpResponse = response as? HTTPURLResponse else {throw APIError.invalidResponse}
guard httpResponse.statusCode == 200 else {throw APIError.httpError(statusCode: httpResponse.statusCode)
}
var result = ""
for try await line in response.body.lines {guard !line.isEmpty else { continue}
guard line != "data: [DONE]" else {break}
if line.hasPrefix("data:") {let jsonString = String(line.dropFirst(6))
// 解析 JSON 并拼接结果
result += parsedContent
// 实时更新 UI
await MainActor.run {viewModel.updateResponse(text: result)
}
}
}
return result
}
性能优化实践
使用 Instruments 进行性能分析时,发现了几个关键点:
- 内存泄漏检测
- 在长时间对话场景下,发现未释放的 URLSession 实例
-
通过弱引用和适当的取消机制解决了这个问题
-
CPU 占用对比
- 流式传输比非流式传输 CPU 占用低 30-40%
-
主线程压力显著降低
-
Token 使用优化
- 实现了对话总结功能
- 自动清理过长的历史记录
避坑指南
在提交 App Store 审核时,遇到了几个坑:
- 用户生成内容处理
- 必须实现内容过滤机制
-
提供举报功能
-
Token 配额监控
- 建议实现实时使用量显示
-
设置使用上限提醒
-
隐私政策
- 必须明确说明数据收集和使用方式
- API Key 必须安全存储(推荐使用 Keychain)
开放性问题
在项目开发过程中,我一直在思考几个问题:
- 如何在本地计算和云端调用之间找到最佳平衡点?
- 对于长对话场景,是否有更好的历史记录管理方案?
- 如何在不牺牲用户体验的前提下进一步降低成本?
期待与各位开发者交流你们的经验和想法。
正文完
