paint-brush
Cách xây dựng chuyên gia AI của riêng bạn: Cách thêm tiếng nói vào LLMby@slavasobolev
661
661

Cách xây dựng chuyên gia AI của riêng bạn: Cách thêm tiếng nói vào LLM

Iaroslav Sobolev11m2024/07/14
Read on Terminal Reader

Vào cuối tháng 2, Bali đã tổ chức lễ hội [Lampu](https://lampu.org/), được tổ chức theo nguyên tắc của Burning Man nổi tiếng. Chúng tôi lấy cảm hứng từ ý tưởng về các tòa giải tội Công giáo và khả năng của LLM hiện tại. Chúng tôi xây dựng tòa giải tội AI của riêng mình, nơi bất kỳ ai cũng có thể nói chuyện với trí tuệ nhân tạo.
featured image - Cách xây dựng chuyên gia AI của riêng bạn: Cách thêm tiếng nói vào LLM
Iaroslav Sobolev HackerNoon profile picture
0-item
1-item

Trong khi OpenAI đang trì hoãn việc phát hành Chế độ giọng nói nâng cao cho ChatGPT, tôi muốn chia sẻ cách chúng tôi xây dựng ứng dụng giọng nói LLM và tích hợp nó vào một gian hàng tương tác.

Nói chuyện với AI trong rừng

Cuối tháng 2, Bali tổ chức lễ hội Lampu được sắp xếp theo nguyên tắc của Burning Man nổi tiếng. Theo truyền thống của nó, những người tham gia tạo ra các tác phẩm nghệ thuật và sắp đặt của riêng họ.


Những người bạn của tôi từ Trại 19:19 và tôi, được truyền cảm hứng từ ý tưởng về các tòa giải tội Công giáo và khả năng của LLM hiện tại, đã nảy ra ý tưởng xây dựng tòa giải tội AI của riêng chúng tôi, nơi bất kỳ ai cũng có thể nói chuyện với trí tuệ nhân tạo.


Đây là cách chúng tôi hình dung nó ngay từ đầu:

  • Khi người dùng bước vào một gian hàng, chúng tôi xác định rằng chúng tôi cần bắt đầu một phiên mới.


  • Người dùng đặt câu hỏi, AI sẽ lắng nghe và trả lời. Chúng tôi muốn tạo ra một môi trường tin cậy và riêng tư, nơi mọi người có thể thảo luận cởi mở về suy nghĩ và trải nghiệm của mình.


  • Khi người dùng rời khỏi phòng, hệ thống sẽ kết thúc phiên và quên tất cả chi tiết cuộc trò chuyện. Điều này là cần thiết để giữ tất cả các hộp thoại ở chế độ riêng tư.

Bằng chứng của khái niệm

Để kiểm tra khái niệm và bắt đầu thử nghiệm lời nhắc cho LLM, tôi đã tạo một triển khai đơn giản trong một buổi tối:

  • Nghe micro.
  • Nhận dạng giọng nói của người dùng bằng mô hình Chuyển giọng nói thành văn bản (STT) .
  • Tạo phản hồi qua LLM .
  • Tổng hợp phản hồi bằng giọng nói bằng mô hình Chuyển văn bản thành giọng nói (TTS) .
  • Phát lại phản hồi cho người dùng.



Để triển khai bản demo này, tôi hoàn toàn dựa vào các mô hình đám mây từ OpenAI: Whisper , GPT-4TTS . Nhờ có thư viện tuyệt vời speech_recognition , tôi đã xây dựng bản demo chỉ trong vài chục dòng mã.


 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())


