Realtime APIとLangChainによる音声対話検索エージェントを試す

## はじめに
Realtime APIはOpenAIより10/2に発表された後、本日ようやくリリースされた低遅延で音声対話が実現可能なAPIで、ChatGPTのAdvanced Voice Modeと同じような感覚(何度か試しましたが感情表現はできなさそう？)でAIと対話することができる優れモノになります。

https://openai.com/index/introducing-the-realtime-api/

これまでですとLLMを使ったAIとの音声対話というは、ユーザーが音声を入力して、音声認識ライブラリでテキストに変換、変換されたテキストをLLMが読み込んで応答テキストを生成、生成されたテキストを音声に合成してユーザーに返却といった処理が必要でした。

これらの体験を少しでも向上させるために例えばチャンクで区切って逐次的に投入させて遅延感を減らすなどの工夫をしていましたが、ユーザーに応答が返却されるまでのそもそものステップ多いのも合って中々に骨が折れるものでした。

今回のRealtime APIでは、それらの工程の一部を省いていくれるという嬉しみがあります。

※音声の種類選択肢が限られることから、一定聞き慣れるとAIっぽさを個人的には感じるようになるので独自の合成音声を用いるこれまでの手法は引き続き必要になるだろうなというのが現在の筆者の見解です。

## Voice ReAct Agentを試す

前置きが長くなってしまいましたが今朝方に本家のRealtime APIが漸く使えるようになったと思ったら、LangChainでそれらを活用したAIエージェントのデモアプリが公開されていたので軽く触ってみたというのが本記事の趣旨になります。

https://github.com/langchain-ai/react-voice-agent?tab=readme-ov-file

Voice ReAct Agentではユーザーからの音声を認識してAIが「LangChain tools」を呼び出して応答に必要な処理を実行して返却するというものになります。

![react.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354022/e9e264f0-e049-4e7b-f6ce-891517683baf.png)

本家Realtime APIでもFunction callingによる関数の実行が可能になっているのでそれをLangChainでも可能になったという感じですかね。

下記のOpenAIが提供するRealtime APIのデモアプリでFunction callingによる関数実行を体験できますのでそちらも是非試してみてください。※例えば東京の天気について尋ねるとWeatherAPIが実行されます。

https://github.com/openai/openai-realtime-console

### Tavily APIキーの取得

「Tavily」のサイトにより、APIキーを取得できますので下記にアクセスしてください。

https://tavily.com/

Overviewよりdefaultで提供されているAPIキーを控えます。

![スクリーンショット_2024-10-04_10_19_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354022/eed37ee7-e3c8-1a21-e406-468feceedc3b.png)

tavilyは毎月1,000回までなら無料でAPIを実行できるのでちょっと試す程度ならば問題なく使えます。

![スクリーンショット 2024-10-04 10.21.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354022/9500e596-ebe0-93e9-518c-1dbbe1643cce.png)

OpenAIのAPIキーも自身のものを用意してREADMEに従って進みます。



### インストール
```
pip install uv
```


### 環境変数設定
```
export OPENAI_API_KEY=your_openai_api_key
export TAVILY_API_KEY=your_tavily_api_key
```

### プロジェクト実行
```
cd server
uv run src/server/app.py
```

http://localhost:3000 にアクセスすると非常にシンプルな画面が表示されたかと思います

![スクリーンショット 2024-10-04 10.25.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354022/6ba84c67-4e89-7c2c-808d-9b4acf454675.png)

「Start Audio」ボタンをクリックしてください。

試しに「今日の東京の天気」や「東京駅近くのレストラン」について検索してみました

