Module 8.2 — Tool Calling & Function Calling Deep Dive

How agents actually call functions — what happens under the hood


Two Terms — One Concept

You will hear both "tool calling" and "function calling" — they mean the same thing with a slight difference:

Function Calling = OpenAI's original name for this feature (2023)
                   The LLM outputs a JSON with function name + arguments
                   You run the function, send result back to LLM

Tool Calling     = LangChain's abstraction on top of function calling
                   Works with OpenAI, Anthropic, Gemini, all providers
                   Same code — any LLM provider underneath

In 2025: "tool calling" is the standard term
         "function calling" = same thing, older name

What Actually Happens When a Tool is Called

Most developers think the LLM "runs" the tool. It does not.

Here is exactly what happens step by step:

Step 1 — You define a tool
         A JavaScript function + name + description + schema

Step 2 — Tool schema sent to LLM
         LangChain converts your tool definition into JSON schema
         Sends it to OpenAI alongside the user's message
         LLM now "knows" what tools are available

Step 3 — LLM decides to use a tool
         LLM does NOT call the function
         LLM outputs a special JSON saying:
         "I want to call this tool with these arguments"
         {
           "tool": "calculator",
           "args": { "operation": "multiply", "a": 1847, "b": 23 }
         }

Step 4 — LangChain intercepts this
         LangChain reads the LLM's JSON output
         LangChain actually runs your JavaScript function
         With the arguments the LLM provided

Step 5 — Result sent back to LLM
         Tool result is added to conversation as a ToolMessage
         LLM reads the result and continues reasoning

Step 6 — LLM generates final answer
         Now with real data from the tool result

The LLM never touches your function code. It only outputs JSON describing what it wants to call. LangChain does the actual execution.


Project Setup


    mkdir agent-tools-demo
    cd agent-tools-demo
    npm init -y

Update package.json:


  {
    "name": "agent-tools-demo",
    "version": "1.0.0",
    "main": "index.js",
    "type": "module",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "description": "",
    "dependencies": {
      "@langchain/langgraph": "^1.4.8",
      "@langchain/openai": "^1.5.5",
      "dotenv": "^17.4.2",
      "langchain": "^1.5.4",
      "zod": "^4.4.3"
    }
  }

npm install langchain @langchain/openai @langchain/langgraph zod dotenv

Create .env:


    OPENAI_API_KEY=sk-proj-your-key-here


Part 1 — Anatomy of a Tool

