前言

像千问、豆包、ChatGPT这样的AI聊天应用已经普及到千家万户,AI Coding能力也日新月异,我想验证下让AI来开发AI聊天应用需要多久,同时也是温故知新。

验证下来,发现一天左右的时间就能开发出一个具备主要功能的AI聊天应用,具体代码见:https://github.com/seizethemoment/hi-my-ai-chat

以下是实现效果,支持文本聊天、图片理解、语音输入和播放、流式回复、多会话管理、搜索和收藏等功能,后面持续更新。

HiChat-Usecase

在这里插入图片描述
在这里插入图片描述

一、环境准备

目标:创建一个App。
验收标准:成功运行App,看到“Hello, world!”输出。

选择移动任意端或者桌面端App来作为落地场景都可以,因为对AI来说都差不多,只不过最新版Xcode自带AI Coding能力,不用登录都能匿名使用ChatGPT能力,所以这里选择用iOS端来开发。

更新Xcode到最新版本,将官方Xcode MCP配置到AI IDE,便于AI在写完代码后进行自动化验收。
这一步只要点几下鼠标:打开Xcode -> File -> New -> Project,一路点击下一步,再点击Run,就能编译运行到模拟器,看到 Hello, world,就算达成目标。

BTW,在AI时代,苹果的产品越发吃香,下单一台Mac mini都要等几个月才有货,💻运行也更省电,以及安全性相对更高。

二、聊天页面创建

目标:创建一个聊天页面。
验收标准:运行App,展示聊天页面

输入:豆包或千问的一张聊天页面截图 + 需求描述,比如“根据图片把App首页改成聊天页面,主要包括顶部导航栏、中间消息展示区域、底部输入区域”。

不同水平的AI会根据你的截图和描述,生成一致度有所差别的效果。高水平的AI甚至会自己调用Xcode MCP来完成消息的发送和展示验收。针对这种水平的AI,你可以额外加一句:开发完成后,你输入一句文本进行发送,并且mock文本作为返回数据并上屏展示。

三、模型服务调通

目标:让AI模型进行消息回复,而不是mock。
验收标准:运行App,发送多条不同的消息,收到真实的模型回复。

上一步是使用mock数据来作为消息回复,这里是跑通真实场景。
需要准备好你自己的模型调用信息(Base Url 和 API Key),比如OpenAI、阿里云百炼平台、智谱或Kimi等平台订阅,容易封号的Anthropic就不推荐了。

  1. 开发阶段可以先把这些信息硬编码便于调用。
  2. 我在HiChat中是提供了一个设置页让用户一次性配置这些信息。

调用模型服务,这里使用的最常见通用、兼容性最强的是OpenAI的核心接口 —— Chat Completions API —— 通过 POST /v1/chat/completions 端点,基于一组角色消息(system / user / assistant)生成模型回复。

基本用法
POST https://api.openai.com/v1/chat/completions
                                                                                
  {
    "model": "gpt-5.4",                                                         
    "messages": [                                                               
      {"role": "system", "content": "你是一个有用的助手"},                      
      {"role": "user", "content": "你好!"}                                     
    ]                                                                           
  }

