What it takes to let an agent run part of a store

What it takes to let an agent run part of a store

Live Demo
Tags
Agents
LangGraph
Human-in-the-loop
Software Development
Published
September 26, 2026
Author
Darsh Vaghela
It is easy to build an agent that answers questions about a store. It is much harder to build one you would let change a price, place a purchase order or send a campaign to fifty thousand customers. The gap between the two is not model quality. It is engineering: what the agent is allowed to touch, how it asks before acting, and what you can see afterwards.
This is a builder's post. Everything here is generic and uses public tools, LangGraph and the Model Context Protocol, but the patterns come from what survives contact with a real business.

Start with a workflow, not an agent

Anthropic's guide to building agents draws a line that saves months: a workflow is a system where the model is called along a path you wrote, and an agent is one where the model decides the path. Their advice is to use the simplest pattern that passes your evaluation, and reach for a full agent only when you cannot hardcode the path but can still verify progress.
Most retail operations are workflows in disguise. Restocking is a workflow: read stock, compare to forecast, draft a purchase order, get approval, submit. Answering "where is my order" is a workflow. Save the open-ended agent for the tasks that really branch, like planning a campaign from a vague goal, and keep it away from the tasks that do not.

Tools are the whole interface

The model sees three things about each tool: a name, a description, and the argument schema. If it picks the wrong tool or passes the wrong argument, look at those three things before you touch the prompt.
Name tools as verbs with the object in them. search_orders_by_customer, not orders. Put in the description when to use the tool and when not to. Enumerate allowed values in argument descriptions, or you will receive "shipped", "Shipped" and "SHIPPED" in the same afternoon. Return errors as instructions the model can act on, not stack traces.
Here is the shape, with LangChain's tool decorator:
from langchain_core.tools import tool @tool def get_stock(sku: str, location_id: int) -> dict: """Current on-hand and reserved quantity for one SKU at one location. Use when the user asks about availability. Does NOT reorder; use propose_purchase_order for that. Returns on_hand, reserved, updated_at.""" row = inventory.get(sku, location_id) if row is None: return {"error": f"No stock record for {sku} at location {location_id}. Check the SKU with search_products first."} return {"on_hand": row.on_hand, "reserved": row.reserved, "updated_at": row.updated_at}
When the tool count grows past a few dozen, stop binding all of them. Keep the hot set loaded and give the model a search tool over the rest, with an execute tool to run what it finds. Anthropic's API has a native version of this called tool search; LangGraph needs a few lines. Either way the model holds a small working set and nothing is dropped.

Anything irreversible waits for a person

A prompt that says "always confirm before changing a price" is a hope. The control that holds is an interrupt inside the tool, so every write, from every path, pauses for approval. In LangGraph:
from langgraph.types import interrupt @tool def update_price(sku: str, new_price: float) -> str: """Change the selling price of one SKU. Always shows the merchant a preview first.""" current = catalog.price(sku) decision = interrupt({ "action": "update_price", "preview": f"{sku}: {current} -> {new_price}", }) if decision != "approve": return "Price change cancelled by the merchant." catalog.set_price(sku, new_price) return f"Updated {sku} to {new_price}."
The graph stops, saves its state through the checkpointer, and returns the preview to your UI. The merchant taps approve. You resume with a Command carrying the decision, and the tool continues from where it paused. Restart the server in between and nothing is lost, because the state is in Postgres, not in the process. LangGraph's docs cover the interrupt and resume mechanics in detail.
Two rules from experience. The node that interrupted re-runs from its start on resume, so keep the work before the interrupt cheap. And pin the list of tools that are allowed to write, in a test, so a new tool cannot join it quietly.

Plan ahead of the calendar

The most valuable thing a store agent can do is not answer questions. It is to notice that a sale is on the calendar in three weeks and start preparing. The pattern is a scheduled job that reads upcoming events, creates a chain of preparation steps working backwards from the date, and runs each step when it is due: check stock at day minus fourteen, propose pricing at minus ten, draft the campaign at minus seven.
Model this as one graph with a checkpointer, so state carries from one step to the next and a rejected step can change what follows. A pile of independent cron jobs cannot do that. Time-based steps are interrupts resumed by a scheduler. Decision steps are interrupts resumed by a merchant's approval. One resume function serves both. Artifacts a step produces, a stock report, a price list, go to object storage keyed by the chain, not to memory in whichever worker ran the step.

Give agents a way in, and a way out

If other agents need to reach your store, expose it through MCP. A server describes its tools once; Claude, ChatGPT, Cursor and your own agents discover them at connect time. Shopify's Storefront MCP server is a good reference for the shape: search the catalogue, manage a cart, answer policy questions, and route payment through the store's own checkout so the agent never touches card details.
The same protocol works in the other direction. The langchain-mcp-adapters package turns any MCP server's tools into LangChain tools, so a store agent can pull in a shipping carrier's server or a payments server without custom glue.

You will need the trace

Something will go wrong, and the first question will be "what did it do?" Set up tracing before the first real run. LangSmith or Langfuse both capture the tree of model calls and tool calls automatically once the environment variables are set. Then do the three things that make the tree useful: name tool spans after the tool, tag every run with the merchant and thread id, and compute cost per model call so you can answer "what does a conversation cost us" before finance asks.
Record human decisions on the run too. A trace that shows a price change without the approval that authorised it is a liability.

Evaluate on real conversations

Once traces are flowing they are your evaluation set. Pick real runs, label whether the outcome was right, and re-run new versions against them. A prompt change that looks better in one manual test and worse across two hundred real conversations is a change you did not ship. Anthropic's practical advice is to start with five to ten real examples and iterate until the agent is reliable in that narrow domain before widening it.

The short version

Use a workflow where the path is known. Make tools small, named and descriptive. Put an interrupt inside every tool that writes. Plan from the calendar with a graph and a checkpointer. Expose the store through MCP and consume other services the same way. Trace everything, tag it, cost it, and evaluate on the traces. None of this is clever. All of it is what separates an agent you can demo from one a merchant will leave running.