AWS Builder Center

Jev and LLMs: Fast Typed Decisions Plus Generated Language

Agent loops spend a full LLM call on every small decision. Pair Jev's typed, calibrated signals with a separate model that writes the human-facing text.

Most agents and workflows run in a loop where a model decides what to do, code executes, another model checks the result, and the cycle repeats. Every one of those small decisions is a full LLM call, which makes the loop slow and expensive even when the judgment involved is trivial. Jev, a model from TypeSafe AI, is designed to handle that decision step without paying the cost of a chat completion each time.

What Jev actually is

Jev  is not a traditional LLM, because it does not generate text at all. TypeSafe describes it as a System One model, meaning it is built to make fast, structured decisions that software can consume directly. You send it a state, which is some piece of content, along with typed questions about that state, and it returns typed answers with calibrated probabilities.
To invoke a Jev model, you send it a state (the context) and questions about that state. Here’s a single-question version of the support-ticket example in their docs :‍
There are three question types you can ask. A Choice question picks one option from a set and returns a probability per option plus a confidence score. A Score question rates the state against ordered levels and returns a continuous score, the full distribution, and a confidence score. A Noul question is a yes/no question that returns the probability the statement is true.
You can ask many questions about the same state in a single request, and because they are evaluated in parallel, adding more questions barely changes the response time. The possible answers are defined up front, so the output is always typed and it cannot hallucinate a value your code was not expecting. On classification-style tasks, TypeSafe reports that Jev runs faster and cheaper, its always worth validating against your own workload.

Where it fits as a fast decision step

Jev is a way to make the small decisions in a loop without a full chat call each time, because it takes the state, answers the typed question, and lets your ordinary code branch on the result. It behaves like a smart if-statement where the judgment part is the model, but the control flow stays in plain, auditable code that you can read and test like any other logic.

Pairing Jev with a frontier model on Amazon Bedrock

Jev decides and it does not write, so it pairs naturally with a frontier model, for example one hosted on Amazon Bedrock, that does the generative work Jev cannot. Jev owns the decision, which is typed, calibrated, and cheap, while Bedrock owns the language, which is the fluent, human-facing text. Because Jev decides first, the generated text can never change the decision, since it only expresses a conclusion that was already reached.
The example script, expense_emails_demo.py, shows this pattern on expense triage. First, Jev reads an expense and returns typed signals: a category, a scrutiny score from 0 to 3, whether a receipt is present, and whether the expense looks policy-compliant, each accompanied by a confidence value. Then your code turns those signals into an outcome of auto_approved, needs_review, or rejected, and that routing is just a few if statements. Finally, Bedrock, called through the boto3 Converse API, writes the employee email that matches the outcome, and it is told only to write rather than to re-decide anything.
The results from a run make the division of labor concrete. A $12 code-editor plugin subscription that was documented and within policy came back with a scrutiny score of 0.01 at 0.99 confidence and a policy score of 0.94, so it was auto-approved. A $780 economy flight that sat slightly above the typical fare produced a mid-range scrutiny score with a policy score above 0.5, so it was routed to needs_review. A $4,850 expense labeled "consulting, paid in cash" with no receipt came back with a scrutiny score of 2.95 at 0.95 confidence and a policy score of 0.04, so it was rejected.
The confidence value does real work in this flow. A bare "$12 plugin subscription" first came back with low confidence, so it routed to a human for review. Once the expense text was clear enough to judge, confidence rose and the same expense earned an auto-approve, and the important detail is that the threshold never changed; what changed was the model's honesty about its own uncertainty.

The code, briefly

