LangChain メッセージタイプ

LangChain では、すべての会話はメッセージ(Message)オブジェクトを介して渡されます。各種メッセージタイプの用途を理解することは、Agent を書くための基礎です。


4つのコアメッセージタイプ

LangChain は4つのコアメッセージタイプを定義しており、それぞれ会話内の異なるロールに対応します:

タイプロール説明典型的な内容
HumanMessageユーザーユーザーが送信したメッセージ「今日の天気は?」
AIMessageAIアシスタントモデルの応答。tool_calls を含む場合があります「今日は杭州は晴れ、25°C」
SystemMessageシステムシステム命令。AI のロールと行動ルールを定義します「あなたはプロの天気アシスタントです」
ToolMessageツールツール実行後の戻り結果「晴れ、25°C、湿度60%」

HumanMessage——ユーザーメッセージ

HumanMessage はユーザーが AI に送信するメッセージを表します。最も一般的なメッセージタイプであり、会話の起点でもあります。

例

from langchain.messages import HumanMessage
from langchain.chat_models import init_chat_model

# ユーザーメッセージを作成
msg = HumanMessage(content="Python とは?")

print(f"タイプ: {msg.type}")        # human
print(f"内容: {msg.content}")      # Python とは?
print(f"ロール: {msg.role}")         # user

