Let’s improve the existing example in level 1 to get better results.

You may have noticed our current prompt is kind of vague:

...
Extract this info from the email in JSON format:
{
  "id": string,
  "date": string,
  "product_name": string,
  "cost": float
}

JSON:

What ID are we looking for here? The Order id? The user id? The prompt doesn’t say so the LLM end up choosing the wrong value.

We could change our OrderInfo.id class property to OrderInfo.order_id, but renaming the property may not be what we want. What if we store this data in a database? The DB schema will not like the name change…

The same goes for the date — we may want to get a specific timestamp format like ISO8601, but we dont want to rename our property to OrderInfo.date_iso8601

To solve this we will add @aliases and @descriptions to rename the properties and make it more understandable to the LLM, without having to change our python code.

Prerequisites

  • Level 1 of this tutorial

Adding aliases and descriptions

Lets add more info to our models first:

// Stays the same, since it's an input to the function. No need to describe these fields.
class Email {
  subject string
  body string
}

// This is the function's output, so we want to add descriptions
class OrderInfo {
  id string
  @alias("order_id")
  @description("The id of the order")
  date string
  @description("The date the order was placed in ISO8601 format")
  product_name string
  @description("The name of the first product listed")
  cost float
  @description("The cost, in dollars, without currency symbols")
}

Now that descriptions are more verbose and clear, our prompt will still look like this — no changes were needed:

...
  prompt #"
    Given the email below:

    Email Subject: {#input.subject}
    Email Body: {#input.body}

    Extract this info from the email in JSON format:
    {#print_type(output)}

    JSON:
  "#

print_type(output) will do the heavy lifting and render JSON with the right descriptions for you. This is what you’ll see in our VSCode Playground.

Given the email below:

Email Subject: {arg.subject}
Email Body: {arg.body}

Extract this info from the email in JSON format:
{
  // The id of the order
  "order_id": string,
  // The date the order was placed in ISO8601 format
  "date": string,
  // The name of the first product listed
  "product_name": string,
  // The cost, in dollars, without currency symbols
  "cost": float
}
JSON:

This is similar to the format OpenAI uses to render schemas, with the description as ”// Description” on top of each field.

The BAML deserializer is smart enough to map each field in your object back to the original property name, so you can still use order_info.id in your python code.

Using Optionals

Some emails may not have all available information. To get around this we can easily modify OrderInfo to use optionals. The @alias and @descriptions have been removed for brevity:

class OrderInfo {
  id string?
  date string?
  product_name string?
  cost float?
}

After the optionals are added, print_type(output) will now append | null to the type:

...
{
  // The id of the order
  "order_id": string | null,
  ...
}

Calling your function

Now that cost might be null, we need to handle that:

from baml_client import baml as b
from baml_client.baml_types import Email
import asyncio

async def main():
  order_info = await b.GetOrderInfo(Email(
      subject="Order #1234",
      body="Your order has been shipped. It will arrive on 1st Jan 2022. Product: iPhone 13. Cost: $999.99"
  ))

  if order_info.cost is None:
      print("No cost found")
  elif order_info.cost > 1000:
      print("You spent a lot of money!")
  elif order_info.cost > 500:
      print("You spent a chunk of money!")
  else:
      print("You spent a little money!")

if __name__ == "__main__":
  asyncio.run(main())

Full code

Check it out!

Conclusion

In this tutorial we learned:

  1. How to use @alias and @description to modify what print_type renders
  2. How to use optionals to handle missing data

We recommend learning more about how to dynamically serialize the input using computed properties. Specifically for serializing a string[] into a prompt.

Want more resiliency? Take a look at adding retry policies and fallbacks to your BAML LLM Client.

Next steps

In the next tutorials we will learn how to use more complex types like enums, lists, unions.