[MUSIC] Welcome to this video on Build Intelligent Agents for Dynamic LLM Tool Use. In this video, you'll explore how to design smart agents that use tools, reason through multi-step tasks, and respond with precision. You'll learn how to define structured tools with typed inputs and outputs, and combine tools with different LLMs. You'll also learn how to debug and fine-tune multi-step agent workflows. In LangChain, an agent combines an LLM and one or more tools, allowing you to build intelligent applications that can reason, act, and interact with real-world data. Agents don't just generate responses. They make decisions, call tools, and guide the flow of logic. This makes them ideal for complex workflows where static prompts alone aren't enough. When building an agent in LangChain, consider three key factors. Not all LLMs support tool use or complex reasoning. Your choice of model directly affects what the agent can do. Newer tools must have JSON-serializable inputs and outputs. Structured tools are preferred, as their schema makes it easier for agents and LLMs to use them correctly. The agent strategy: Some agents are best for simple tasks. Others, like ReAct agents, handle multi-step, tool-driven workflows. LangChain and LLMs evolve quickly, so while documentation helps, effective agent design often requires hands-on experimentation. Let's see how to load and configure an LLM, one of the foundational elements of an agent. First, initialize the LLM with IBM watsonx.ai language model Granite using the langchain_ibm integration, allowing it to be used as an LLM within LangChain workflows. You can ask the LLM a question and get a response. Once your LLM is ready, you can pair it with tools and define the agent's behavior strategy. That's where the real intelligence begins. "initialize_agent" is a convenient starting point for building agents in LangChain. It lets you choose from multiple predefined agent types using a single constructor, making it easy to combine an LLM with tools and get up and running quickly. In general, agents follow a standard reasoning loop based on the prompt. The agent begins by taking the user's query. It reasons about what to do, often deciding which tool to call. It then calls the selected tool. After observing the tool's output, the agent decides what to do next. This step can be complex and often involves feeding the result back into itself for further reasoning. Finally, the agent generates the output, typically based on the tool's result and its internal reasoning trace. The ReAct agent framework essentially works as follows. After receiving the user input, the agent performs a reasoning step, thinking through the problem step-by-step. It then takes action, using tools to gather information or perform operations. It observes and processes the results returned by the tools. It plans the next steps based on those observations, either continuing the loop or generating the final answer. A Zero-Shot ReAct Agent uses zero-shot reasoning, the ability to solve tasks it hasn't seen before by thinking through the problem step-by-step. This approach is especially useful for simple or well-structured tasks. Here, the add_numbers function is used as the first tool for the zero-shot ReAct agent to demonstrate how it processes in response to a simple query. Next, wrap the add_numbers function into a LangChain-compatible tool by casting it to a tool object, making it usable by the agent. You can create a zero-shot ReAct agent using LangChain's initialize_agent function. Start by importing initialize_agent from langchain.agents. Pass in a list of tools (in this case, [add_tool]) and the LLM instance. The parameter agent="zero-shot-react-description" tells LangChain to use the ReAct strategy where the LLM observes the question, reasons about the next step, and selects a tool to call, all without being shown any specific examples, hence zero-shot. Set verbose=True to print the agent's reasoning process step-by-step, and handle_parsing_errors=True to allow the agent to recover if the LLM's tool output is slightly malformed. You can call the agent using the run method by passing in a natural language question. For example: "In 2023, the U.S. GDP was approximately $27.72 trillion, while Canada's was around $2.14 trillion, and Mexico's was about $1.79 trillion. What is the total?" The agent will follow a reasoning and action loop powered by the LLM. The LLM begins by understanding the question and recognizing that it needs to sum the GDP values. It then determines an Action and an Action Input. The action corresponds to a specific tool (in this case, AddTool) and the input includes a list of numbers to be added. The observation represents the output from the tool, the computed sum of the GDPs. Finally, the agent returns the final answer, formatting the result in natural language. In this case, it converts the large number into trillions and replies: "The total GDP of the U.S., Canada, and Mexico in 2023 was approximately $31.55 trillion." The run method doesn't work with some agents, so you use the invoke method instead. It's especially useful for debugging, particularly when working with more complex agents. When selecting an agent in LangChain, the tool format, as well as the tool's input and return types, matters. For example, the zero-shot-react-description agent expects tools to accept and return plain strings. In contrast, the structured-chat-zero-shot-react-description agent supports StructuredTools, which allows for typed inputs and structured outputs (for example, JSON). Here is a demonstration of the add_numbers_with_options tool. This function is used because it's designed to handle multiple inputs, including an optional argument for performing calculations with absolute values. This structured approach ensures flexibility and clarity in how the tool interacts with the LLM. To handle structured inputs, use an initialize_agent, passing in the tool "add_numbers_with_options" and the LLM. Setting agent as "structured-chat-zero-shot-react-description" enables ReAct-style reasoning with support for typed inputs and structured outputs. Call the agent using invoke; the input prompt appears under "input" and the result under "output". This structured format simplifies debugging and tracking in complex workflows. You can try different LLMs with your agent. For example, different tools work better with different agents. Here, sum_numbers_with_complex_output() may cause issues with certain LLM-agent combinations as it returns a dictionary type. To handle this, use an agent that supports structured outputs. First, import and create a gpt-4.1-nano instance. This is capable of handling complex outputs. Then create an openai-functions agent using initialize_agent, passing in the tool (add_numbers_with_options) and the LLM. Setting the agent as openai-functions enables structured tool support. You can call the agent just like before. If you want to use Granite for this use case, you'll have to change the agent to "structured-chat-zero-shot-react-description", because this specific agent can invoke tools that accept multiple inputs. In this video, you learned to Distinguish LLMs from agents by exploring the critical role of tools in enabling real-world interactions. Extend LLM capabilities by integrating tools for data access, precise calculations, and multi-step reasoning. Design effective tool interfaces with clear inputs, outputs, and descriptions to enhance agent responses. Build structured tools for flexible, context-aware interactions supporting complex inputs and robust data handling. [MUSIC]