# メッセージリストを作成(複数ターンの会話履歴を表す)
messages = [
    HumanMessage(content="こんにちは"),
    HumanMessage(content="サンプルにはどのようなコースがありますか?"),
    HumanMessage(content="Python コースは初心者向けですか?"),
]

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
response = model.invoke(messages)
print(f"\n"モデル応答: {response.content}")

実行結果:

タイプ: human
内容: Pythonとは何ですか?
役割: user

モデルの返答: Pythonは、構文が明確で読みやすいことで知られる高水準の解釈型プログラミング言語であり、さまざまな分野で広く使われています。
豊富なエコシステムと初心者に優しい設計により、ゼロから始める人にとって最適な選択肢となっています。

HumanMessage のクイック作成方法

メッセージリストを構築する際、タプルや辞書をショートカットとして使用できます:

例

from langchain.messages import HumanMessage

# 方法1:標準的な構築
msg1 = HumanMessage(content="こんにちは")

# 方法2:タプルのショートカット (role, content)
msg2 = ("user", "こんにちは")
msg3 = ("human", "こんにちは")

# 方法3:辞書のショートカット
msg4 = {"role": "user", "content": "こんにちは"}

# 4つの方法は同等で、いずれも Agent 内部で HumanMessage に変換されます
print(type(msg1))  # <class 'langchain_core.messages.human.HumanMessage'>

AIMessage——AI レスポンス

AIMessage はモデルの応答を表します。通常のテキストと異なり、AIMessage には次のものが含まれる場合があります:tool_calls(ツール呼び出しリクエスト)。

例

from langchain.messages import AIMessage

# 通常の AI 応答(ツール呼び出しなし)
ai_msg = AIMessage(content="サンプルはプログラミング学習プラットフォームです")

# ツール呼び出しを含む AI 応答
ai_with_tools = AIMessage(
    content="",  # ツール呼び出し時は content は通常空です
    tool_calls=[
        {
            "name": "get_weather",
            "args": {"city": "杭州"},
            "id": "call_abc123",
            "type": "tool_call",
        }
    ]
)

print("=== 通常の AI メッセージ ===")
print(f"content: {ai_msg.content}")
print(f"tool_calls: {ai_msg.tool_calls}")   # []

print("\n"=== ツール呼び出しを含む AI メッセージ ===")
print(f"content: {ai_with_tools.content}")
print(f"tool_calls: {ai_with_tools.tool_calls}")
# [{'name': 'get_weather', 'args': {'city': '杭州'}, ...}]

AIMessage の付加情報

例

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke("サンプル を紹介")

# AIMessage は豊富なメタデータを含む
print(f"内容: {response.content}")
print(f"メッセージID: {response.id}")
print(f"モデル名: {response.response_metadata.get('model_name')}")
print(f"完了理由: {response.response_metadata.get('finish_reason')}")

# usage_metadata は Token 使用量情報を含む
if response.usage_metadata:
    print(f"入力 tokens: {response.usage_metadata.get('input_tokens')}")
    print(f"出力 tokens: {response.usage_metadata.get('output_tokens')}")
    print(f"合計 tokens: {response.usage_metadata.get('total_tokens')}")

実行結果:

内容: サンプルは初心者向けの無料プログラミング学習プラットフォームです...
メッセージID: msg_abc123def456
モデル名: deepseek-v4-flash
完了理由: stop
入力トークン: 19
出力トークン: 47
合計トークン: 66

SystemMessage——システム命令

SystemMessage は AI の動作、ロール、制約を設定するために使われます。メッセージリストの先頭に置かれ、モデルがどのように応答するかを導きます。

例

from langchain.messages import HumanMessage, SystemMessage
from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0.7)

# システム命令なしの応答
messages_no_system = [HumanMessage(content="サンプルを紹介")]
response = model.invoke(messages_no_system)
print(f"システム命令なし: {response.content[:80]}...")

# システム命令ありの応答
messages_with_system = [
    SystemMessage(content="あなたは小红书風のブロガーです。返信は活発に、絵文字を使い、ハッシュタグを付けます"),
    HumanMessage(content="サンプルを紹介")
]
response = model.invoke(messages_with_system)
print(f"\n"システム命令あり: {response.content}")

実行結果:

システム指示なし: サンプルは初心者向けのオンラインプログラミング学習プラットフォームで、豊富なチュートリアルとサンプルを提供しています...

システム指示あり: 素晴らしいサイト!プログラミングを学びたい初心者にサンプルをおすすめします!
HTMLからPythonまで何でも揃う、充実の無料チュートリアル!
サンプルが豊富ですぐ始められ、プログラミング初心者の入門に最適です!
#プログラミング学習 #無料チュートリアル #ゼロから入門 #サンプル

ToolMessage——ツールの戻り結果

ToolMessage はツール実行後の戻り結果を含みます。対応する tool_call に関連付ける必要があります。

例

from langchain.messages import HumanMessage, AIMessage, ToolMessage
from langchain.chat_models import init_chat_model

# ツール呼び出しの一連の完全な会話をシミュレート
messages = [
    HumanMessage(content="杭州の天気は?"),

    # モデルがツール呼び出しを要求
    AIMessage(
        content="",
        tool_calls=[
            {"name": "get_weather", "args": {"city": "杭州"},
             "id": "call_abc", "type": "tool_call"}
        ]
    ),

    # ツールの戻り結果(tool_call_id が上記の id と対応する必要があります)
    ToolMessage(
        content="晴れ、25°C、湿度60%",
        tool_call_id="call_abc",   # tool_call の id に対応
        name="get_weather",        # ツール名
    ),
]

model = init_chat_model("deepseek:deepseek-v4-flash")
response = model.invoke(messages)
print(f"モデルのツール結果に基づく応答: {response.content}")

実行結果:

ツール結果に基づくモデルの返答: 杭州は今日晴れ、気温25°C、湿度60%で、屋外活動に適しています。

ToolMessage の tool_call_id は AIMessage 内の tool_call の id と正確に一致する必要があります。一致しない場合、モデルはこのツール結果を無視したり、混乱した動作を起こす可能性があります。


AIMessageChunk——ストリーミング出力のメッセージ断片

stream() でストリーミング出力を使用する場合、到着する各断片は AIMessageChunk であり、完全な AIMessage ではありません:

例

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")

print(ストリーミング出力プロセス:)
# stream() が返すのは AIMessageChunk イテレータ
for chunk in model.stream("サンプル を一言で紹介"):
    # 各 chunk は小さなテキスト断片
    print(chunk.content, end="", flush=True)
print()  # 改行

実行結果:

ストリーミング出力:
サンプルはプログラミング初心者向けの無料オンライン学習プラットフォームで、豊富なチュートリアルとサンプルを提供しています。

メッセージタイプ早見表

メッセージタイプtype 属性role 属性キーフィールド使用するタイミング
HumanMessagehumanusercontentユーザー入力
AIMessageaiassistantcontent, tool_calls, usage_metadataモデルの応答
AIMessageChunkaiassistantcontent(増分)ストリーミング出力の断片
SystemMessagesystemsystemcontentAI の役割を設定
ToolMessagetooltoolcontent, tool_call_id, nameツールの実行結果

ContentBlock——構造化メッセージ内容

これまで使用してきたメッセージコンテンツはすべてプレーン文字列です。しかし実際には、各メッセージのコンテンツは複数にすることができますContentBlock(コンテンツブロック)で構成されるリストです。

最もよく使われる3つのコンテンツブロック:

タイプ説明用途
PlainTextContentBlockプレーンテキストのコンテンツ通常のテキストメッセージ
ImageContentBlock画像コンテンツ(base64 または URL)マルチモーダルモデルの画像入力
ToolCallツール呼び出しリクエストAI がツール呼び出しを要求

例

from langchain.messages import HumanMessage
from langchain.messages import PlainTextContentBlock, ImageContentBlock

# コンテンツブロックを使ってメッセージを構築
# content はプレーン文字列でもよい(単純なシナリオ)
simple_msg = HumanMessage(content="こんにちは")

# content は ContentBlock のリストでもよい(複雑なシナリオ)
complex_msg = HumanMessage(content=[
    PlainTextContentBlock(text="この画像には何が写っていますか?"),
    # 画像は URL または base64 エンコードにできる
    ImageContentBlock(
        url="https://example.com/photo.jpg"
    ),
])

print(f"単純なメッセージコンテンツタイプ: {type(simple_msg.content)}")
# 出力: <class 'str'>

print(f"複雑なメッセージコンテンツタイプ: {type(complex_msg.content)}")
# 出力: <class 'list'>

print(f"コンテンツブロック数: {len(complex_msg.content)}")
# 出力: 2

プレーンテキストのみを送信する場合は、文字列を直接渡すだけで、LangChain が自動的に処理します。テキストと画像を1つのメッセージに混在させる必要がある場合にのみ、ContentBlock リストを手動で構築する必要があります。


マルチモーダルメッセージ——モデルに画像を「見せる」

モデルがマルチモーダル入力(GPT-4o、Claude 3+ など)をサポートしている場合、画像コンテンツを分析させることができます:

例

import base64
from pathlib import Path
from langchain.messages import HumanMessage
from langchain.chat_models import init_chat_model

# ローカル画像を base64 にエンコードする
def encode_image(image_path: str) -> str:
    """画像ファイルを読み取り、base64 エンコードに変換する"""
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

# マルチモーダルモデルを作成(画像入力のサポートが必要)
model = init_chat_model("deepseek:deepseek-v4-flash")

# 画像を含むメッセージを構築
# 注意:content はリスト形式を使用し、テキストブロックと画像ブロックを含む
image_data = encode_image("screenshot.png")
messages = [
    HumanMessage(content=[
        {"type": "text", "text": "このサンプル 公式サイトのスクリーンショットの内容を説明してください"},
        {
            "type": "image_url",
            "image_url": {
                "url": f"data:image/png;base64,{image_data}",
                "detail": "auto"  # オプション:low, high, auto
            }
        }
    ])
]

response = model.invoke(messages)
print(f"画像分析結果: {response.content}")

メッセージに画像のみが含まれテキストが含まれない場合は、次のように直接使えます:

例

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash")

# URL を使ってオンライン画像を直接参照する
messages = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "この画像は何を説明していますか?"},
            {
                "type": "image_url",
                "image_url": {"url": "https://www.example.com/wp-content/uploads/2024/01/logo.png"}
            }
        ]
    }
]