Những vấn đề chúng tôi phải giải quyết ngay lập tức trở nên rõ ràng sau những lần thử nghiệm đầu tiên của bản demo này:

  • Độ trễ phản hồi . Trong cách triển khai đơn giản, độ trễ giữa câu hỏi và câu trả lời của người dùng là 7-8 giây hoặc lâu hơn. Điều này không tốt nhưng rõ ràng có rất nhiều cách để tối ưu hóa thời gian phản hồi.


  • Tiếng ồn xung quanh . Chúng tôi phát hiện ra rằng trong môi trường ồn ào, chúng tôi không thể dựa vào micrô để phát hiện thời điểm người dùng tự động bắt đầu và kết thúc nói. Nhận biết phần đầu và phần cuối của một cụm từ ( điểm cuối ) là một nhiệm vụ không hề đơn giản. Kết hợp điều này với môi trường ồn ào của một lễ hội âm nhạc và rõ ràng là cần có một cách tiếp cận khác về mặt khái niệm.


  • Bắt chước cuộc trò chuyện trực tiếp . Chúng tôi muốn cung cấp cho người dùng khả năng làm gián đoạn AI. Để đạt được điều này, chúng tôi sẽ phải bật micrô. Nhưng trong trường hợp này, chúng tôi sẽ phải tách giọng nói của người dùng không chỉ khỏi âm thanh nền mà còn với giọng nói của AI.


  • Nhận xét . Do độ trễ phản hồi, đôi khi chúng tôi có cảm giác như hệ thống đã bị đóng băng. Chúng tôi nhận ra rằng chúng tôi cần thông báo cho người dùng biết thời gian xử lý phản hồi


Chúng tôi có quyền lựa chọn cách giải quyết những vấn đề này: bằng cách tìm kiếm giải pháp kỹ thuật hoặc sản phẩm phù hợp.

Suy nghĩ về UX của Booth

Trước khi viết mã, chúng tôi phải quyết định cách người dùng sẽ tương tác với gian hàng:

  • Chúng ta nên quyết định cách phát hiện người dùng mới trong gian hàng để đặt lại lịch sử hộp thoại trong quá khứ.


  • Cách nhận biết phần đầu và phần cuối bài phát biểu của người dùng và phải làm gì nếu họ muốn làm gián đoạn AI.


  • Cách triển khai phản hồi khi AI phản hồi chậm.


Để phát hiện người dùng mới trong gian hàng, chúng tôi đã xem xét một số tùy chọn: cảm biến mở cửa, cảm biến trọng lượng sàn, cảm biến khoảng cách và camera + mẫu YOLO. Đối với chúng tôi, cảm biến khoảng cách phía sau lưng có vẻ đáng tin cậy nhất vì nó loại trừ các tác nhân vô tình, chẳng hạn như khi cửa không đóng đủ chặt và không yêu cầu lắp đặt phức tạp, không giống như cảm biến trọng lượng.


Để tránh khó khăn khi nhận dạng phần đầu và phần cuối của hộp thoại, chúng tôi đã quyết định thêm một nút lớn màu đỏ để điều khiển micrô. Giải pháp này cũng cho phép người dùng làm gián đoạn AI bất cứ lúc nào.


Chúng tôi có nhiều ý tưởng khác nhau về việc triển khai phản hồi trong quá trình xử lý yêu cầu. Chúng tôi đã quyết định chọn một tùy chọn có màn hình hiển thị những gì hệ thống đang thực hiện: nghe micrô, xử lý câu hỏi hoặc trả lời.


Chúng tôi cũng cân nhắc một phương án khá thông minh với chiếc điện thoại cố định cũ. Phiên sẽ bắt đầu khi người dùng nhấc điện thoại và hệ thống sẽ lắng nghe người dùng cho đến khi anh ta gác máy. Tuy nhiên, chúng tôi quyết định rằng nó sẽ chân thực hơn khi người dùng được "trả lời" bằng buồng thay vì bằng giọng nói từ điện thoại.


Trong quá trình lắp đặt và tại lễ hội


Cuối cùng, luồng người dùng cuối cùng xuất hiện như thế này:

  • Một người dùng bước vào một gian hàng. Một cảm biến khoảng cách được kích hoạt sau lưng anh ấy và chúng tôi chào anh ấy.


  • Người dùng nhấn nút màu đỏ để bắt đầu hộp thoại. Chúng tôi nghe micrô trong khi nhấn nút. Khi người dùng nhả nút, chúng tôi bắt đầu xử lý yêu cầu và hiển thị yêu cầu đó trên màn hình.


  • Nếu người dùng muốn hỏi một câu hỏi mới trong khi AI đang trả lời, họ có thể nhấn nút một lần nữa và AI sẽ ngay lập tức ngừng trả lời.


  • Khi người dùng rời khỏi buồng, cảm biến khoảng cách sẽ kích hoạt lại và chúng tôi xóa lịch sử hộp thoại.

