Asking an AI Model
A program can ask an AI model a question in one line: summarize a message, sort a support request, pull the details out of an email, or rate a review. Quill’s ai module does it with one word, ask.
bring ai
permit net "api.anthropic.com"
permit env "ANTHROPIC_API_KEY"
message = "Hi! We'd like the brass band for our wedding on 12 June, about 150 guests."
summary = ask "Summarize this booking request in one sentence: {message}"
on ModelFailed -> "No summary right now."
say summary
Each line has a job:
bring aibrings Quill’saimodule, so a reader sees at the top that the program asks a model.permit net "api.anthropic.com"lets the program reach the model’s service.permit env "ANTHROPIC_API_KEY"lets it read the key, and says which provider it uses: Claude. The key stays in a setting on your computer, never in the code (see Permits).ask "..."sends the question and gives back the answer as text.
The examples in this chapter need a real model and a key, so the book shows them without running them.
A model can fail
A model lives somewhere else. It can be out of reach, too busy, or it can decline a question. All of these are the failure ModelFailed, and like every failure, the program has to say what happens then: carry on with a value, stop, or pass it on with try.
Answers you can trust: ask … as
Text is fine for a person to read, but a program needs answers it can use. Add as and a kind, and the model must answer in that shape:
bring ai
permit net "api.anthropic.com"
permit env "ANTHROPIC_API_KEY"
type Request
act: text
guests: number where 1..500
date: text
email = "Hi! We'd like the brass band for our wedding on 12 June, about 150 guests."
request = ask "Pull out the act, the number of guests and the date: {email}" as Request
on ModelFailed -> Request act = "unknown", guests = 1, date = "unknown"
on Invalid -> Request act = "check by hand", guests = 1, date = "unknown"
say "{request.act} for {request.guests} guests on {request.date}"
as Request does two things:
- Quill sends the shape of
Requestto the model, so it answers with exactly those fields. - Quill reads the answer the same way
asreads any JSON: every field must be there, the right kind, and inside itswhererange.
A model can still get something wrong, like 900 guests for a hall of 500. Then ask fails with Invalid, and the program never holds a Request that breaks its rules. Any kind works after as: as number, as true/false, as list of text, or a type.
Choosing the provider
The key a program permits chooses who answers:
| Permit | Asks | Settings |
|---|---|---|
permit env "ANTHROPIC_API_KEY" | Claude, at api.anthropic.com | The model is claude-opus-5-5 unless QUILL_AI_MODEL names another. |
permit env "OPENAI_API_KEY" | any service that speaks the OpenAI chat format | QUILL_AI_MODEL names the model. OPENAI_BASE_URL points at the service, like http://localhost:11434/v1 for a model running on your own computer. |
The host still needs its permit net: "api.anthropic.com", "api.openai.com", or "localhost" for a model on your computer.
When Claude’s safeguards decline a question, Quill asks Anthropic’s recommended model to answer instead, so a cautious answer doesn’t stop your program. If no model answers, it is ModelFailed.
Putting a model to work
A model answers well when the question says exactly what you want and gives it the facts. Put the facts in with {}, and keep the kind small:
bring ai
permit net "api.anthropic.com"
permit env "ANTHROPIC_API_KEY"
type Mood
happy: true/false
reason: text
fn mood_of(review: text) -> Mood
fails ModelFailed, Invalid
return try ask "Is this customer happy? Give a short reason. Review: {review}" as Mood
reviews = list "The band was wonderful!", "They arrived two hours late."
var mood = Mood happy = false, reason = "not asked yet"
for review in reviews
set mood = mood_of(review)
on ModelFailed -> Mood happy = false, reason = "no answer"
on Invalid -> Mood happy = false, reason = "unclear"
say "{mood.happy}: {mood.reason}"
try passes a failure up to the function’s caller, so each review decides for itself what an unanswered question means.
Common mistakes
A program has to bring the module:
permit env "ANTHROPIC_API_KEY"
answer = ask "Hello"
on ModelFailed -> "no answer"
Problem on line 3 of example.quill:
answer = ask "Hello"
^^^
ask is part of Quill's ai module, which this program doesn't bring.
Add bring ai at the top, so a reader sees that the program asks a model.
Did you mean: bring ai
It needs a permit for a key, so a reader knows which provider it uses:
bring ai
answer = ask "Hello"
on ModelFailed -> "no answer"
Problem on line 3 of example.quill:
answer = ask "Hello"
^^^
ask needs a key for a model, and the program permits none.
Add permit env "ANTHROPIC_API_KEY" to ask Claude, or permit env "OPENAI_API_KEY" for an OpenAI-compatible service. The key itself stays in the setting, out of the code.
Did you mean: permit env "ANTHROPIC_API_KEY"
And a permit to reach the service:
bring ai
permit env "ANTHROPIC_API_KEY"
answer = ask "Hello"
on ModelFailed -> "no answer"
Problem on line 4 of example.quill:
answer = ask "Hello"
^^^
ask sends the question to Claude at api.anthropic.com, and the program has no permit to reach it.
Add a permit for the host at the top of the program.
Did you mean: permit net "api.anthropic.com"
ask can fail, so the failure has to be handled:
bring ai
permit net "api.anthropic.com"
permit env "ANTHROPIC_API_KEY"
say ask "Hello"
Problem on line 5 of example.quill:
say ask "Hello"
^^^
Asking a model can fail with ModelFailed: the model may be out of reach, or say no.
Handle each with on under this line, or write try in front, inside a function that fails the same way.
Try it
- Set
ANTHROPIC_API_KEYon your computer, and ask a model to write one sentence about Quill. - Write
type Citywithname: textandcountry: text, and ask foras list of Citywith “Name three cities in Cameroon.” - Add a
whererange to a number field, and ask a question that tempts the model to break it. Watchon Invalidrun. - Run the same program against a model on your own computer, with
permit env "OPENAI_API_KEY",OPENAI_BASE_URLandQUILL_AI_MODEL.