# Jev Decides, OpenAI Reasons: A Practical Hybrid Agent Architecture

> Source: <https://dev.to/sreeni5018/jev-decides-openai-reasons-a-practical-hybrid-agent-architecture-4ol3>
> Published: 2026-09-22 02:14:40+00:00

**Code Controls, Jev Decides, OpenAI Reasons: A Hybrid Agent Architecture That Doesn't Waste LLM Calls**

When we build an AI agent today, the default architecture puts an LLM in the middle of everything. The user sends a request, the LLM interprets it, reasons about what to do, picks a tool, generates arguments, reads the result, reasons again, and finally responds.

That approach works. But while experimenting with agents, I started asking a different question: **does every decision inside an agent really need a general purpose LLM?**

Consider a simple request:

"I forgot my password."

The application already knows the possible workflows:

`reset_password`` unlock_account``check_order`` cancel_order``openai_reasoning`` human_support`
We're not asking AI to invent a new solution. We just need it to answer: **which of these known capabilities best matches the request?** That's a very different problem from asking an LLM to investigate an outage, explain an architecture, or compare options.

This distinction led me to a hybrid agent architecture using **Jev** and **OpenAI**. The basic idea:

```
CODE    = CONTROL
JEV     = DECIDE
OPENAI  = REASON
TOOLS   = ACT
```

Instead of asking one model to do everything, the agent uses the right kind of intelligence for each step.

Jev is TypeSafe AI's first public **System One Model** a model built specifically for decisions inside software, not for open ended generation.

A general-purpose LLM works like this:

```
Context → LLM → Generate tokens → Explanation / Code / JSON / Tool Call / Answer
```

Jev works differently:

```
Application State → Jev → Typed Probabilistic Decision
```

TypeSafe describes this as *unstructured state in, typed probabilistic decisions out*.

Suppose our application sends the user request plus the available capabilities. Jev might return something conceptually like:

```
reset_password       1.00
unlock_account       0.00
check_order          0.00
cancel_order         0.00
openai_reasoning     0.00
human_support        0.00
```

The application executes `reset_password()`. Jev doesn't write a paragraph explaining what to do — its job is the **bounded semantic judgment**: which known capability best fits this context?

It's easy to assume Jev is a cheaper model used for classification, but that misses the point.

A general-purpose LLM is optimized for flexible language generation it can explain, reason, summarize, write, generate code, compare alternatives, plan, and troubleshoot. Jev focuses on a narrower interface: decisions whose structure the application already defines. TypeSafe's workflow examples use three decision primitives **Choice**, **Score**, and **Noul**.

The application defines the decision space. Jev is most useful when the system already knows the possible actions but still needs semantic understanding to pick the right one.

Two requests make the difference clear.

**Request 1:** "I forgot my password." The possible workflows are already known this is a bounded decision.

**Request 2:** "Our application authenticates successfully through SSO, but users start getting errors when their OAuth access token expires. Analyze what could be happening and explain what we should investigate."

There's no predefined answer here. The system might need to think about refresh tokens, token rotation, scopes, audience, session expiration, IdP configuration, or token caching. That's open ended reasoning.

| Area | Jev | General-Purpose LLM | 
|---|---|---|
| Primary role | Decision | Reasoning + generation | 
| Answer space | Defined beforehand | Potentially open-ended | 
| Output | Typed decisions | Generated content | 
| Probabilities | Core part of interface | Possible, not primary | 
| Long explanation | Not the purpose | Strong fit | 
| Semantic routing | Strong fit | Possible | 
| Tool selection from known set | Strong fit | Possible | 
| Troubleshooting | Limited | Strong fit | 
| Content creation | Not the purpose | Strong fit | 
| Planning | Bounded | Open-ended | 

The right question isn't *Jev versus OpenAI, which is better* it's *which one fits the decision I'm making right now?*

An agent doesn't contain only one kind of problem. Inside the same interaction we usually have deterministic rules, semantic decisions, open ended reasoning, and real world actions. Forcing all four through the same model is wasteful. Instead, separate the responsibilities:

```
                    USER
                      │
                      ▼
              CONVERSATION STATE
                      │
                      ▼
                     JEV
                Decide / Route
                      │
        ┌─────────────┼──────────────┐
        │             │              │
        ▼             ▼              ▼
 Deterministic     OpenAI          Human
   Workflow        Reasoning       Support
        │             │
        └──────┬──────┘
               │
               ▼
         TOOLS & SYSTEMS
               │
               ▼
           UPDATE STATE
               │
               ▼
         RESPONSE TO USER
```

**Figure 1** Jev decides, OpenAI reasons, code orchestrates, and tools execute.

The implementation is surprisingly small. Start with two API keys.

```
TYPESAFE_API_KEY=your_typesafe_api_key
OPENAI_API_KEY=your_openai_api_key
OPENAI_MODEL=gpt-6-astra
python
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

TYPESAFE_API_KEY = os.getenv("TYPESAFE_API_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-6-astra")

openai_client = OpenAI(api_key=OPENAI_API_KEY)
```