Ngành kiến trúc


Arduino theo dõi trạng thái của cảm biến khoảng cách và nút màu đỏ. Nó gửi tất cả các thay đổi đến chương trình phụ trợ của chúng tôi thông qua API HTTP, cho phép hệ thống xác định xem người dùng đã vào hay rời khỏi gian hàng và liệu có cần kích hoạt nghe micrô hay bắt đầu tạo phản hồi hay không.


Giao diện người dùng web chỉ là một trang web được mở trong trình duyệt liên tục nhận trạng thái hiện tại của hệ thống từ phần phụ trợ và hiển thị nó cho người dùng.


Phần phụ trợ điều khiển micrô, tương tác với tất cả các mô hình AI cần thiết và đưa ra phản hồi LLM. Nó chứa logic cốt lõi của ứng dụng.

Phần cứng

Cách viết mã bản phác thảo cho Arduino, kết nối chính xác cảm biến khoảng cách và nút và lắp ráp tất cả trong gian hàng là một chủ đề cho một bài viết riêng. Hãy xem xét ngắn gọn những gì chúng tôi nhận được mà không đi sâu vào chi tiết kỹ thuật.


Chúng tôi đã sử dụng Arduino, chính xác hơn là model ESP32 với mô-đun Wi-Fi tích hợp. Bộ vi điều khiển được kết nối với cùng mạng Wi-Fi với máy tính xách tay đang chạy chương trình phụ trợ.



Danh sách đầy đủ phần cứng chúng tôi đã sử dụng:

Phần phụ trợ

Các thành phần chính của quy trình là Chuyển giọng nói thành văn bản (STT), LLM và Chuyển văn bản thành giọng nói (TTS). Đối với mỗi nhiệm vụ, nhiều mô hình khác nhau có sẵn cả cục bộ và qua đám mây.



Vì chúng tôi không có sẵn GPU mạnh mẽ nên chúng tôi quyết định chọn phiên bản mô hình dựa trên đám mây. Điểm yếu của phương pháp này là cần có kết nối internet tốt. Tuy nhiên, tốc độ tương tác sau khi tối ưu hóa vẫn ở mức chấp nhận được, ngay cả với Internet di động mà chúng tôi có tại lễ hội.


Bây giờ, chúng ta hãy xem xét kỹ hơn từng thành phần của đường ống.

Nhận dạng giọng nói

Nhiều thiết bị hiện đại từ lâu đã hỗ trợ nhận dạng giọng nói. Ví dụ: API Apple Speech có sẵn cho iOS và macOS và API Web Speech dành cho trình duyệt.


Thật không may, chúng có chất lượng rất kém so với Whisper hoặc Deepgram và không thể tự động phát hiện ngôn ngữ.


Để giảm thời gian xử lý, tùy chọn tốt nhất là nhận dạng giọng nói theo thời gian thực khi người dùng nói. Dưới đây là một số dự án có ví dụ về cách triển khai chúng: thì thầm_streaming , thì thầm.cpp


Với máy tính xách tay của chúng tôi, tốc độ nhận dạng giọng nói bằng phương pháp này hóa ra khác xa so với thời gian thực. Sau một số thử nghiệm, chúng tôi đã quyết định sử dụng mô hình Whisper dựa trên đám mây của OpenAI.

LLM và Kỹ thuật nhanh chóng

Kết quả của mô hình Speech To Text ở bước trước là văn bản chúng tôi gửi tới LLM cùng với lịch sử hộp thoại.


Khi chọn LLM, chúng tôi đã so sánh GPT-3.5. GPT-4 và Claude. Hóa ra yếu tố quan trọng không phải là kiểu máy cụ thể mà là cấu hình của nó. Cuối cùng, chúng tôi đã quyết định chọn GPT-4, câu trả lời mà chúng tôi thích hơn những câu trả lời khác.


