← トップにもどる

画像・動画・音声認識 — 写真も動画も、聞いた話も分かる

実体: templates/line-bot/(このキットのLINE公式アカウントAI社員bot金型)
実証: line-harness-oss(ai-shain.link で実運用中の「りんく」)

💬 まず、りんくに聞いてみる(人向け・やさしい説明)

りんく りんく
💬 猫の動画を送ってみました
(動画: 黒猫が道を歩いている15秒の動画)
すごい!この黒猫、かわいいですね!目がとってもきれい!立ち上がって、車の横を通り過ぎていく様子も、猫の動きが自然で可愛らしいですね!

りんくは、写真だけでなく動画音声メッセージを送っても、中身をちゃんと見て・聞いて反応します。 単に「動画が届きました」で終わらず、映っている内容・聞こえた話の内容をふまえて、 りんくらしい言葉で返事をします。

🖼️
画像
写真・スクリーンショット・イラストなど
🎬
動画
15MBまでの短い動画
🎤
音声
LINEの音声メッセージ(15MBまで)
正直にお伝えすると: 15MBを超える大きな動画・音声は、内容の理解を諦めて「動画が届いた」ことだけを 記録し、返信はしません(誤った内容で答えるより、答えないほうが誠実だという判断)。 大きなファイルを送ってりんくが黙っていたら、それが理由です。

🛠️ 仕組み・対応モデル一覧(AI向け・技術的な説明)

2段方式: 客観描写 → 人格で反応

画像・動画・音声を受け取ると、まず①AIモデルに「内容を客観的に説明して」とだけ頼み、 人格を含まない説明文を得る。その説明文を②「あなたらしく反応してください」という指示付きで 通常の会話パイプラインに渡し、りんくの人格・KB・会話履歴・エスカレーション判定をそのまま使って返信を作る。 この2段構成にすることで、「AIが淡々とした説明文をそのまま返してしまう」事故を防いでいる。

対応モデル一覧

種類使うモデル方式フォールバック
🖼️ 画像 1番手Groq qwen/qwen3.6-27b
2番手Gemini gemini-2.5-flash-lite
OpenAI互換 chat/completionsimage_url あり(2段)
🎬 動画 Geminiのみgemini-2.5-flash-lite ネイティブ generateContent APIの inline_data なし
🎤 音声 Geminiのみgemini-2.5-flash-lite OpenAI互換 chat/completionsinput_audio なし

画像だけGroq→Geminiの2段フォールバックがあり、動画・音声はGeminiのみという非対称な構成になっている。 これは仕様の手抜きではなく、実機検証で確定した技術的な制約による。

なぜ動画・音声はGeminiだけなのか

Groq・Cloudflare Workers AIは、現時点で動画・音声を直接受け取れるAPIを持っていない (画像のみ両プロバイダとも対応)。Gemini側も一枚岩ではなく、実際にAPIを叩いて検証した結果、 次のことが分かった。

実機検証で判明した罠(動画・音声固有)

画像には無い、動画・音声特有の落とし穴が2つあった。どちらも「原因を推測せず、実際にAPIを叩いて 実測値を確認する」ことで初めて特定できたもの。

  1. トランスコード待ち: LINEの動画・音声メッセージには「準備状態 (processingsucceeded / failed)」という仕組みがあり、 webhookを受け取った直後はまだ変換処理中で、コンテンツの取得に失敗することがある。 対策として /content/transcoding エンドポイントで状態を確認してから本体を取得するよう変更し、 processing中は最大約9秒ポーリングして待つ。
  2. 音声のcontent-type: LINEアプリの音声メッセージは、実機で送って調べたところ audio/x-m4a というcontent-typeで届くことが分かった(audio/mp4ではない)。 この値が対応表から漏れていたため、音声メッセージにだけ一切反応しないという 分かりにくい不具合が実際に発生した。今は対応表に追加済みで解消している。

サイズ上限とfail-closed設計

動画・音声は既定で15MBを上限としている(bot.config.jsonllm.video.maxInputBytes / llm.audio.maxInputBytesで調整可)。 超過した場合は理解を諦め、[動画]/[音声]ラベルのみを記録して 返信をしない(誤った内容で答えるくらいなら黙るという、無応答ゼロ化チェーンとは逆方向の 安全側の判断。テキスト・画像の応答自体には影響しない)。

関連ファイル

