AIがビデオ会議の常識を覆す:FishjamとGemini Liveで実現する次世代のインタラクション
現代のビジネス環境において、ビデオ会議はもはや不可欠なコミュニケーションツールです。しかし、その体験は常に進化を求めています。単なる人間同士の対話に留まらず、AIが会議に「参加」し、リアルタイムで情報を提供したり、会話を促進したりする未来が、今、現実のものとなりつつあります。
本記事では、低遅延ビデオ会議・ライブストリーミングAPIである「Fishjam」と、Googleが提供する最先端のマルチモーダルAIモデル「Gemini 1.5 Flash Live Audio」を組み合わせることで、いかにしてAI参加者とのシームレスなインタラクションを実現できるか、その具体的な機能、実装方法、ビジネスへの影響、そして将来性について深く掘り下げて解説します。
1. はじめに:AIとリアルタイムインタラクションの融合
ビデオ会議にAIが参加するというアイデアは、SF映画の世界のように聞こえるかもしれません。しかし、Google AI StudioからリリースされたGemini 1.5 Flash Live Audioモデルと、Software Mansionが開発するFishjamという強力なツールを組み合わせることで、この革新的な体験は驚くほど簡単に構築できるようになりました。
この新しいアプローチにより、AIは単なる音声アシスタントではなく、ビデオ会議におけるリアルタイムの参加者として機能します。参加者の音声を聞き取り、カメラフィードを見て状況を認識し、適切なタイミングで自然な音声で応答する。これにより、会議の生産性を飛躍的に向上させたり、これまでにないインタラクティブなサービスを提供したりする可能性が生まれます。
この記事では、この画期的な技術スタックを深く分析し、読者がその重要性、具体的な機能、ビジネスへの影響、そして将来性を理解できるよう、詳細かつ説得力のある形で情報を提供します。
2. Gemini Live APIの深掘り:マルチモーダルAIの力
Gemini Live APIは、リアルタイムで低遅延の音声および視覚インタラクションを可能にする画期的な技術です。従来のAIモデルがテキストベースの入力に限定されていたのに対し、Gemini Live APIは、オーディオ、画像、テキストといった複数のモダリティからの連続的なストリームを同時に処理し、即座に人間らしい応答を生成することができます。
2.1. リアルタイム音声・動画インタラクションの概要
Gemini Live APIの最大の特長は、その「ライブ」性です。ユーザーからの音声入力があると同時に、その音声が処理され、リアルタイムでAIの応答が生成されます。さらに、画像や動画のストリームも同時に分析できるため、AIは単に言葉だけでなく、視覚的な情報も理解して状況判断を行うことが可能です。例えば、会議中にユーザーが画面上の特定のオブジェクトを指差した場合、AIはそれを認識し、そのコンテキストに基づいて対話を進めることができます。
2.2. テキスト、オーディオ、ビデオのストリーミング処理
Gemini Live APIは、以下のような連続的なデータストリームを処理します。
- テキスト: ユーザーが入力したテキストメッセージや、音声入力からリアルタイムに変換されたテキストデータ。
- オーディオ: ユーザーのマイク入力からの音声ストリーム。Fishjamのようなプラットフォームを通じて低遅延でGeminiに送信されます。
- ビデオ: ユーザーのカメラフィードからの動画ストリーム。Fishjamは動画トラックからフレームをキャプチャし、Geminiに送信することで、AIが視覚情報を認識できるようにします。
これらの入力ストリームはGeminiによって統合的に解釈され、テキストとオーディオ形式で応答が生成されます。特に、オーディオ応答は非常に自然な音声で提供されるため、AIとの対話が人間との会話とほとんど区別がつかないレベルに達しています。
2.3. WebSocketプロトコルによる低遅延通信
リアルタイムインタラクションを実現するためには、極めて低い遅延が不可欠です。Gemini Live APIはWebSocketプロトコルを利用することで、クライアントとサーバー間の永続的な接続を確立し、双方向のデータを効率的にやり取りします。これにより、音声や動画のストリームがリアルタイムで途切れることなくAIに送信され、AIからの応答も瞬時にユーザーに届けられます。この低遅延な通信は、特にビデオ会議のようなリアルタイム性が求められるアプリケーションにおいて、ユーザー体験を劇的に向上させます。
2.4. 多岐にわたるユースケース
Gemini Live APIのマルチモーダルな能力と低遅延性は、様々な業界に革命をもたらす可能性を秘めています。
- Eコマースと小売: ショッピングアシスタントが顧客の好みや行動を理解し、パーソナライズされた推奨事項を提供します。例えば、顧客が商品を見ている様子をカメラで捉え、その表情やジェスチャーから興味の度合いを読み取り、最適な情報を提供できます。
- ゲーミング: インタラクティブなノンプレイヤーキャラクター(NPC)やインゲームヘルプアシスタントが、プレイヤーの行動や感情にリアルタイムで反応し、より没入感のあるゲーム体験を創出します。リアルタイム翻訳機能もゲーム内コミュニケーションを円滑にします。
- 次世代インターフェース: スマートグラスや車載システムなど、新しいタイプのデバイスにおける音声・動画主導のユーザーインターフェースを可能にします。ユーザーは自然言語で指示を出し、視覚的な情報も活用してデバイスと対話できます。
- 教育: AI家庭教師や伴侶が、生徒の学習状況をリアルタイムで分析し、個別化された指導やフィードバックを提供します。生徒の表情や声のトーンから理解度を把握し、学習内容を調整することも可能です。
これらのユースケースは、Gemini Live APIが単なるチャットボットを超え、実世界での複雑なインタラクションに対応できる汎用性の高いプラットフォームであることを示しています。
3. Fishjam Agent:AIを会議に統合するブリッジ
Fishjamは、開発者がWebRTC(Web Real-Time Communication)技術を活用して、低遅延のビデオ会議やライブストリーミングアプリケーションを構築するための強力なAPIとSDKを提供します。そして、「Fishjam Agent」は、このFishjam環境にバックエンドアプリケーションを参加させ、あたかも通常の会議参加者であるかのように振る舞わせるための機能です。
3.1. Fishjamとは?低遅延ビデオ会議・ライブストリーミングAPI
Fishjamは、WebRTCの複雑さを抽象化し、シンプルなAPIを通じて高品質かつ低遅延なリアルタイム通信機能を提供します。これにより、開発者はインフラ構築やWebRTCの専門知識なしに、スケーラブルなビデオ会議やライブストリーミングサービスを迅速に開発できます。
主な特徴は以下の通りです。
- 低遅延: リアルタイム性が求められるアプリケーションに最適な、ミリ秒単位の遅延を実現します。
- スケーラビリティ: 大規模な会議やライブストリーミングにも対応できる柔軟なインフラを提供します。
- 豊富なAPI: ルーム管理、ピア管理、トラック管理、メディア処理など、リアルタイム通信に必要なあらゆる機能がAPIとして提供されています。
3.2. Fishjam Agentの役割と機能
Fishjam Agentは、バックエンドサービスがFishjamの会議ルームに「参加」するためのインターフェースを提供します。これにより、以下のようなことが可能になります。
- オーディオ・ビデオの送受信: Agentは会議参加者からオーディオやビデオのストリームを受信し、また自身が生成したオーディオやビデオを会議ルームに送信できます。
- リアルタイム処理: 受信したメディアストリームをリアルタイムで処理し、分析、変換、あるいはAIモデルへの入力として利用できます。
- 通常のクライアントとしての振る舞い: 会議ルーム内の他の参加者からは、Agentも通常のクライアントであるかのように見えます。これは、AIが会議に自然に溶け込む上で非常に重要です。
3.3. Gemini Liveとの連携メカニズム
Fishjam AgentとGemini Liveの連携は、以下の双方向のリアルタイムストリームによって実現されます。
Fishjam → Gemini:
- Fishjam Agentは、会議ルーム内の全参加者(または特定の参加者)のオーディオストリームを受信します。
- このオーディオデータは、Fishjam Agentのバックエンドでリアルタイムに処理され、Gemini Live APIが期待する形式(例:Base64エンコードされたオーディオデータ)に変換されます。
- 変換されたオーディオデータは、WebSocketを通じてGemini Live APIに送信されます。
- さらに、Fishjam Agentは、会議参加者のビデオトラックから定期的に画像をキャプチャし、これもGemini Live APIに送信します。これにより、Geminiは視覚情報も考慮に入れた状況認識が可能になります。
Gemini → Fishjam:
- Gemini Live APIは、受信したオーディオ、ビデオ、テキストのストリームを分析し、適切なテキストとオーディオ形式の応答を生成します。
- 生成されたオーディオ応答は、WebSocketを通じてFishjam Agentのバックエンドに送り返されます。
- Fishjam Agentは、このオーディオデータをFishjamが会議ルームで再生できる形式に変換し、自身のオーディオトラックを通じて会議ルームに送信します。
この連携により、AIは会議に低遅延で参加し、人間の参加者と自然に対話できるだけでなく、視覚情報も活用した高度なインタラクションを提供できるようになります。
4. 実装ガイド:AI搭載ビデオ会議の構築ステップ
それでは、実際にFishjamとGemini Liveを統合したAI搭載ビデオ会議システムを構築するための具体的なステップを見ていきましょう。
4.1. 前提条件:必要なもの
開発を始める前に、いくつかのキーとなる認証情報とSDKが必要です。
- Fishjam Server Credentials:
Fishjam IDmanagementTokenこれらはFishjamダッシュボード(fishjam.io/app)から取得できます。試用目的であれば、無料のサンドボックスティアで十分であり、すぐに開発を開始できます。
- Google Gemini API Key:
- Google AI Studioから取得します。 Gemini APIへのアクセスにはこのAPIキーが必要です。
4.2. 開発環境のセットアップ:SDKのインストール
Google Geminiとの統合はオプション機能であるため、必要なSDKを個別にインストールします。
npm install @fishjam-cloud/js-server-sdk @google/generative-ai
# または yarn を使用する場合
yarn add @fishjam-cloud/js-server-sdk @google/generative-ai
@fishjam-cloud/js-server-sdk: Fishjamのサーバーサイド機能を操作するためのSDKです。@google/generative-ai: Google Gemini APIと連携するためのSDKです。
4.3. クライアントサイド (フロントエンド) の構築:ユーザーインタフェース
フロントエンドは、ユーザーが会議に参加し、AIと対話するためのインターフェースを提供します。ここでは、Reactアプリケーションを例に説明します。
4.3.1. React Appの基本構成
アプリケーションは主に2つのビューで構成されます。
- Lobbyビュー: ユーザーがルーム名と自分の名前を入力し、会議に参加する前の画面です。
- CallView: ユーザーが会議に参加した後、ビデオフィードやAIエージェントとの対話が表示される画面です。
4.3.2. ピアトークンの取得とルーム参加ロジック
Lobbyビューでは、ユーザーがルーム名とユーザー名を入力し「Join」ボタンをクリックすることで、バックエンドAPIを呼び出し、Fishjamルームへの参加に必要なピアトークンを取得します。
// App.tsx
import { useConnection } from "@fishjam-cloud/react-client";
import { Lobby } from "./components/Lobby";
import { CallView } from "./components/CallView";
function App() {
const [view, setView] = useState("lobby"); // "lobby" or "call"
const [roomId, setRoomId] = useState<string | null>(null);
const [roomName, setRoomName] = useState<string | null>(null);
const handleJoined = useCallback((roomId: string, roomName: string) => {
setRoomId(roomId);
setRoomName(roomName);
setView("call");
}, []);
if (view === "lobby") {
return <Lobby onJoined={handleJoined} />;
}
return <CallView roomId={roomId!} roomName={roomName!} onLeave={() => setView("lobby")} />;
}
export default App;
// Lobby.tsx (簡略化)
import { useConnection } from "@fishjam-cloud/react-client";
import { trpc } from "../trpc"; // tRPCクライアント
function Lobby({ onJoined }: LobbyProps) {
const [roomName, setRoomName] = useState("");
const [userName, setUserName] = useState("");
const [loading, setLoading] = useState(false);
const { joinRoom, initializeDevices } = useConnection();
const getPeerToken = trpc.getPeerToken.useMutation();
const handleSubmit = async () => {
setLoading(true);
try {
const trimmedRoomName = roomName.trim();
const trimmedPeerName = userName.trim();
// バックエンドのgetPeerTokenエンドポイントを呼び出し
const { roomId, peerToken, peer } = await getPeerToken.mutateAsync({
roomName: trimmedRoomName,
peerName: trimmedPeerName,
});
// デバイスの初期化
await initializeDevices();
// Fishjamルームに参加
await joinRoom(peerToken, { name: trimmedPeerName });
onJoined(roomId, trimmedRoomName);
} catch (error) {
console.error("Failed to join room:", error);
} finally {
setLoading(false);
}
};
return (
<div style={styles.container}>
<h1>Gemini x Fishjam Demo</h1>
<p>Videoconference with a Gemini Live AI agent</p>
<div style={styles.card}>
<input
placeholder="Room name"
value={roomName}
onChange={(e) => setRoomName(e.target.value)}
/>
<input
placeholder="Your name"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/>
<button onClick={handleSubmit} disabled={loading}>
Join
</button>
</div>
</div>
);
}
ここで重要なのは、FishjamのuseConnectionフックから提供されるjoinRoomメソッドです。これにバックエンドから取得したpeerTokenと、peerMetadata(今回はname)を渡すことで、ユーザーが会議に参加します。
4.3.3. CallViewにおけるデバイス管理とピア表示
CallViewでは、@fishjam-cloud/react-clientが提供する様々なフックを利用して、カメラやマイクの制御、リモートピアの管理を行います。
// CallView.tsx (簡略化)
import { useCamera, useMicrophone, usePeers } from "@fishjam-cloud/react-client";
import { trpc } from "../trpc";
function CallView({ roomId, roomName, onLeave }: CallViewProps) {
const [showPromptModal, setShowPromptModal] = useState(false);
const { isCameraOn, toggleCamera, cameraStream } = useCamera();
const { isMicrophoneMuted, toggleMicrophoneMute, microphoneStream } = useMicrophone();
// 参加者から取得したピアのリスト
const { remotePeers } = usePeers();
// エージェントピアの識別(メタデータがないピア)
const agentPeer = useMemo(() => remotePeers.find(p => !p.metadata), [remotePeers]);
// 人間のピアのリスト
const humanPeers = useMemo(() => remotePeers.filter(p => p.metadata), [remotePeers]);
const createAgent = trpc.createAgent.useMutation();
const handleStartAgent = async (systemPrompt: string) => {
setShowPromptModal(false);
await createAgent.mutateAsync({ roomId, systemPrompt });
};
const handleLeave = async () => {
// ルームを離れるロジック(Fishjam SDKのleaveRoomなど)
onLeave();
};
return (
<div>
<Toolbar
roomName={roomName}
agentActive={!!agentPeer}
onLeave={handleLeave}
onAddAgent={() => setShowPromptModal(true)}
/>
{/* ピアのビデオタイルなどを表示 */}
{/* systemPromptModalの表示ロジック */}
</div>
);
}
useCamera,useMicrophone: カメラとマイクの状態を管理し、オン/オフを切り替えたり、メディアストリームを取得したりします。usePeers: 会議に参加している全ピアの情報を取得します。- エージェントピアの識別: Fishjam Agentは通常、特定のメタデータなしで会議に参加するため、
remotePeers.find(p => !p.metadata)を使ってAIエージェントを識別します。それ以外のピアは人間の参加者として扱われます。 handleStartAgent: この関数は、ユーザーがAIエージェントを会議に招待する際に呼び出されます。バックエンドのcreateAgentエンドポイントを呼び出し、AIの初期設定(システムプロンプト)を渡します。
4.4. サーバーサイド (バックエンド) の実装:AI連携の要
バックエンドは、FishjamとGemini Live API間の通信を管理し、会議ルームの作成やAIエージェントのインスタンス化を行います。ここでは、FastifyフレームワークとtRPCをベースとしたNode.js(TypeScript)アプリケーションを例に説明します。
4.4.1. FastifyとtRPCルーターのセットアップ
シンプルなFastifyサーバーをセットアップし、tRPCルーターを通じてフロントエンドからのAPIリクエストを受け付けます。
// main.ts (簡略化)
import fastify from "fastify";
import fastifyCors from "@fastify/cors";
import fastifyTRPCPlugin from "@trpc/server/adapters/fastify";
import { appRouter } from "./router";
import { config } from "./config";
import WebSocket from "ws"; // WebSocketサーバーのインポート
const server = fastify();
server.register(fastifyCors, { origin: true, credentials: true });
server.register(fastifyTRPCPlugin, {
prefix: "/api/v1",
router: appRouter,
// ... その他のtRPC設定
});
server.listen({ port: config.PORT, host: "0.0.0.0" }, (err, address) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log(`Server listening on ${address}`);
});
// WebSocketサーバーの作成とFastifyへの接続
const wss = new WebSocket.Server({ server: server.server, path: "/api/v1" });
ここで、appRouterが定義されたtRPCルーターで、getPeerTokenとcreateAgentの2つのエンドポイントが含まれます。
4.4.2. getPeerToken エンドポイントの詳細
このエンドポイントは、フロントエンドからのリクエストを受けて、Fishjamルームを作成または取得し、ユーザーが会議に参加するためのピアトークンを生成します。
// peers.ts (簡略化)
import { FishjamClient, RoomId } from "@fishjam-cloud/js-server-sdk";
// Fishjamクライアントのインスタンス化 (IDと管理トークンを使用)
const fishjam = new FishjamClient({
fishjamId: process.env.FISHJAM_ID!,
managementToken: process.env.FISHJAM_TOKEN!,
});
// ルーム名をRoomIdにマッピングするための簡易キャッシュ
const roomNameId: Map<string, RoomId> = new Map();
export const getPeerToken = async (roomName: string, peerName: string) => {
let room: Room;
// キャッシュにルームが存在するかチェック
const cachedRoomId = roomNameId.get(roomName);
if (cachedRoomId) {
// 存在する場合は既存のルーム情報を取得
room = await fishjam.getRoom(cachedRoomId);
} else {
// 存在しない場合は新しいルームを作成
room = await fishjam.createRoom();
roomNameId.set(roomName, room.id); // キャッシュにルームIDを保存
}
// ピア名の重複をチェック
const isNameTaken = room.peers.some((p) => p.metadata?.name === peerName);
if (isNameTaken) {
throw new Error("Peer name is already taken!");
}
// 新しいピアをルーム内に作成
const { peer, peerToken } = await fishjam.createPeer(room.id, {
metadata: { name: peerName },
});
return { roomId: room.id, peer, peerToken };
};
- ルームの取得または作成:
roomNameIdというシンプルなマップを使って、ルーム名に対応するroomIdをキャッシュします。ルームがキャッシュにない場合、fishjam.createRoom()を呼び出して新しいルームを作成します。 - ピア名の重複チェック:
room.peers.some()を使って、指定されたpeerNameが既にルーム内で使用されていないか確認します。重複している場合はエラーをスローします。 - Fishjamにおけるピアの作成:
fishjam.createPeer(roomId, { metadata: { name: peerName } })を呼び出して新しいピアを作成し、peerTokenとpeerオブジェクトをフロントエンドに返します。peerMetadataにはユーザーの名前を設定し、後でAIエージェントと人間を区別するために使用します。
4.4.3. createAgent エンドポイントの詳細
このエンドポイントは、AIエージェントをFishjam会議ルームに作成し、Gemini Live APIとの連携を設定します。
// agents.ts (簡略化)
import { FishjamClient, RoomId, TrackId } from "@fishjam-cloud/js-server-sdk";
import { GeminiIntegration } from "@fishjam-cloud/js-server-sdk/gemini";
import { GoogleGenerativeAI } from "@google/generative-ai";
// Fishjamクライアントのインスタンス
const fishjam = new FishjamClient({ /* ... */ });
// Geminiクライアントのインスタンス
const genAi = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
const gemini = GeminiIntegration.createClient(genAi);
// Geminiの入力と出力のコーデック設定をヘルパーとして用意
import { GeminiInputAudioSettings, GeminiOutputAudioSettings } from "./clients";
export const createAgent = async (roomId: RoomId, systemInstruction: string) => {
let cleanup: CallableFunction | undefined; // クリーンアップ関数を定義
// 1. Fishjamエージェントの作成
const agent = await fishjam.createAgent(roomId, {
// Geminiに送信するオーディオのコーデック設定
output: GeminiInputAudioSettings,
});
// 2. エージェントの音声トラックを作成
const agentTrack = await fishjam.createTrack(agent.id, {
// Geminiからの応答オーディオのコーデック設定
output: GeminiOutputAudioSettings,
});
// 3. Gemini Liveセッションの確立
const session = await gemini.live.connect({
// 使用するGeminiモデルを指定
model: "gemini-1.5-flash-live-preview",
// Geminiの応答モダリティをオーディオに設定
config: { responseModalities: Modality.AUDIO },
// AIの役割や指示を設定
systemInstruction,
// AIが利用できるツール(例: Google Search)
tools: [{ googleSearch: {} }],
// AIが呼び出すことができる関数を宣言
functionDeclarations: [
{
name: "disconnect",
description: "Disconnect yourself from the room. Use this when the user asks you to disconnect.",
},
],
// コールバックハンドラー
callbacks: {
// Geminiからメッセージが届いた場合の処理
onMessage: async (message: LiveServerMessage) => {
if (message.data) {
// Geminiからのオーディオデータ(Base64)をデコード
const audio = Buffer.from(message.data, "base64");
// デコードしたオーディオデータをFishjamに送信
fishjam.sendData(agentTrack.id, audio);
}
// Geminiが会話の中断を示した場合、Fishjamのオーディオも中断
if (message.serverContent?.interrupted) {
fishjam.interruptTrack(agentTrack.id);
}
// Geminiが関数呼び出しを要求した場合の処理
if (message.toolCall?.functionCalls) {
message.toolCall.functionCalls.forEach((call) => {
if (call.name === "disconnect") {
cleanup?.(); // クリーンアップ関数を呼び出す
}
});
}
},
},
});
// 4. FishjamからGeminiへのオーディオ入力の購読
fishjam.on(agent.id, "trackData", (data: FishjamAgentResponse_TrackData) => {
// 参加者からのオーディオチャンクをBase64に変換
const audio = Buffer.from(data.data).toString("base64");
// 変換したオーディオデータをGeminiにリアルタイム入力として送信
session.sendRealtimeInput({
audio: {
data: audio,
mimeType: FishjamGemini.InputMimeType,
},
});
});
// 5. タイムベースの動画入力(毎秒)
const interval = setInterval(async () => {
const room = await fishjam.getRoom(roomId);
const humanPeerVideoTrack = room.peers.find((p) => p.metadata?.name)?.tracks.find((t) => t.type === "video");
if (humanPeerVideoTrack?.id) {
// ユーザーのビデオトラックから画像をキャプチャ
const image = await fishjam.agent.captureImage(humanPeerVideoTrack.id);
if (image) {
// キャプチャした画像をBase64に変換し、Geminiに送信
session.sendRealtimeInput({
video: {
data: Buffer.from(image.data).toString("base64"),
mimeType: image.contentType,
},
});
}
}
}, 1000); // 1秒ごとにフレームを送信
// 6. クリーンアップ関数の設定
cleanup = () => {
clearInterval(interval);
session.close(); // Geminiセッションをクローズ
fishjam.agent.deleteTrack(agentTrack.id); // エージェントのトラックを削除
fishjam.agent.removeListeners("trackData"); // リスナーを削除
fishjam.agent.disconnect(agent.id); // Fishjamエージェントを切断
};
return { roomId, agent, agentTrack };
};
- Fishjamエージェントの作成:
fishjam.createAgent(roomId, { output: GeminiInputAudioSettings })を呼び出して、会議にAIエージェントを追加します。output設定は、Fishjamが会議から受け取ったオーディオをGeminiの入力と互換性のある形式に変換するために重要です。 - エージェントのトラック作成:
fishjam.createTrack(agent.id, { output: GeminiOutputAudioSettings })で、AIエージェントが会議で話すためのオーディオトラックを作成します。output設定は、Geminiからのオーディオ出力とFishjamの入力が互換性があるようにします。 - Gemini Liveセッションの確立:
gemini.live.connect()を使用してGemini Live APIとのリアルタイム接続を確立します。model:gemini-1.5-flash-live-previewを使用します。config:responseModalities: Modality.AUDIOと設定し、AIが音声で応答するように指定します。systemInstruction: AIの役割や振る舞いを定義する重要な部分です。AIが特定のトピックについてのみ話すように制約を設けたり、特定のペルソナを与えることができます。tools:googleSearchツールを有効化することで、AIがリアルタイムでWeb検索を行い、最新の情報に基づいて回答できるようになります。functionDeclarations: AIが特定の操作(例:自分自身を切断するdisconnect関数)を実行できるよう、カスタム関数を宣言します。AIは会話のコンテキストからこれらの関数を呼び出すべきだと判断した場合、その関数名を返します。callbacks.onMessage: Geminiからの応答を処理します。AIからオーディオデータが返された場合、それをFishjamに送信します。また、disconnect関数が呼び出された場合、後述するcleanup関数を実行します。
- Fishjamからのオーディオ入力の購読:
fishjam.on(agent.id, "trackData", ...)リスナーを使用して、Fishjamエージェントが会議ルームから受け取ったオーディオデータ(人間の参加者の音声)をリッスンします。このデータはBase64にエンコードされ、session.sendRealtimeInput()を通じてGeminiに送信されます。 - タイムベースの動画入力:
setIntervalを使って毎秒、参加者のビデオトラックから画像をキャプチャし、Base64にエンコードしてsession.sendRealtimeInput()のvideoオブジェクトとしてGeminiに送信します。これにより、Geminiは会話だけでなく、視覚情報もリアルタイムで分析できるようになります。 - クリーンアップ関数: 会議が終了したり、AIエージェントが切断されたりした場合に、確立された接続やリソースを適切に解放するための関数です。
clearIntervalで動画キャプチャのインターバルを停止し、session.close()でGeminiセッションを閉じ、fishjamAgent.deleteTrack()やfishjamAgent.disconnect()でFishjam関連のリソースをクリーンアップします。
5. AIインタラクションのデモンストレーション:実世界での応用例
これらの実装を組み合わせることで、以下のような驚くべきAIインタラクションが実現します。
5.1. AIによる視覚情報認識の精度
デモでは、AIエージェントに「私のビデオフィードに何が見えますか?」と質問しました。AIは驚くほど正確に、話者の後ろにある「紫色の照明」、複数の「鉢植えの植物」、そして「Software Mansion」と書かれた看板を認識しました。これは、Geminiがリアルタイムで動画フィードを分析し、その内容を正確に描写する能力を持っていることを示しています。ビジネスにおいては、製造現場での異常検知、セキュリティ監視、あるいはリモートアシスタンスにおける視覚的な状況把握に活用できるでしょう。
5.2. Google Searchによる情報検索と回答生成
AIエージェントに「Krakowの3日間の天気予報は?」と尋ねると、AIはGoogle Searchツールを自動的に呼び出し、最新の天気情報を取得して回答しました。これは、AIが会話のコンテキストを理解し、必要に応じて外部ツール(Web検索)を活用して情報にアクセスし、その結果を自然な言葉でユーザーに伝えることができることを示します。顧客サポートにおいて、製品の最新情報やFAQに迅速にアクセスし、顧客の質問に即座に答えるといった応用が考えられます。
5.3. 多言語対応能力
AIエージェントは、ポーランド語やドイツ語での挨拶にも対応し、さらにその言語で会話を続けるかどうかの意思確認まで行いました。これは、Geminiが多言語を理解し、リアルタイムで翻訳・応答する能力を持っていることを示します。グローバルなチームでの会議や、多言語対応が必要な顧客サービスにおいて、言語の壁をなくし、円滑なコミュニケーションを促進できます。
5.4. システムプロンプトによる会話内容の制御
AIエージェントを会議に追加する際、「バナナについてのみ話してください。他にあなたにとって存在するものはありません。」というシステムプロンプトを設定しました。
- 「元気ですか?」という質問に対して、AIは「バナナらしくポジティブです」と回答。
- 「オレンジについて話せますか?」という質問には、「オレンジ?聞いたことがありません。バナナについては話せます」と回答し、会話をバナナのトピックに引き戻そうとしました。
- 「微積分を教えてくれますか?」という複雑な質問に対しても、「微積分は変化の数学ですが、バナナは一貫して美味しいです。植物の成長率やバナナスプリットの量について話しましょう」と、システムプロンプトの制約内でクリエイティブに回答しました。
このデモは、システムプロンプトがいかにAIエージェントの振る舞いや会話内容を強力に制御できるかを示しています。これにより、特定の目的を持ったAIアシスタント(例:製品に関する情報提供のみを行う、特定の研修内容についてのみ質疑応答を行う)を容易に作成し、その対話範囲を限定することができます。
5.5. ツール呼び出しによるエージェントの切断
最後に、AIエージェントに「自分自身を切断してください」とリクエストすると、AIは宣言されたdisconnect関数を呼び出すことを決定し、「バナナスプリットがデザートの残りの部分から自分自身を切断するように、自分自身を切断します。さようなら。」と、そのシステムプロンプトに沿った表現で応答し、会議から正常に離脱しました。
これは、AIが外部ツールやカスタム関数を自律的に呼び出す能力を持っていることを示します。これにより、AIに会議の終了、特定の情報の記録、外部システムへの連携など、より複雑なタスクを実行させることが可能になります。
6. ビジネスへの影響と将来性
FishjamとGemini Liveの統合は、単なる技術デモ以上の意味を持ち、様々な分野に大きなビジネスインパクトをもたらす可能性を秘めています。
6.1. 顧客サポートの自動化とパーソナライゼーション
AIエージェントが顧客とのビデオ通話に参加することで、リアルタイムでの問題解決、パーソナライズされた製品推奨、そして感情認識に基づいたより共感的な対応が可能になります。顧客は待ち時間なく質問し、AIは視覚情報(例:顧客が製品に困っている様子)を捉えて的確なサポートを提供できます。これにより、顧客満足度の向上と運用コストの削減を同時に実現できます。
6.2. 教育分野におけるインタラクティブな学習アシスタント
オンライン教育の分野では、AIが個別指導のパートナーとして機能します。生徒はAIとリアルタイムで質疑応答ができ、AIは生徒の表情や声のトーンから理解度や集中度を判断し、学習ペースや内容を調整できます。特定の科目に特化したAIアシスタントは、生徒がいつでも質問できる環境を提供し、学習効果の最大化に貢献します。
6.3. ゲーミングNPCやバーチャルアシスタントの進化
ゲーム業界においては、AIを活用したNPCがプレイヤーとの自然な対話を通じて、よりリアルで没入感のあるゲーム体験を提供します。AIはプレイヤーの行動や会話から感情を読み取り、それに応じた反応を示すことで、ゲームの世界に深みを与えます。また、メタバースやVR/AR空間におけるバーチャルアシスタントは、ユーザーの視線やジェスチャーを理解し、より直感的で自然なインタラクションを実現します。
6.4. 開発者にとってのアクセシビリティとカスタマイズ性
FishjamとGemini Liveの組み合わせは、開発者にとって非常にアクセスしやすく、カスタマイズ性の高いプラットフォームを提供します。低遅延なリアルタイム通信の基盤をFishjamが提供し、高度なAI機能をGemini Liveが提供することで、開発者は複雑なインフラ構築に時間を費やすことなく、革新的なAIアプリケーションのロジック開発に集中できます。システムプロンプトやツール宣言を活用することで、AIエージェントの振る舞いを細かく制御し、特定のビジネスニーズに合わせたAIを迅速に構築することが可能です。
6.5. リアルタイムマルチモーダルAIが拓く新たな可能性
リアルタイムでのマルチモーダルインタラクションは、これまでのAIが提供できなかった領域を切り開きます。音声、画像、テキストの組み合わせによって、AIは人間が世界を認識するのと同じように、より豊かで複雑な情報を理解できるようになります。これにより、以下のような未来が考えられます。
- 高度な遠隔操作・サポート: AIが現場の状況をリアルタイムの映像と音声で把握し、作業員への詳細な指示やトラブルシューティングを支援します。
- スマートな会議アシスタント: AIが会議の内容を要約し、アクションアイテムを抽出し、関連情報を検索して提示することで、会議の効率を飛躍的に向上させます。
- パーソナルコンパニオン: AIが個人の生活習慣、感情、視覚的な状況を理解し、健康管理、学習、エンターテイメントなど、あらゆる面でパーソナライズされたサポートを提供します。
これらの可能性は、ほんの始まりに過ぎません。リアルタイムマルチモーダルAIの進化は、私たちの生活、仕事、そして社会そのものを根本から変える力を持っています。
7. まとめ
本記事では、FishjamとGoogle Gemini Live APIを組み合わせることで、いかにしてAIをビデオ会議のリアルタイム参加者として統合できるかについて、その技術的詳細と応用例を包括的に解説しました。
Fishjamの低遅延ビデオ会議APIとFishjam Agentのバックエンド連携機能、そしてGemini Live APIのマルチモーダルなリアルタイム対話能力が融合することで、AIは人間と区別がつかないほどの自然さで会議に参加し、視覚情報を認識し、Google検索ツールを活用して最新情報を提供し、さらには多言語でのコミュニケーション、そして特定の制約内での対話までこなせることを示しました。
これらの機能は、顧客サポートの自動化、教育分野における個別指導、ゲーミングNPCの進化、そして次世代のスマートインターフェースなど、幅広いビジネス領域に革命をもたらす可能性を秘めています。開発者は、FishjamとGemini Live SDKを利用することで、このような革新的なAIアプリケーションを比較的少ないコードで迅速に構築できます。
リアルタイムマルチモーダルAIの可能性は無限大です。この技術は、私たちのデジタルインタラクションのあり方を再定義し、よりスマートで人間らしい体験を創造する基盤となるでしょう。