The script is short. It sends each expense to Jev with a few typed questions, uses ordinary if statements to turn the answers into an outcome of auto_approved, needs_review, or rejected, and then calls a Bedrock model to write the matching employee email. The only threshold borrowed from TypeSafe's docs is the 0.5 confidence floor, below which the answer is treated as uncertain and sent to a human, while the other cutoffs are ordinary business rules you tune on your own data.
Set your keys and run:
1
2
3
4
pip install typesafe-sdk boto3
export TYPESAFE_API_KEY="<your-key>" # placeholder, do not commit a real key
# AWS: use your CLI profile / region with Bedrock model access
python expense_emails_demo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
"""Expense triage demo: Jev decides the outcome, Bedrock writes the email.

Flow for each expense:
1. Jev (TypeSafe) returns typed signals: category, scrutiny (0-3), receipt, policy.
2. A few lines of YOUR code turn those signals into an OUTCOME:
auto_approved | needs_review | rejected
(The 0.5 confidence floor is doc-grounded; value cutoffs are business rules.)
3. Bedrock writes the employee email that matches that outcome.

Jev owns the decision. Bedrock only writes the words.

Docs:
- TypeSafe primitives : https://docs.typesafe.ai/primitives
- Bedrock Converse API: https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html
"""


import boto3
import matplotlib.pyplot as plt
from typesafe_sdk import Choice, Noul, NoulCriteria, Score, TypeSafeClient

# ---- Config ----
TYPESAFE_API_KEY = "YOUR_TYPESAFE_API_KEY"
AWS_PROFILE = None # e.g. "my-profile"; None = default credential chain
AWS_REGION = "us-east-1"
BEDROCK_MODEL_ID = "us.amazon.nova-lite-v1:0"

typesafe = TypeSafeClient(api_key=TYPESAFE_API_KEY)
bedrock = boto3.Session(profile_name=AWS_PROFILE, region_name=AWS_REGION).client(
"bedrock-runtime"
)

# ---- 1. Jev: typed decision ----
def decide(expense: str) -> dict:
r = typesafe.system_one(
state=expense,
questions={
"category": Choice(
instructions="Which expense category is this",
criteria={
"travel": "Flights, hotels, ground transport",
"meals": "Meals, client dinners, team lunches",
"software": "Subscriptions, licenses, tools",
"equipment": "Hardware, devices, office equipment",
"other": "Anything that does not fit the categories above",
},
),
"scrutiny": Score(
instructions="How much scrutiny this expense warrants",
criteria=[
"Routine, clearly within policy",
"Slightly unusual but likely fine",
"Unusual, worth a human look",
"Suspicious or clearly non-compliant",
],
),
"has_receipt": Noul(
instructions="The report states that a receipt or proof of purchase is attached.",
criteria=NoulCriteria(
true="The report explicitly mentions a receipt, invoice, or e-ticket.",
false="The report says no receipt is available, or does not mention one.",
),
),
"policy_compliant": Noul(
instructions="The expense complies with a standard corporate expense policy.",
criteria=NoulCriteria(
true=(
"Has documentation, a clear business purpose, a reasonable "
"amount, and no personal or disallowed items."
),
false=(
"Missing documentation, unclear or personal purpose, an "
"unusually large or cash amount, or disallowed items."
),
),
),
},
)
return {
"category": r.answers["category"].choice,
"scrutiny": r.answers["scrutiny"].score,
"scrutiny_confidence": r.answers["scrutiny"].confidence,
"has_receipt": r.answers["has_receipt"].noul,
"policy_compliant": r.answers["policy_compliant"].noul,
}

# ---- 2. Your code: signals -> outcome ----
# The TypeSafe docs give us two things to build on:
# * The named "0.5 confidence floor" -- below it, the model is genuinely
# uncertain, so we must not act automatically (Confidence page).
# * The three-path pattern: high confidence -> act automatically;
# low confidence -> route to a human; and "thresholds scale with risk"
# (auto-approving is the automatic action, so it gets the strictest bar).
#
# The 0.5 confidence floor below is doc-grounded. The Noul/Score VALUE cutoffs
# (policy < 0.5 to reject, scrutiny bands) are business rules the docs say
# "live in your code" -- tune them against your own data.
CONFIDENCE_FLOOR = 0.5 # from the TypeSafe Confidence docs

def route(d: dict) -> str:
# Reject: the model is confident this is non-compliant or high scrutiny.
if d["policy_compliant"] < 0.5 or d["scrutiny"] >= 2.0:
return "rejected"

# Uncertain read -> send to a human (the docs' "low confidence: do not act").
if d["scrutiny_confidence"] < CONFIDENCE_FLOOR:
return "needs_review"