response = model.invoke(messages)
print(response.content)

すべてのモデルがマルチモーダル入力をサポートしているわけではありません。OpenAI の GPT-4o シリーズ、Anthropic の Claude 3+、Google の Gemini シリーズがサポートしています。サポートしていないモデルで画像を送信すると、エラーが発生します。


ToolCall——ツール呼び出しメッセージ

AIMessage の tool_calls フィールドは ToolCall のリストで、各 ToolCall はモデルがツールの呼び出しを要求していることを表します:

例

from langchain.messages import AIMessage
from langchain.messages.tool import ToolCall

# ToolCall を手動で構築する
tool_call = ToolCall(
    name="get_weather",        # ツール名
    args={"city": "杭州"},     # 呼び出しパラメータ
    id="call_abc123",         # 一意の識別子
    type="tool_call",         # 固定値
)

# tool_calls を含む AIMessage を作成
ai_message = AIMessage(
    content="",               # tool_calls がある場合、content は通常空です
    tool_calls=[tool_call],
)

print(f"ツール名: {ai_message.tool_calls)
print(f"呼び出しパラメータ: {ai_message.tool_calls)
print(f"呼び出し ID: {ai_message.tool_calls)

AIMessage がツール呼び出しを含むか確認する

例

from langchain.chat_models import init_chat_model

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# ツールをバインドすると、モデルは tool_calls を返す可能性があります
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "天気を調べる",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "都市名"}
            },
            "required": ["city"]
        }
    }
}]

model_with_tools = model.bind_tools(tools)

# ツール呼び出しをトリガーする質問
response = model_with_tools.invoke("杭州の天気はどうですか?")

# ツール呼び出しがあるかどうかを判断する2つの方法
# 方法1:tool_calls リストが空でないか確認する
if response.tool_calls:
    print("モデルがツール呼び出しを要求しました")

# 方法2:content が空かどうかを確認する
# (ほとんどのモデルは tool_calls があるとき content が空です)
if not response.content:
    print("モデルの content が空の場合、直接返信するのではなくツールを呼び出そうとしていることを意味します")

trim_messages()——メッセージ履歴のトリミング

会話が長くなるにつれて、メッセージリストがモデルのコンテキストウィンドウを超える可能性があります。trim_messages()この関数は、メッセージ履歴をスマートにトリミングするのに役立ちます。

例

