Agents · Mixed
Select an Agent Tool with Jev
Jev tool-selection example inspired by TypeSafe's skill-suggestion cookbook: a Choice over tool names plus a Noul for 'should we call anything?'.
- Published
- Sep 20, 2026
- Updated
- Sep 20, 2026
- Last verified
- Sep 20, 2026
Quick answer
Pick at most one tool from a closed catalog, and ask a Noul whether any tool should run.
Problem
An agent turn can search docs, create a ticket, or do nothing. Letting the LLM emit a free-form tool name hallucinates functions you do not have.
Why Jev fits this task
Official skill-suggestion cookbook ranks a catalog with TypeSafe questions, then decides whether to suggest a skill at all. Choice is relative (which tool). Each Noul is absolute (whether to fire). Those are different questions.
Input state
Send only the fields the questions name. Official docs warn that extra unrelated state costs accuracy.
{
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none"
],
"user_turn": "Where do we document the 14-day refund window for annual plans?"
}Question
Which registered tool, if any, should this turn call?
Question type: Mixed.
Jev schema
{
"model": "jev-latest",
"state": {
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none"
],
"user_turn": "Where do we document the 14-day refund window for annual plans?"
},
"questions": {
"tool": {
"type": "choice",
"instructions": "Which entry in `available_tools` should handle `user_turn`?",
"criteria": {
"search_docs": "The user wants existing documentation or a policy look-up.",
"create_ticket": "The user wants a human ticket created.",
"lookup_order": "The user names an order, invoice, or shipment to fetch.",
"none": "No registered tool fits, or the user is only chatting."
}
},
"needs_tool": {
"type": "noul",
"instructions": "Does `user_turn` require calling a tool before the agent replies?",
"criteria": {
"true": "A look-up or write action is required to answer correctly",
"false": "The agent can reply from the turn text alone"
}
}
}
}Python example
from typesafe_sdk import Choice, Noul, TypeSafeClient
state = {
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none",
],
"user_turn": "Where do we document the 14-day refund window for annual plans?",
}
with TypeSafeClient() as client:
response = client.system_one(
state=state,
questions={
"tool": Choice(
instructions="Which entry in `available_tools` should handle `user_turn`?",
criteria={
"search_docs": "The user wants existing documentation or a policy look-up.",
"create_ticket": "The user wants a human ticket created.",
"lookup_order": "The user names an order, invoice, or shipment to fetch.",
"none": "No registered tool fits, or the user is only chatting.",
},
),
"needs_tool": Noul(
instructions="Does `user_turn` require calling a tool before the agent replies?",
criteria={
"true": "A look-up or write action is required to answer correctly",
"false": "The agent can reply from the turn text alone",
},
),
},
)
print(response.answers["tool"].choice)
print(response.model)TypeScript example
import { choice, noul, TypeSafeClient } from "@typesafe-ai/sdk";
const client = new TypeSafeClient();
const response = await client.systemOne({
state: {
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none"
],
"user_turn": "Where do we document the 14-day refund window for annual plans?"
},
questions: {
tool: choice("Which entry in `available_tools` should handle `user_turn`?", {
search_docs: "The user wants existing documentation or a policy look-up.",
create_ticket: "The user wants a human ticket created.",
lookup_order: "The user names an order, invoice, or shipment to fetch.",
none: "No registered tool fits, or the user is only chatting.",
}),
needs_tool: {
type: "noul",
instructions: "Does `user_turn` require calling a tool before the agent replies?",
criteria: {"true":"A look-up or write action is required to answer correctly","false":"The agent can reply from the turn text alone"},
},
},
});
console.log(response.answers.tool.choice);
console.log(response.model);JavaScript example
import { choice, noul, TypeSafeClient } from "@typesafe-ai/sdk";
const client = new TypeSafeClient();
const response = await client.systemOne({
state: {
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none"
],
"user_turn": "Where do we document the 14-day refund window for annual plans?"
},
questions: {
tool: choice("Which entry in `available_tools` should handle `user_turn`?", {
search_docs: "The user wants existing documentation or a policy look-up.",
create_ticket: "The user wants a human ticket created.",
lookup_order: "The user names an order, invoice, or shipment to fetch.",
none: "No registered tool fits, or the user is only chatting.",
}),
needs_tool: {
type: "noul",
instructions: "Does `user_turn` require calling a tool before the agent replies?",
criteria: {"true":"A look-up or write action is required to answer correctly","false":"The agent can reply from the turn text alone"},
},
},
});
console.log(response.answers.tool.choice);
console.log(response.model);cURL example
curl -s https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<'EOF'
{
"model": "jev-latest",
"state": {
"available_tools": [
"search_docs",
"create_ticket",
"lookup_order",
"none"
],
"user_turn": "Where do we document the 14-day refund window for annual plans?"
},
"questions": {
"tool": {
"type": "choice",
"instructions": "Which entry in `available_tools` should handle `user_turn`?",
"criteria": {
"search_docs": "The user wants existing documentation or a policy look-up.",
"create_ticket": "The user wants a human ticket created.",
"lookup_order": "The user names an order, invoice, or shipment to fetch.",
"none": "No registered tool fits, or the user is only chatting."
}
},
"needs_tool": {
"type": "noul",
"instructions": "Does `user_turn` require calling a tool before the agent replies?",
"criteria": {
"true": "A look-up or write action is required to answer correctly",
"false": "The agent can reply from the turn text alone"
}
}
}
}
EOFExpected output
{
"model": "jev-1.13.0",
"answers": {
"tool": {
"type": "choice",
"choice": "search_docs",
"probabilities": {
"search_docs": 0.9,
"create_ticket": 0.03,
"lookup_order": 0.02,
"none": 0.05
},
"confidence": 0.86
},
"needs_tool": {
"type": "noul",
"noul": 0.84
}
},
"usage": {
"input_tokens": 270,
"output_tokens": 34
}
}Confidence handling
Call a tool only if needs_tool is high and tool.confidence is high and tool.choice !== none. Official notes: do not reuse the Choice confidence as a Noul threshold.
Production considerations
Keep the catalog in code. If the catalog is huge, official skill-suggestion flow fetches full text for the top three and asks again. That second call is required only when the first answer must change the next state.
Coding agents, support agents, and any runtime with a fixed tool registry.
When to use Jev
The tool list is known and the model must not invent a name.
When not to use Jev
You need the agent to write the user-facing answer, or you have one tool and a regex already decides.
Official cookbook for 182 Hermes skills uses a two-request pattern: rank, then re-read the top few. This page is the first request only. Jev will not implement the tool.
Common mistakes
- Omitting none, so every chit-chat becomes create_ticket.
- Asking Jev to generate the search query. Extract or template the query in code; use Jev to pick the tool.
FAQ
Is this the official function-calling API?
No. TypeSafe publishes a function-calling cookbook that maps names and closed arguments to questions. This page is a small independent recipe using that idea.
Sources
- Skill suggestionTypeSafe · accessed 2026-09-20 · documentation
- Primitives (Questions)TypeSafe · accessed 2026-09-20 · documentation
- ConfidenceTypeSafe · accessed 2026-09-20 · documentation