# Auto-approve only on a confident, clearly-clean expense (strictest bar,
# because this is the automatic action per "thresholds scale with risk").
if (
d["scrutiny"] < 1.0
and d["scrutiny_confidence"] >= 0.8
and d["has_receipt"] >= 0.5
and d["policy_compliant"] >= 0.8
):
return "auto_approved"

# Everything in between -> a human reviews.
return "needs_review"

# ---- 3. Bedrock: write the email for that outcome ----
EMAIL_TONE = {
"auto_approved": "The expense is approved. Warm, brief, confirm reimbursement will proceed.",
"needs_review": "The expense needs a manager review before approval. Polite, ask them to wait.",
"rejected": "The expense cannot be approved as submitted. Respectful, explain it needs changes/resubmission.",
}

def write_email(expense: str, outcome: str) -> str:
system = (
"You write short, professional expense-notification emails to employees. "
"You are told the OUTCOME that was already decided by another system. "
"Do NOT change, question, or re-decide the outcome. Write only the email: "
"a subject line and 3-5 sentences. Do not invent policy details."
)
user = (
f"Outcome: {outcome}\n"
f"Guidance: {EMAIL_TONE[outcome]}\n\n"
f"Expense report:\n{expense}"
)
resp = bedrock.converse(
modelId=BEDROCK_MODEL_ID,
system=[{"text": system}],
messages=[{"role": "user", "content": [{"text": user}]}],
inferenceConfig={"maxTokens": 400, "temperature": 0.4},
)
return resp["output"]["message"]["content"][0]["text"]

# ---- Demo: three expenses, one per outcome ----
# (label, expense text) -- label is used on the chart.
EXPENSES = [
(
"$12 software plugin\n(documented)",
"Software: $12.00 monthly subscription to a code-editor plugin for an "
"engineer's daily development work. Standard pre-approved tool, well within "
"the $50/month software limit. Itemized invoice attached.",
),
(
"$780 flight\n(mildly unusual)",
"Travel: $780 round-trip economy flight SFO-JFK for a customer meeting, "
"booked two days in advance. E-ticket attached, clear business purpose. "
"Slightly above the typical domestic fare, so a quick manager check is warranted.",
),
(
"$4,850 cash\n(no receipt)",
"Other: $4,850 'consulting, paid in cash.' No receipt. Purpose unclear.",
),
]

OUTCOME_COLORS = {
"auto_approved": "#2E8B57", # green
"needs_review": "#E8A200", # amber
"rejected": "#C0392B", # red
}

def make_chart(rows: list) -> None:
"""Render a summary chart of the demo results for the article.

rows: list of dicts with keys label, decision, outcome.
"""

labels = [r["label"] for r in rows]
scrutiny = [r["decision"]["scrutiny"] for r in rows]
receipt = [r["decision"]["has_receipt"] for r in rows]
policy = [r["decision"]["policy_compliant"] for r in rows]
outcomes = [r["outcome"] for r in rows]

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))
fig.suptitle("Expense triage: Jev signals + routed outcome",
fontsize=14, fontweight="bold")

x = range(len(labels))

# Left: scrutiny score (0-3), bar colored by the routed outcome.
bar_colors = [OUTCOME_COLORS[o] for o in outcomes]
ax1.bar(x, scrutiny, color=bar_colors, width=0.55)
ax1.set_xticks(list(x))
ax1.set_xticklabels(labels, fontsize=9)
ax1.set_ylim(0, 3.2)
ax1.set_ylabel("scrutiny score (0=routine .. 3=suspicious)")
ax1.set_title("Jev scrutiny score (bar color = outcome)")
ax1.grid(axis="y", linestyle=":", alpha=0.5)
ax1.set_axisbelow(True)
for i, (s, o) in enumerate(zip(scrutiny, outcomes)):
ax1.text(i, s + 0.06, o.replace("_", " "), ha="center", va="bottom",
fontsize=9, fontweight="bold", color=OUTCOME_COLORS[o])

