LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

Three tools and a model that picks

The desk is an agent with three tools that answers two kinds of question: where an order is, and what the shop's policies say.

Last updated: 27 Sep, 2026 · LangChain 1.4

The tests in lesson 32 proved the pieces work. Now assemble the desk itself. Each order carries an owner as well as a status, and Customer is the runtime context: the name comes from your code, never from the model. Each of the three tools is something you wrote earlier.

Reading the runtime context in a tool

python
@tool
def a_tool(order_id: str, runtime: ToolRuntime[Customer]) -> str:
    """One line the model reads to know when to call this."""
    name = runtime.context.name   # who is asking, from your code not the model
    ...

The orders and the customer context

The orders map an id to an owner and a status. Customer is the runtime context, a small object your code fills in.

python
from dataclasses import dataclass

from langchain.tools import ToolRuntime, tool

ORDERS = {"A17": ("ravi", "shipped on 3 March"), "C40": ("mei", "waiting for stock")}


@dataclass
class Customer:
    name: str          # who is asking, set by your code

The order lookup, scoped to the owner

The lookup answers only about the asking customer's orders. A question about someone else's order gets the same reply as one about an order that does not exist, which is the answer a shop should give.

python
@tool
def lookup_order(order_id: str, runtime: ToolRuntime[Customer]) -> str:
    """Look up one of the customer's orders by its id, such as A17."""
    owner, status = ORDERS.get(order_id, (None, None))
    if owner != runtime.context.name:                 # someone else's order
        return f"{order_id} is not one of your orders."
    return f"{order_id} {status}."

The refund tool, scoped too

refund_order checks the owner too, so a customer cannot refund someone else's order even if a reviewer approves the call by mistake. The third tool is search_policies, the one over the vector store, unchanged.

python
@tool
def refund_order(order_id: str, runtime: ToolRuntime[Customer]) -> str:
    """Refund one of the customer's orders in full. This cannot be undone."""
    owner, _ = ORDERS.get(order_id, (None, None))
    if owner != runtime.context.name:
        return f"{order_id} is not one of your orders, so it cannot be refunded."
    return f"Refunded {order_id}."

A model that picks between them

An order id means the order tools; anything else is a question for the policies. Start with the imports and the class header.

python
import re

from langchain.messages import AIMessage
from shop_model import ShopModel


class DeskModel(ShopModel):
    def decide(self, messages):
        last = messages[-1]

When a tool has answered, its text is the reply. An order id or a tool result goes to ShopModel, which picks the order tool; anything else searches the policies.

python
        if last.type == "tool" and last.text == "No policy covers this.":
            return AIMessage("Our policies do not cover that. A person will reply.")
        if last.type == "tool" or re.findall(r"\b[A-Z]\d+\b", last.text):
            return super().decide(messages)        # order id or tool result
        query = {"name": "search_policies", "args": {"query": last.text}, "id": "call_p"}
        return AIMessage("", tool_calls=[query])   # otherwise search policies

Build the desk with the three tools and the customer context. context_schema tells the agent what shape Customer has.

python
from langchain.agents import create_agent
from desk_model import DeskModel
from search import search_policies
from tools import Customer, lookup_order, refund_order

desk = create_agent(DeskModel(), tools=[lookup_order, refund_order, search_policies],
                    context_schema=Customer)

Running three questions from Ravi

Three questions from Ravi, each passing his name as the context.

Example
from langchain.agents import create_agent
from desk_model import DeskModel
from search import search_policies
from tools import Customer, lookup_order, refund_order

desk = create_agent(DeskModel(), tools=[lookup_order, refund_order, search_policies],
                    context_schema=Customer)
for text in ["Where is A17?", "Is shipping free?", "Where is C40?"]:
    result = desk.invoke({"messages": [{"role": "user", "content": text}]},
                         context=Customer("ravi"))
    print(text, "->", result["messages"][-1].text)

How the desk answered each question

  • Where is A17? matched an order Ravi owns, so lookup_order answered.
  • Is shipping free? had no order id, so the model searched the policies and answered from the shipping document.
  • Where is C40? is Mei's order, so lookup_order refused it, even for Ravi.
  • No rules yet, and nothing stopping a refund; the next lesson adds both.

The three tools

ToolAnswers aboutChecks the owner
lookup_orderOne order's statusYes
refund_orderRefunding one orderYes
search_policiesRefunds, shipping, accountsNo, policies are public

When a desk mixes lookups and policy

  • A single desk that mixes account-specific lookups with public policy answers.
  • Any tool that must act only on the asking user's own data.
Watch out. The owner check lives inside the tool, not in the prompt. Move it into the model's instructions and a customer can talk the model into looking up someone else's order.
Try it yourself
  • Ask about an order id the shop has never heard of.
  • Take search_policies out of the tool list and ask the shipping question again.
  • Print the whole message list for one question and count the steps.

You understood something today that you didn't yesterday.