LangChain メッセージタイプ
LangChain では、すべての会話はメッセージ(Message)オブジェクトを介して渡されます。各種メッセージタイプの用途を理解することは、Agent を書くための基礎です。
4つのコアメッセージタイプ
LangChain は4つのコアメッセージタイプを定義しており、それぞれ会話内の異なるロールに対応します:
| タイプ | ロール | 説明 | 典型的な内容 |
|---|---|---|---|
| HumanMessage | ユーザー | ユーザーが送信したメッセージ | 「今日の天気は?」 |
| AIMessage | AIアシスタント | モデルの応答。tool_calls を含む場合があります | 「今日は杭州は晴れ、25°C」 |
| SystemMessage | システム | システム命令。AI のロールと行動ルールを定義します | 「あなたはプロの天気アシスタントです」 |
| ToolMessage | ツール | ツール実行後の戻り結果 | 「晴れ、25°C、湿度60%」 |
HumanMessage——ユーザーメッセージ
HumanMessage はユーザーが AI に送信するメッセージを表します。最も一般的なメッセージタイプであり、会話の起点でもあります。
例
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 のクイック作成方法
メッセージリストを構築する際、タプルや辞書をショートカットとして使用できます:
例
# 方法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(ツール呼び出しリクエスト)。
例
# 通常の 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 の付加情報
例
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.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.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 ではありません:
例
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 属性 | キーフィールド | 使用するタイミング |
|---|---|---|---|---|
| HumanMessage | human | user | content | ユーザー入力 |
| AIMessage | ai | assistant | content, tool_calls, usage_metadata | モデルの応答 |
| AIMessageChunk | ai | assistant | content(増分) | ストリーミング出力の断片 |
| SystemMessage | system | system | content | AI の役割を設定 |
| ToolMessage | tool | tool | content, tool_call_id, name | ツールの実行結果 |
ContentBlock——構造化メッセージ内容
これまで使用してきたメッセージコンテンツはすべてプレーン文字列です。しかし実際には、各メッセージのコンテンツは複数にすることができますContentBlock(コンテンツブロック)で構成されるリストです。
最もよく使われる3つのコンテンツブロック:
| タイプ | 説明 | 用途 |
|---|---|---|
| PlainTextContentBlock | プレーンテキストのコンテンツ | 通常のテキストメッセージ |
| ImageContentBlock | 画像コンテンツ(base64 または URL) | マルチモーダルモデルの画像入力 |
| ToolCall | ツール呼び出しリクエスト | AI がツール呼び出しを要求 |
例
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+ など)をサポートしている場合、画像コンテンツを分析させることができます:
例
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}")
メッセージに画像のみが含まれテキストが含まれない場合は、次のように直接使えます:
例
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.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 がツール呼び出しを含むか確認する
例
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()この関数は、メッセージ履歴をスマートにトリミングするのに役立ちます。
例
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——特定メッセージの削除
高度なシナリオでは、メッセージ履歴から特定のメッセージを削除する必要がある場合があります(機密コンテンツのクリーニング、応答の再生成など):
例
# 会話があると仮定する
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 を継承し、共通のメソッドを共有しています:
例
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 ================================= こんにちは、世界その他の拡張