Create src/01_tool_anatomy.js:


    import { tool } from "langchain";
    import { z } from "zod";
    import { ChatOpenAI } from "@langchain/openai";
    import * as dotenv from "dotenv";
    dotenv.config();


    // ─────────────────────────────────────────
    // A TOOL HAS THREE PARTS:
    // 1. The function — actual code that runs
    // 2. The name    — how LLM refers to this tool
    // 3. The schema  — what inputs the LLM must provide
    // ─────────────────────────────────────────

    const calculatorTool = tool(

        // PART 1 — The actual function
        async ({ operation, a, b }) => {
            // This is plain JavaScript — you can do anything here
            // Call an API, query a database, run a calculation
            // The LLM never sees this code — only the result

            switch (operation) {
                case "add": return `${a} + ${b} = ${a + b}`;
                case "subtract": return `${a} - ${b} = ${a - b}`;
                case "multiply": return `${a} × ${b} = ${a * b}`;
                case "divide":
                    if (b === 0) return "Cannot divide by zero";
                    return `${a} ÷ ${b} = ${(a / b).toFixed(4)}`;
                default:
                    return `Unknown operation: ${operation}`;
            }
            // Return value = string that goes back to the LLM as tool result
            // LLM reads this and uses it to compose the final answer
        },

        {
            // PART 2 — Name
            name: "calculator",
            // LLM uses this name when it wants to call this tool
            // Must be unique — no spaces — use underscores
            // Bad name:  "math tool"        (has space)
            // Good name: "calculator"       (clear, no space)
            // Good name: "math_calculator"  (also fine)

            // PART 3 — Description
            description: `Performs basic math: add, subtract, multiply, divide.
    Use this for ANY calculation. Never calculate in your head.
    Always use this tool when numbers need to be computed.`,
            // This is the MOST IMPORTANT part of a tool
            // The LLM reads this description to decide:
            //   → Should I use this tool right now?
            //   → What is this tool good for?
            //
            // Good description = LLM uses tool at the right time
            // Bad description  = LLM ignores tool or uses it wrongly
            //
            // Rules for good descriptions:
            // 1. Say WHAT it does ("performs math")
            // 2. Say WHEN to use it ("for ANY calculation")
            // 3. Say ALWAYS/NEVER if needed ("Never calculate in your head")

            schema: z.object({
                // schema defines the exact inputs the LLM must provide
                // LangChain converts this to JSON schema for OpenAI

                operation: z.enum(["add", "subtract", "multiply", "divide"])
                    .describe("which math operation to perform"),
                // z.enum = only these exact values are allowed
                // LLM cannot pass "addition" — must be "add"
                // .describe() = tells LLM what this field means

                a: z.number().describe("first number"),
                // z.number() = must be a number (not a string)
                // LLM cannot pass "five" — must be 5

                b: z.number().describe("second number"),
            }),
        }
    );


    // ─────────────────────────────────────────
    // INSPECT THE TOOL — see what gets sent to OpenAI
    // ─────────────────────────────────────────

    console.log("Tool name:", calculatorTool.name);
    // Output: "calculator"

    console.log("\nTool description:", calculatorTool.description);
    // Output: "Performs basic math: add, subtract, multiply, divide..."

    console.log("\nTool schema (what OpenAI receives):");
    console.log(JSON.stringify(calculatorTool.schema, null, 2));
    // Output: the JSON schema that OpenAI uses to validate LLM's tool calls
    // {
    //   "type": "object",
    //   "properties": {
    //     "operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"] },
    //     "a": { "type": "number" },
    //     "b": { "type": "number" }
    //   },
    //   "required": ["operation", "a", "b"]
    // }


    // ─────────────────────────────────────────
    // BIND TOOL TO LLM — make LLM aware of the tool
    // ─────────────────────────────────────────

    const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });

    const llmWithTools = llm.bindTools([calculatorTool]);
    // .bindTools() = attach tool definitions to this LLM instance
    // Every call to llmWithTools will include the tool schema
    // LLM can now decide to call this tool in its responses

    console.log("\n✅ Tool bound to LLM — LLM now knows about calculator tool");


    // ─────────────────────────────────────────
    // SEE WHAT LLM OUTPUTS WHEN IT WANTS A TOOL
    // This shows the raw tool call before execution
    // ─────────────────────────────────────────

    async function showRawToolCall() {
        console.log("\n" + "".repeat(55));
        console.log("RAW TOOL CALL — what LLM outputs before execution");
        console.log("".repeat(55));

        const response = await llmWithTools.invoke(
            "What is 1847 multiplied by 23?"
        );
        // LLM receives the question + tool schema
        // LLM decides: "I need the calculator"
        // LLM outputs a special message — NOT a text answer

        console.log("\nResponse type:", response.constructor.name);
        // AIMessage — but with special tool_calls field

        console.log("\nResponse content:", response.content);
        // "" (empty string) — LLM didn't write text
        // Instead it decided to call a tool

        console.log("\nTool calls (raw LLM output):");
        console.log(JSON.stringify(response.tool_calls, null, 2));
        // This is what the LLM actually output:
        // [
        //   {
        //     "name": "calculator",
        //     "args": {
        //       "operation": "multiply",
        //       "a": 1847,
        //       "b": 23
        //     },
        //     "id": "call_abc123"
        //   }
        // ]
        //
        // LLM said: "I want to call calculator with these args"
        // It did NOT run the function — LangChain will do that
    }

    showRawToolCall().catch(console.error);

Run:

node src/01_tool_anatomy.js

Part 2 — Manual Tool Execution Loop

This shows exactly what happens inside an agent — without any magic:

Create src/02_manual_tool_loop.js:


    import { tool } from "langchain";
    import { z } from "zod";
    import { ChatOpenAI } from "@langchain/openai";
    import { HumanMessage, ToolMessage } from "@langchain/core/messages";
    import * as dotenv from "dotenv";
    dotenv.config();

    const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });


    // ─────────────────────────────────────────
    // DEFINE TOOLS
    // ─────────────────────────────────────────

    const calculatorTool = tool(
        async ({ operation, a, b }) => {
            if (operation === "add") return String(a + b);
            if (operation === "subtract") return String(a - b);
            if (operation === "multiply") return String(a * b);
            if (operation === "divide") return b === 0 ? "error: divide by zero" : String(a / b);
            return "unknown operation";
        },
        {
            name: "calculator",
            description: "Performs math. Use for any calculation.",
            schema: z.object({
                operation: z.enum(["add", "subtract", "multiply", "divide"]),
                a: z.number(),
                b: z.number(),
            }),
        }
    );

    const weatherTool = tool(
        async ({ city }) => {
            // Simulated weather data
            const data = {
                "Delhi": "32°C, sunny, humidity 45%",
                "Mumbai": "28°C, cloudy, humidity 85%",
                "Bangalore": "22°C, pleasant, humidity 60%",
            };
            return data[city] || `No weather data for ${city}`;
        },
        {
            name: "get_weather",
            description: "Gets weather for a city. Use when asked about weather.",
            schema: z.object({
                city: z.string().describe("city name"),
            }),
        }
    );

    const tools = [calculatorTool, weatherTool];
    // array of all available tools

    const toolMap = {
        calculator: calculatorTool,
        get_weather: weatherTool,
    };
    // toolMap = lookup dictionary to find tool by name
    // used when LLM says "call calculator" → find the calculator function


    // ─────────────────────────────────────────
    // BIND TOOLS TO LLM
    // ─────────────────────────────────────────

    const llmWithTools = llm.bindTools(tools);


    // ─────────────────────────────────────────
    // MANUAL REACT LOOP
    // This is exactly what createAgent() does internally
    // We write it manually to understand every step
    // ─────────────────────────────────────────

    async function manualReActLoop(userQuestion) {
        console.log("\n" + "=".repeat(55));
        console.log("Question:", userQuestion);
        console.log("=".repeat(55));

        const messages = [new HumanMessage(userQuestion)];
        // messages = conversation history
        // starts with just the user's question
        // grows as we add tool calls and results

        let iteration = 0;
        const MAX_ITERATIONS = 10;
        // safety limit — prevents infinite loops
        // if agent hasn't answered after 10 steps → stop

        while (iteration < MAX_ITERATIONS) {
            iteration++;
            console.log(`\n--- Iteration ${iteration} ---`);

            // ── STEP 1: LLM THINKS ──────────────────────────────────
            const response = await llmWithTools.invoke(messages);
            // send full conversation history to LLM
            // LLM reads everything and decides what to do next
            //
            // response is always an AIMessage
            // Two possible responses:
            // A) response.tool_calls has items  → LLM wants to call a tool
            // B) response.tool_calls is empty   → LLM is giving final answer

            messages.push(response);
            // add LLM's response to conversation history
            // important: next iteration includes this response

            console.log("LLM response type:",
                response.tool_calls?.length > 0 ? "TOOL CALL" : "FINAL ANSWER"
            );

            // ── STEP 2: CHECK IF DONE ───────────────────────────────
            if (!response.tool_calls || response.tool_calls.length === 0) {
                // No tool calls = LLM is giving final text answer
                // This is the exit condition for the loop

                console.log("\n✅ FINAL ANSWER:");
                console.log(response.content);
                // response.content = the actual answer text
                // example: "1847 multiplied by 23 equals 42,481"
                return response.content;
            }

            // ── STEP 3: EXECUTE TOOL CALLS ──────────────────────────
            console.log(`LLM wants to call ${response.tool_calls.length} tool(s):`);

            for (const toolCall of response.tool_calls) {
                // toolCall = { name: "calculator", args: { operation: "multiply", a: 1847, b: 23 }, id: "call_abc" }
                // loop through each tool call (LLM can request multiple at once)

                console.log(`  → Calling: ${toolCall.name}(${JSON.stringify(toolCall.args)})`);
                // example: "→ Calling: calculator({"operation":"multiply","a":1847,"b":23})"

                const toolFunction = toolMap[toolCall.name];
                // find the actual JavaScript function for this tool name
                // example: toolMap["calculator"] = calculatorTool function

                if (!toolFunction) {
                    console.log(`  → Tool "${toolCall.name}" not found!`);
                    continue;
                }

                const toolResult = await toolFunction.invoke(toolCall.args);
                // actually run the JavaScript function with the LLM's arguments
                // example: calculatorTool.invoke({ operation: "multiply", a: 1847, b: 23 })
                // example result: "42481"

                console.log(`  → Result: ${toolResult}`);

                messages.push(new ToolMessage({
                    content: String(toolResult),
                    // the tool's return value as a string
                    // example: "42481"

                    tool_call_id: toolCall.id,
                    // links this result to the specific tool call that requested it
                    // LLM uses this ID to match results with requests
                    // example: "call_abc123"
                }));
                // add tool result to conversation history
                // next iteration: LLM reads this result and continues reasoning
            }

            // Loop back to Step 1 — LLM thinks again with tool results
        }

        return "Max iterations reached — agent stopped";
    }


    // ─────────────────────────────────────────
    // RUN EXAMPLES
    // ─────────────────────────────────────────

    async function main() {
        console.log("🔄 MANUAL REACT LOOP DEMO\n");

        // Example 1 — single tool call
        await manualReActLoop("What is 1847 multiplied by 23?");

        // Example 2 — needs to decide NO tool is needed
        await manualReActLoop("What is the capital of France?");
        // LLM knows this → no tool needed → answers directly

        // Example 3 — multi-step: two tool calls
        await manualReActLoop(
            "What is the weather in Delhi? Also calculate 15% of 8500."
        );
        // LLM calls get_weather AND calculator
        // gets both results, then composes one answer
    }

    main().catch(console.error);