OpenAI's current Responses API uses `client.responses.create(...)` and exposes the generated text through `response.output_text`.

Instead of a giant `if/elif` routing block, register capabilities so they can describe themselves:

``` python
TOOLS = {}

def register_tool(name, description):
    def decorator(func):
        TOOLS[name] = {
            "description": description,
            "function": func
        }
        return func
    return decorator
@register_tool(
    name="reset_password",
    description="Use when the user forgot their password or wants to reset their password."
)
def reset_password(message, step=None):
    return "Password reset workflow started."

@register_tool(
    name="unlock_account",
    description="Use when the user's account is locked or needs to be unlocked."
)
def unlock_account(message, step=None):
    return "Account unlock workflow started."
```

We can also register the LLM itself as a capability:

```
@register_tool(
    name="openai_reasoning",
    description=(
        "Use when the request requires explanation, analysis, comparison, "
        "troubleshooting, summarization, writing, or open-ended reasoning."
    )
)
def openai_reasoning(message, step=None):
    ...
```

This is the part I find most interesting: OpenAI becomes **one capability** available to the agent, rather than automatically being the whole agent.

The tool descriptions *become* Jev's decision space:

``` python
def build_jev_choices():
    return {
        name: config["description"]
        for name, config in TOOLS.items()
    }
```

Add a new capability — say `refund_order` — and it automatically becomes another candidate. No router rewrite needed.

``` python
from typesafe_sdk import Choice, TypeSafeClient

def route_with_jev(user_message):
    choices = build_jev_choices()

    state = {
        "user_request": user_message,
        "available_capabilities": list(choices.keys())
    }

    with TypeSafeClient() as client:
        response = client.system_one(
            state=state,
            questions={
                "route": Choice(
                    instructions=(
                        "Choose the single best capability for handling the "
                        "user's request. Use openai_reasoning when the task "
                        "requires explanation, analysis, troubleshooting, "
                        "comparison, writing, or open-ended reasoning."
                    ),
                    criteria=choices
                )
            }
        )

    return response.choices["route"]
```

Jev's job is now narrow: given the request and the available capabilities, return the best one.

**Example — password reset:** the user says "I forgot my password," Jev sees the six capabilities, and selects `reset_password` with a confidence of 1.0. Job done.

My first implementation just returned "Password reset workflow started." Technically correct, not very useful. A real agent needs to continue the conversation:

```
You:   I forgot my password.
Agent: I can help with that. What email address is associated with your account?
You:   sreeni@example.com
Agent: I've initiated the password-reset workflow. Please check your registered email.
```

That means the agent needs **state**.

```
SESSION = {
    "pending_tool": None,
    "pending_step": None,
    "data": {},
    "history": []
}
```

After Jev chooses `reset_password`, the app stores `pending_tool = "reset_password"` and `pending_step = "awaiting_email"`. When the user replies with an email address, we don't send that back to Jev the application already knows what it means, because it's answering the question the active workflow just asked.

This led me to a principle I keep coming back to: **don't ask AI to decide something your application already knows.**

**Example — order cancellation** follows the same shape: Jev selects `cancel_order` once, up front, and everything after that (asking for the order number, confirming, executing) is normal software. We don't need an LLM to understand "Yes."

```
@register_tool(
    name="openai_reasoning",
    description=(
        "Use when the user's request requires open-ended reasoning, "
        "explanation, analysis, comparison, summarization, "
        "troubleshooting, or generation."
    )
)
def openai_reasoning(message, step=None):
    response = openai_client.responses.create(
        model=OPENAI_MODEL,
        instructions=(
            "You are the reasoning component inside a hybrid AI agent. "
            "Provide a clear and concise answer."
        ),
        input=message
    )
    return response.output_text
```

OpenAI is no longer called for every request. Jev decides, up front, whether this matches a known workflow or whether it actually needs open ended reasoning.

**Example a request that actually needs an LLM:** "Our application works with SSO initially, but starts failing after the access token expires. Explain what could be wrong and what we should investigate." Jev evaluates the six capabilities and selects `openai_reasoning`. Only now does OpenAI get involved, and it can discuss refresh-token expiration, token rotation, scope problems, audience, session expiration, IdP configuration, and caching behavior exactly the kind of work where an LLM earns its cost.

A conventional LLM-centric agent:

```
USER → LLM understands → LLM reasons → LLM chooses tool → TOOL
     → LLM reads result → LLM reasons again → ANSWER
```

The hybrid architecture:

```
USER → JEV → Which capability?
              ├── Deterministic → CODE
              ├── Open-ended    → OPENAI
              └── Uncertain     → HUMAN
            → TOOLS → RESULT
```

Neither is universally better. They optimize for different workloads.

``` python
def run_agent(user_message):

    # Continue an existing workflow
    if SESSION["pending_tool"]:
        return execute_tool(
            SESSION["pending_tool"],
            user_message,
            SESSION["pending_step"]
        )

    # Otherwise ask Jev to route
    decision = route_with_jev(user_message)
    selected = decision.choice
    confidence = decision.confidence

    # Low confidence: ask for clarification
    if confidence < 0.50:
        return (
            "I'm not completely sure what you'd like me to do. "
            "Could you provide a little more detail?"
        )

    # Execute selected capability
    return execute_tool(selected, user_message)
```

