Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Defining Tool Schemas for LLMs

Hello! Welcome to the first lesson in our module on implementing agent capabilities.

In the previous module, we built a conceptual model of an AI agent, covering its core cognitive functions: reasoning (ReAct), planning, and memory. We concluded by exploring how agents use short-term and long-term memory to maintain context and learn over time. But an agent that can only think and remember is incomplete; it needs to act. This lesson begins our journey into how agents interact with the world beyond text generation.

Our learning outcome for this lesson is to: Define tool schemas that describe function signatures for an LLM.

This is the foundational step in giving an agent practical abilities. By the end of this lesson, you will understand:

  • Why LLMs need structured descriptions of external tools.
  • The key components of a tool schema, such as its name, description, and parameters.
  • How to define these schemas using standard formats like JSON and more developer-friendly methods like Pydantic models.
  • Best practices for writing effective schemas that an LLM can reliably understand and use.

Let's begin by understanding why this is a necessary layer between an LLM and your code.

Why Do LLMs Need Tools?

Large Language Models, despite their impressive capabilities, have fundamental limitations. They are trained on a static dataset, meaning they have no access to real-time information, private data, or the ability to execute actions in the outside world. They can't check the current weather, query your company's database, or send an email on your behalf.

The following video explains this limitation and introduces the concept of providing the LLM with information about available tools to help it answer questions it otherwise couldn't.

How LLM Tool Calling Works

The video 'How LLM Tool Calling Works' from the Tommy Eberle channel provides a great demonstration of why an LLM needs external information to answer questions about a user's local system.

Watch the first 2 minutes and 30 seconds of this video. Notice how ChatGPT, by itself, cannot answer a question about local files but can once it's provided with the output of the ls command.

This "phone a friend" capability is what we call tool use or function calling. But for the LLM to know which friend to call and what to ask, we need to provide it with a "contact list"—a clear, structured description of each available tool. This is where tool schemas come in.

The Anatomy of a Tool Schema

A tool schema is a structured, machine-readable description of a function that an LLM can call. Think of it as an API specification, like an OpenAPI (Swagger) document, for your functions. It serves as a formal contract that tells the LLM three crucial things:

  1. What is the tool's name? (e.g., get_current_weather)
  2. What does it do? (e.g., "Gets the current weather for a specific location.")
  3. What information does it need to run? (e.g., a location string and a unit string like "celsius" or "fahrenheit").

The most common format for this is JSON Schema. The OpenAI API provides a clear structure for this. Let's start with a video that walks through a practical example.

LLM Function Calling - AI Tools Deep Dive

The video 'LLM Function Calling - AI Tools Deep Dive' by Adam Lucek provides a clear, code-oriented explanation of how to define a tool schema in JSON format.

Watch the segment from 03:12 to 05:50. Pay close attention to the get_exchange_rate tool example and the three key parts of its schema: name, description, and parameters.

As the video demonstrated, a standard tool schema (specifically, a function tool) includes these key fields:

  • type: This is always "function".
  • name: A unique, descriptive name for the function. This is what the LLM will output when it wants to call this tool.
  • description: This is arguably the most critical part. It's a natural language description telling the LLM what the function does and when it should be used. The model's decision to use a tool is based almost entirely on this description.
  • parameters: A JSON Schema object defining the function's arguments.
    • type: Must be "object".
    • properties: An object where each key is a parameter name (e.g., "location") and its value is another object describing that parameter's type (e.g., "string") and description.
    • required: An array of strings listing which of the properties are mandatory.

Here is a visual example of a schema for a get_weather function, which neatly summarizes these components.

JSON Schema for 'get_weather' Function
This image illustrates a complete JSON schema for a `get_weather` function. It clearly shows the function's name, its purpose via the description, and its expected parameters (`location` and `unit`) with their types.

Now that you understand the structure, let's explore more practical and efficient ways to define these schemas in code.

Methods for Defining Tool Schemas

While you can always write the JSON schema by hand, this can be verbose and error-prone, especially for complex functions. Modern libraries offer more developer-friendly abstractions.

Using Pydantic for Pythonic Schemas

For a software engineer, using data validation libraries to define schemas is a natural fit. In Python, Pydantic is the go-to choice. You can define a BaseModel that represents your function's arguments, using Python's type hints and field descriptions. The LLM framework can then automatically convert this class into the required JSON Schema.

This approach keeps your code clean, organized, and leverages Python's type system for clarity and validation.

LLM Function Calling - AI Tools Deep Dive

Let's return to Adam Lucek's video, where he demonstrates how to use a Pydantic BaseModel to define a complex schema for an 'email campaign' tool.

