お疲れ様です。
LangChainのChatでは、LLMが画像をサポートしていれば画像入力ができることを知ったので試してみました。
今回作成したコードは以下に置いていますので参考にしてください。
入力方法
早速ですが入力の方法について解説します。
LangChainのMessageを使用し、contentに"type": "image_url"を指定することで画像を入力することができます。
画像データはURLで渡すか、ローカルから読み込んだ画像ファイルをbase64エンコードした文字列を渡す形になります。
LangChainの場合、この2パターンが基本になります。
一部モデルの公式SDKでは画像データ(PIL.Image形式)を直接渡すことができるようですが、今回はLangChainで使う想定なので割愛します。
from langchain_core.messages import HumanMessage message = HumanMessage( content=[ { "type": "text", "text": PROMPT, }, { "type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}, }, ] )
ローカルの画像ファイルを入力したい場合が多いと思うので、base64エンコードをするコードを載せておきます。
一旦PIL.Imageで読み込んだ後に変換する形になります。
def image_to_bytes(image: Image.Image) -> str: buffer = BytesIO() if image.mode != "RGB": image = image.convert("RGB") image.save(buffer, format="PNG") image_base64 = base64.b64encode(buffer.getvalue()).decode() return image_base64
base64エンコードについて詳しくはこちらが参考になります。
動作確認
前述したGitHubリポジトリのコードを実際に動かしてみました。
使用するモデルは以下になります。
- gemma4:31b(Gemini APIを使用して実行)
- minimax-m3(Ollama Cloud APIを使用して実行)
- qwen3.5:9b(ローカル環境のOllamaで実行)
入力する画像としてはこちらを使用しました。日本武道館ですね。

また、画像と一緒にLLMに渡すプロンプトは以下の内容です。
PROMPT = """ 添付された画像を詳細に観察し、以下の項目に沿って日本語でわかりやすく説明してください。 1. 主な被写体(何が写っているか) 2. 背景や周囲の状況(場所や設定) 3. 全体的な色合い・照明・雰囲気 4. 画像から伝わる特徴や特記事項(文字がある場合はその内容も含む) """
gemma4:31b
まずは、無料枠でもレートリミットが緩くて扱いやすいGemini APIのgemma4:31bから試してみました。 マルチモーダル対応で日本語性能が高いのが魅力ですね。ローカルLLMでは今一番よく使うモデルかもです。
日本武道館という固有名詞までは出てきていませんが、「アリーナ」や「イベント」というところは画像から読み取れているようです。

minimax-m3
次にOllama Cloud APIからminimax-m3を試してみました。 無料枠だと使えるモデルの種類がかなり制限されますが、Google系以外を試せるのがメリットでしょうか…。
なんとなくの雰囲気は読み取れていますが、ちょっと怪しいですね。看板部分に書かれている文字を間違えて、そこから間違った解釈で推論してしまっている感じです。

qwen3.5:9b
最後にローカル環境のOllamaで動くモデルとしてqwen3.5:9bを試してみました。 APIから利用できるモデルに比べるとモデルサイズが小さく、出力に時間がかかりますが、モデルが選び放題です。 今回はQwen3.5を使いましたが、他にも画像入力に対応したモデルはあります。
こちらもなんとなくの雰囲気は読み取れている感じです。写真の構図的に読み取れる情報が少ないということでしょうかね。 ともあれ、ローカルLLMでもある程度は実用的に画像入力も使えそうだなという印象は受けました。
