Picks one value from a fixed set of choices with an LLM.

Gut presents the subject, question, and choices to the configured adapter. It then maps the adapter's answer back to the original Elixir value. It does not return a string copy of that value or create atoms from model output.

Examples

{:ok, true} =
  Gut.feel(
    "Please fix this today.",
    "Does this convey time pressure?",
    [true, false]
  )

Use descriptions when a choice needs more context:

{:ok, :billing} =
  Gut.feel(
    ticket,
    "Which team should handle this?",
    billing: "Payment, invoice, or refund problems",
    technical: "Product defects or access problems",
    other: "Anything else"
  )

Integer ranges are also valid choices:

{:ok, 4} = Gut.feel(messages, "How frustrated is the customer?", 1..5)

A successful result means that the adapter returned a valid choice. It does not mean that the judgment is correct.

Set allow_unsure: true to offer uncertainty as a choice. Gut returns {:ok, :unsure} if the adapter selects it. The adapter may still select a choice when it should be unsure.

Subjects

Gut sends string subjects as plain text. It encodes all other subjects as JSON. Derive Gut.Subject to control which struct fields the adapter receives without changing the struct's general JSON representation:

defmodule Ticket do
  @derive {Gut.Subject, only: [:subject, :body]}
  defstruct [:subject, :body, :internal_notes]
end

This also works with structs defined by Ecto.Schema.

Gut treats the subject as untrusted data when it builds a prompt. This reduces accidental prompt confusion, but it does not prevent prompt injection.

Configuration

Configure an adapter as a module or a {module, options} tuple:

config :gut,
  adapter: {Gut.ReqLLM, model: "anthropic:claude-haiku-4-5"}

Pass :adapter to feel/4 or feel!/4 to replace the configured adapter for one call:

Gut.feel(subject, question, choices,
  adapter: {MyApp.Adapter, model: MyApp.Classifier}
)

The call option takes precedence over application configuration. See Gut.Adapter to implement an adapter.

Errors

Adapter and provider failures return {:error, %Gut.Error{}}. Use the error's reason field for control flow and its cause field for logging. feel!/4 raises the same error instead.

Invalid local input raises ArgumentError before the adapter runs. This includes invalid questions, choices, options, subjects, and adapter configuration.

Summary

Functions

Asks the configured adapter to choose the value that best answers question.

Asks the configured adapter to choose a value and returns it directly.

Functions

feel(subject, question, choices, opts \\ [])

@spec feel(term(), String.t(), list() | Range.t(), keyword()) ::
  {:ok, term()} | {:error, Gut.Error.t()}

Asks the configured adapter to choose the value that best answers question.

subject can be a string or any value that implements Jason.Encoder. Structs can derive or implement Gut.Subject for a Gut-specific representation. question must be a non-empty string.

choices must contain between 1 and 100 choices. It can be:

  • a list of unique values;
  • an integer range; or
  • a keyword list with string descriptions.

Lists and ranges return the selected value. Keyword lists present each value as a description and return the selected key.

Options

  • :adapter - a Gut.Adapter module or a {module, options} tuple. It replaces the adapter from the :gut application configuration for this call.
  • :allow_unsure - adds an unsure choice when true. Gut returns {:ok, :unsure} if the adapter selects it. Defaults to false.

Adapter and provider failures return {:error, %Gut.Error{}}. Invalid local input raises ArgumentError before the adapter runs.

Examples

Gut.feel(review, "What is the sentiment?", [:positive, :neutral, :negative])
#=> {:ok, :positive}

Gut.feel(ticket, "Which team should handle this?",
  billing: "Payment or invoice problems",
  technical: "Product or access problems"
)
#=> {:ok, :technical}

Gut.feel(messages, "How frustrated is the customer?", 1..5)
#=> {:ok, 4}

feel!(subject, question, choices, opts \\ [])

@spec feel!(term(), String.t(), list() | Range.t(), keyword()) :: term()

Asks the configured adapter to choose a value and returns it directly.

This function accepts the same arguments and options as feel/4. It returns the selected value, including :unsure when enabled. It raises Gut.Error for an adapter or provider failure. Invalid local input raises ArgumentError.

Examples

Gut.feel!(ticket, "Which team should handle this?",
  billing: "Payment or invoice problems",
  technical: "Product or access problems"
)
#=> :billing