Building a conversational shopping assistant that actually completes purchases is harder than demoing a Q&A bot. This tutorial walks through an ecommerce chatbot search to checkout langchain implementation that uses LangChain’s tool-calling agent to move a user from natural-language product discovery all the way to a mock order confirmation.
Prerequisites
- Python 3.10 or newer
langchain,langchain-openai,langchain-community,faiss-cpu- An OpenAI API key (or any OpenAI-compatible endpoint)
- A minimal product catalog in memory
Install dependencies:
pip install langchain langchain-openai langchain-community faiss-cpu python-dotenv
Catalog and vector search
Semantic search beats keyword matching when users say “something for cold feet” instead of “socks”. We embed a small catalog and use FAISS for similarity lookup. For a production catalog with millions of SKUs you’d swap FAISS for a managed vector database, but the interface stays identical.
import os
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
products = [
{"id": "p1", "name": "Trailrunner Shoes", "description": "Lightweight hiking shoes with grip soles", "price": 89.99, "category": "footwear"},
{"id": "p2", "name": "Summit Backpack 30L", "description": "Water-resistant backpack for day hikes", "price": 59.50, "category": "bags"},
{"id": "p3", "name": "Merino Wool Socks", "description": "Breathable socks for cold weather", "price": 14.00, "category": "apparel"},
]
texts = [f"{p['name']}: {p['description']} (${p['price']})" for p in products]
metadatas = [{"id": p["id"], "price": p["price"]} for p in products]
embeddings = OpenAIEmbeddings(api_key=os.environ["OPENAI_API_KEY"])
vectorstore = FAISS.from_texts(texts, embeddings, metadatas=metadatas)
Checkpoint — confirm retrieval works before wiring the agent:
hits = vectorstore.similarity_search("hiking shoes", k=1)
print(hits[0].page_content)
# Trailrunner Shoes: Lightweight hiking shoes with grip soles ($89.99)
Tools for cart and checkout
LangChain tools are just decorated functions with docstrings the model reads to decide when to call them. The agent decides when to call them. We keep cart state in a module-level dict for the tutorial; in production use a session-backed store keyed by user ID.
from langchain.tools import tool
cart = {}
@tool
def search_products(query: str) -> str:
"""Search the catalog by semantic query. Returns top matches with IDs."""
docs = vectorstore.similarity_search(query, k=2)
return "\n".join(f"{d.metadata['id']}: {d.page_content}" for d in docs)
@tool
def add_to_cart(product_id: str, quantity: int = 1) -> str:
"""Add a product to the cart by its ID. Returns current cart."""
if product_id not in [p["id"] for p in products]:
return f"Unknown product {product_id}"
cart[product_id] = cart.get(product_id, 0) + quantity
return f"Added {quantity} x {product_id}. Cart: {cart}"
@tool
def checkout() -> str:
"""Compute total and place the order. Returns confirmation with total."""
if not cart:
return "Cart is empty."
total = sum(next(p["price"] for p in products if p["id"] == pid) * qty for pid, qty in cart.items())
order_id = "ORD-" + str(hash(frozenset(cart.items())) % 10000)
cart.clear()
return f"Order {order_id} placed. Total ${total:.2f}."
Test the search tool directly to verify formatting:
print(search_products.invoke({"query": "cold weather gear"}))
# p3: Merino Wool Socks: Breathable socks for cold weather ($14.0)
# p1: Trailrunner Shoes: Lightweight hiking shoes with grip soles ($89.99)
Agent assembly
We use the standard openai-tools-agent prompt from the LangChain hub. For production resilience, point ChatOpenAI at n4n.ai’s OpenAI-compatible endpoint: it honors client routing directives and provides automatic fallback when a provider is degraded, which keeps an ecommerce chatbot search to checkout langchain flow online under rate limits.
from langchain_openai import ChatOpenAI
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain import hub
prompt = hub.pull("hwchase17/openai-tools-agent")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [search_products, add_to_cart, checkout]
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
Running from search to checkout
A single user turn can trigger the full pipeline. The agent searches, adds items, then checks out.
result = executor.invoke({
"input": "I need shoes for hiking and a pair of wool socks. Then check out."
})
print(result["output"])
Expected trimmed output:
> Entering agent executor...
Call: search_products("hiking shoes")
Call: add_to_cart("p1", 1)
Call: add_to_cart("p3", 1)
Call: checkout()
Order ORD-1234 placed. Total $103.99.
The agent inferred two product IDs from the search results and completed the purchase without explicit UI steps. That is the core value of the ecommerce chatbot search to checkout langchain pattern: the LLM maps intent to existing backend operations.
Multi-turn conversations
AgentExecutor is stateless per call. Pass chat_history to maintain context across turns:
from langchain_core.messages import HumanMessage, AIMessage
history = []
def chat(user_msg: str) -> str:
resp = executor.invoke({"input": user_msg, "chat_history": history})
history.append(HumanMessage(content=user_msg))
history.append(AIMessage(content=resp["output"]))
return resp["output"]
chat("Show me bags under $60")
chat("Add the backpack to my cart")
chat("Now check out")
In a real deployment you’d persist history in the same session store as the cart and load it per request.
Hardening the flow
- Validate tool inputs. The
add_to_carttool already rejects unknown IDs, but you should also enforce quantity limits and price sanity from the server, not trust the LLM’s arithmetic. - Isolate cart state. Replace the global
cartdict with Redis or a database row keyed by session ID. The tool closures should read/write that store. - Make checkout idempotent. Real orders need an idempotency key; if the agent retries
checkout()after a network blip you must not double-charge. - Stream tokens. Wrap the executor in
RunnableWithMessageHistoryand use.stream()so the user sees progress while the agent thinks. - Log tool calls. Keep an audit trail of which products were added and when, for dispute resolution and analytics.
The ecommerce chatbot search to checkout langchain pattern is fundamentally an agentic wrapper around three primitives: retrieval, state mutation, and transactional action. Get those right and the LLM is just the router that translates messy human text into clean function calls.