State¶
The state is what Jev reads: the material you'd hand to an expert before asking them to decide. query and
ask accept it in three forms.
Text¶
// Plain text: a message, a document, a transcript.
val result = jev.ask(Triage, state = "My card was charged twice for order A-104.")
A string suits a single message, document or transcript.
JSON¶
For anything with parts, prefer a JSON object, so each part has a name and the relationships stay clear:
// Named parts keep related context together and let questions point at a specific part.
val state =
buildJsonObject {
putJsonObject("ticket") {
put("subject", "Duplicate charge")
putJsonArray("messages") {
addJsonObject {
put("from", "customer")
put("text", "I was charged twice for order A-104. Please refund the duplicate.")
}
addJsonObject {
put("from", "support")
put("text", "We are checking the charges.")
}
}
}
putJsonObject("order") {
put("id", "A-104")
putJsonArray("charges") {
add("49.00 USD captured")
add("49.00 USD captured")
}
}
put("refund_policy", "Duplicate charges are eligible for a refund.")
}
val result =
jev.query(state = state) {
noul("refund_requested", "Does `ticket.messages[0].text` request a refund?")
noul("policy_supports_refund", "Does `refund_policy` support the refund requested, given `order.charges`?")
}
This is one state, even though it holds a conversation, an order and a policy. Put information together when the decision needs to compare the parts.
@Serializable values¶
Any @Serializable value works as state. Fields that equal their defaults are still sent, so the model sees
them:
@Serializable
data class Order(
val id: String,
val status: String = "open", // fields equal to their default are still sent
val items: List<String> = emptyList(),
)
@Serializable
data class RefundCase(
val message: String,
val order: Order,
val policy: String,
)
suspend fun refundSupported(
jev: JevApi,
case: RefundCase,
): Boolean {
val result =
jev.query(state = case) {
noul("supported", "Does `policy` support the refund requested in `message` for `order`?")
}
return result.noul("supported").isTrue(threshold = 0.8)
}
Order("A-104") is sent as {"id":"A-104","status":"open","items":[]}. A value that isn't @Serializable
is rejected with a JevValidationException that says so.
Pointing questions at fields¶
Name the part of the state a question is about with a backticked dot-and-index path, such as
`ticket.messages[0].text` or `order.charges`, as in the JSON example above. Explicit paths tell the
model which part to judge.
Send only what's needed¶
Unrelated detail acts as a distractor: accuracy falls as the state fills with content the question doesn't need. Filter in code first. Given a ticket type like this:
@Serializable
data class SupportTicket(
val subject: String,
val body: String,
val customerPlan: String = "free",
)
send only the parts the question reads:
// Send only what the questions need. Unrelated detail lowers accuracy.
suspend fun triageFromTicket(
jev: JevApi,
ticket: SupportTicket,
): Team {
val focused = "${ticket.subject}\n\n${ticket.body}" // leave out account data the question doesn't use
return jev.ask(Triage, state = focused)[Triage.team].choice
}
- Text only. Jev reads text: strings, JSON objects, and arrays of text. Convert images, audio and binary data to text or fields first.
- English works best. Other languages, including CJK scripts, work but less well; test on your own data.
- Context limits. A request's state plus all its questions must fit in 64k tokens, and the state plus the longest single question in 32k.