In this lesson, you'll build a simple social media management agent using OpenAI's Structured Output API. You'll also learn the basics of specifying your desired output structure using Pydantic. All right. Let's delve in. Structured output often uses what is called a JSON schema to enforce output structure. JSON schemas or standards that describe the shape of a JSON message. So these schemas provide the type information that is required for structured output. As an example, here's a JSON message with Dolly Parton's name, age, and email address. So we can see here we have keys on the left such as name, age and email followed by their values on the right, Dolly Parton, aged 79, and
[email protected]. Let's take a look at an example of a Json schema that describes a person like Dolly Parton. Here we can see this is also Json and includes all of our keys such as name is an email as well as the types of those keys. Such as title equals name and type equals string. Let's talk a little bit about pydantic. Maintaining JSON schemas by hand is difficult and prone to error. As you saw, they're pretty complicated and for anything that's non-trivial they can be an enormous pain to work with. So, AI engineers often use pydantic to describe model output structure. Pydantic is an open source data validation library where you can flexibly define data structures for use in your application. It's also feature-rich. You should go check out all the things that it supports in the documentation. We won't be able to cover all of the tools that it has available. Using pydantic makes language model outputs programmable. As we'll see in just a second. So here is how you would write a user class inside of pydantic. Inheriting from base model is all you need to make a user a pydantic class. Next, you're going to use type annotations to define the structure that your model should generate. For example, name can be any string like Dolly Parton. Age can be any integer like 79, and email can be either nothing or a string. Note, however, that OpenAI does not allow you to enforce the formatting of email or i.e. always in the form of test, at test.com . Outlines and Instructor, and a few other tools do. So let's build a social media customer support agent. We have a lot of users who like to tweet at us saying at Tech Corp your app is amazing. The new design is perfect. And then we're going to put this inside of our customer support agent, and then we're going to output some structured data telling us what product it's about, what the sentiment is from the user, whether or not we should respond, and a support ticket if anything happens to be broken. All right. So, let's go to the code. We're going to start with a little bit of housekeeping here. So these two lines just filter out some warnings that we may not want to see while we're learning. And here's how you load the OpenAI API key that we're going to use. These three lines go and set up your OpenAI API key that DeepLearning.AI has configured. Here's how you initialize the OpenAI client using the OpenAI key. So let's see an example of how we define structure with pydantic. We'll start by pasting in this user class that we added from the slides. So let's generate a user object using OpenAI. Here, we're going to use the client that we defined. And then we're going to call dot Beta dot chat dot completions dot parse. We're going to select our model, here we're going to use GPT-4o-mini. Then we're going to provide some messages. Here, the first message is the system prompt which says you're a helpful assistant. And then we're going to pass the user prompt that you might type into ChatGPT. And then we're just going to say make up a user. Then in response format we specify the user class we just made. When we run this, it'll take a second. And then OpenAI will provide us an user. Let's look at this user by taking it out of our completion object. Normally, OpenAI returns a list of choices when you request multiple values. But here, we've only requested one completion. Then, we're going to extract the message. And then the parse object inside of dot parsed. Let me run this. We see we got a user named John Doe, age 30, with an email John Doe at example.com. Let's build our social media management agent. Anytime you're working with structured output, you typically define the structure that you want to see before you start working with the rest of your code. In this case, we've added a mention which is a pydantic class that has product and sentiment. And you'll see here that these are literals, which means that the language model has to choose between app, website or not applicable. Same thing with sentiment has to be positive, negative or neutral. Next, the model can choose to respond to the user so it is allowed to choose true or false. whether or not the company needs to respond. And then it has a response which is an optional string. So that can be either a string or nothing. This is the response that we would provide to the user. Lastly, if the model determines that a support ticket needs to be open, the model can write a description for the developers. And this may also be either nothing or string. Now, let's give ourselves some example data. I'm going to call this the mentions list and it includes a few examples. So we have a positive review of our app. They like the design. Next, there's a user letting us know that the website is down and that we need to fix it. And then lastly we have "Hey at Tech Corp you're so evil", which we probably don't want to respond to though. We might wish to generally address concerns about our evilness. So here's how you add an analyze mentioned function. This is how we take the user messages that they sent to us, and then we construct this mention object that we just designed. So here we're going to define a function called analyze mention. We're going to provide the post that the user sent to us. And then an optional keyword argument for the personality, which we'll talk about in just a second. By default, this is friendly. As before we're going to use dot beta, dot chat, dot completions, dot parse. Select our model and provide a few messages. In this case, we've provided the system prompt which says what we expect from the model. We want the model to provide the product mention, the mentioned sentiment, whether or not to respond. We let the model know that it should not respond to inform inflammatory messages or bait, and we ask it for a customized message to send to the user. Lastly, we ask the model to provide an optional support ticket description to upload to our ticket management system. Then, finally we let the model know what its personality is. And then inside of the user prompt we just add the mention. Lastly, we request a mention object. Then we're just going to return the parsed object at the end. So let's go ahead and use this analyze mention function. We're going to use the first mention which if you remember was the positive review about our app. We're going to pass that to the are analyze mention function and put it in processed mention. When we run this we'll see. Hey Techcorp, your app so amazing. And then we see the models provided us with product app. So it's identifying that it's about the app as well as positive sentiment. The model has flagged that there needs to be a response. And then it includes a response that we can choose to send back to the user. We're thrilled to hear that you love the new design. And then lastly, there's no support ticket because there's no obvious issues to address with our software. If you would like to adjust the personality of this model, you can simply change the personality. I've changed the personality here to be rude. In which case the model says thanks for the praise. We're glad you love the new design, but don't get too comfy. We're constantly improving, which could be rude or try playing around with the different personalities. And for those who are curious, the underlying content that was generated by the model is just Json. So when we print these out using pedantic model dump JSON method, we can see what was generated by the language model before it was passed into the pydantic object that we have. In this case, we can see the raw text. Here's an example of something you can play with on your own. In this example, you're going to create a user post which has just a message inside of it. And then you're going to ask a language model to act as if it was a person, and then provide tweets to techcorp. We describe roughly what's going on there. And then we can see the output. Hey, just wanted to shout out to TechCorp for their amazing app. Now, we can provide that output from the user and put it back into our analyze mentioned function. When we see this, we're going to get app positive sentiment. Thanks so much for your kind words. And of course the language model just seems to like saying thanks so much for your kind words. So try adjusting this user post class. In this example, I've added a user post with extras class which is also a pydantic class that includes a couple of extra fields such as user mood which has to be between awful, bad, and evil. Product also has to be app website not applicable as in the mentioned class. And then we can also include a internal monologue which includes a list of strings. Now when we run this, we'll get user post with extras. User mood is awful. Sentiment is negative. Can't believe how buggy the app is, etc. And then the user just says it's very frustrated with the latest version of your app. And then of course we can pass this back into our social media analyzer and see what it says. And as we can see here, the social media analysis bot detects that it's about the app, that the sentiment is negative. Provides an example response that hopefully placate the user. And then adds a support ticket where we note that the user has reported constant crashes with the latest version of the app. So the purpose of structured output is to turn language model responses into programable data. So here's an example of a way that we can program with our language model output. Here, we're going to construct a list to contain all of the rows that we generate. We're going to iterate across dimensions that our users have provided. We're going to analyze those mentioned by creating these mentioned objects. If there's a response that is needed, we can print out that we're responding to that user message using the standard attributes from any Python class, such as processed mention dot sentiment. If there's a support ticket, we can print that out as well. Let's take a look at how we program with our mentions. Here, we're going to iterate through each of the mentions that we have available. We're going to call the LLM to get a mention object that we can program with, right here. Then we're going to print out some information about that process mentioned that's in the helper file. If you want to take a look. We're going to convert our process data to a dictionary using pydantic tools in this case the model dump method. Next, we're going to store this original message in a data frame row. So we'll basically just be adding it to this processed dict object. Then we're going to add that dictionary to our list of rows. And then print out a blank row so that we can read everything that gets printed out. So let's run this and see what the result looks like. As we go through, we can see the model responding to positive.= app feedback. The negative website feedback. It will describe its responses and add support tickets if relevant such as right here. The user has reported our website is down. And then of course the user who has specified hey, at Tech Corp, you're so evil, we've chosen not to respond to it. And then lastly, the advantage of working with structured output is that you know, that it follows an exact format, which means that you can put it into various other data formats such as a data frame. So in here we're going to import pandas and construct a data frame using the dictionaries. Here, we can see that it's extremely simple. We have the product, the website the sentiment, whether it needs a response, the original mention, and whatever other features we might wish to add in the future. In this example, you've learned how to define a model's output structure using pydantic how to use OpenAI's structured output API and basic program with language model outputs. Next, you're going to use Instructor to get structured output from various other inference providers.