Việc tùy chỉnh lời nhắc cho các mô hình LLM đã trở thành một hình thức nghệ thuật riêng biệt. Có nhiều hướng dẫn trên Internet về cách điều chỉnh mô hình của bạn khi bạn cần:



Chúng tôi đã phải thử nghiệm nhiều cách cài đặt lời nhắc và nhiệt độ để làm cho mô hình phản hồi một cách hấp dẫn, chính xác và hài hước.

Chuyển văn bản thành giọng nói

Chúng tôi nói lên phản hồi nhận được từ LLM bằng mô hình Chuyển văn bản thành giọng nói và phát lại cho người dùng. Bước này là nguyên nhân chính gây ra sự chậm trễ trong bản demo của chúng tôi.


LLM mất khá nhiều thời gian để phản hồi. Tuy nhiên, chúng hỗ trợ tạo phản hồi ở chế độ phát trực tuyến - từng mã thông báo. Chúng tôi có thể sử dụng tính năng này để tối ưu hóa thời gian chờ đợi bằng cách nói ra từng cụm từ khi chúng được nhận mà không cần đợi phản hồi đầy đủ từ LLM.


Phát âm từng câu riêng lẻ


  • Thực hiện một truy vấn tới LLM.


  • Chúng tôi tích lũy phản hồi trong mã thông báo bộ đệm theo mã thông báo cho đến khi chúng tôi có một câu hoàn chỉnh có độ dài tối thiểu . Tham số độ dài tối thiểu rất quan trọng vì nó ảnh hưởng đến cả ngữ điệu của giọng nói và thời gian trễ ban đầu.


  • Gửi câu đã tạo đến mô hình TTS và phát kết quả cho người dùng. Ở bước này, cần đảm bảo rằng không có điều kiện đua nào trong thứ tự phát lại.


  • Lặp lại bước trước cho đến khi kết thúc phản hồi LLM


Chúng tôi sử dụng thời gian trong khi người dùng nghe đoạn đầu tiên để ẩn độ trễ trong việc xử lý các phần còn lại của phản hồi từ LLM. Nhờ cách tiếp cận này, độ trễ phản hồi chỉ xảy ra ở đầu và là ~ 3 giây.


 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; }


Lần chỉnh sửa cuối cùng

Ngay cả với tất cả các tối ưu hóa của chúng tôi, độ trễ 3-4 giây vẫn rất đáng kể. Chúng tôi quyết định chăm sóc giao diện người dùng bằng phản hồi để giúp người dùng khỏi cảm giác phản hồi bị treo. Chúng tôi đã xem xét một số cách tiếp cận:


  • Chỉ báo LED . Chúng tôi cần hiển thị năm trạng thái: nhàn rỗi, chờ đợi, lắng nghe, suy nghĩ và nói. Nhưng chúng tôi không thể tìm ra cách thực hiện điều đó một cách dễ hiểu với đèn LED.


  • Các từ bổ sung , chẳng hạn như "Để tôi suy nghĩ", "Hmm", v.v., bắt chước lời nói trong đời thực. Chúng tôi đã từ chối tùy chọn này vì phần bổ sung thường không khớp với giọng điệu phản hồi của người mẫu.


  • Đặt một màn hình trong gian hàng. Và hiển thị các trạng thái khác nhau bằng hình ảnh động.


Chúng tôi đã quyết định tùy chọn cuối cùng bằng một trang web đơn giản thăm dò phần phụ trợ và hiển thị hoạt ảnh theo trạng thái hiện tại.


Kết quả

Phòng xưng tội AI của chúng tôi đã hoạt động trong bốn ngày và thu hút hàng trăm người tham dự. Chúng tôi chỉ chi khoảng 50 USD cho API OpenAI. Đổi lại, chúng tôi đã nhận được những phản hồi tích cực đáng kể và những ấn tượng có giá trị.


Thử nghiệm nhỏ này cho thấy rằng có thể thêm giao diện giọng nói trực quan và hiệu quả vào LLM ngay cả với nguồn lực hạn chế và các điều kiện bên ngoài đầy thách thức.


Nhân tiện, các nguồn phụ trợ có sẵn trên GitHub