from langchain.messages import (
    HumanMessage, AIMessage, SystemMessage, trim_messages
)
from langchain.chat_models import init_chat_model

# 長い会話履歴をシミュレートする
messages = [
    SystemMessage(content="あなたはサンプル の AI アシスタントです"),
    HumanMessage(content="Python はどうやって始めればいいですか?"),
    AIMessage(content="Python 入門は基礎知識から始められます..."),
    HumanMessage(content="おすすめの IDE はありますか?"),
    AIMessage(content="VS Code か PyCharm をおすすめします..."),
    HumanMessage(content="サードパーティのライブラリをインストールするには?"),
    AIMessage(content="pip install コマンドを使います..."),
    HumanMessage(content="NumPy とは何ですか?"),
    AIMessage(content="NumPy は科学計算ライブラリです..."),
    HumanMessage(content="pandas と NumPy の違いは何ですか?"),
]

model = init_chat_model("deepseek:deepseek-v4-flash")

# モデルのコンテキストウィンドウに合わせてメッセージをトリミング(最大 1000 tokens)
# strategy="last" は最後のシステムメッセージと最近の会話を保持する
trimmed = trim_messages(
    messages,
    max_tokens=1000,           # 最大 1000 tokens を保持
    strategy="last",           # 最後のシステムメッセージ + 最近の会話を保持
    token_counter=model,       # モデルのトークン計数方式を使用
    include_system=True,       # 常に SystemMessage を保持
    start_on="human",          # トリミング後は human メッセージで始まる
)

print(f"トリミング前: {len(messages)} 件のメッセージ")
print(f"トリミング後: {len(trimmed)} 件のメッセージ")
for msg in trimmed:
    snippet = msg.content[:50] if isinstance(msg.content, str) else str(msg.content)[:50]
    print(f"  [{msg.type}] {snippet}...")

実行結果:

トリミング前: 10件のメッセージ
トリミング後: 5件のメッセージ
  [system] あなたはサンプルのAIアシスタントです...
  [human] サードパーティ製ライブラリはどうやってインストールしますか?...
  [ai] pip install コマンドを使用します...
  [human] NumPyとは何ですか?...
  [ai] NumPyは科学計算ライブラリです...
戦略説明適用シナリオ
strategy="last"system メッセージ + 最近の会話を保持長い会話では最新のコンテキストだけを気にする
strategy="first"system メッセージ + 最初の会話を保持重要なコンテキストがトリミングされないようにする

start_on="human" は、トリミング後のメッセージリストがユーザーメッセージで始まることを保証します(AI メッセージではなく)。モデルが孤立した AI 応答で始まるのを防ぎます。


RemoveMessage——特定メッセージの削除

高度なシナリオでは、メッセージ履歴から特定のメッセージを削除する必要がある場合があります(機密コンテンツのクリーニング、応答の再生成など):

例

from langchain.messages import HumanMessage, AIMessage, RemoveMessage

# 会話があると仮定する
messages = [
    HumanMessage(content="こんにちは", id="msg_1"),
    AIMessage(content="こんにちは!何かお手伝いできることはありますか?", id="msg_2"),
    HumanMessage(content="天気を調べてください", id="msg_3"),
]

# RemoveMessage を使用して特定のメッセージを削除(ID で指定)
# RemoveMessage は add_messages reducer と組み合わせて使用
# Agent 状態を更新するとき、RemoveMessage はリストから対応する ID のメッセージを削除します
removal = RemoveMessage(id="msg_3")

print(f"削除するメッセージ ID: {removal.id}")
print(f"タイプ: {removal.type}")  # remove

RemoveMessage は通常、AgentState の add_messages reducer と組み合わせて使用します。middleware または after_model フックで RemoveMessage を返すと、メッセージ履歴を動的にクリーンアップできます。


メッセージ属性の共通メソッド

すべてのメッセージタイプは BaseMessage を継承し、共通のメソッドを共有しています:

例

from langchain.messages import HumanMessage, AIMessage

msg = HumanMessage(content="こんにちは、サンプル")

# 基本属性
print(f"content: {msg.content}")      # メッセージコンテンツ
print(f"type: {msg.type}")            # メッセージタイプ(human/ai/system/tool)
print(f"id: {msg.id}")                # 自動生成された一意のID

# text 属性:テキストコンテンツの場合はテキストを返し、それ以外の場合は \"\" を返す
print(f"text: {msg.text}")

# pretty_repr():フォーマットして出力し、デバッグに適している
print(f"美化出力:\n{msg.pretty_repr()}")

実行結果:

content: こんにちは、世界
type: human
id: 00000000-0000-4000-8000-000000000000
text: こんにちは、世界
整形出力:
================================ Human Message =================================
こんにちは、世界
その他の拡張