instructor_ex

Structured, Ecto outputs with OpenAI (and OSS LLMs)


KinoShell versionHex DocsGitHub starsTwitter Follow

Instructor.ex is a spiritual port of the great Instructor Python Library by @jxnlco. This library brings structured prompting to LLMs. Instead of receiving text as output, Instructor will coax the LLM to output valid JSON that maps directly to the provided Ecto schema. If the LLM fails to do so, or provides values that do not pass your validations, it will provide you utilities to automatically retry with the LLM to correct errors. By default it's designed to be used with the OpenAI API, however it provides an extendable adapter behavior to work with ggerganov/llama.cpp and Bumblebee (Coming Soon!).

At it's simplest, usage is pretty straight forward,

defmodule SpamPredicition do
  use Ecto.Schema
  use Instructor.Validator

  @doc """
  ## Field Descriptions:
  - class: Whether or not the email is spam
  - reason: A short, less than 10 word rationalization for the classification
  - score: A confidence score between 0.0 and 1.0 for the classification
  """
  @primary_key false
  embedded_schema do
    field(:class, Ecto.Enum, values: [:spam, :not_spam])
    field(:reason, :string)
    field(:score, :float)
  end

  @impl true
  def validate_changeset(changeset) do
    changeset
    |> Ecto.Changeset.validate_number(:score,
      greater_than_or_equal_to: 0.0,
      less_than_or_equal_to: 1.0
    )
  end
end

is_spam? = fn text ->
  Instructor.chat_completion(
    model: "gpt-3.5-turbo",
    response_model: SpamPredicition,
    messages: [
      %{
        role: "user",
        content: """
        You purpose is to classify customer support emails as either spam or not.
        This is for a clothing retailer business.
        They sell all types of clothing.

        Classify the following email: #{text}
        """
      }
    ]
  )
end

is_spam?.("Hello I am a Nigerian prince and I would like to send you money")

# => {:ok, %SpamPredicition{class: :spam, reason: "Nigerian prince email scam", score: 0.98}}

Simply create an ecto schema, optionally provide a @doc to the schema definition which we pass down to the LLM, then make a call to Instructor.chat_completion/1 with contect about the task you'd like the LLM to complete.

Installation

def deps do
  [
    {:instructor, "~> 0.0.1"}
  ]
end

TODO