TypeSafe AI's Jev model answers only in a shape you define. You send it state (your text) and a set of typed questions, and it returns typed values with calibrated probabilities instead of free text to parse. I wired it into two Logic Apps Standard workflows: a triage demo that turns typed answers into a routing decision, and a router agent that uses the same idea to pick which of two existing agents should handle a task. This post covers both, the real mistakes along the way, and the runs that prove they work.
What Jev answers with
The System One API (POST https://api.typesafe.ai/v1/systemone) takes three question types, confirmed against the docs and a live call:
| Type | You give it | It returns |
|---|---|---|
noul |
a yes/no question, with criteria describing true and false |
a single number from 0 to 1 — the probability of "true" |
choice |
named options with descriptions | the picked option key, plus probabilities and confidence
|
score |
an ordered list of levels | a numeric score, a legend, and probabilities per level |
A real request, testing a billing complaint, returned:
{"answers":{
"urgent":{"type":"noul","noul":0.98},
"category":{"type":"choice","choice":"billing","confidence":1.0,
"probabilities":{"technical":0.0,"billing":1.0,"account":0.0,"general":0.0}},
"sentiment":{"type":"score","score":0.0,"confidence":1.0,
"legend":{"0":"Negative…","1":"Neutral…","2":"Positive…"}}
}}
Every value is one of the three typed shapes above. There's no free text to parse, and nothing it can hallucinate outside the schema.
Part 1: Triage
The project already had a starting point: a single Http action calling Jev with one noul question and returning the raw response. I built it out to ask three questions in one call — urgent (noul), category (choice), and sentiment (score) — then added a Compose_Triage action that turns the typed answers into an actual decision:
"priority": "@if(greater(body('Call_TypeSafeAI')?['answers']?['urgent']?['noul'], 0.5), 'High — needs immediate attention', 'Normal')",
"team": "@if(equals(body('Call_TypeSafeAI')?['answers']?['category']?['choice'], 'billing'), 'Billing', if(...))",
"suggestedReplyOpener": "@if(less(body('Call_TypeSafeAI')?['answers']?['sentiment']?['score'], 0.5), 'I''m sorry to hear about the trouble you''ve had.', if(...))"
score comes back as a float that matches the legend index exactly — 0.0 for Negative, 1.0 for Neutral, 2.0 for Positive in our test calls — which is what makes a plain less(..., 0.5) a safe way to test for "Negative" specifically, rather than needing to round or compare against an integer.
That's the point of asking typed questions instead of a free-text prompt: the workflow can branch on urgent.noul and category.choice directly, with no parsing step in between.
It runs
This is the published triage workflow running in Logic Apps Automation, after both fixes: RcvMsg → Call_TypeSafeAI (809 ms) → Compose_Triage (77 ms) → Response (179 ms), succeeded in 2.4 seconds total. Most of the time is the Jev call itself; the triage logic on top of it costs under 100 ms.
Part 2: A router agent
The obvious next step: use the same typed classification to decide which agent handles a task, instead of one do-everything agent or parsed free text. RouterAgent takes {"task": "..."} and does three things:
-
Classify. Ask Jev a single
choicequestion — is thismathorgeneral? - Switch. Branch on the answer.
-
Dispatch. Call the agent that matches:
BodmasAgentfor math, a newGeneralAgentfor everything else.
"taskType": {
"type": "choice",
"instructions": "What kind of task is this? Pick math only when it needs a precise numeric calculation.",
"criteria": {
"math": "A specific arithmetic expression or calculation to compute — numbers with +, -, *, /, ^, or a word problem that reduces to one",
"general": "Anything else: questions, explanations, writing, conversation, or requests that are not a calculation"
}
}
GeneralAgent is new: a tool-free, single-turn agent that answers directly and returns synchronously, built specifically so the router has a general-purpose target that doesn't need MCP tools or an agent loop.
Call workflow doesn't work in Automation — use Http instead
Logic Apps Standard has a built-in action for calling another workflow in the same app directly (inputs.host.workflow.id), no URL or SAS signature needed. It's the obvious choice for dispatching to BodmasAgent and GeneralAgent, both in the same app as RouterAgent. It didn't work here. Automation's workflows are proper Standard workflows, but they don't carry over every Standard-only capability, and this is one of them. The fix is the same Http pattern this project already uses elsewhere: POST straight to the target workflow's own trigger URL.
It works — both branches
This run took the default (general) branch: ReqTask → Classify_Task (808 ms) → Route_By_Task_Type → Dispatch_To_GeneralAgent (7.9 s) → Response_General (132 ms), succeeded in 10 seconds total. The Case_Math branch — Dispatch_To_BODMASAgent and Response_Math — is greyed out and dashed, because the Switch only runs the branch that matches.
The math branch works too, tested separately:
-
Math:
(12 + 8) * 3 - 5 / 5→ classifiedmath(confidence 1.0) → dispatched toBodmasAgent→"Answer: 59". Correct. -
General:
Why is the sky blue?→ classifiedgeneral(confidence 1.0) → dispatched toGeneralAgent→ a real explanation of Rayleigh scattering.
Conclusion
Jev's typed answers turned out to be useful for more than one thing in Logic Apps: first as a triage step that feeds straight into @if branches with no parsing in between, then as the classifier behind a router that picks which existing agent handles a task. Both ran as real workflows against the real API, not as a sketch — the triage run and the router run shown above are both actual, successful executions in Logic Apps Automation.
The one platform gap worth remembering: the built-in "call workflow in this app" action doesn't work there, even though Automation runs on the same Standard engine. A plain Http call to the target workflow's own trigger URL is the reliable way to dispatch between workflows in an Automation app today.


Top comments (0)