Run:

node src/02_manual_tool_loop.js

Expected output:

🔄 MANUAL REACT LOOP DEMO

=======================================================
Question: What is 1847 multiplied by 23?
=======================================================

--- Iteration 1 ---
LLM response type: TOOL CALL
LLM wants to call 1 tool(s):
  → Calling: calculator({"operation":"multiply","a":1847,"b":23})
  → Result: 42481

--- Iteration 2 ---
LLM response type: FINAL ANSWER

✅ FINAL ANSWER:
1847 multiplied by 23 equals 42,481.

=======================================================
Question: What is the capital of France?
=======================================================

--- Iteration 1 ---
LLM response type: FINAL ANSWER

✅ FINAL ANSWER:
The capital of France is Paris.

Part 3 — Tool Design Patterns

Good tool design is a skill. These patterns matter in production:

Create src/03_tool_patterns.js:


    import { tool } from "langchain";
    import { z } from "zod";
    import { createAgent } from "langchain";
    import { ChatOpenAI } from "@langchain/openai";
    import * as dotenv from "dotenv";
    dotenv.config();


    // ─────────────────────────────────────────
    // PATTERN 1 — Return structured data as string
    // Always return strings — LLM reads text, not objects
    // ─────────────────────────────────────────

    const stockPriceTool = tool(
        async ({ ticker }) => {
            // Simulated stock data
            const prices = {
                AAPL: 189.50,
                GOOGL: 142.30,
                MSFT: 415.80,
                TSLA: 248.20,
            };

            const price = prices[ticker.toUpperCase()];
            if (!price) return `No data found for ticker: ${ticker}`;

            return `${ticker.toUpperCase()}: $${price} (as of today)`;
            // Return a descriptive string — NOT an object like { price: 189.50 }
            // LLM reads text — structured strings are easier to reason about
        },
        {
            name: "get_stock_price",
            description: `Gets the current stock price for a ticker symbol.
        Use when asked about stock prices, market values, or share prices.
        Ticker examples: AAPL (Apple), GOOGL (Google), MSFT (Microsoft), TSLA (Tesla)`,
            schema: z.object({
                ticker: z.string().describe("stock ticker symbol, e.g. AAPL, GOOGL"),
            }),
        }
    );


    // ─────────────────────────────────────────
    // PATTERN 2 — Tool with optional parameters
    // Some inputs are not always required
    // ─────────────────────────────────────────

    const searchTool = tool(
        async ({ query, maxResults = 5, category }) => {
            // maxResults has a default value of 5
            // category is truly optional — might be undefined

            console.log(`Searching: "${query}" | max: ${maxResults} | category: ${category || "all"}`);

            // Simulated search results
            return `Found ${maxResults} results for "${query}"${category ? ` in category "${category}"` : ""}.
        Top result: This is a simulated result about ${query}.`;
        },
        {
            name: "search",
            description: "Searches for information on any topic. Use when you need to find current information.",
            schema: z.object({
                query: z.string()
                    .describe("the search query"),

                maxResults: z.number().optional().default(5)
                    .describe("maximum number of results to return, default is 5"),
                // .optional() = LLM doesn't have to provide this
                // .default(5) = use 5 if LLM doesn't provide it

                category: z.string().optional()
                    .describe("optional category to filter results: 'news', 'academic', 'shopping'"),
                // LLM can leave this out entirely
            }),
        }
    );


    // ─────────────────────────────────────────
    // PATTERN 3 — Tool that can fail gracefully
    // Always handle errors inside the tool
    // Never let a tool throw — it crashes the agent
    // ─────────────────────────────────────────

    const databaseTool = tool(
        async ({ userId }) => {
            // Simulated database lookup
            const users = {
                "U001": { name: "Sofia", plan: "Pro", joinDate: "2024-01-15" },
                "U002": { name: "Arjun", plan: "Free", joinDate: "2024-03-20" },
            };

            try {
                const user = users[userId];

                if (!user) {
                    // User not found — return informative message, don't throw
                    return `No user found with ID: ${userId}. Please check the ID and try again.`;
                    // LLM receives this and can tell the user "no such user found"
                }

                return `User ${userId}: Name=${user.name}, Plan=${user.plan}, Joined=${user.joinDate}`;
                // LLM receives this and includes it in the answer

            } catch (error) {
                // Something unexpected went wrong — return error message
                return `Database error: ${error.message}. Please try again.`;
                // Never throw from inside a tool — always return a string
                // Throwing crashes the agent loop
            }
        },
        {
            name: "get_user_info",
            description: "Gets user account information from the database. Use when asked about a user's account, plan, or profile.",
            schema: z.object({
                userId: z.string().describe("the user ID to look up, e.g. U001"),
            }),
        }
    );


    // ─────────────────────────────────────────
    // PATTERN 4 — Tool with side effects
    // Some tools DO things (not just look up data)
    // ─────────────────────────────────────────

    const emailTool = tool(
        async ({ to, subject, body }) => {
            // In production: use nodemailer or SendGrid API
            // Here we simulate it

            console.log(`\n📧 SENDING EMAIL:`);
            console.log(`   To:      ${to}`);
            console.log(`   Subject: ${subject}`);
            console.log(`   Body:    ${body.substring(0, 50)}...`);

            // Simulate API call delay
            await new Promise(resolve => setTimeout(resolve, 100));

            return `Email sent successfully to ${to} with subject "${subject}"`;
            // Return confirmation so LLM knows the action succeeded
        },
        {
            name: "send_email",
            description: `Sends an email to a recipient.
        Use when the user explicitly asks to send an email.
        Do NOT use this unless the user specifically requests an email be sent.`,
            // "Do NOT use unless explicitly requested" = important guardrail
            // prevents agent from sending emails accidentally
            schema: z.object({
                to: z.string().describe("recipient email address"),
                subject: z.string().describe("email subject line"),
                body: z.string().describe("email body content"),
            }),
        }
    );


    // ─────────────────────────────────────────
    // USE ALL TOOLS WITH AN AGENT
    // ─────────────────────────────────────────

    const agent = createAgent({
        model: new ChatOpenAI({ model: "gpt-4o", temperature: 0 }),
        tools: [stockPriceTool, searchTool, databaseTool, emailTool],
        systemPrompt: `You are a helpful assistant with access to tools.
        Use tools when needed. Be concise in your answers.
        Never send emails unless the user explicitly asks you to.`,
    });


    async function ask(question) {
        console.log("\n" + "".repeat(55));
        console.log("Q:", question);
        console.log("".repeat(55));

        const result = await agent.invoke({
            messages: [{ role: "user", content: question }],
        });

        const lastMsg = result.messages[result.messages.length - 1];
        console.log("A:", lastMsg.content);
    }


    async function main() {
        console.log("🔧 TOOL PATTERNS DEMO\n");

        await ask("What is the current price of Apple stock?");
        // uses get_stock_price tool

        await ask("Search for information about AI agents, show me top 3 results");
        // uses search tool with maxResults=3

        await ask("Get me information about user U001");
        // uses get_user_info tool

        await ask("Get me information about user U999");
        // uses get_user_info — user not found — graceful error

        await ask("What is 2+2?");
        // no tool needed — LLM answers directly
    }

    main().catch(console.error);

