惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
腾讯CDC
Jina AI
Jina AI
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
小众软件
小众软件
M
MIT News - Artificial intelligence
MyScale Blog
MyScale Blog
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
月光博客
月光博客
L
LangChain Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
C
Check Point Blog
U
Unit 42
人人都是产品经理
人人都是产品经理

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Converting LangChain Messages to OpenAI and Amazon Bedroc...
Moya Richard · 2026-05-16 · via DEV Community

When building AI applications with LangChain, your chat history usually lives as a list of LangChain message objects like SystemMessage, HumanMessage, AIMessage, and ToolMessage.

That format is convenient inside LangChain, but you may eventually need to save the conversation history, inspect it, or send it to different providers such as OpenAI-compatible APIs or Amazon Bedrock Converse.

Out Langchain conversation history will be:

  1. Converted to OpenAI chat-completions format.
  2. Converted to Amazon Bedrock Converse format.
  3. Saved to chat_history_v1.json.
  4. Loaded back into LangChain message objects.
  5. Converted again after loading to prove the round trip works.
  6. Used with an optional Bedrock toolConfig and client.converse() call.

Install Packages

pip install -U langchain-core langchain-aws boto3

Enter fullscreen mode Exit fullscreen mode

Import Dependencies

import json
from pathlib import Path

from langchain_core.messages import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    ToolCall,
    ToolMessage,
    convert_to_openai_messages,
    messages_from_dict,
    messages_to_dict,
)
from langchain_aws.chat_models.bedrock_converse import _messages_to_bedrock

Enter fullscreen mode Exit fullscreen mode

Create a LangChain Chat History

This example includes regular messages and a simple tool-calling exchange.

LangChain uses:

  • ToolCall for the assistant/model asking to call a tool
  • ToolMessage for the application returning the tool result
file_path = Path("chat_history_v1.json")

weather_tool_call: ToolCall = {
    "name": "get_weather",
    "args": {
        "city": "Chicago",
        "unit": "fahrenheit",
    },
    "id": "tool_call_001",
}

updated_messages = [
    SystemMessage(content="You are a helpful assistant."),
    HumanMessage(content="What is RAG?"),
    AIMessage(content="RAG means retrieval-augmented generation."),
    HumanMessage(content="Explain it in simple terms."),

    # The user asks a question that needs a tool.
    HumanMessage(content="What is the weather in Chicago?"),

    # The assistant decides to call a tool.
    AIMessage(
        content="",
        tool_calls=[weather_tool_call],
    ),

    # Your application runs the tool and adds the tool result back to the chat history.
    ToolMessage(
        content=json.dumps(
            {
                "city": "Chicago",
                "unit": "fahrenheit",
                "temperature": 72,
                "condition": "Partly cloudy",
            }
        ),
        tool_call_id="tool_call_001",
    ),

    # The assistant can now answer using the tool result.
    AIMessage(content="The current weather in Chicago is 72°F and partly cloudy."),
]

updated_messages

Enter fullscreen mode Exit fullscreen mode

Convert to OpenAI Message Format

OpenAI-compatible APIs expects a list of dictionaries with role and content fields.

openai_messages = convert_to_openai_messages(updated_messages)

print(json.dumps(openai_messages, indent=4, ensure_ascii=False))

Enter fullscreen mode Exit fullscreen mode

The OpenAI-style output keeps the system message inside the main message list.

Note : This format is useful for OpenAI-compatible APIs, but Bedrock Converse does not keep the system prompt as a normal message in the messages array.

{
  "role": "system",
  "content": "You are a helpful assistant."
}

Enter fullscreen mode Exit fullscreen mode

Convert to Bedrock Converse Format

Amazon Bedrock Converse separates the system prompt into a top-level system field. Regular user and assistant messages go into messages.

bedrock_messages, system = _messages_to_bedrock(updated_messages)

bedrock_payload = {
    "messages": bedrock_messages,
}

if system:
    bedrock_payload["system"] = system

print(json.dumps(bedrock_payload, indent=4, ensure_ascii=False))

Enter fullscreen mode Exit fullscreen mode

With tools, Bedrock Converse represents assistant tool requests as toolUse blocks and tool responses as toolResult blocks.
The internal _messages_to_bedrock() helper handles that conversion from LangChain tool_calls and ToolMessage objects.

{
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "text": "What is RAG?"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "text": "RAG means retrieval-augmented generation."
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "text": "Explain it in simple terms.\nWhat is the weather in Chicago?"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "toolUse": {
            "toolUseId": "tool_call_001",
            "input": {
              "city": "Chicago",
              "unit": "fahrenheit"
            },
            "name": "get_weather"
          }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "toolResult": {
            "content": [
              {
                "text": "{\"city\": \"Chicago\", \"unit\": \"fahrenheit\", \"temperature\": 72, \"condition\": \"Partly cloudy\"}"
              }
            ],
            "toolUseId": "tool_call_001",
            "status": "success"
          }
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "text": "The current weather in Chicago is 72°F and partly cloudy."
        }
      ]
    }
  ],
  "system": [
    {
      "text": "You are a helpful assistant."
    }
  ]
}