That's the whole architecture in one function: is a workflow already active? If so, continue it no LLM call needed. If not, ask Jev which capability fits, then route to code, OpenAI, or a human.

One thing I like about Jev's interface is that decisions come with **probabilities and confidence** as first-class outputs, not an afterthought.

If Jev returns `**reset_password**: 0.97`, the app can safely continue. But if it returns something closer to `** reset_password**: 0.43, **unlock_account**: 0.39, **human_support**: 0.18`, the harness shouldn't just pick the highest score blindly it can ask a clarifying question instead:

```
if confidence >= 0.85:
    execute()
elif confidence >= 0.50:
    ask_for_clarification()
else:
    escalate_to_human()
```

The exact thresholds need to be tuned per use case rather than treated as universal.

There's an important limitation here. If the allowed outputs are `**reset_password**`, `** unlock_account**`, and `** human_support**`, Jev will always return something structurally valid but a structurally valid decision can still be the *wrong* one. The model might select `**reset_password**` when `** unlock_account**` was correct. Both are valid outputs; only one matches reality.

**Typed output does not mean guaranteed correctness.** The application still needs evaluation, confidence policies, validation, guardrails, human escalation, observability, and testing. That doesn't go away just because the decision is typed.

The experiment got clearer once I stopped treating everything as an AI problem. There are really four distinct responsibilities:

**CODE = CONTROL** — use normal code when the rule is already known (`if order.status == "SHIPPED": cancellation_allowed = False`). There's no reason to ask a model to rediscover a deterministic business rule.

**JEV = DECIDE** — use Jev when the possible outcomes are known but semantic understanding is required: which workflow, which tool, which specialist agent, relevant or not, retry or stop, escalate or continue. This is **bounded semantic judgment**.

**OPENAI = REASON** — use a general-purpose LLM when the problem requires analysis, explanation, planning, generation, comparison, or synthesis for example, "analyze the last five production incidents and identify recurring failure patterns." That's not choosing from a menu; it requires reasoning across information.

**TOOLS = ACT** — tools perform the real-world action: Okta, Entra ID, ServiceNow, Jira, Salesforce, OpenSearch, Qdrant, databases, REST APIs, MCP servers, A2A agents. The model shouldn't just say "the password has been reset" unless the identity provider actually did it.

Above all four sits the **agent harness**, which orchestrates.

Password reset is intentionally a simple example. The architecture gets more interesting in real agent systems, where an agent is repeatedly asking bounded questions: which tool should I use, which MCP server should receive this request, is this retrieved document relevant, should I retry this failed operation, which specialist agent should handle this task, should this action require human approval, has enough evidence been gathered? Many of these don't need a full general-purpose reasoning model every time.

**Jev + MCP.** One obvious next step is dynamic MCP tool discovery — instead of manually registering capabilities, the system pulls them from an MCP server's `list_tools()` call, and Jev's decision space becomes dynamic:

```
MCP SERVER → list_tools() → Tool names + descriptions → JEV → Select best tool → call_tool()
```

**Jev + multi-agent systems.** The same idea works one level up — instead of selecting a tool, Jev selects a specialist agent:

```
                 ORCHESTRATOR
                      │
                      ▼
                     JEV
                      │
             Which specialist?
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
     Finance       Supply        Engineering
      Agent        Chain Agent      Agent
```

The choices are known. The intelligence is in picking the one that fits the current context.

Ask: **can I clearly define the possible answers before making the model call?** If yes, this is probably a good candidate for bounded semantic judgment — approve/review/reject, billing/shipping/technical, retry/stop/escalate, low/medium/high risk.

If you can't define the answer space because the problem requires exploration, explanation, synthesis, or creation, a general-purpose LLM is the better fit something like "analyze everything we know about this outage and identify the three most plausible root causes" is genuinely open ended.

For years, AI architecture discussions started with *which LLM should we use?* Agentic systems push us toward a better question: **what kind of intelligence does this particular step require?**

Sometimes the answer is code. Sometimes Jev. Sometimes OpenAI. Sometimes no model at all just call the tool. That produces a more modular architecture:

```
                     AGENT HARNESS

          ┌──────────────┼──────────────┐
          │              │              │
          ▼              ▼              ▼
        CODE            JEV           OPENAI
       Control         Decide          Reason
          │              │              │
          └──────────────┼──────────────┘
                         │
                         ▼
                       TOOLS
                         │
                         ▼
                        ACT
```

I don't think the future of AI agents means finding **one giant model and letting it control every decision. A more interesting** architecture uses the right intelligence for the right decision: code when the rule is deterministic, Jev when the choices are known but semantic judgment is required, OpenAI (or another LLM) when genuine reasoning or generation is needed, and tools when something actually has to happen in the real world.

**Code controls. Jev decides. OpenAI reasons. Tools act. The agent harness orchestrates everything.**

That's the hybrid agent architecture I wanted to explore.

**Thanks Sreeni Ramadorai**
