LangChain Mastery

Course Content

LangChain Mastery

7 sections · 109 lessons

How do you implement a LangChain agent with tool prioritization?


Which tools the model can see at each stepforcedhiddenhiddendonehiddenvisibledonevisiblevisiblesearch_help_centreget_accountweb_searchStep 1Later, unverifiedLater, verifiedSet by a wrap_model_call middleware with request.override(tools=..., tool_choice=...).
A prompt can ask for an order; hiding the other tools until the first one has run makes the order impossible to skip.

What you need to know

Soft priority versus hard priority

Soft: tell the model

  • Order stated in the system prompt
  • "Use when / do not use when" in docstrings
  • Cheap, flexible
  • The model can still ignore it

Hard: enforce in code

  • Filter tools per step with middleware
  • Force the first call with tool_choice
  • Remove forbidden tools entirely
  • Guaranteed, but less flexible

Use soft priority for preferences ("usually try X first") and hard priority for rules that must hold ("never search the web before the internal KB", "no refund tool for unverified users").

Soft: prompt and descriptions

Python
SYSTEM = """Answer support questions for our broadband service.Tool order:1. search_help_centre — always try this first.2. get_account — only for questions about this customer's own plan or bills.3. web_search — only if the help centre has no answer, and never for prices."""

Also write each docstring to point at the others: web_search's says "Use only after search_help_centre returned nothing relevant."

Hard: middleware that gates tools

Python
from langchain.agents import create_agentfrom langchain.agents.middleware import wrap_model_call@wrap_model_calldef help_centre_first(request, handler):    used = {m.name for m in request.state["messages"] if m.type == "tool"}    if "search_help_centre" not in used:        # First step: only the help centre is available, and it must be called.        kb = [t for t in request.tools if t.name == "search_help_centre"]        request = request.override(tools=kb, tool_choice="search_help_centre")    elif not request.runtime.context.verified:        request = request.override(            tools=[t for t in request.tools if t.name != "get_account"])    return handler(request)agent = create_agent(model, tools=[search_help_centre, get_account, web_search],                     system_prompt=SYSTEM, middleware=[help_centre_first],                     context_schema=SupportContext)

The model cannot choose a tool it cannot see. On the first step it sees only search_help_centre and is forced to call it. After that it sees the other tools, except get_account when the user is not verified. request.runtime.context holds values your backend passes at invoke time, such as context=SupportContext(verified=False).

Other options

  • Route before the agent. A cheap classifier chooses a tool group ("billing" or "technical") and the agent only sees that group.
  • LLMToolSelectorMiddleware with always_include=["search_help_centre"] keeps the priority tool present while a small model picks the rest.
  • ToolCallLimitMiddleware on expensive tools, such as web_search with run_limit=1, makes them a last resort in practice.

A real-life example

A broadband company's help-centre support bot had three tools. The team noticed the bot answering "How do I reset my router?" with steps from a web search — for a different brand of router — about 8% of the time, even though the help centre had the correct article.

A stronger system-prompt rule brought this down to 3%. The help_centre_first middleware brought it to zero, because the web search tool is not visible until the help centre has been searched. Web search calls fell by 70%, and the cost per conversation fell with them. The eval set now includes 40 questions whose expected first tool is search_help_centre.

Follow-up questions to expect

  • "What does tool_choice do?" — It tells the model it must call a tool (a specific one, or any); forcing it on every step would stop the agent from ever answering, so apply it only to the first step.
  • "How do you handle permissions?" — Remove the tool for that user in middleware, and also check permissions inside the tool itself; never rely on a prompt instruction.
  • "How do you test priorities?" — An eval set with the expected first tool or tool sequence, run on every prompt or model change.