So far you have been using function calling to well call functions, but function calling can also be used to extract structured data from a natural language input. Let's dive into structured extraction. In the previous lessons, we used function calling as a way of interfacing with tools both internal and external. In this lesson, we'll actually extend the ability of function calling even more to do something called structured extraction. Structured extraction is when we need to extract details and insights from unstructured texts. For example, if we have texts like "Mary had a little lamb whose fleece was white as snow" and we wanted to attract the person mentioned in the text as well as the object that's owned by the person, we can use function calling as a means of extracting that information. The way you would do it is we would simply provide a function with the person name and the owned object as the arguments that the LLM needs to extract from the unstructured text. Remember, this is very similar to the behavior that the function calling LLM used to fill in the arguments from the user query when interfacing with both Python as well as open API tools in previous examples. We will simply use that same behavior but here we will just annotate the kinds of information we want to be extracted and allow the other LLM to extract that information from the unstructured text. In this example, the LLM will extract Mary as the person's name and lamb as the owned object. Let's take a look at making this more concrete through an example. Let's look at a simple example of abstract extraction. You have a very small passage here with various names and addresses associated with each name. Suppose you want to extract the names and map it to the addresses that the names are associated with. You can use function calling to do this. You will simply provide a Raven prompt with the Function annotation, indicating that you want Raven to extract the names as lists of string and the addresses of the names as another list of string. You mentioned to Raven that you wanted to give the names and the associated addresses, and you'll provide the text from earlier. You can now call Raven. Great. You've called Raven with the prompt you had earlier, and you've just printed it in this tool that you've defined here. And the print matches what we expect. We have the names as well as the associated addresses of the name. For more complex ways of doing structured extraction, there is another method you can use to pull out information. You can use data classes to communicate to Raven about the different ways you want to associate the information you want Raven to abstract. Data class is a Python class decorator. It signals the variables or fields that will follow and define it with type annotations. The LLM, which has been trained on Python, will understand this. And this creates a new and convenient way to define parameters. See in this example you have unstructured text yet again. Here, even though you have names and addresses, certain names are associated with multiple addresses, such as John Tech being associated with 555 Silicon Street, 777 Data Drive, and 123 Digital Domain. The previous approach will not work well because we have an unbalanced mapping of names to addresses. In such more complex cases, you can use the data class method. You will simply define a data class called record, with a name as one of the attributes, and a list of strings as the address attributes. Keep in mind the name of the data class isn't too important, but it is how Raven will understand your intentions so it's better to keep meaningful names. You will define a function annotation that dates in a list of the data class as the argument, and you will provide the unbalanced text from the first cell. You'll run this. And the output will have Raven issue a function call, with the argument values being instantiations of the record data class. You'll notice that some of the names are mapped to multiple addresses, such as John Tech being mapped to the three addresses we listed earlier. This is exactly what we wanted, and shows a better way of extracting more complex insights out of your unstructured texts using function calling. Another really powerful implementation of function calling is in generating valid jsons. Sometimes it's very hard to get small LLMs such as 7 or 13 LLMs to generate valid jsons. Function calling can help here. Suppose you want to generate a json that looks like this, with the first level of the json having the city name. The second level of the json having information regarding the country, and the third level of the json having information regarding the continent. You can now define tools with the same level of hierarchy. Here you've defined the first tool which contains the city info, which is simply a city name with the function annotation indicating a location dictionary is required as one of the arguments. You will define another function called construct location dict. The dates in the country information and a continent dictionary as one of the arguments. You will define a third tool that provides the continent dictionary. The dates and two arguments, each as a simple name and the other name. For your LLM to successfully call this function, it will need to first, construct a dictionary for location, which it will do by issuing a nested call to the location dictionary, which in turn, will require a continent dictionary, which it will head by issuing a nested call to the continent dictionary function. This will allow you to convert the nested calls into the same hierarchy that you want in your json. It is at this point where it's beneficial to talk about what the locals does. It simply returns the current variables defined in the current scope back as a dictionary with the variable name and a variable values provided. Because of the way it's used here, it will simply return the argument values back as a dictionary. Let's take a look. It just returns back the two arguments. We passed it as a dictionary, with the argument name and the argument value as being the key value pairs. It is at this point where we can talk about how to use this approach to convert nested calls into valid jsons. Let's give it a try. You will simply provide the three tools you have created earlier in your Raven prompt, and have a placeholder for your question. Here you will define your question, saying you want the city info for London, which is in the United Kingdom, which is in Europe or Afro-Eur-Asia. This contains the necessary information for your json. However, by passing in the tools from earlier, you can now force your LLM to generate the nested hierarchy that you need. You will issue a call to Raven and you will print the json. Great! We've gotten back a json in the exact format that we want. We can now generate the valid json. Great. We now have a valid json out of our LLMs more predictably.