Watch the segment from 14:45 to 17:56. Note how Python typing (e.g., List[str], Optional[Dict]) and Pydantic's BaseModel simplify the creation of a rich, structured schema without writing raw JSON.

Using Frameworks like LangChain

Frameworks like LangChain are designed to streamline the process of building with LLMs. They provide high-level abstractions for tool creation.

Let's examine the main components and methods for defining tools in LangChain.

LangChain Tools: Complete Guide to Creating and Using Custom ...

The article 'LangChain Tools: Complete Guide' provides a great breakdown of the core components of a LangChain tool and the different ways to create them.

Please read the following sections: 'Key Components of LangChain Tools', 'Decorator vs. BaseTool Subclass Methods', and 'How to Create a Basic Tool'. This will introduce you to the core concepts and two primary methods for defining tools in LangChain.

As the article explains, you have two primary methods:

  1. The @tool Decorator: This is the simplest method. You write a standard Python function with type hints and a detailed docstring. Applying the @tool decorator automatically converts it into a LangChain tool, using the function name, type hints, and docstring to generate the schema.
  2. The BaseTool Subclass: For more complex tools that might require custom initialization (e.g., connecting to a database) or more intricate logic, you can subclass BaseTool. This gives you full control, allowing you to explicitly define the name, description, and args_schema (often using a Pydantic model).

The following video shows these LangChain methods in action.

LLM Function Calling - AI Tools Deep Dive

Finally, this clip from Adam Lucek shows how to implement tools in LangChain using the decorator, Pydantic models, and pre-built integrations.

Watch from 20:44 to 23:03. This provides a practical demonstration of the concepts discussed in the article you just read, showing how easily a Python function can be turned into an LLM-callable tool.

Best Practices for Defining Schemas

How well an agent uses its tools depends heavily on how well you define them. A poorly described tool will either be ignored or used incorrectly.

Here are some essential best practices, drawn from OpenAI's official documentation and community experience.

Function calling | OpenAI API

OpenAI's official documentation provides an excellent, concise list of best practices for defining functions. These principles are universal, regardless of the framework you use.

Read the subsection titled 'Best practices for defining functions'. Pay special attention to the advice on writing clear descriptions and applying software engineering principles.

To summarize and reinforce the most important points:

  • Write Descriptions for the LLM, Not for Humans: The description field is your primary instruction to the model. Be explicit. State what the tool does, when to use it, and what the output represents.
  • Use Clear and Descriptive Names: A tool named calc is ambiguous. add_numbers is much better. Action-oriented names like search_documents or create_user_profile are ideal.
  • Be Specific in Parameter Descriptions: Don't just say a parameter is a "date"; specify the expected format (e.g., "A date in YYYY-MM-DD format"). If a parameter has a limited set of valid values, use an enum.

This example from the LangChain guide perfectly illustrates the difference between a poor and an effective description:




# Poor description
@tool
def calc(x: float, y: float) -> float:
    """Does math stuff"""
    return x + y




# Effective description
@tool
def add_numbers(first_number: float, second_number: float) -> float:
    """
    Add two numbers together and return the sum. 
    Use this tool when you need to perform basic addition of numeric values. 
    Both inputs must be numbers (integers or decimals).
    
    Args:
        first_number: The first number to add.
        second_number: The second number to add.
        
    Returns:
        The sum of the two input numbers.
    """
    return first_number + second_number

The effective description leaves no room for ambiguity, guiding the LLM to use the tool correctly.

Conclusion

In this lesson, we established the critical link between an AI agent's reasoning and its ability to act: the tool schema. This structured description acts as a clear and unambiguous contract, allowing the LLM to understand and request the use of external functions.

Key Takeaways:

  • A tool schema defines a function's name, its description (its purpose), and its parameters (its signature).
  • The description is the most important element for the LLM's decision-making process.
  • You can define schemas manually using JSON Schema, or more efficiently using Pythonic tools like Pydantic models.
  • Frameworks like LangChain provide abstractions like the @tool decorator to simplify schema generation from standard Python functions.
  • Writing clear, descriptive, and unambiguous schemas is a critical engineering practice for building reliable AI agents.

We now know how to describe a tool to an LLM. But what happens next? How does our application receive the LLM's request, run the code, and send the result back?

Our next lesson, "Implement function calling with an LLM API to invoke external tools," will answer exactly that. We will move from defining schemas to implementing the full, multi-step conversation loop that makes tool use a reality.

Can't find a good explanation? Sign up and we'll make it for you

Sign up