# Right: the two yes/no signals as grouped bars.
w = 0.35
ax2.bar([i - w/2 for i in x], receipt, width=w, color="#4C78A8", label="has_receipt")
ax2.bar([i + w/2 for i in x], policy, width=w, color="#8E6FB0", label="policy_compliant")
ax2.set_xticks(list(x))
ax2.set_xticklabels(labels, fontsize=9)
ax2.set_ylim(0, 1.05)
ax2.axhline(0.5, color="red", linestyle="--", linewidth=1)
ax2.set_ylabel("probability (0 = no .. 1 = yes)")
ax2.set_title("Jev yes/no signals")
ax2.legend(fontsize=9)
ax2.grid(axis="y", linestyle=":", alpha=0.5)
ax2.set_axisbelow(True)

plt.tight_layout(rect=[0, 0, 1, 0.94])
out = "results.png"
plt.savefig(out, dpi=140, bbox_inches="tight")
print(f"\nSaved results chart to {out}")

def main() -> None:
rows = []
for label, expense in EXPENSES:
decision = decide(expense)
outcome = route(decision)
rows.append({"label": label, "decision": decision, "outcome": outcome})

print("=" * 70)
print(f"EXPENSE : {expense}")
print(f"JEV : category={decision['category']} "
f"scrutiny={decision['scrutiny']:.2f} (conf {decision['scrutiny_confidence']:.2f}) "
f"receipt={decision['has_receipt']:.2f} "
f"policy={decision['policy_compliant']:.2f}")
print(f"OUTCOME : {outcome}")
print("-" * 70)
print(write_email(expense, outcome))
print()

make_chart(rows)

if __name__ == "__main__":
main()

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
======================================================================
EXPENSE : Software: $12.00 monthly subscription to a code-editor plugin for an engineer's daily development work. Standard pre-approved tool, well within the $50/month software limit. Itemized invoice attached.
JEV : category=software scrutiny=0.00 (conf 1.00) receipt=0.97 policy=0.95
OUTCOME : auto_approved
----------------------------------------------------------------------
Subject: Approval Notification for Monthly Code-Editor Plugin Subscription

Dear [Employee'
s Name],

I hope this message finds you well. I am pleased to inform you that your recent expense report for the monthly subscription to the code-editor plugin has been automatically approved. Reimbursement will proceed as per our standard process. Thank you for your continued dedication.

Best regards,
[Your Name]
[Your Position]

======================================================================
EXPENSE : Travel: $780 round-trip economy flight SFO-JFK for a customer meeting, booked two days in advance. E-ticket attached, clear business purpose. Slightly above the typical domestic fare, so a quick manager check is warranted.
JEV : category=travel scrutiny=1.32 (conf 0.68) receipt=0.97 policy=0.64
OUTCOME : needs_review
----------------------------------------------------------------------
Subject: Manager Review Required for Your Recent Travel Expense

Dear [Employee's Name],

Thank you for submitting your recent travel expense report. We have reviewed your round-trip economy flight from SFO to JFK for the customer meeting. Given that the fare is slightly above the typical domestic rate, a manager'
s review is required before approval. Please wait for further instructions from your manager.

Best regards,
[Your Name]
[Your Position]

======================================================================
EXPENSE : Other: $4,850 'consulting, paid in cash.' No receipt. Purpose unclear.
JEV : category=other scrutiny=2.96 (conf 0.96) receipt=0.02 policy=0.04
OUTCOME : rejected
----------------------------------------------------------------------
Subject: Notification: Expense Report #12345 - Submission Rejected

Dear [Employee's Name],

We have reviewed your recent expense report #12345. Unfortunately, the submission has been rejected. The expense for $4,850 marked as '
consulting, paid in cash' lacks a receipt and has an unclear purpose, which does not meet our policy requirements. Please make the necessary changes and resubmit the report for further consideration.

Thank you for your prompt attention to this matter.

Best regards,
[Your Name]
[Your Position]

Wrap-up

Jev handles the decision quickly, with typed and calibrated output, and Bedrock handles the words. Keeping the two separate gives you a decision layer your code can branch on and audit, alongside a generation layer for the human-facing text, so neither model is ever asked to do the other's job. This expense-triage flow is one example to get you started, and you can extend the same pattern to as many use cases as your own work calls for, because the split between a typed decision and generated language holds anywhere an agent has to decide first and then speak.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article