AI & Agents · T100

What does LangChain actually do?

LangChain is a framework for apps using language models. Its agent loop lets a model request a tool, runs your function, returns the result to the model and continues until a final reply or a stopping rule.

The important bit
Tested langchain 1.5.11 with predetermined model messages, not live predictions. Schema validation is not authorization. Keep credentials and ownership checks on the server, handle errors, and set explicit limits. A model-call limit is not a money or time budget.

Understand it. Then fix it.

The model requests; your app executes

The user asks “Where is order 42?” The model returns a tool call named order_status with orderId "42". LangChain invokes your registered function and sends its ToolMessage back to the model. In our fixture, the next model response says the order is in the kitchen. The model itself does not execute your server function.

Define the tool contract

tool comes from langchain; z comes from zod, a validation library. The description tells the model the job, while the schema checks arguments. readMyOrder is our server function; it must check the signed-in user before returning data. It is not a built-in LangChain method.

import { createAgent, tool, modelCallLimitMiddleware } from "langchain";
import { z } from "zod";

const orderStatus = tool(
  ({ orderId }) => readMyOrder(orderId),
  {
    name: "order_status",
    description: "Read my order status",
    schema: z.object({ orderId: z.string() }),
  }
);

Keep data access in your server function

Synthetic demo only: in an application, obtain the user identity from a trusted server session, never from a tool argument chosen by the model. This function handles missing and unowned orders identically. A string can pass Zod validation and still be unauthorized.

const demoOrders = new Map([
  ["42", { owner: "demo-user", status: "In the kitchen" }],
  ["43", { owner: "another-user", status: "Out for delivery" }],
]);
function readMyOrder(orderId, signedInUser = "demo-user") {
  const order = demoOrders.get(orderId);
  if (!order || order.owner !== signedInUser) {
    throw new Error("Order unavailable");
  }
  return order.status;
}

Connect the model and bound the loop

model is an initialized, tool-capable chat model from your chosen LangChain provider integration, configured on the server. Our verified example injects a test model and uses no external inference. A normal final answer has no tool calls. The middleware below allows at most three model calls in this run, then throws; catch errors at the application boundary. It does not set a currency budget, timeout or per-tool limit.

const agent = createAgent({
  model,
  tools: [orderStatus],
  middleware: [
    modelCallLimitMiddleware({ runLimit: 3, exitBehavior: "error" }),
  ],
});
const result = await agent.invoke({
  messages: [{ role: "user", content: "Where is order 42?" }],
});
const answer = result.messages.at(-1).content;

Choose the smallest useful flow

For one model response without tool selection, invoke the chat model directly. This still uses a LangChain model wrapper, but no createAgent loop. A provider SDK is another direct-call option. If the application already knows exactly which function to call, call that function directly. Use the agent when the model needs to choose tools and use their results. LangChain agents use LangGraph underneath; custom graphs are a separate topic.

// Chat-model invocation without an agent loop.
const reply = await model.invoke("Say hello.");

Code blocks are teaching excerpts. Keep the surrounding error handling and application requirements.

Save the code excerpts ↓
Read the full transcript

LangChain is a framework for apps that use language models. How does it check my pizza order? The model asks for a tool. A tool is a function your app exposes. LangChain runs it and sends the result back to the model. Here, order status is our tool. Its description tells the model what it does. Zod, a validation library, checks that the order number is a string. Our server function checks ownership and reads the status. Create Agent connects a model that supports tools to our function. Invoke starts the run with the user message. In this test, the model requests order status for forty-two. LangChain runs our function. It returns, In the kitchen. The model gets that result and writes the reply. These model messages are test fixtures, not live predictions. What stops it from checking my pizza forever? A reply with no tool calls ends the normal loop. Add this middleware, a rule around the run, to allow at most three model calls. Our test blocks the fourth. This is not a money limit. For a single reply, call the model directly. Use an agent when the model needs to choose tools and read their results. Tool access still needs your permission checks. Two model calls. One tool. My pizza is still in the kitchen. We automated the waiting.

Go to the source