以下は応答のログです。
```log
INFO:     connection open
input_audio_buffer.speech_started
input_audio_buffer.speech_stopped
input_audio_buffer.committed
input_audio_buffer.speech_started
user: こんにちは

input_audio_buffer.speech_stopped
input_audio_buffer.committed
response.output_item.added
model: わかりました。ちょっと調べますね。 お待ちください。
user: 今日の東京の天気について教えて頂ければと思います。

response.output_item.added
tool call {'type': 'response.function_call_arguments.done', 'event_id': 'event_AESbCdfdxrP5pfR4PQ4ki', 'response_id': 'resp_AESbAzK0r9eyoNem8F97P', 'item_id': 'item_AESbB4JK4qHkY4hH4oAi0', 'output_index': 1, 'call_id': 'call_yH5JEPWpoEsd6FZx', 'name': 'tavily_search_results_json', 'arguments': '{"query":"Tokyo weather today"}'}
tool output {'type': 'conversation.item.create', 'item': {'id': 'call_yH5JEPWpoEsd6FZx', 'call_id': 'call_yH5JEPWpoEsd6FZx', 'type': 'function_call_output', 'output': '[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Tokyo\', \'region\': \'Tokyo\', \'country\': \'Japan\', \'lat\': 35.6895, \'lon\': 139.6917, \'tz_id\': \'Asia/Tokyo\', \'localtime_epoch\': 1728009858, \'localtime\': \'2024-10-04 11:44\'}, \'current\': {\'last_updated_epoch\': 1728009000, \'last_updated\': \'2024-10-04 11:30\', \'temp_c\': 28.1, \'temp_f\': 82.6, \'is_day\': 1, \'condition\': {\'text\': \'Partly cloudy\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/116.png\', \'code\': 1003}, \'wind_mph\': 17.7, \'wind_kph\': 28.4, \'wind_degree\': 201, \'wind_dir\': \'SSW\', \'pressure_mb\': 1019.0, \'pressure_in\': 30.09, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 79, \'cloud\': 75, \'feelslike_c\': 29.9, \'feelslike_f\': 85.7, \'windchill_c\': 28.7, \'windchill_f\': 83.7, \'heatindex_c\': 30.9, \'heatindex_f\': 87.5, \'dewpoint_c\': 20.2, \'dewpoint_f\': 68.3, \'vis_km\': 10.0, \'vis_miles\': 6.0, \'uv\': 6.0, \'gust_mph\': 21.2, \'gust_kph\': 34.2}}"}, {"url": "https://world-weather.info/forecast/japan/tokyo/april-2024/", "content": "Extended weather forecast in Tokyo. Hourly Week 10 days 14 days 30 days Year. Detailed \\u26a1 Tokyo Weather Forecast for April 2024 - day/night \\ud83c\\udf21\\ufe0f temperatures, precipitations - World-Weather.info."}, {"url": "https://weatherspark.com/h/m/143809/2024/4/Historical-Weather-in-April-2024-in-Tokyo-Japan", "content": "Tokyo Temperature History April 2024. The daily range of reported temperatures (gray bars) and 24-hour highs (red ticks) and lows (blue ticks), placed over the daily average high (faint red line) and low (faint blue line) temperature, with 25th to 75th and 10th to 90th percentile bands."}, {"url": "https://www.weather-atlas.com/en/japan/tokyo-weather-april", "content": "Tokyo records a mean low-temperature of 10.6\\u00b0C (51.1\\u00b0F) in April. Humidity In April, the average relative humidity is 67%. Rainfall In Tokyo, in April, during 12.8 rainfall days, 39mm (1.54\\") of precipitation is typically accumulated. In Tokyo, during the entire year, the rain falls for 134.5 days and collects up to 425mm (16.73\\") of ..."}, {"url": "https://www.wunderground.com/hourly/jp/tokyo/RJTT/date/2024-4-10", "content": "Tokyo Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for the Tokyo area."}]'}}
response.output_item.added
model: 東京の今日の天気をお知らせしますね。 現在の気温は28.1℃で、部分的に曇っています。風が南南西から吹いており、風速は約28.4 km/hです。 湿度は79%で、体感温度は29.9℃と感じられるでしょう。 追加の情報が必要な場合はお知らせください。
```

