For an LLM agent to reliably use a tool, it needs a clear understanding of what information the tool expects and what information it will return. This is where input and output schemas come into play. Think of schemas as the formal contract between your LLM agent and its tools. A well-defined schema removes ambiguity, reduces errors, and makes your agent more dependable. Here are the best practices for defining these schemas, ensuring your LLM can interact with its tools effectively.As we've discussed the importance of clear tool descriptions for LLM comprehension, schemas are the structural backbone that underpins these descriptions. They provide the precise format for data exchange.Defining Input SchemasThe input schema dictates the structure and type of data your tool requires to perform its function. A clearly defined input schema allows the LLM to correctly format its requests, significantly increasing the chances of successful tool execution.Elements of an Input SchemaData Types: Be explicit about the data type for each input parameter. Common types include:string: For textual data.number: For numerical data (integers or floats).integer: For whole numbers.boolean: For true/false values.array: For lists of items (specify the type of items in the array).object: For structured data with key-value pairs.Specifying "number" for a port instead of a generic "string" helps the LLM provide valid input and allows your tool to perform type checking more easily.Required vs. Optional Parameters: Clearly distinguish which parameters are mandatory for the tool to function and which are optional. This helps the LLM make informed decisions about what information it absolutely must gather or generate.Parameter Descriptions: Just as the tool itself needs a good description, each parameter within the input schema should also have a clear, concise description. This description guides the LLM in understanding the purpose of each parameter and what kind of value is expected. For example, for a parameter named user_id, a description like "The unique identifier for the user" is much more helpful than no description at all.Enumerations (Enums): If a parameter can only accept a limited set of predefined values, use an enumeration. For example, a unit parameter for a weather tool might accept "celsius" or "fahrenheit". Defining this as an enum prevents the LLM from attempting to use unsupported units like "kelvin".Nested Structures: For tools that require complex input, you can use nested objects or arrays of objects. For example, an order processing tool might expect an array of line_item objects, where each line_item has product_id and quantity.Using JSON Schema for Input DefinitionsJSON Schema is a widely adopted standard for describing the structure of JSON data. It's an excellent choice for defining your tool's input schemas because it's expressive, machine-readable, and human-readable. Many LLM frameworks and agent systems are designed to work with tool definitions that use JSON Schema for specifying parameters.Here’s an example of an input schema for a simple "get_weather" tool using JSON Schema:{ "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g., San Francisco, CA" }, "unit": { "type": "string", "description": "The temperature unit", "enum": ["celsius", "fahrenheit"], "default": "celsius" } }, "required": ["location"] }In this schema:The overall input is an object.It has two properties: location (a string) and unit (a string).location is a required parameter.unit is optional, has a default value of "celsius", and can only be one of the two specified enum values.Each property has a description to help the LLM understand its purpose.Defining Output SchemasJust as input schemas define what a tool expects, output schemas define the structure and type of data the tool will return upon successful execution. This predictability is essential for the LLM to effectively parse the tool's response and use it for subsequent reasoning or to formulate an answer.Elements of an Output SchemaPredictable Structure: The LLM needs to know what fields to expect in the output and their data types. An output schema provides this roadmap.Consistency: The tool must consistently return data that adheres to its defined output schema. If a field is defined as a number, it should always return a number, not sometimes a string representing a number.Sufficient Detail: The output should provide enough information for the LLM to achieve its goal but avoid overwhelming it with unnecessary data. The schema helps define this balance.Error Information: While detailed error handling is covered in its own section, your output schema design should also consider how errors are communicated. A common practice is to have a consistent way to indicate success or failure, and if failure, a structured error message. For example, the primary output fields might be present on success, or an error object might be present on failure.Using JSON Schema for Output DefinitionsJSON Schema is equally effective for defining output structures. It allows the LLM to anticipate the format of the data it will receive.Continuing with our "get_weather" tool, here's an example of its output schema:{ "type": "object", "properties": { "temperature": { "type": "number", "description": "The current temperature in the specified unit." }, "condition": { "type": "string", "description": "A brief description of the weather conditions (e.g., Sunny, Cloudy, Rainy)." }, "humidity_percent": { "type": "number", "description": "The current humidity as a percentage." }, "requested_location": { "type": "string", "description": "The location for which the weather was fetched." } }, "required": ["temperature", "condition", "humidity_percent", "requested_location"] }This schema tells the LLM to expect an object containing temperature, condition, humidity_percent, and requested_location, along with their types and descriptions. Knowing this structure, the LLM can reliably extract the needed pieces of information.The following diagram illustrates how input and output schemas facilitate the interaction between an LLM agent and a tool:digraph G { rankdir=TB; node [shape=box, style="rounded,filled", fillcolor="#e9ecef", fontname="Arial"]; edge [fontname="Arial", fontsize=10]; LLM [label="LLM Agent", fillcolor="#a5d8ff"]; Tool [label="Custom Tool", fillcolor="#96f2d7"]; InputSchema [label="Input Schema\n(e.g., JSON Schema)", shape=note, fillcolor="#ffec99"]; OutputSchema [label="Output Schema\n(e.g., JSON Schema)", shape=note, fillcolor="#ffec99"]; InputData [label="Formatted Input Data", shape=parallelogram, fillcolor="#ced4da"]; OutputData [label="Structured Output Data", shape=parallelogram, fillcolor="#ced4da"]; LLM -> InputSchema [label=" understands"]; InputSchema -> LLM [label=" guides input\n construction"]; LLM -> InputData [label=" prepares"]; InputData -> Tool [label=" feeds into"]; Tool -> OutputData [label=" produces"]; OutputData -> OutputSchema [label=" conforms to"]; OutputSchema -> LLM [label=" guides interpretation"]; OutputData -> LLM [label=" returns to"]; }Schemas act as contracts ensuring the LLM and the tool understand each other's data expectations.General Best Practices for SchemasWhether defining input or output schemas, several best practices will help you create effective and maintainable contracts for your tools:Be Explicit and Unambiguous: Avoid vague definitions. The LLM relies on the schema's precision. For example, if a field represents a date, specify if it's a date string (and ideally, the format, e.g., ISO 8601) or a Unix timestamp.Use Specific Data Types: Prefer integer or number over string if the data is inherently numeric. Use boolean for true/false values. This allows for better validation and clearer intent.Descriptive Naming and Descriptions: Choose clear, self-explanatory names for your parameters and fields. Supplement these with detailed descriptions aimed at the LLM. These descriptions are often more important than comments in code because the LLM directly uses them for reasoning.Strive for Simplicity: Design schemas that are as simple as possible while still conveying the necessary information. Avoid overly nested or complex structures if a flatter, simpler design achieves the same purpose. Complexity increases the chance of misinterpretation by the LLM or errors in tool implementation.Consider the LLM's Perspective: Always design your schemas with the LLM as the primary consumer. How can you make it easiest for the LLM to understand what to provide and what to expect?Iterate and Test: Tool and schema design is often an iterative process. Test your tools with an LLM, observe how it interprets the schemas and uses the tool, and refine your schemas based on these observations.Include Units or Context where Necessary: If a numerical value has a unit (e.g., meters, seconds, Celsius), either make the unit part of the parameter (e.g., duration_seconds) or provide a separate parameter for the unit, as shown in the weather example. This prevents misinterpretation.Plan for Schema Evolution: As your tools evolve, their schemas might need to change. Consider how you will manage versions of your schemas to maintain compatibility with agents that might be using older definitions. (We'll cover versioning in more detail in Chapter 6).By adhering to these practices, you create a foundation for communication between your LLM agent and its tools. Well-defined input and output schemas are not just a formality; they are a key component in building reliable, predictable, and intelligent agent systems. They turn tools from black boxes into clearly defined components that an LLM can understand and utilize with confidence.