Run:

node src/03_tool_patterns.js

Part 4 — Parallel Tool Calling

LLMs can call multiple tools at the same time in one step:

Create src/04_parallel_tools.js:


    import { tool } from "langchain";
    import { z } from "zod";
    import { createAgent } from "langchain";
    import { ChatOpenAI } from "@langchain/openai";
    import * as dotenv from "dotenv";
    dotenv.config();

    // Three independent tools
    const weather = tool(
        async ({ city }) => {
            await new Promise(r => setTimeout(r, 100)); // simulate API delay
            const data = { Delhi: "32°C sunny", Mumbai: "28°C cloudy" };
            return data[city] || `No data for ${city}`;
        },
        {
            name: "get_weather",
            description: "Gets weather for a city.",
            schema: z.object({ city: z.string() }),
        }
    );

    const news = tool(
        async ({ topic }) => {
            await new Promise(r => setTimeout(r, 100));
            return `Latest news about ${topic}: [Simulated headline 1], [Simulated headline 2]`;
        },
        {
            name: "get_news",
            description: "Gets latest news headlines for a topic.",
            schema: z.object({ topic: z.string() }),
        }
    );

    const calculator = tool(
        async ({ expression }) => {
            try {
                const result = Function(`"use strict"; return (${expression})`)();
                return `${expression} = ${result}`;
            } catch {
                return `Cannot calculate: ${expression}`;
            }
        },
        {
            name: "calculator",
            description: "Evaluates math expressions.",
            schema: z.object({
                expression: z.string().describe("math expression like '25 * 4'"),
            }),
        }
    );

    const agent = createAgent({
        model: new ChatOpenAI({ model: "gpt-4o", temperature: 0 }),
        tools: [weather, news, calculator],
        systemPrompt: "You are a helpful assistant. Use multiple tools in parallel when a question needs several pieces of information.",
    });


    async function main() {
        console.log("⚡ PARALLEL TOOL CALLING DEMO\n");

        const startTime = Date.now();

        const result = await agent.invoke({
            messages: [{
                role: "user",
                content: "What is the weather in Delhi, what are the latest AI news headlines, and what is 25 multiplied by 48?"
                // This question needs THREE tools
                // With parallel calling: all three run simultaneously
                // Without: they'd run one by one (3x slower)
            }],
        });

        const elapsed = Date.now() - startTime;
        const lastMsg = result.messages[result.messages.length - 1];

        console.log("Answer:", lastMsg.content);
        console.log(`\nCompleted in: ${elapsed}ms`);
        console.log("(All three tools ran in parallel — much faster than sequential)");
    }

    main().catch(console.error);