```log
input_audio_buffer.speech_started
input_audio_buffer.speech_stopped
input_audio_buffer.committed
response.output_item.added
user: 東京駅近辺のイタリアンレストランについてお調べで

model: わかりました。東京駅近辺のイタリアンレストランを調べますね。 少々お待ちください。
response.output_item.added
tool call {'type': 'response.function_call_arguments.done', 'event_id': 'event_AESbn9wRQ5HJkHqfGHKgn', 'response_id': 'resp_AESbknowqEb5R4UQA60jV', 'item_id': 'item_AESbm4ruq9RrgG8M7FVso', 'output_index': 1, 'call_id': 'call_HhER6b5IlQ96HgJI', 'name': 'tavily_search_results_json', 'arguments': '{"query":"Italian restaurants near Tokyo Station"}'}
tool output {'type': 'conversation.item.create', 'item': {'id': 'call_HhER6b5IlQ96HgJI', 'call_id': 'call_HhER6b5IlQ96HgJI', 'type': 'function_call_output', 'output': '[{"url": "https://www.yelp.com/search?cflt=italian&find_loc=Tokyo+Station,+Chiyoda,+\\u6771\\u4eac\\u90fd", "content": "Best Italian in Tokyo Station, Chiyoda, \\u6771\\u4eac\\u90fd, Japan - EATALY \\u30b0\\u30e9\\u30f3\\u30b9\\u30bf\\u4e38\\u306e\\u5185\\u5e97, \\u30d1\\u30ea\\u30a2\\u30c3\\u30c1\\u30e7 \\u4e38\\u306e\\u5185\\u4ef2\\u901a\\u308a, \\u30d6\\u30eb\\u30fc\\u30ce \\u6771\\u4eac, \\u30c8\\u30e9\\u30c3\\u30c8\\u30ea\\u30a2 \\u30ea\\u30a2\\u30ca, Il Ponentino, \\u30d4\\u30e3\\u30c1\\u30a7\\u30fc\\u30ec, \\u30a4\\u30eb\\u30fb\\u30d0\\u30ed\\u30c3\\u30b3 \\u6709\\u697d\\u753a\\u5e97, \\u30b3\\u30c3\\u30ec\\u30d9\\u30ec\\u30fc\\u30c8, A16 Tokyo, \\u9280\\u5ea7\\u30b7\\u30b7\\u30ea\\u30a2"}, {"url": "https://www.tripadvisor.com/Restaurants-g298184-c26-zfn20928881-Tokyo_Tokyo_Prefecture_Kanto.html", "content": "10 Best Italian Restaurants in Tokyo Station / Ginza / Nihonbashi Tokyo Restaurants Italian Food in Tokyo Station / Ginza / Nihonbashi, Tokyo Prefecture Italian Restaurants in Tokyo Station / Ginza / Nihonbashi Italian Tokyo Station / Ginza / Nihonbashi Ginza / Tokyo Nihonbashi Top Restaurants in Tokyo Italian Tokyo Station / Ginza / Nihonbashi Italian$$ - $$$Menu Italian$$$$Menu Italian$$ - $$$Menu Italian$$ - $$$Menu Very good Italian meal in Tokyo Highly recommended restaurant with fast service and delicious food Italian, Japanese$$ - $$$Menu Italian$$ - $$$Menu The dinner menu was in Italian and Japanese. Italian$$$$Menu Italian$$ - $$$ Italian$$$$Menu Italian$$ - $$$Menu Italian$$ - $$$ Italian$$ - $$$"}, {"url": "https://www.yelp.com/search?cflt=italian&find_loc=Tokyo+Station+Marunouchi,+Chiyoda,+\\u6771\\u4eac\\u90fd&start=20", "content": "Best Italian in Tokyo Station Marunouchi, Chiyoda, \\u6771\\u4eac\\u90fd, Japan - \\u679c\\u5b9f\\u5712 \\u6771\\u4eac\\u5e97, \\u30b5\\u30d0\\u30c6\\u30a3\\u30fc\\u30cb\\u30fb\\u30c7\\u30a3\\u30fb\\u30d5\\u30a3\\u30ec\\u30f3\\u30c4\\u30a7, NIHONBASHI BREWERY. T.S, Eataly \\u65e5\\u672c\\u6a4b\\u4e09\\u8d8a\\u5e97, PASTAVOLA, \\u30c8\\u30e9\\u30c3\\u30c8\\u30ea\\u30a2 \\u30ac\\u30eb\\u30ac, ACORN \\u6771\\u4eac\\u99c5\\u30b0\\u30e9\\u30f3\\u30eb\\u30fc\\u30d5\\u30d5\\u30ed\\u30f3\\u30c8\\u5e97, \\u3042\\u307e\\u3054\\u3053\\u308d, Dazzle, XEX TOKYO"}, {"url": "https://www.yelp.com/search?cflt=italian&find_loc=Tokyo+Station+Marunouchi,+Chiyoda,+\\u6771\\u4eac\\u90fd", "content": "Best Italian in Tokyo Station Marunouchi, Chiyoda, \\u6771\\u4eac\\u90fd, Japan - EATALY \\u30b0\\u30e9\\u30f3\\u30b9\\u30bf\\u4e38\\u306e\\u5185\\u5e97, \\u30d6\\u30eb\\u30fc\\u30ce \\u6771\\u4eac, \\u30a4\\u30eb\\u30fb\\u30d0\\u30ed\\u30c3\\u30b3 \\u6709\\u697d\\u753a\\u5e97, \\u30d1\\u30ea\\u30a2\\u30c3\\u30c1\\u30e7 \\u4e38\\u306e\\u5185\\u4ef2\\u901a\\u308a, \\u30ec\\u30b9\\u30c8\\u30e9\\u30f3\\u30c6\\u30fb\\u30d2\\u30ed\\u30fb\\u30c1\\u30a7\\u30f3\\u30c8\\u30ed, \\u30a4\\u30eb \\u30ae\\u30aa\\u30c3\\u30c8\\u30fc\\u30cd \\u4e38\\u306e\\u5185, A16 Tokyo, \\u30d4\\u30e3\\u30c1\\u30a7\\u30fc\\u30ec, \\u30a2\\u30f3\\u30d5\\u30a3\\u30cb, \\u30b3\\u30fc\\u30c9\\u30cd\\u30fc\\u30e0 \\u30df\\u30af\\u30bd\\u30ed\\u30b8\\u30fc \\u30c8\\u30a6\\u30ad\\u30e7\\u30a6"}, {"url": "https://savorjapan.com/contents/more-to-savor/10-italian-restaurants-in-tokyo-for-authentic-inventive-dining/", "content": "10 Italian Restaurants in Tokyo for Authentic & Inventive Dining | SAVOR JAPAN -Japanese Restaurant Guide- Discover Creative Italian Food in Tokyo at These Restaurants The restaurant focuses on the flavor of \\u201cmamma\\u2019s cooking\\u201d, or the authentic taste of traditional Italian food, and aims to serve food that tastes as close as possible to that served in Italy, even when made with Japanese ingredients. A leader of the next generation of Italian cuisine in Ginza, Ristorante Kurodino was founded by chef Keisuke Kuroda, who worked for 20 years at the Tokyo satellite of Ristorante Enoteca Pinchiorri in Florence, Italy, one of the best restaurants in the world, before opening his own restaurant. Try Tokyo Italian Restaurants For a Unique Japan Food Experience"}]'}}
response.output_item.added
model: 東京駅近辺のイタリアンレストランをいくつかご紹介いたします。

1. **EATALY グランスタ丸の内店** - 本格的なイタリア料理とともに、イタリアの食材も購入できるお店です。
  
2. **パリアッチョ 丸の内仲通り** - カジュアルな雰囲気で楽しめるイタリアンレストランです。

3. **ブルーノ 東京** - 洗練された空間で、本格的なイタリア料理を楽しめます。

4. **イル・バロッコ 有楽町店** - 落ち着いた雰囲気で楽しむことができるお店です。

5. **A16 Tokyo** - カリフォルニア発祥のピザを中心としたイタリアンレストランです。

これらのレストランは、東京駅近辺でおいしいイタリア料理を提供しているので、ぜひ訪れてみてください。
input_audio_buffer.speech_started
input_audio_buffer.speech_stopped
input_audio_buffer.committed
response.output_item.added
user: ありがとうございます
```

