LangChain With Amazon Bedrock in Python: ChatBedrockConverse, Streaming, Tools, Agents, Embeddings, Knowledge Base RAG, Guardrails, Caching and Every Error Fixed
To use LangChain with Amazon Bedrock in Python, install langchain-aws and create a ChatBedrockConverse model: pip install -U langchain-aws, then ChatBedrockConverse(model="us.amazon.nova-pro-v1:0", region_name="us-east-1") and .invoke("your question"). It reads your normal AWS credentials (a profile, environment variables, an IAM role, or a Bedrock API key in AWS_BEARER_TOKEN_BEDROCK). ChatBedrockConverse is the class to use for every model on Bedrock's Converse API, with streaming, tool calling and structured output; the older ChatBedrock is only needed for custom models, and the even older from langchain.llms.bedrock import Bedrock no longer works in LangChain 1.x. For RAG, use BedrockEmbeddings with Titan Text Embeddings V2, or AmazonKnowledgeBasesRetriever to query a Bedrock knowledge base directly. Below: setup and IAM permissions, model IDs and cross-Region inference profiles, streaming, tools, an agent, embeddings and the vector dimension mismatch, knowledge base RAG, guardrails, prompt caching, deploying to AgentCore, and every common error with its fix.
Jake's computer shop keeps its warranty terms, repair prices and supplier notes in a folder of PDFs and spreadsheets that only Ethan can find anything in. Ethan wanted a small assistant the front desk could ask: "Is a cracked screen covered on the refurbished Lenovos?" He already had an AWS account for the shop's website, so Amazon Bedrock was the obvious model source, and LangChain was the obvious way to wire documents, a model and a few tools together in Python. The first hour went on errors that had nothing to do with AI: an import from an old tutorial, a model ID that needed a prefix, and a permission nobody mentioned. This page is the version without that hour.
New to Bedrock? Our plain-English Bedrock explainer covers what it is and how it bills, and the cards below fill in the rest.
LangChain and Bedrock: what each one does
People search "LangChain vs Bedrock," but the two are not rivals; you usually use them together.
- Amazon Bedrock is the AWS service that hosts AI models from many companies (Amazon's Nova, OpenAI, Anthropic, Meta, Mistral, Cohere and others) behind one API, billed per token on your AWS bill. It also offers managed extras: knowledge bases for your documents, guardrails, and AgentCore for running agents.
- LangChain is an open-source Python (and JavaScript) framework for building applications around models: prompts, chat history, tools the model can call, document loaders, vector stores, retrievers and agents. It works with dozens of model providers through the same interface.
The bridge between them is the langchain-aws package, maintained with AWS. It gives LangChain classes for Bedrock's chat models, embeddings, knowledge bases and more. Because LangChain's interface is the same for every provider, code you write against Bedrock can switch to another provider, or to a local model through Ollama, by changing one line.
Jake: "So Bedrock is the brain and LangChain is the wiring?"
Ethan: "Bedrock rents the brains. LangChain is how I plug our documents and tools into whichever brain I pick."
Setup: install, credentials, Region and IAM permissions
1. Install the packages. In a virtual environment, with Python 3.10 or later:
pip install -U langchain langchain-aws
langchain-aws brings in boto3, the AWS SDK for Python, which does the actual calls. langchain gives you agents and the general building blocks.
2. Give it credentials. LangChain uses whatever boto3 finds, in the usual order. Any one of these works:
- A named profile from
aws configureoraws configure sso: passcredentials_profile_name="shop", or setAWS_PROFILE=shop. - Environment variables:
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY(plusAWS_SESSION_TOKENfor temporary credentials). - An IAM role, automatically, when the code runs on Lambda, EC2, ECS or AgentCore. This is the right choice in production: no keys in your code at all.
- A Bedrock API key, a simpler key that only works for Bedrock: set
AWS_BEARER_TOKEN_BEDROCKto its value. Handy for quick experiments; use a role or profile for anything lasting.
3. Pick a Region. Models are offered per Region, so set one explicitly with region_name="us-east-1" (or AWS_REGION) instead of relying on a default that may differ between your laptop and your server. US East (N. Virginia) and US West (Oregon) have the widest choice.
4. Allow the calls in IAM. The identity your code runs as needs permission to invoke models. The minimum for chat, including streaming:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:*:123456789012:inference-profile/*"
]
}]
}
The Converse API that LangChain uses is authorized by those same two actions. The inference-profile line matters: when you call a us. or global. model ID, the request may be served in another Region, so the permission must cover the profile and the model in every Region the profile can route to; the wildcards above do that. Tighten them to specific models once your choice settles. Knowledge base queries additionally need bedrock:Retrieve, and guardrails bedrock:ApplyGuardrail.
5. Model access. In current accounts, most serverless models can be called straight away, and some providers ask for a short one-time form or a Marketplace subscription on first use. If your first call fails with an access error, that first-use step is usually what is missing; the error table below covers it.
Your first call: ChatBedrockConverse
from langchain_aws import ChatBedrockConverse
llm = ChatBedrockConverse(
model="us.amazon.nova-pro-v1:0",
region_name="us-east-1",
temperature=0.2,
max_tokens=800,
)
reply = llm.invoke([
("system", "You answer questions for a small computer repair shop. Be brief."),
("human", "How long should a laptop battery last before it needs replacing?"),
])
print(reply.text)
print(reply.usage_metadata) # input, output and total tokens
reply.text is the answer as plain text; reply.content can be a list of content blocks (text, reasoning, tool calls), which is why .text is the safer thing to print. usage_metadata shows the tokens you are billed for, worth logging from day one.
The model ID. Most current models are called through an inference profile, an ID with a prefix: us. (routes among US Regions), eu., apac., jp. and others for those geographies, or global. (any commercial Region with capacity). Many newer models have no single-Region option at all, so the bare ID, such as amazon.nova-pro-v1:0, fails with "on-demand throughput isn't supported" while us.amazon.nova-pro-v1:0 works. Our Bedrock model IDs list has every model with the prefixes it supports, and aws bedrock list-inference-profiles prints the ones in your Region.
Switching models is a one-word change. The same code with OpenAI's model on Bedrock:
llm = ChatBedrockConverse(model="global.openai.gpt-6-sol", region_name="us-east-1")
With init_chat_model. LangChain's general-purpose loader works too, and keeps your code provider-neutral:
from langchain.chat_models import init_chat_model
llm = init_chat_model(
"us.amazon.nova-pro-v1:0",
model_provider="bedrock_converse",
region_name="us-east-1",
)
Pass model_provider="bedrock_converse" explicitly. Without it, LangChain guesses from the name, and IDs that start with amazon. or meta. are guessed as the older bedrock provider (the ChatBedrock class), which is not what you want.
Jake: "It answered! What did it say about batteries?"
Ethan: "Two to four years, depending on charge cycles. And it cost a fraction of a cent. The hard part was the 'us.' in front of the name."
ChatBedrockConverse vs ChatBedrock vs ChatAnthropicBedrock (and the old Bedrock import)
| Class | Uses | When to use it |
|---|---|---|
ChatBedrockConverse | Bedrock's Converse API, one format for every model | The default for everything except custom models |
ChatBedrock | InvokeModel, with each provider's own request format | Custom (imported or fine-tuned) models, which Converse does not support yet |
ChatAnthropicBedrock | Anthropic's own SDK pointed at Bedrock | Anthropic models when you want Anthropic-specific features first; install langchain-aws[anthropic] |
bedrock_mantle_openai provider | Bedrock's OpenAI-compatible endpoint | Code written for the OpenAI API; install langchain-aws[openai] |
Both chat classes support tool calling, structured output, image input, token streaming and token usage. For almost everyone, ChatBedrockConverse is the one: one request format for all models means switching models never breaks your code. The Mantle option is explained in our Mantle vs bedrock-runtime guide.
The old tutorials. Code from 2023 and 2024 often starts with one of these:
from langchain.llms.bedrock import Bedrock # very old from langchain_community.llms import Bedrock # old from langchain_community.chat_models import BedrockChat # old
In LangChain 1.x these fail with an import error, because the Bedrock integration moved to its own package. Moving old code across takes four steps:
- Install the new package:
pip install -U langchain-aws, and remove pins on oldlangchainversions from your requirements file. - Change the import to
from langchain_aws import ChatBedrockConverse, and the class name where it is created. - Rename the settings:
model_id=becomesmodel=(both are accepted, butmodelis the current name), andmodel_kwargssuch as{"temperature": 0.2, "max_tokens_to_sample": 500}become the named parameterstemperature=0.2andmax_tokens=500. Update the model ID too: old tutorials use models that have since been retired. - Read the result differently. Old "LLM" classes took a string and returned a string; chat models take messages and return a message object, so print
.text. Old chains built withLLMChainbecome a prompt piped into the model:chain = prompt | llm, thenchain.invoke({...}).
Streaming responses
For a chat interface, show the answer as it is written instead of waiting for the whole thing:
for chunk in llm.stream("Explain what a laptop's BIOS does, in three sentences."):
print(chunk.text, end="", flush=True)
Under the hood this uses ConverseStream, which is why the IAM policy needs bedrock:InvokeModelWithResponseStream; without it, invoke works and stream fails with an access error, a confusing combination until you know why. In async code, use async for chunk in llm.astream(...) the same way. Each chunk's .text is the new piece of text; tool calls arrive as partial JSON spread across chunks, and LangChain assembles them when you add chunks together.
To stream into a web page, a few lines of FastAPI pass the chunks straight through to the browser:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
@app.get("/ask")
async def ask(q: str):
async def gen():
async for chunk in llm.astream(q):
yield chunk.text
return StreamingResponse(gen(), media_type="text/plain")
Run it with uvicorn app:app and open http://localhost:8000/ask?q=hello; the answer appears word by word. In production, put authentication in front of an endpoint like this, because every request costs tokens.
Many questions at once: batch() and Bedrock batch inference
For a handful to a few hundred requests, LangChain's batch sends them in parallel and returns the answers in the same order:
questions = [
"Summarize this repair note in one line: replaced keyboard, tested OK, customer notified.",
"Summarize this repair note in one line: battery swelling, unit not powered on, awaiting part.",
]
answers = llm.batch(questions, config={"max_concurrency": 4})
for a in answers:
print(a.text)
max_concurrency keeps you under Bedrock's per-minute request quota; raise it slowly if you see throttling errors.
For thousands or millions of requests that do not need an instant answer, such as tagging a whole archive of repair notes overnight, Bedrock's own batch inference is cheaper: you write the requests to a JSONL file in S3, start a batch job (CreateModelInvocationJob), and collect the results from S3 when it finishes, at about half the on-demand token price for supported models. That runs through boto3 rather than LangChain, and it suits jobs measured in hours, not seconds.
Tool calling and structured output
Tools let the model ask your code to do things: look up a price, check stock, open a ticket. You describe each tool with a Python function, and the model decides when to call it:
from langchain_core.tools import tool
@tool
def repair_price(device: str, repair: str) -> str:
"""Look up the shop's price for a repair, e.g. device='ThinkPad T14', repair='screen'."""
prices = {("thinkpad t14", "screen"): "$189", ("thinkpad t14", "battery"): "$99"}
return prices.get((device.lower(), repair.lower()), "No fixed price; ask for a quote.")
llm_tools = llm.bind_tools([repair_price])
msg = llm_tools.invoke("How much is a new screen for a ThinkPad T14?")
print(msg.tool_calls)
# [{'name': 'repair_price', 'args': {'device': 'ThinkPad T14', 'repair': 'screen'}, 'id': '...'}]
The model does not run the function; it returns a request to run it. Your code (or an agent, next section) runs it and sends the result back. The function's docstring is what the model reads to decide when the tool fits, so write it for the model: what it does, and an example of the arguments.
Structured output is the same machinery used to get data back in a fixed shape, which is far more reliable than asking for JSON in the prompt:
from pydantic import BaseModel, Field
class RepairTicket(BaseModel):
customer_name: str
device: str
problem: str = Field(description="Short description of the fault")
urgent: bool
extract = llm.with_structured_output(RepairTicket)
ticket = extract.invoke("Hi, it's Maria Lopez, my Dell XPS won't charge and I need it for work tomorrow.")
print(ticket)
# customer_name='Maria Lopez' device='Dell XPS' problem='Does not charge' urgent=True
Not every model on Bedrock supports tool use through Converse. Current Nova, OpenAI, Anthropic, Mistral Large and Llama models do; if a model does not, Bedrock returns a validation error saying the model does not support tool use, and the fix is a model that does.
A LangChain agent on Bedrock
An agent runs the loop for you: the model asks for a tool, the agent runs it, sends back the result, and repeats until the model has an answer. In LangChain 1.x that is one function:
from langchain.agents import create_agent
@tool
def in_stock(part: str) -> str:
"""Check whether a replacement part is in stock, e.g. part='T14 screen'."""
stock = {"t14 screen": 2, "t14 battery": 0}
count = stock.get(part.lower())
return "unknown part" if count is None else f"{count} in stock"
agent = create_agent(
model=llm,
tools=[repair_price, in_stock],
system_prompt="You help the front desk of a computer repair shop. Use the tools for prices and stock.",
)
result = agent.invoke({"messages": [("human", "Can we replace a T14 battery today, and what does it cost?")]})
print(result["messages"][-1].text)
The agent will typically call both tools, see that the battery costs $99 but none are in stock, and answer accordingly. create_agent is built on LangGraph, LangChain's library for agent workflows, so you can add memory between conversations, human approval before a tool runs, or a fixed multi-step graph when you outgrow the simple loop.
Remembering the conversation. By default each invoke starts fresh. Give the agent a checkpointer and a conversation ID, and it keeps the history between calls:
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(model=llm, tools=[repair_price, in_stock], checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "front-desk-1"}}
agent.invoke({"messages": [("human", "How much is a T14 screen?")]}, cfg)
follow_up = agent.invoke({"messages": [("human", "And is one in stock?")]}, cfg)
print(follow_up["messages"][-1].text) # it knows "one" means a T14 screen
InMemorySaver forgets everything when the program stops, which is fine for testing. For a real service, use a database-backed checkpointer, or AgentCore Memory if the agent runs on AgentCore. Keep long conversations in check, too: every earlier message is sent again with each new question, so token costs grow as a chat goes on.
Running it in production on AWS. A LangGraph or LangChain agent runs unchanged on Amazon Bedrock AgentCore Runtime, which gives each user session its own isolated environment, scales to zero, and adds memory, identity and observability services. The AgentCore CLI scaffolds and deploys it (agentcore create, then agentcore deploy), and your code reaches Bedrock models through the runtime's IAM role, with no keys. Our AgentCore explainer covers what each piece costs and when it is worth it. Smaller agents also run well on plain Lambda.
Bedrock embeddings in LangChain, and the vector dimension mismatch
Embeddings turn text into lists of numbers so that similar meanings end up close together; they are the search half of RAG. LangChain's BedrockEmbeddings calls Bedrock's embedding models:
from langchain_aws import BedrockEmbeddings
emb = BedrockEmbeddings(
model_id="amazon.titan-embed-text-v2:0",
region_name="us-east-1",
model_kwargs={"dimensions": 512, "normalize": True},
)
vector = emb.embed_query("Is a cracked screen covered by warranty?")
print(len(vector)) # 512
Titan Text Embeddings V2 is the usual choice: cheap, good at English and many other languages, and it can output 1,024 (the default), 512 or 256 numbers per text. Fewer dimensions mean a smaller, cheaper index with slightly lower accuracy; 512 is a sensible middle for most document sets. The older Titan Embeddings G1 model (amazon.titan-embed-text-v1) outputs 1,536, and Cohere's Embed v3 models output 1,024.
"Vector dimension mismatch." This error, from OpenSearch, pgvector or any vector store, means the index was created for one size of vector and you are sending another. The usual causes: the index was built with Titan G1 (1,536) and the code now uses Titan V2 (1,024); or the index expects 1,024 and the code sets dimensions to 512; or two parts of the app use different embedding models. The fix is to use exactly the same model and dimensions for indexing and for searching, and if you change either, rebuild the index from scratch. Vectors from different embedding models are not comparable even when the sizes match.
RAG with Bedrock: your own vector store, or a Bedrock knowledge base
RAG (retrieval-augmented generation) means finding the passages in your documents that relate to a question and giving them to the model with the question, so it answers from your material instead of from memory. With Bedrock and LangChain there are two routes.
Route 1: LangChain manages the documents. Load, split, embed and store them yourself. This example uses the in-memory store, so it runs with nothing else installed; swap in Chroma, FAISS, pgvector or OpenSearch for real use:
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_core.documents import Document
docs = [
Document(page_content="Refurbished Lenovo laptops have a 12-month warranty covering hardware faults. Accidental damage, including cracked screens, is not covered.", metadata={"source": "warranty.pdf"}),
Document(page_content="Screen replacement for a ThinkPad T14 costs $189 including labor.", metadata={"source": "prices.xlsx"}),
]
store = InMemoryVectorStore.from_documents(docs, embedding=emb)
retriever = store.as_retriever(search_kwargs={"k": 3})
question = "Is a cracked screen covered on the refurbished Lenovos?"
context = "\n\n".join(d.page_content for d in retriever.invoke(question))
answer = llm.invoke([
("system", "Answer only from the context. If the context does not say, say you don't know.\n\nContext:\n" + context),
("human", question),
])
print(answer.text)
For real files, LangChain's loaders read PDFs, Word documents, web pages and spreadsheets, and RecursiveCharacterTextSplitter (from the langchain-text-splitters package) cuts them into passages of a few hundred words before embedding.
Route 2: Bedrock manages the documents. A Bedrock knowledge base reads your files from S3, SharePoint, Confluence and other sources, chunks, embeds and indexes them for you, and keeps them in sync. LangChain then only has to ask it:
from langchain_aws import AmazonKnowledgeBasesRetriever
kb = AmazonKnowledgeBasesRetriever(
knowledge_base_id="ABCDEFGHIJ",
region_name="us-east-1",
retrieval_config={"vectorSearchConfiguration": {"numberOfResults": 4}},
)
for doc in kb.invoke("Is a cracked screen covered on the refurbished Lenovos?"):
print(doc.metadata.get("score"), doc.page_content[:120])
The retriever returns ordinary LangChain documents, so it drops into the same prompt as Route 1, or into an agent as a tool. The identity running the code needs bedrock:Retrieve on the knowledge base. Our knowledge base setup guide covers creating one, the managed and customer-managed types, and what each costs.
Which route? Route 1 when you want full control, already run a vector database, or want the same code to work with local models; Route 2 when the documents live in S3 or SharePoint, change often, and you would rather not run the indexing yourself.
Jake: "So I could ask it about the Lenovo warranty right now?"
Ethan: "Route 1 already answers it: not covered, cracked screens count as accidental damage. Route 2 is for when you keep adding PDFs and I stop wanting to rebuild the index by hand."
Build it: a document assistant with citations, step by step
Here is the whole of Route 1 as a small program Ethan could leave running on the shop PC: it indexes a folder of PDFs once, stores the index on disk, and answers questions with the file and page each answer came from.
- Install the extra pieces:
pip install -U langchain-aws langchain-community langchain-text-splitters langchain-chroma pypdf. Chroma is a free vector database that runs inside your program and saves to a folder. - Load and split the PDFs into passages small enough to be precise but large enough to make sense on their own.
- Embed and store them with Titan Text Embeddings V2, in a Chroma folder on disk, so this step only runs when documents change.
- Retrieve and answer: find the closest passages, put them in the prompt with their source labels, and ask the model to cite.
- Ask questions in a simple loop in the terminal.
from pathlib import Path
from langchain_aws import BedrockEmbeddings, ChatBedrockConverse
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
REGION = "us-east-1"
emb = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0", region_name=REGION,
model_kwargs={"dimensions": 512, "normalize": True})
llm = ChatBedrockConverse(model="us.amazon.nova-pro-v1:0", region_name=REGION, temperature=0)
db_dir = "shop_index"
if not Path(db_dir).exists(): # steps 2 and 3, first run only
pages = []
for pdf in Path("shop_docs").glob("*.pdf"):
pages += PyPDFLoader(str(pdf)).load()
chunks = RecursiveCharacterTextSplitter(chunk_size=1200, chunk_overlap=150).split_documents(pages)
Chroma.from_documents(chunks, emb, persist_directory=db_dir)
db = Chroma(persist_directory=db_dir, embedding_function=emb)
retriever = db.as_retriever(search_kwargs={"k": 4})
def ask(question: str) -> str: # step 4
hits = retriever.invoke(question)
context = "\n\n".join(
f"[{Path(d.metadata['source']).name}, page {d.metadata.get('page', 0) + 1}]\n{d.page_content}"
for d in hits)
reply = llm.invoke([
("system", "Answer only from the sources below and cite them like [file, page]. "
"If they do not contain the answer, say so.\n\n" + context),
("human", question)])
return reply.text
while True: # step 5
q = input("\nQuestion (blank to quit): ").strip()
if not q:
break
print(ask(q))
Put the PDFs in a folder named shop_docs next to the script and run it. The first run reads and embeds everything, which for a few dozen documents takes a minute and costs a few cents; later runs start instantly. When the documents change, delete the shop_index folder and run it again. temperature=0 keeps answers consistent from one asking to the next, which matters more than creativity for warranty questions.
Jake: "It says 'not covered, see warranty.pdf, page 2.' Can I trust that?"
Ethan: "Trust it enough to open page 2. That's the point of the citation: it shows you where to look instead of asking you to believe it."
Test for free against a local model, then switch to Bedrock
Because LangChain's interface is the same for every provider, you can build and debug against a free local model and only switch to Bedrock when the logic works. With Ollama installed and a model downloaded:
import os
from langchain.chat_models import init_chat_model
if os.environ.get("USE_LOCAL") == "1":
llm = init_chat_model("llama3.2", model_provider="ollama") # pip install langchain-ollama
else:
llm = init_chat_model("us.amazon.nova-pro-v1:0", model_provider="bedrock_converse",
region_name="us-east-1")
Everything else in the program stays the same: tools, agents, structured output and retrievers. Ethan used this to iterate on prompts all afternoon without a single Bedrock charge, then ran the final checks against Bedrock, where the stronger model answered better on the tricky warranty clauses. Local models are smaller, so expect weaker answers and occasional tool-calling mistakes; the point is to get the plumbing right for free. If you would rather run the local model on a shared machine, our Ollama in Docker guide sets one up for a team.
Asking about a photo
Several Bedrock models read images, Nova Pro and Nova Lite among them, and LangChain passes images as content blocks next to the text. For the front desk, that meant photographing a damaged laptop and asking what the damage looks like before writing the ticket:
import base64
with open("intake_photo.jpg", "rb") as f:
photo = base64.b64encode(f.read()).decode()
msg = llm.invoke([("human", [
{"type": "text", "text": "Describe any visible damage on this laptop in two short lines."},
{"type": "image", "base64": photo, "mime_type": "image/jpeg"},
])])
print(msg.text)
Keep photos reasonably small (resize to around 1,500 pixels on the long side), because images are billed as input tokens and a full-size phone photo costs more without improving the answer. Models without image support return a validation error; switch to one that has it.
You can send more than one image in the same message, for example "before" and "after" photos of a repair, and ask the model to compare them. The answers are descriptions, not measurements: good for drafting a ticket or flagging "looks like liquid damage near the keyboard," not for deciding a warranty claim on their own.
Running it on AWS Lambda
For a small assistant that is used now and then, Lambda is the cheapest home: no server running between questions, and the function's IAM role supplies credentials, so no keys go into the code. Four things trip people up:
- The timeout. Lambda's default is 3 seconds, and a model answer often takes longer, so the function is cut off mid-reply with "Task timed out." Set 30 to 60 seconds for chat, more for agents; our Lambda timeout guide shows where.
- Package size. LangChain plus its dependencies can approach the 250 MB unzipped limit for zip deployments, especially with document loaders. A container image (up to 10 GB) avoids the problem entirely and is the simpler choice for LangChain apps.
- The role's permissions. The function's execution role, not your own user, needs the Bedrock invoke actions from the setup section, plus
bedrock:Retrieveif it queries a knowledge base. - Create clients once. Build the
ChatBedrockConverseobject outside the handler function, so warm invocations reuse it instead of setting up a new connection on every request.
import json, os
from langchain_aws import ChatBedrockConverse
llm = ChatBedrockConverse(model="us.amazon.nova-pro-v1:0", region_name=os.environ["AWS_REGION"])
def handler(event, context):
question = json.loads(event.get("body") or "{}").get("question", "")
reply = llm.invoke([("system", "Answer briefly for a repair shop."), ("human", question)])
return {"statusCode": 200, "headers": {"Content-Type": "text/plain"}, "body": reply.text}
Lambda sets AWS_REGION automatically, so the function uses Bedrock in its own Region. Put a function URL or API Gateway in front of it, with authentication, and the front-desk page can call it.
Bedrock Guardrails with LangChain
Guardrails are filters you configure in Bedrock: blocked topics, harmful content, personal information to mask, words to deny, and checks that answers stay grounded in the provided sources. You create a guardrail in the Bedrock console or with the API, publish a version, and then attach it to calls:
llm_guarded = ChatBedrockConverse(
model="us.amazon.nova-pro-v1:0",
region_name="us-east-1",
guardrail_config={
"guardrailIdentifier": "gr-abc123example",
"guardrailVersion": "1",
"trace": "enabled",
},
)
When the guardrail blocks a request or an answer, the reply contains the blocked-message text you configured instead of the model's answer, and with trace enabled, the response metadata shows which policy fired. For the front desk, Ethan used one to mask customers' phone numbers and to keep the assistant off topics like legal advice. Calls with a guardrail need bedrock:ApplyGuardrail permission on it, and each guardrail check is billed separately from the model tokens.
Prompt caching and reasoning effort
Prompt caching saves money and time when many requests start with the same long text, such as a big system prompt or a document you ask several questions about. You mark where the reusable part ends with a cache point, and later requests that share that beginning read it from the cache at a lower price:
long_policy = open("warranty_policy.txt", encoding="utf-8").read()
messages = [
("system", [
{"type": "text", "text": "Answer from this warranty policy:\n" + long_policy},
{"cachePoint": {"type": "default"}},
]),
("human", "Are batteries covered on refurbished laptops?"),
]
first = llm.invoke(messages)
print(first.usage_metadata.get("input_token_details")) # cache_creation on the first call, cache_read after
Not every model supports caching, and each has a minimum length before anything is cached (typically around a thousand tokens), so short prompts gain nothing. For agents, langchain-aws includes a BedrockPromptCachingMiddleware that places cache points for you. Our Bedrock pricing guide shows how much caching and the other levers save.
Reasoning effort. Models that can "think" before answering accept a single setting in recent versions of langchain-aws (1.7.0 and later):
thinker = ChatBedrockConverse(
model="global.amazon.nova-2-lite-v1:0",
region_name="us-east-1",
reasoning_effort="medium",
)
LangChain translates it into each provider's own setting (Nova 2, OpenAI's gpt-oss and others). Models that do not know the setting ignore it with a warning. Higher effort means better answers on hard questions and more output tokens on your bill.
Common LangChain + Bedrock errors and fixes
| Error | Cause | Fix |
|---|---|---|
ImportError on langchain.llms.bedrock or langchain_community | Old tutorial code; the integration moved | pip install -U langchain-aws; from langchain_aws import ChatBedrockConverse |
Unable to locate credentials / NoCredentialsError | boto3 found no credentials | Set AWS_PROFILE, pass credentials_profile_name, or run with an IAM role |
ValidationException: on-demand throughput isn't supported | Bare model ID for a model that needs an inference profile | Use us., eu. or global. in front of the ID; see our throughput error guide |
ValidationException: the provided model identifier is invalid | Typo, or the model is not offered in this Region | Check the ID and region_name; list models with aws bedrock list-foundation-models --region us-east-1 |
AccessDeniedException | Missing IAM permission, or the model's first-use step not done | Add the invoke actions for the profile and model ARNs; complete the provider's first-use form if asked |
invoke works but stream is denied | Policy lacks the streaming action | Add bedrock:InvokeModelWithResponseStream |
ThrottlingException: too many requests | Per-minute request or token quota reached | Retry with backoff (max_retries), use a global. profile, or raise the quota; see our throttling guide |
| Model does not support tool use | bind_tools with a model lacking tool support in Converse | Pick a model with tool use |
| Vector dimension mismatch | Index and embeddings use different models or sizes | Same model and dimensions for indexing and search; rebuild the index after changes |
| Reply prints a list, not text | .content holds content blocks | Use reply.text |
ExpiredTokenException | Temporary or SSO credentials expired | aws sso login --profile shop, or refresh the session token |
When an error is unclear, call the same model once with the AWS CLI or plain boto3 outside LangChain. If that fails too, the problem is AWS-side (permissions, Region, ID, quota); if it works, it is in your LangChain code or package versions. pip show langchain-aws langchain-core boto3 prints the versions to compare against.
LangChain vs Bedrock Agents vs AgentCore
| LangChain (your code) | Bedrock Agents | AgentCore | |
|---|---|---|---|
| What it is | An open-source framework | Agents configured in the Bedrock console | AWS's platform to run your own agent code |
| Where it runs | Anywhere: laptop, Lambda, containers | Fully managed by AWS | Managed runtime, your code inside |
| Models | Any provider, including local | Bedrock models | Any, inside or outside Bedrock |
| Control | Total | Limited to what the console offers | Total, plus managed memory and identity |
| Best for | Building and testing, portable apps | Simple agents without code | LangChain or LangGraph agents in production |
They combine naturally: write the agent with LangChain, test it on your laptop, then run the same code on AgentCore. Bedrock Agents remains the quickest route for simple agents if you prefer configuration to code. And if you are still choosing between Bedrock and AWS's other AI service, our Bedrock vs SageMaker AI guide makes that call.
What it costs
LangChain is free; you pay Bedrock for tokens, embeddings, knowledge base storage and queries, and guardrail checks, all on your normal AWS bill. For a small internal assistant like the shop's, a few hundred questions a day with a mid-priced model and a short context costs a few dollars a month. The bill grows with three things: long prompts (whole documents pasted into every request), expensive models used where a cheaper one would do, and agents that loop many times per question. Logging usage_metadata from the first day tells you which one it is. A budget alert in AWS Budgets, set before you share the assistant with anyone, catches surprises within a day.
Frequently asked questions
Does LangChain support Amazon Bedrock?
Yes, through the langchain-aws package. It provides ChatBedrockConverse and ChatBedrock for chat, BedrockEmbeddings, and AmazonKnowledgeBasesRetriever for knowledge bases.
How do I use Bedrock with LangChain in Python?
pip install -U langchain-aws, then from langchain_aws import ChatBedrockConverse and ChatBedrockConverse(model="us.amazon.nova-pro-v1:0", region_name="us-east-1").invoke("Hi").
What is the difference between ChatBedrock and ChatBedrockConverse?
ChatBedrockConverse uses the Converse API with one format for every model and is the recommended default. ChatBedrock uses InvokeModel and is needed only for custom models.
What replaced from langchain.llms.bedrock import Bedrock?
from langchain_aws import ChatBedrockConverse. The old Bedrock and BedrockChat classes were removed from LangChain's core packages.
How do I use Bedrock embeddings in LangChain?
Use BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0"), optionally with model_kwargs={"dimensions": 512, "normalize": True}.
How many dimensions does Titan Text Embeddings V2 use?
1,024 by default, or 512 or 256 if you set dimensions. Titan Embeddings G1 uses 1,536. Index and search must use the same model and size.
How do I query a Bedrock knowledge base from LangChain?
Use AmazonKnowledgeBasesRetriever with your knowledge_base_id and a retrieval_config, then call .invoke(question). It needs bedrock:Retrieve permission.
How do I stream Bedrock responses in LangChain?
Use llm.stream(...) and print each chunk.text. The IAM policy must allow bedrock:InvokeModelWithResponseStream.
Does LangChain support Bedrock Guardrails?
Yes. Pass guardrail_config with guardrailIdentifier, guardrailVersion and optionally trace to ChatBedrockConverse.
How do I use cross-Region inference with LangChain?
Use an inference profile ID such as us.amazon.nova-pro-v1:0 or a global. ID as the model, and allow invoke permissions on the profile and model ARNs.
Why do I get "on-demand throughput isn't supported" in LangChain?
The model needs an inference profile. Add the us., eu. or global. prefix to the model ID.
Can I use Anthropic models on Bedrock with LangChain?
Yes, with ChatBedrockConverse and their inference profile IDs, or with ChatAnthropicBedrock after installing langchain-aws[anthropic].
Does Bedrock prompt caching work in LangChain?
Yes, for supported models. Add a {"cachePoint": {"type": "default"}} block after the reusable text; agents can use BedrockPromptCachingMiddleware.
Can I run a LangChain agent on Bedrock AgentCore?
Yes. AgentCore Runtime runs LangChain and LangGraph agents unchanged; the AgentCore CLI scaffolds and deploys them.
What is the difference between LangChain and Bedrock Agents?
LangChain is an open-source framework for your own code; Bedrock Agents are configured in the console and run by AWS. Many teams use LangChain and deploy on AgentCore.
What IAM permissions does LangChain need for Bedrock?
bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream on the models and inference profiles, plus bedrock:Retrieve for knowledge bases and bedrock:ApplyGuardrail for guardrails.
Can I use a Bedrock API key with LangChain?
Yes. Set the AWS_BEARER_TOKEN_BEDROCK environment variable to the key; boto3 picks it up. Prefer IAM roles for production.
For readers preparing for an AWS certification
We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.
Exams that test this topic: AI Practitioner (AIF-C01) • AI Business Strategist (AIB-C01) • Developer – Associate (DVA-C03 from December 1, 2026).
Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:
1. An application must call foundation models from several providers through one API, without managing servers. Which service?
Amazon Bedrock.
2. A chatbot must answer from company documents stored in S3, with the indexing and syncing managed by AWS. What do you use?
An Amazon Bedrock knowledge base (retrieval-augmented generation).
3. Responses must block certain topics and mask personal information such as phone numbers. Which Bedrock feature?
Amazon Bedrock Guardrails.
4. A model call fails because the model needs to be invoked through an inference profile. What do you change?
Use the inference profile ID, such as a us. or global. prefixed model ID, for cross-Region inference.
5. Thousands of prompts must be processed overnight at the lowest cost, with no need for instant answers. Which option?
Amazon Bedrock batch inference, which runs jobs from S3 input files at a lower price than on-demand.
6. Which IAM actions does an application need to call Bedrock chat models, including streaming responses?
bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream.
7. Many requests repeat the same long system prompt. Which feature lowers cost and latency?
Prompt caching, which reuses the cached prefix at a reduced price.
Exam codes and dates change often. Our AWS certification changes page and our guide to the AI Business Strategist exam keep track of what is current.
By the end of the week, the front desk had a small chat page that answered warranty and price questions from the shop's own documents, cited which file each answer came from, masked phone numbers, and cost less per month than one coffee a week. Ethan's notes for the next person were three lines long: "langchain-aws, ChatBedrockConverse, us. in front of the model ID." Jake added a fourth: "Ask it before asking Ethan."
📌 If you keep one line from this page
Install langchain-aws, use ChatBedrockConverse with an inference profile ID such as us.amazon.nova-pro-v1:0 and an explicit region_name, allow bedrock:InvokeModel and InvokeModelWithResponseStream, and use the same embeddings model and dimensions for indexing and search.
Old imports fail in LangChain 1.x · reply.text, not .content · knowledge bases: AmazonKnowledgeBasesRetriever · guardrails: guardrail_config.
Revision note. Written October 19, 2026, for LangChain 1.x and langchain-aws 1.x. The model is the easy part; the prefix in front of its name is where the afternoon goes.