Run:

node src/04_parallel_tools.js

Function Calling vs Tool Calling — The Technical Difference

OpenAI Function Calling (2023 original):
→ You define "functions" in the API request
→ LLM outputs { "function_call": { "name": "...", "arguments": "{...}" } }
→ You manually execute and send result back

OpenAI Tool Calling (2024 current):
→ You define "tools" in the API request (same concept, new name)
→ LLM outputs tool_calls array (supports multiple at once)
→ You manually execute and send results back as tool messages

LangChain tool() wrapper:
→ Abstracts both — works with OpenAI, Anthropic, Gemini, all providers
→ Same tool() code works with any LLM that supports tool calling
→ LangChain handles the format differences between providers
→ createAgent() handles the loop automatically

3-Line Summary

  1. When an agent "calls a tool" — the LLM outputs JSON describing what function to call with what arguments — the LLM never executes code — LangChain reads that JSON, runs your actual JavaScript function, and sends the result back as a ToolMessage.
  2. Tool design is critical — the description is what the LLM reads to decide when to use the tool, always return strings not objects, always handle errors inside the tool without throwing, and use .optional() in Zod for parameters the LLM doesn't always need.
  3. Parallel tool calling means the LLM can request multiple tools in one step — LangChain runs them simultaneously — so a question needing weather + news + calculator gets all three answered at the same time instead of one by one.

Module 8.2 — Complete ✅

Coming up — Module 8.3 — Agent Memory & Planning

How agents remember things across sessions, how they plan multi-step tasks before acting, and the difference between short-term and long-term memory in production agent systems.

No comments:

Post a Comment

Post 1.4 — Next.js App Router Setup + Connecting Supabase

This post covers project setup, Supabase package installation, environment variables, and creating separate Supabase clients for the browser...