🎭 人格は文章で作る(1つ書けば、画像・動画・音声にも反映される)

「りんくらしい返事」は、AIに何かをアップロードして学習させているわけではない。 knowledge-pack/persona.md という1枚のテキストファイルに、口調や性格を 文章で書いているだけ。書き換えて再デプロイすれば、その場で反映される。

ポイントは、この1枚のファイルが、テキストの会話だけでなく画像・動画・音声への反応にも そのまま使われるということ。仕組みは前述の「2段方式」の通りで、①AIが客観的に内容を 説明する部分には人格が入らず、②その説明を受け取って反応を作る部分でだけpersona.md が使われる。つまり①と②が分かれているおかげで、persona.mdを1つ調整するだけで、 テキスト・画像・動画・音声のすべての返信の口調が同時に揃う。「音声用の人格」「動画用の人格」を 別々に用意する必要はない。

💬 例: persona.mdに「一人称は僕、文末は〜だね」と書いた場合(仮のキャラクター「たろう社長」)
(猫の写真を送信)
お、いい写真だね🐱 このコ、もう登録してある?
(声のメモ「使い方が分からない」を送信)
それは焦るよね、大丈夫、一緒に見てみよう。右上の「?」ボタンから使い方ガイドに飛べるよ。

画像への反応も、声のメモへの反応も、同じ「僕」「〜だね」口調になっている。 これはpersona.mdに書いた性格が両方の経路で同じように使われているから。

書き方の型

persona.mdには次の4つを書く。①役割の一文、②話し方の一般原則(既定のひな形を流用可)、 ③キャラクターの個性(一人称・文末・絵文字の使い方など)、④よくある質問への案内。 1つの長文で説明しようとせず、③のように短い箇条書きに分けて書くほど、AIが安定して守りやすい。

詳しい書き方の事例(サンプルの人格丸ごと1つ)と調整のコツは templates/line-bot/README.md の「人格を作る・調整する」の章にまとめてある。

📚 実例(実際に起きた不具合と直し方)

事例: 音声メッセージにだけ、りんくが一切反応しない

画像・動画は問題なく反応するのに、音声メッセージだけ送っても既読になるだけで返信が来ない、 という報告があった。ログだけでは分からなかったため、D1(会話履歴データベース)を直接確認したところ、 音声メッセージの保存自体(R2へのファイル保存)は成功していたが、その先の「客観描写を作る」処理が 毎回無言で失敗していることが分かった。

調べたこと(推測せず実測する)

  1. 保存済みの音声ファイルに実際にHTTPリクエストを送り、Content-Typeヘッダーを直接確認
  2. 返ってきたのは audio/x-m4a。事前に用意していた対応表には audio/mp4 しか 入れておらず、この値が抜けていた
  3. 対応表に無いcontent-typeは「未対応」として静かに処理を打ち切る設計(fail-closed)になっていたため、 エラーは出るが返信もされない、という一番気づきにくい壊れ方をしていた
直したこと: audio/x-m4a を対応表に追加。あわせて、原因調査用に 「未対応のcontent-typeだった場合、その実測値を会話ログに残す」という仕組みも入れて、 次に似た問題が起きてもログイン不要のデータベース確認だけで即座に特定できるようにした。

教訓: 「たぶんこの値だろう」という推測でcontent-typeの対応表を書くと、実機の挙動と ズレたまま気づかないことがある。実際にAPIを叩いて返ってきた値を確認してから対応表を作るのが安全。

🚀 自分のアプリで使う

  1. app.config.jsonlineBot セクションを埋めて enabled: true
  2. templates/line-bot/ をアプリの line-bot/ にコピーし、{{...}} を置換
  3. npx wrangler r2 bucket create {{shortName}}-line-images でR2バケットを作成 (画像・動画・音声の保存先。作成しなくてもテキストのAI応答には影響しない)
  4. Secretsを投入: LINE_CHANNEL_ACCESS_TOKEN / LINE_CHANNEL_SECRET / GROQ_API_KEY
  5. (動画・音声認識に必須)GEMINI_API_KEY を投入する。 Google AI Studioで無料発行できる。 未設定だと動画・音声認識は静かにスキップされる(テキスト・画像の応答には影響しない)
  6. wrangler.toml[[r2_buckets]] バインディングはテンプレートに同梱済み。 バケット名の置換だけ確認する

くわしい手順は templates/line-bot/README.md の「画像・動画・音声認識」の章を参照。