> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 打造人工智能忏悔室
- URL: https://www.hubwiz.com/blog/build-your-own-ai-confessional/
- Published: 2024-11-29T03:28:10.000Z
- Updated: 2024-11-29T03:28:10.000Z
- Description: 本文分享我们如何构建我们的 LLM 语音应用程序并将其集成到忏悔室的交互式展台中。
- Author: admin
- Tags: APPLICATION, STT, TTS, LLM, ARDUINO, WHISPER, WHISPER.CPP, WHISPER_STREAMING, OPENAI, CLAUDE, PROMPT ENGINEERING, #Import 2026-10-14 14:41

虽然 OpenAI 推迟了 ChatGPT 高级语音模式的发布，但我想分享我们如何构建我们的 LLM 语音应用程序并将其集成到忏悔室的交互式展台中。

## 1、与丛林中的人工智能交谈

2 月底，巴厘岛举办了 Lampu 节，根据著名的火人节的原则安排。根据传统，参与者可以创建自己的装置和艺术品。

我和 Camp 19:19 的朋友受到天主教忏悔室的想法和当前 LLM 的能力的启发，提出了建立我们自己的人工智能忏悔室的想法，任何人都可以与人工智能交谈。

以下是我们最初的设想：

- 当用户进入展台时，我们确定需要开始新的会话。
- 用户提出问题，人工智能倾听并回答。我们希望创造一个信任和私密的环境，每个人都可以公开讨论他们的想法和经历。
- 当用户离开房间时，系统会结束会话并忘记所有对话细节。这对于保持所有对话的私密性是必要的。

## 2、概念验证

为了测试这个概念并开始尝试 LLM 的提示，我在一个晚上创建了一个基本的实现：

- 听麦克风。
- 使用语音转文本 (STT) 模型识别用户语音。
- 通过 LLM 生成响应。
- 使用文本转语音 (TTS) 模型合成语音响应。
- 向用户回放响应。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1737.png)

为了实现这个演示，我完全依赖 OpenAI 的云模型：[Whisper](https://platform.openai.com/docs/guides/speech-to-text?ref=hubwiz.com)、[GPT-4](https://platform.openai.com/docs/guides/text-generation?ref=hubwiz.com) 和[ TTS](https://platform.openai.com/docs/guides/text-to-speech?ref=hubwiz.com)。多亏了优秀的[speech\_recognition](https://github.com/Uberi/speech%5Frecognition/?ref=hubwiz.com)语音识别库，我只用了几十行代码就构建了这个演示。

```
import os
import asyncio
from dotenv import load_dotenv
from io import BytesIO
from openai import AsyncOpenAI
from soundfile import SoundFile
import sounddevice as sd
import speech_recognition as sr

load_dotenv()

aiclient = AsyncOpenAI(
    api_key=os.environ.get("OPENAI_API_KEY")
)

SYSTEM_PROMPT = """
  You are helpfull assistant. 
"""

async def listen_mic(recognizer: sr.Recognizer, microphone: sr.Microphone):
    audio_data = recognizer.listen(microphone)
    wav_data = BytesIO(audio_data.get_wav_data())
    wav_data.name = "SpeechRecognition_audio.wav"
    return wav_data

async def say(text: str):
    res = await aiclient.audio.speech.create(
        model="tts-1",
        voice="alloy",
        response_format="opus",
        input=text
    )
    buffer = BytesIO()
    for chunk in res.iter_bytes(chunk_size=4096):
        buffer.write(chunk)
    buffer.seek(0)
    with SoundFile(buffer, 'r') as sound_file:
        data = sound_file.read(dtype='int16')
        sd.play(data, sound_file.samplerate)
        sd.wait()

async def respond(text: str, history):
    history.append({"role": "user", "content": text})
    completion = await aiclient.chat.completions.create(
        model="gpt-4",
        temperature=0.5,
        messages=history,
    )
    response = completion.choices[0].message.content
    await say(response)
    history.append({"role": "assistant", "content": response})

async def main() -> None:
    m = sr.Microphone()
    r = sr.Recognizer()
    messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    with m as source:
        r.adjust_for_ambient_noise(source)
        while True:
            wav_data = await listen_mic(r, source)
            transcript = await aiclient.audio.transcriptions.create(
                model="whisper-1",
                temperature=0.5,
                file=wav_data,
                response_format="verbose_json",
            )
            if transcript.text == '' or transcript.text is None:
                continue
            await respond(transcript.text, messages)

if __name__ == '__main__':
    asyncio.run(main())
```

我们必须解决的问题在对该演示进行首次测试后，立即显现出以下问题：

- 响应延迟。在简单的实现中，用户提问和响应之间的延迟为 7-8 秒或更长时间。这并不好，但显然，有很多方法可以优化响应时间。
- 环境噪音。我们发现，在嘈杂的环境中，我们不能依靠麦克风自动检测用户何时开始和结束说话。识别短语的开始和结束（端点）并非易事。再加上音乐节的嘈杂环境，很明显需要一种概念上不同的方法。
- 模仿现场对话。我们希望让用户能够中断 AI。为了实现这一点，我们必须保持麦克风打开。但在这种情况下，我们不仅必须将用户的声音与背景声音分开，还必须将用户的声音与 AI 的声音分开。
- 反馈。由于响应延迟，有时我们觉得系统被冻结了。我们意识到我们需要告知用户响应需要多长时间处理

我们有一个解决这些问题的方法：寻找合适的工程或产品解决方案。

## 3、思考展台的用户体验

在我们开始编码之前，我们必须决定用户如何与展台互动：

- 我们应该决定如何检测展台中的新用户以重置过去的对话历史记录。
- 如何识别用户语音的开始和结束，以及如果他们想打断人工智能该怎么办。
- 当人工智能响应延迟时如何实现反馈。

为了检测展台中的新用户，我们考虑了几种选择：开门传感器、地板重量传感器、距离传感器和摄像头 + YOLO 模型。背后的距离传感器在我们看来是最可靠的，因为它排除了意外触发，例如当门关得不够紧时，并且不需要复杂的安装，不像重量传感器。

为了避免识别对话开始和结束的挑战，我们决定添加一个大红色按钮来控制麦克风。该解决方案还允许用户随时中断人工智能。

关于在处理请求时实现反馈，我们有很多不同的想法。我们决定采用一种屏幕选项，显示系统正在做什么：收听麦克风、处理问题或回答问题。

我们还考虑了使用旧固定电话的相当智能的选项。会话将在用户拿起电话时开始，系统将收听用户直到用户挂断电话。但是，我们认为，当用户通过展台“回答”而不是通过电话的声音时，它更真实。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1738.png)

