How to Ask Helm a Question
Every Helm request has three parts: the state, the questions, and the options. Put each piece of information where it belongs and Helm gives you its best answers. This guide shows where things go, and why it matters.
Helm is a decision model: you give it a situation and a few possible answers, and it returns a probability for each answer instead of writing text. It doesn't need a prompt. It needs a well-formed request, and a well-formed request has exactly three parts:
state: the situation you are deciding about. A support ticket, a chat message, an order record, a document.questions: what you want to know about that situation, one decision per question.options: the possible answers to each question, named by what they mean.
Helm was trained on requests shaped this way. When each piece of information sits in its own field, Helm reads the situation, the question and the answers the way it learned to, and you get its best accuracy. When the pieces are mixed together, it still answers, but less well. The rest of this guide shows how to get the shape right.
A complete request
Here is a request that routes a banking support message:
curl https://api.saina.run/v1/ask \
-H "Authorization: Bearer $SAINA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"model": "saina-helm-2-0.8b",
"state": "I ordered a new card two weeks ago and it still has not arrived.",
"questions": {
"topic": {
"type": "single_choice",
"question": "What is the customer asking about?",
"options": {
"card arrival": null,
"card linking": null,
"exchange rate": null,
"lost or stolen card": null
}
},
"urgent": {"type": "yes_no", "question": "Does the customer need a reply today?"}
}
}'
The customer's message is the state. Each question asks one thing. Each option is a plain name that says what it means. That's the whole pattern.
1. Put the context in state
The state is everything Helm should look at before answering. Put the customer's message, the ticket, or the record there, and nowhere else.
A common mistake is to paste the message into the question and leave the state empty:
{
"state": {},
"questions": {
"topic": {
"type": "single_choice",
"question": "Classify this customer message:\nI ordered a new card two weeks ago and it still has not arrived.",
"options": {"card arrival": null, "card linking": null}
}
}
}
This is valid, and Helm will answer it. But Helm learned to read the situation from the state and the question from the question, so mixing them costs accuracy. In our own testing on a banking intent benchmark, requests phrased this way (message in the question, empty state, opaque option names) scored 3.4 points lower than the same items rewritten into the shape this guide recommends.
The fix is simple: move the message into state, and keep the question to the question.
{
"state": "I ordered a new card two weeks ago and it still has not arrived.",
"questions": {
"topic": {
"type": "single_choice",
"question": "What is the customer asking about?",
"options": {"card arrival": null, "card linking": null}
}
}
}
State can be structured
The state doesn't have to be a single string. If your situation is a record, send it as a JSON object with meaningful field names:
{
"state": {
"channel": "email",
"customer_tier": "business",
"subject": "Card still missing",
"message": "I ordered a new card two weeks ago and it still has not arrived."
}
}
Helm reads objects as JSON, so field names are part of what it sees. customer_tier tells it more than f3. Leave out fields that have nothing to do with the decision: they cost input tokens and add noise.
2. Ask one decision per question
Each question should ask one thing, in plain words, the way you would ask a colleague who has just read the state.
- Good: "Which team should handle this?", "Is the customer asking for a refund?", "How urgent is this?"
- Avoid: "Which team should handle this, and is it urgent?" That is two decisions. Ask them as two questions.
You don't need to restate the context or add instructions like "read carefully" or "answer with one option". The question type already tells Helm how to answer, and the state already holds the context.
Asking several questions about the same state is cheap. Put them all in one request: the state is charged once, however many questions read it, and Helm 2 answers every question in a request from a shared read of the state.
3. Name options by what they mean
Option keys are not just identifiers. Helm reads them. Each option reaches the model as its key, or as key: description when you give a description. So the key should carry the meaning.
| Option | What Helm reads | Verdict |
|---|---|---|
"card arrival": null |
card arrival | Clear |
"billing": "Charges, refunds and invoices" |
billing: Charges, refunds and invoices | Clear, with useful detail |
"option_0": "card_arrival" |
option_0: card_arrival | Works, but the meaning is buried behind an arbitrary label |
"A": null |
A | Meaningless to the model |
A few habits help:
- Use plain words for keys. "card arrival" is better than
opt_7. Your code gets the key back in the answer, so pick keys you are happy to branch on. - Add a description when the name alone is ambiguous. "escalate" could mean many things;
"escalate": "Hand off to a human agent now"does not. - Keep options distinct. If two options would both be right for the same message, Helm will split probability between them. Merge them, or describe the difference.
- Offer a way out when nothing fits. If some messages won't match any option, add one such as
"none of these": "The message doesn't match any listed topic". Otherwise Helm has to pick the closest wrong answer.
4. Pick the right question type
The type tells Helm what shape of answer you want. There are four:
| Type | Use it for | You send | You get back |
|---|---|---|---|
yes_no |
A single fact that is true or false | question (optional descriptions for yes and no) |
yes and no probabilities |
single_choice |
Exactly one answer from a list (2 to 255 options) | question, options |
A probability for every option |
multi_choice |
Any number of answers that can apply at once (2 to 255 options) | question, options |
An independent membership score for every option |
rating |
A position on an ordered scale (2 to 10 levels) | question, levels in order |
A probability for every level and the expected level |
Two choices people often get wrong:
- Tags are
multi_choice, notsingle_choice. If a ticket can be about billing and shipping at the same time, a single choice forces Helm to split its confidence between them. A multi choice scores each tag on its own. - Ordered scales are
rating, notsingle_choice. "Low, medium, high" has an order. A rating question knows that and also returns the expected level.
"severity": {
"type": "rating",
"question": "How severe is the customer's problem?",
"levels": ["cosmetic", "inconvenient", "blocking", "data loss or money lost"]
}
5. Let Helm say when it isn't sure
By default Helm returns its full probability distribution ("mode": "distribution"). For automation you usually want a decision you can act on, plus a safe way out when the model is unsure. That's decision mode:
{
"model": "saina-helm-2-0.8b",
"mode": "decision",
"threshold": 0.8,
"min_margin": 0.2,
"state": "I ordered a new card two weeks ago and it still has not arrived.",
"questions": {
"topic": {
"type": "single_choice",
"question": "What is the customer asking about?",
"options": {"card arrival": null, "card linking": null, "none of these": null}
}
}
}
Every answer then comes with a reason. accepted means the top option cleared both bars and its key is in selection. below_threshold, below_margin and tie mean Helm declined to choose; send those to a person or a fallback path instead of guessing. You can set threshold and min_margin for the whole request or per question.
A checklist before you ship
- The situation is in
state, and the question doesn't repeat it. - Structured data goes in as a JSON object with meaningful field names.
- Each question asks one thing.
- Option keys are plain words that say what they mean; descriptions clarify ambiguous ones.
- There's a "none of these" option wherever nothing might fit.
- Tags use
multi_choice; ordered scales userating. - Anything automated runs in decision mode, with a fallback path for answers that aren't
accepted.
Try it
- Hosted API: sign up, create a key, and send the first request on this page. The console playground builds requests in this shape and turns them into curl, Python, JavaScript or n8n code.
- Full reference: every field, limit and error code is in the API docs.
- Background: what Helm is and how it was built, in Introducing Saina Helm.
Questions about structuring a request for your use case: [email protected].