Enter fullscreen mode Exit fullscreen mode

The Bedrock Converse shape looks more like this:

{
  "system": [
    {
      "text": "You are a helpful assistant."
    }
  ],
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "text": "What is RAG?"
        }
      ]
    }
  ]
}

Enter fullscreen mode Exit fullscreen mode

Save the LangChain Message History

Use messages_to_dict() when saving LangChain messages.

Do not save openai messages or bedrock messages if the goal is to restore the messages later with messages_from_dict().

serialized_langchain_messages = messages_to_dict(updated_messages)

with file_path.open("w", encoding="utf-8") as file:
    json.dump(serialized_langchain_messages, file, indent=4, ensure_ascii=False)

print(f"Saved LangChain message history to: {file_path}")

Enter fullscreen mode Exit fullscreen mode

Load the Chat History Back

messages_to_dict() preserves the information LangChain needs to recreate the correct message classes later.

with file_path.open("r", encoding="utf-8") as file:
    raw_messages = json.load(file)

restored_messages = messages_from_dict(raw_messages)

restored_messages

Enter fullscreen mode Exit fullscreen mode

At this point, restored_messages contains real LangChain message objects again.

[SystemMessage(content='You are a helpful assistant.', additional_kwargs={}, response_metadata={}),
 HumanMessage(content='What is RAG?', additional_kwargs={}, response_metadata={}),
 AIMessage(content='RAG means retrieval-augmented generation.', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[]),
 HumanMessage(content='Explain it in simple terms.', additional_kwargs={}, response_metadata={}),
 HumanMessage(content='What is the weather in Chicago?', additional_kwargs={}, response_metadata={}),
 AIMessage(content='', additional_kwargs={}, response_metadata={}, tool_calls=[{'name': 'get_weather', 'args': {'city': 'Chicago', 'unit': 'fahrenheit'}, 'id': 'tool_call_001', 'type': 'tool_call'}], invalid_tool_calls=[]),
 ToolMessage(content='{"city": "Chicago", "unit": "fahrenheit", "temperature": 72, "condition": "Partly cloudy"}', tool_call_id='tool_call_001'),
 AIMessage(content='The current weather in Chicago is 72°F and partly cloudy.', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[])]

Enter fullscreen mode Exit fullscreen mode

Convert the Restored Messages to Bedrock Again

This verifies that the save/load round trip worked.

restored_bedrock_messages, restored_system = _messages_to_bedrock(restored_messages)

restored_bedrock_payload = {
    "messages": restored_bedrock_messages,
}

if restored_system:
    restored_bedrock_payload["system"] = restored_system

print(json.dumps(restored_bedrock_payload, indent=4, ensure_ascii=False))

Enter fullscreen mode Exit fullscreen mode

The full round trip is:

LangChain messages
        ↓
messages_to_dict()
        ↓
JSON file
        ↓
messages_from_dict()
        ↓
LangChain messages restored
        ↓
_messages_to_bedrock()
        ↓
Bedrock Converse payload

Enter fullscreen mode Exit fullscreen mode

Define the Bedrock Tool Schema

To enable tool use for the next llm response, you provide a toolConfig in client.converse(), which tells Amazon Bedrock which tools are available.

tool_config = {
    "tools": [
        {
            "toolSpec": {
                "name": "get_weather",
                "description": "Get the current weather for a city in the requested temperature unit.",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "city": {
                                "type": "string",
                                "description": "The city to get the weather for, such as Chicago.",
                            },
                            "unit": {
                                "type": "string",
                                "enum": ["fahrenheit", "celsius"],
                                "description": "The temperature unit to return.",
                            },
                        },
                        "required": ["city", "unit"],
                    }
                },
            }
        }
    ]
}


tool_config

Enter fullscreen mode Exit fullscreen mode

Optional Bedrock Converse Call

Note: AWS credentials and Bedrock model access required.

import boto3

client = boto3.client("bedrock-runtime", region_name="us-east-1")

response = client.converse(
    modelId="anthropic.claude-3-5-sonnet-20240620-v1:0",
    **restored_bedrock_payload,
    toolConfig=tool_config,
    inferenceConfig={
        "maxTokens": 1000,
        "temperature": 0.2,
    },
)

print(json.dumps(response, indent=4, ensure_ascii=False))

Enter fullscreen mode Exit fullscreen mode

Important Warning About _messages_to_bedrock

The function _messages_to_bedrock starts with an underscore:

from langchain_aws.chat_models.bedrock_converse import _messages_to_bedrock

Enter fullscreen mode Exit fullscreen mode

Because this is an internal helper, LangChain AWS may change it in a future release. For a tutorial, using it inline makes the flow easier to understand. In a production app, you may want to extract this logic into your own function.

Final Takeaway

Use messages_to_dict(updated_messages) when saving LangChain messages.

Use messages_from_dict(raw_messages) when restoring LangChain messages.

Use convert_to_openai_messages(updated_messages) when you need OpenAI chat-completions format.

Use _messages_to_bedrock(updated_messages) when you need Amazon Bedrock Converse format.

Use toolConfig=tool_config when you want Bedrock Converse to know which tools are available for the llm model response.