安装期间和节日期间

最终的用户流程如下：

- 用户走进展台。 距离传感器在他背后触发，我们向他打招呼。
- 用户按下红色按钮开始对话。 按下按钮时，我们会收听麦克风。 当用户释放按钮时，我们开始处理请求并在屏幕上显示。
- 如果用户想在 AI 回答时提出新问题，他们可以再次按下按钮，AI 将立即停止回答。
- 当用户离开隔间时，距离传感器再次触发，我们清除对话历史记录。

## 4、架构

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1739.png)

Arduino 监控距离传感器和红色按钮的状态。它通过 HTTP API 将所有更改发送到我们的后端，这使系统能够确定用户是否已进入或离开隔间，以及是否需要激活监听麦克风或开始生成响应。

Web UI 只是一个在浏览器中打开的网页，它不断从后端接收系统的当前状态并将其显示给用户。

后端控制麦克风，与所有必要的 AI 模型交互，并发出 LLM 响应的声音。它包含应用程序的核心逻辑。

## 5、硬件

如何为 Arduino 编写sketch，正确连接距离传感器和按钮，并在隔间中组装它们，这是一个单独的文章主题。让我们简要回顾一下我们得到的东西，而不涉及技术细节。

我们使用了 Arduino，更准确地说，是带有内置 Wi-Fi 模块的型号 ESP32。微控制器与运行后端的笔记本电脑连接到同一个 Wi-Fi 网络。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1740.png)

我们使用的硬件完整列表：