核心参数                                                                      
  - model — 指定使用的模型(如 gpt-5.4、qwen3.6-plus 等)                             
  - messages — 对话消息列表,每条消息包含 role 和 content         
  - temperature — 控制输出随机性(0~1- max_tokens — 限制最大输出长度                                               
  - stream — 是否启用流式输出                                                   
  - tools — 工具调用(function calling)定义                                    
  - top_p / frequency_penalty / presence_penalty — 采样控制 

不过,现在模型都在向上生长、内置工具,OpenAI也推出了新的Responses API,建议大家迁移:https://developers.openai.com/api/docs/guides/migrate-to-responses ,阿里云百炼也有相应支持:https://help.aliyun.com/zh/model-studio/compatibility-with-openai-responses-api ,具体可以参见:深入理解 OpenAI 的三代 API:从 Completions 到 Responses 的演进之路

在HiChat中,是由OpenAIChatService这个类来承接这个功能。

四、流式回复的打字机效果

目标:让AI回复具备打字机效果。
验收标准:运行App,发送多条消息,观察消息回复上屏是逐字更新的,而不是完整一次性上屏。

AI聊天应用的一个经典交互就是AI回复的内容是逐字或逐token展示出来的,大大提升了交互体验:

  1. 低延迟,用户不用等所有内容思考生成完才看到内容。
  2. 模拟了一边思考一边打字的拟人交,让用户也可以一边等待一边消费内容。

首先,通过设置stream=true开启流式,让服务端发你SSE增量文本。

    func streamReply(
        for conversation: [OpenAIChatTurn],
        timeoutInterval: TimeInterval = 45,
        maxRetryCount: Int = 1,
        onRetry: (@Sendable (Int) async -> Void)? = nil,
        onDelta: @escaping @Sendable (String) async -> Void
    ) async throws -> String {
        let request = try makeRequest(for: conversation, stream: true, timeoutInterval: timeoutInterval)

当用户发送一条消息后,先立刻插入一条空的 assistant 消息,状态设为 streaming,这样 UI 有一个固定占位可持续更新。
随后 generateAssistantReply 做两件并行的事:

  1. 启一个 typewriterTask 消费本地字符流。
  2. service.streamReply(...) 去读 OpenAI 的 SSE 增量,每拿到一段文本就回调 onDelta(delta)

真正的“打字机”效果是由端侧本地加工实现的:

  1. TypewriterBuffer来处理每次的增量文本,拆成单个字符。
  2. playTypewriter一个字符一个字符消费,切到主线程调用 appendText(...) 更新消息文本,然后 sleep 一小段时间。
  3. 延时是按字符类型区分的:普通字符 18ms,换行 34ms,标点 42ms,所以视觉上会比纯匀速更像人在打字。

这种方案比较简单直接,适用于普通文本场景,但面对富文本场景就不适合了,需要另外实现。

更多功能:语音输入和播放、图片理解、多会话管理、搜索和收藏……

当完成了上面的步骤之后,已经具备了基本的AI聊天App的雏形,只不过局限于文本交流,接下来就要扩展到语音、图片、工具调用等场景,甚至还需要考虑上下文窗口大小和压缩,如果有想法也可以进一步引入记忆系统。这里就不完全展开了,只提一下语音输入和播放。

当你选择了使用原生代码开发后,AI对主流平台系统库是很了解的。
比如语音输入和播放,输入部分采用的是AVAudioEngine + SFSpeechRecognizer(iOS 原生语音识别),

  流程:1. 权限请求 → 2. 音频引擎启动 → 3. 实时语音识别 → 4. 输出文字
                                                                                
  1. 权限申请 — 分别请求麦克风权限(AVAudioApplication.requestRecordPermission)
  和语音识别权限(SFSpeechRecognizer.requestAuthorization)                     
  2. 音频采集 — AVAudioEngine 的 inputNode 安装 tap,每 1024 buffer             
  将麦克风音频数据送入识别引擎:                                                
  inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {
  buffer, _ in                                                                  
      recognitionRequest?.append(buffer)                          
  }
  3. 实时识别 — SFSpeechAudioBufferRecognitionRequest 配置为                    
  shouldReportPartialResults = true,边说边返回中间结果,通过                   
  speechRecognizer.recognitionTask 的回调持续更新 transcript                    
  4. 结束处理 — 用户松手后 endAudio() 停止输入,设置 1.5s                       
  超时兜底(scheduleRecognitionFallback),等 isFinal 回调或超时后将文字写入    
  finalTranscript

输出部分采用的是AVSpeechSynthesizer(iOS 原生 TTS 文字转语音)。

  流程: 1. 用户点击播放 → 2. 语言检测 → 3. 配置语音 → 4. 播放音频                     
                                                                                
  1. 语言自动检测 — 用 NLLanguageRecognizer                                     
  分析消息文本的语种,按优先级匹配语音:检测到的语言 → 用户首选语言 → zh-CN →   
  en-US                                                                         
  2. 创建语音片段 — AVSpeechUtterance(string: text) 包装文本,设置参数:
  utterance.rate = 0.45  // 中文语速
  utterance.pitchMultiplier = 1.0                                               
  utterance.postUtteranceDelay = 0.05
  3. 播放 — synthesizer.speak(utterance) 开始播放,通过                         
  AVSpeechSynthesizerDelegate 监听完成/取消事件来清理状态                       
  4. 切换控制 — togglePlayback(for:) 实现播放/暂停切换,同一消息再次点击则
  stop()                                                                        
                                                                  
  音频会话配置: .playback 类别 + .spokenAudio 模式 +                           
  .duckOthers(降低其他音频音量) 

TTS全称是Text-to-Speech,通常包含三个主要组件:语言分析器、声学模型和语音声码器。豆包、千问的声音就是这个领域。如果你经常用iPhone的图书app在看书,比如看一些英文书籍,很大概率会使用到。
之前我在使用AVSpeechSynthesizer时,要吭哧吭哧看官方文档、API接口和说明来调用,现在根本不需要,只需要告诉AI我要实现什么功能 —— 体验大大提升。

踩坑和回顾

这是一个周末间隙让AI开发的应用,累计加起来若干个小时,没有具体统计。
因为周六上午我带娃出去户外,周六下午去西溪湿地逛到晚饭时间才回,周天也类似早上、下午都出门。以下是一些做到目前的感受:

在使用AI时,我们给AI的输入以 目标 和 验收标准 为主,用来指导AI自主完成长程任务。

开发好某个功能,到达一定阶段,要做git记录,避免AI改坏代码很难修。

AI写代码容易一味堆积,需要适当把握方向和引导它进行重构优化。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