ログ内部を見るとtool callが実行されていることが分かりますね！
``tool call {'type': 'response.function_call_arguments.done', 'event_id': 'event_AESbn9wRQ5HJkHqfGHKgn', 'response_id': 'resp_AESbknowqEb5R4UQA60jV', 'item_id': 'item_AESbm4ruq9RrgG8M7FVso', 'output_index': 1, 'call_id': 'call_HhER6b5IlQ96HgJI', 'name': 'tavily_search_results_json', 'arguments': '{"query":"Italian restaurants near Tokyo Station"}'}``

### デモ動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/uizxBvTdF1I?si=f2pRfjfrIAe1O0n-" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## ツールの定義 
tools.py上でtavily_tool関連の定義が記載されてましたので、他のツールを呼び出したいときは追記していけばよさそうですね。

```
from langchain_core.tools import tool

from langchain_community.tools import TavilySearchResults


@tool
def add(a: int, b: int):
    """Add two numbers. Please let the user know that you're adding the numbers BEFORE you call the tool"""
    return a + b


tavily_tool = TavilySearchResults(
    max_results=5,
    include_answer=True,
    description=(
        "This is a search tool for accessing the internet.\n\n"
        "Let the user know you're asking your friend Tavily for help before you call the tool."
    ),
)

TOOLS = [add, tavily_tool]
```

## おわりに
Realtime APIとLangChainによるTool Callを組み合わせた音声対話検索エージェントを実際に試してみましたが、音声のレスポンス速度もそうですがToolと組み合わせることで様々な音声対話エージェントの扉が一気に開いたと思われます。

LangChainだけでなく、本家Realtime APIの仕組みをじっくりと理解して今後の事業開発に役立てていきたいですね！

簡単に試せるので是非みなさんも体験してみてください。