- [Skype 展台](https://www.conrayn.net/prefab-pod/prefab-office-booth/outdoor-phone-booth-single-person.html?ref=hubwiz.com) — 我们从朋友那里借来的 :)
- [ESP32](https://en.wikipedia.org/wiki/ESP32?ref=hubwiz.com) — 29 美元
- [大红按钮](https://id.aliexpress.com/item/4001241312568.html?gatewayAdapt=glo2idn&ref=hubwiz.com) — 5 美元
- [距离传感器](https://shopee.co.id/Time-of-Flight-Distance-Sensor-TOF10120-Sensor-Jarak-Laser-10-180cm-i.14439227.11785995407?sp%5Fatk=a763b90b-7759-46f0-ac6b-3497956c4e60&xptdk=a763b90b-7759-46f0-ac6b-3497956c4e60&ref=hubwiz.com) — 10 美元
- 旧 Mackbook Air — 我们有一台。

## 6、后端

管道的主要组件是语音转文本 (STT)、LLM 和文本转语音 (TTS)。对于每个任务，许多不同的模型都可以在本地和云端使用。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1741.png)

由于我们手头没有强大的 GPU，我们决定选择基于云的模型版本。这种方法的弱点是需要良好的互联网连接。尽管如此，经过所有优化后的交互速度还是可以接受的，即使在节日期间使用移动互联网也是如此。

现在，让我们仔细看看管道的每个组件。

### 6.1 语音识别

许多现代设备早已支持语音识别。例如，[Apple Speech API ](https://developer.apple.com/documentation/speech/?ref=hubwiz.com)适用于 iOS 和 macOS，[Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web%5FSpeech%5FAPI/Using%5Fthe%5FWeb%5FSpeech%5FAPI?ref=hubwiz.com) 适用于浏览器。

不幸的是，它们的质量远不如 [Whisper](https://openai.com/index/whisper/?ref=hubwiz.com) 或 [Deepgram](https://deepgram.com/?ref=hubwiz.com)，并且无法自动检测语言。

为了减少处理时间，最好的选择是在用户说话时实时识别语音。以下是一些项目及其实现示例：[whisper\_streaming](https://github.com/ufal/whisper%5Fstreaming?ref=hubwiz.com)、[whisper.cpp](https://github.com/ggerganov/whisper.cpp/tree/master/examples/stream?ref=hubwiz.com)

在我们的笔记本电脑上，使用这种方法的语音识别速度远非实时。经过几次实验，我们决定使用 OpenAI 的基于云的 Whisper 模型。

### 6.2 LLM 和 提示工程

上一步中的语音转文本模型的结果是我们发送给 LLM 的带有对话历史记录的文本。

在选择 LLM 时，我们比较了 GPT-3.5、GPT-4 和 Claude。结果发现关键因素与其说是具体的模型，不如说是它的配置。最终，我们选择了 GPT-4，我们更喜欢它的答案。

LLM 模型的提示定制已成为一种独立的艺术形式。互联网上有许多关于如何根据需要调整模型的指南：

- [OpenAI 提示工程指南](https://platform.openai.com/docs/guides/prompt-engineering/prompt-engineering?ref=hubwiz.com)
- [Anthropic 提示工程](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview?ref=hubwiz.com)
- [提示工程指南](https://www.promptingguide.ai/?ref=hubwiz.com)

我们必须对提示和温度设置进行广泛的实验，以使模型做出引人入胜、简洁和幽默的响应。

### 6.3 文本转语音

我们使用文本转语音模型将从 LLM 收到的响应语音化，并将其播放给用户。这一步是我们演示中延迟的主要来源。

LLM 需要很长时间才能做出响应。但是，它们支持以流式模式生成响应 - 逐个标记。我们可以使用此功能来优化等待时间，方法是在收到单个短语时将其发声，而无需等待 LLM 的完整响应。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1742.png)

单个句子的发声

- 向 LLM 发出查询。
- 我们将响应逐个标记地累积在缓冲区中，直到得到一个长度最小的完整句子。最小长度参数非常重要，因为它会影响发声的语调和初始延迟时间。
- 将生成的句子发送到 TTS 模型，并将结果播放给用户。在此步骤中，必须确保播放顺序中没有竞争条件。
- 重复上一步，直到 LLM 响应结束

我们利用用户收听初始片段的时间来隐藏处理 LLM 响应剩余部分的延迟。由于采用了这种方法，响应延迟仅发生在开始时，大约为 3 秒。

```
  async generateResponse(history) {
    const completion = await this.ai.completion(history);
    
    const chunks = new DialogChunks();
    for await (const chunk of completion) {
      const delta = chunk.choices[0]?.delta?.content;
      if (delta) {
        chunks.push(delta);
        if (chunks.hasCompleteSentence()) {
          const sentence = chunks.popSentence();
          this.voice.ttsAndPlay(sentence);
        }
      }
    }
    const sentence = chunks.popSentence();
    if (sentence) {
      this.voice.say(sentence);
    }
    return chunks.text;
  }

```

## 7、最后润色

即使我们进行了所有优化，3-4 秒的延迟仍然很严重。我们决定通过反馈来处理 UI，以免用户感觉响应被挂起。我们研究了几种方法：

- LED 指示灯。我们需要显示五种状态：空闲、等待、聆听、思考和说话。但我们无法想出如何用 LED 轻松理解的方式来做到这一点。
- 填充词，例如“让我想想”、“嗯”等，模仿现实生活中的言语。我们拒绝了此选项，因为填充词通常与模型响应的语气不匹配。
- 在展台上放置一个屏幕。并用动画显示不同的状态。

我们选择了最后一个选项，即一个简单的网页，该网页轮询后端并根据当前状态显示动画。

![](https://www.hubwiz.com/blog/content/images/2024/11/image-1743.png)

## 8、结束语

我们的 AI 忏悔室运行了四天，吸引了数百名与会者。我们在 OpenAI API 上只花了大约 50 美元。作为回报，我们收到了大量积极的反馈和宝贵的印象。

这个小实验表明，即使在资源有限和外部条件具有挑战性的情况下，也可以为 LLM 添加直观高效的语音界面。

顺便说一句，后端源代码可在 [GitHub ](https://github.com/sslava/ai-voice-chat?ref=hubwiz.com)上找到

---

原文链接：[How Build Your Own AI Confessional: How to Add a Voice to the LLM](https://hackernoon.com/how-build-your-own-ai-confessional-how-to-add-a-voice-to-the-llm?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处