ChatGPTを使ってImageFlux Live Streamingを音声で操作してみた

こんにちは、テリーです。
ChatGPTにはFunction Callingという機能があります。ChatGPTが外部の関数やAPIを呼び出すための仕組みです。通常のテキスト応答ではなく、関数の引数を含むJSONを返し、システム側で処理を実行できます。例えば「東京の天気は?」と聞くと、天気APIを呼ぶ関数を提案し、その結果をもとに回答を生成します。これにより、最新のデータ取得や自動化が可能になり、アプリ開発での活用が広がります。
ここでポイントとなるのは、ChatGPTが直接関数を呼ぶのではなく、「会話の中で関数が呼ばれようとしていることをイベントで通知」する仕組みです。主となる操作の主体はブラウザ上で行われますのでChatGPTに認証キー等を提供する必要がなく、関数呼び出しイベントを受けて何をするかはアプリケーション次第です。
前回の記事ではRealTime APIを使って、人間とChatGPTが会話のキャッチボールをすることができました。今回は、人間が「ライブ配信を始めて」「ライブ配信を止めて」というような指示を出すとImageFlux Live StreamingのAPIを使ってライブ配信を開始・停止するサンプルを作ってみました。
動作確認環境
- Chrome 134.0.6998.166
- openai-realtime-console 2025年3月11日付 コミットID 059bf8d
RealTime APIのFunction Callingのサンプルを解説
openai-realtime-consoleが最もシンプルな公式サンプルです。まずはオリジナルのコードを実行し、動作を確認します。
git clone https://github.com/openai/openai-realtime-console
cd openai-realtime-console
npm i
.envを修正して環境変数 OPENAI_API_KEY を設定
npm run dev
ブラウザで http://localhost:3000/ にアクセスし、ChatGPTと会話ができることを確認します。
openai-realtime-consoleには、Function Callingのデモも含まれています。Function Callingの定義はToolと呼ばれ、カラーパレットを提案する機能が実装されています。まずは「カラーパレットを表示して」と口に出して、応答を見てみましょう。すると、ChatGPTからは次のような返答があります。
「どんなテーマのカラーパレットをお探しですか?例えば、春らしいものやビンテージ風など、何かイメージがあれば教えてください」
ここで「4月の日本をイメージして」と追加で依頼すると、画面右側に5つのカラーコードが表示されます。
この一連の流れは、単に音声で会話のキャッチボールをしただけではなく、ブラウザ上のHTMLを書き換えるJavaScriptが実行されたことを意味します。このコードはToolPanel.jsxで実装されています。
Function Callingを使うためには、Toolを事前に定義し、ChatGPTに知らせる必要があります。カラーパレットのToolは、ToolPanel.jsxの11〜34行目に定義されています。なかでも最も重要なのは、4行目に記述されているdescriptionです。ここには「ユーザーがカラーパレットを要求したときにこの関数を呼び出します」と書かれています。どのような文脈でこのツールが呼び出されるかを、できる限り具体的に記述する必要があります。
次に重要なのが、19〜30行目に定義されているプロパティです。これは、ChatGPTが会話の文脈を踏まえて、このJSON形式で値を返してきます。ここでは、テーマ文字列と、16進数のカラーコード配列が定義されています。それぞれのdescriptionに詳しく説明が書かれているほど、意図した動作が得られやすく、失敗も少なくなります。
3. const functionDescription = `
4. Call this function when a user asks for a color palette.
5. `;
6.
7. const sessionUpdate = {
8. type: "session.update",
9. session: {
10. tools: [
11. {
12. type: "function",
13. name: "display_color_palette",
14. description: functionDescription,
15. parameters: {
16. type: "object",
17. strict: true,
18. properties: {
19. theme: {
20. type: "string",
21. description: "Description of the theme for the color scheme.",
22. },
23. colors: {
24. type: "array",
25. description: "Array of five hex color codes based on the theme.",
26. items: {
27. type: "string",
28. description: "Hex color code",
29. },
30. },
31. },
32. required: ["theme", "colors"],
33. },
34. },
35. ],
36. tool_choice: "auto",
37. },
38. };
この定義をChatGPTに知らせるのが79行目です。
78. if (!functionAdded && firstEvent.type === "session.created") {
79. sendClientEvent(sessionUpdate);
80. setFunctionAdded(true);
81. }
さらに深掘りしていくと、App.tsxの90行目で、ChatGPTにデータを送信していることがわかります。
84. function sendClientEvent(message) {
85. if (dataChannel) {
86. const timestamp = new Date().toLocaleTimeString();
87. message.event_id = message.event_id || crypto.randomUUID();
88.
89. // send event before setting timestamp since the backend peer doesn't expect this field
90. dataChannel.send(JSON.stringify(message));
91.
92. // if guard just in case the timestamp exists by miracle
93. if (!message.timestamp) {
94. message.timestamp = timestamp;
95. }
96. setEvents((prev) => [message, ...prev]);
97. } else {
98. console.error(
99. "Failed to send message - no data channel available",
100. message,
101. );
102. }
103. }
定義したToolが呼び出された後の処理は、ToolPanel.jsxの89〜105行目に記述されています。90〜91行目の条件式が、Toolの定義にマッチしたことを指します。このサンプルでは、Tool実行後に、ChatGPTが追加で質問を人間に投げかけるように指示を出しています。
89. if (
90. output.type === "function_call" &&
91. output.name === "display_color_palette"
92. ) {
93. setFunctionCallOutput(output);
94. setTimeout(() => {
95. sendClientEvent({
96. type: "response.create",
97. response: {
98. instructions: `
99. ask for feedback about the color palette - don't repeat
100. the colors, just ask if they like the colors.
101. `,
102. },
103. });
104. }, 500);
105. }
ImageFlux Live Streamingを呼ぶためのToolの定義
Toolの定義方法と呼び出し方がわかったところで、自分の作りたい機能を実装してみましょう。今回はライブ配信の開始と終了を音声で指示をすると、Function Callingが反応するように記述します。
ToolPanel.jsxの11行目あたりに、下記のようにToolの定義を追加します。Toolは最大10個まで定義できます。
7. const sessionUpdate = {
8. type: "session.update",
9. session: {
10. tools: [
11. {
12. type: "function",
13. name: "start_live_streaming",
14. description: "Call this function to start live streaming.",
15. },
16. {
17. type: "function",
18. name: "stop_live_streaming",
19. description: "Call this function to stop live streaming.",
20. },
次に、Toolが反応したことをまずはログで表示します。
100. if (
101. output.type === "function_call" &&
102. output.name === "start_live_streaming"
103. ) {
104. startLiveStreaming();
105. }
106. else if (
107. output.type === "function_call" &&
108. output.name === "stop_live_streaming"
109. ) {
110. stopLiveStreaming();
111. }
・・・
200. async function startLiveStreaming() {
201. console.warn("Start live streaming...");
202. }
203.
204. async function stopLiveStreaming() {
205. console.warn("Stop live streaming...");
206. }
「ライブ配信を開始して」「ライブ配信を終了して」と話しかけると、ログにメッセージが表示され、Function Callingが期待通り反応していることを確認できます。
実行結果の通知
ここまで来ると、一般的なイベント処理、例えば「ボタンを押したらライブ配信を開始する」時と同じようにコードを書きます。
200. const videoRef = useRef(null);
201. const sora = useRef(null);
202. const publisher = useRef(null);
203. const pub_stream = useRef(null);
204. async function startLiveStreaming() {
205. console.warn("Starting live streaming...");
206. const {channel_id, sora_url} = await getChannelId();
207. pub_stream.current = await navigator.mediaDevices.getUserMedia({
208. video: true,
209. audio: true,
210. });
211. const options = {};
212. sora.current = Sora.connection(sora_url, true);
213. publisher.current = sora.current.sendonly(channel_id, null, options);
214. await publisher.current.connect(pub_stream.current);
215. videoRef.current.srcObject = pub_stream.current;
216. }
217.
218. async function stopLiveStreaming() {
219. console.warn("Stopping live streaming...");
220. publisher.current?.disconnect();
221. publisher.current = null;
222. pub_stream.current?.getTracks().forEach((track) => track.stop());
223. pub_stream.current = null;
224. }
225.
226. async function getChannelId() {
227. // (略)
228. }
ChatGPTに関数の実行結果を伝えると、それを踏まえて言葉で表現してくれます。下記の105〜112行目が関数の実行結果を伝える部分で、113行目がそれを受けてChatGPTに話をさせる処理です。同様の処理を、ライブ配信の停止処理にも追加します。
100. if (
101. output.type === "function_call" &&
102. output.name === "start_live_streaming"
103. ) {
104. startLiveStreaming();
105. sendClientEvent({
106. type: "conversation.item.create",
107. item: {
108. type: "function_call_output",
109. call_id: output.call_id,
110. "output": "Live streaming started."
111. },
112. });
113. sendClientEvent({ type: "response.create" });
114. }
115. else if (
116. output.type === "function_call" &&
117. output.name === "stop_live_streaming"
118. ) {
119. stopLiveStreaming();
120. sendClientEvent({
121. type: "conversation.item.create",
122. item: {
123. type: "function_call_output",
124. call_id: output.call_id,
125. "output": "Live streaming stopped."
126. },
127. });
128. sendClientEvent({ type: "response.create" });
129. }
サンプル
最後に、実際にライブ配信を行ったサンプル動画をお見せします。指示は日本語でも英語でも出すことができ、「終了」「止めて」「もう一回やって」など、多少のニュアンスの違いも文脈から解釈してくれるのが、ChatGPTならではの挙動です。
まとめ
ChatGPTのFunction Calling機能を使って、ImageFlux Live Streamingに配信を開始・停止するサンプルを紹介しました。本記事ではオプションなしの単純な一方向の指示のみですが、カメラの種類を選択したり、音声をミュートにすることもできます。ぜひ挑戦してみてください。より詳しい実装方法に興味がある方はお気軽にご相談ください。