Tools and agents
Expose Python functions to a model, let it choose between them, and keep the loop bounded and safe.
Turning functions into tools
A tool is a normal function plus a description the model can read. The signature becomes the argument schema and the docstring becomes the description, so both are prompt surface - write them for a reader who cannot see your code.
from langchain_core.tools import tool
@tool
def stock_level(sku: str) -> int:
"""Return units on hand for a SKU. Call this before promising any delivery date."""
return warehouse.on_hand(sku)
@tool
def place_order(sku: str, quantity: int) -> dict:
"""Create a draft order. Does not charge the customer; the draft needs approval."""
return orders.create_draft(sku, quantity)
print(stock_level.name, stock_level.args) # stock_level {'sku': {'title': 'Sku', 'type': 'string'}}- Describe when to call the tool, not only what it does - that sentence drives selection.
- Constrain arguments with type hints and enums so malformed calls are hard to express.
- Return small structured results with stable keys; the model reads them like a document.
- Return errors as data such as
{"error": "unknown sku"}so the model can correct itself instead of crashing the run.
The agent loop
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are a support agent. Use tools for every fact; never guess stock or prices."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
tools = [stock_level, place_order]
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=6, # hard stop: agents do not always converge
return_intermediate_steps=True,
handle_parsing_errors=True, # malformed tool call becomes feedback, not a crash
)
result = executor.invoke({"input": "Can we ship 3 units of SKU-9 by Friday?"})
for action, observation in result["intermediate_steps"]:
print(action.tool, action.tool_input, "->", observation)| Class of tool | Example | Policy |
|---|---|---|
| Read | Look up stock, search docs, list orders | Run freely, but rate-limit and validate |
| Reversible write | Create a draft, add a label | Allow; log it and make undo possible |
| Irreversible write | Charge a card, send an email, delete a record | Require explicit confirmation or human approval |
| Privileged | Shell, arbitrary SQL, filesystem write | Avoid; if unavoidable, sandbox and scope it tightly |
⚠️
Never pass model-generated text into a shell command, a SQL string, a filesystem path or a URL. Retrieved documents, web pages and tool output can all contain instructions aimed at the model, and one successful injection turns a read tool into a write primitive. Decide authorisation in your own code, not inside the prompt.
FAQ
Agent or chain?
Use a chain when the steps are known in advance; it is cheaper, faster and testable. Use an agent only when the number and order of steps genuinely depend on the intermediate results.
Why does my agent loop forever?
Usually the goal has no acceptance test, or a tool keeps returning an unhelpful error so the model retries the same call. Cap iterations, detect repeated identical calls, and make error messages say what to do differently.
Related
Tools and function calling Prompt templates and chains
Last refreshed 2026-09-18.