# Clef Decision (Jev-compatible)

URL: /docs/experiments/clef-decision
> Jev / System One API-compatible decision model on Workers AI - Clef & Clef-Flash return probabilities for noul/choice questions without free-form text
Tags: ai, workers-ai, decision-model, jev, systemone, clef, agents, classification, structured-output
Bindings: AI
Markdown: /llms.mdx/experiments/clef-decision/content.md



**Clef** is Cloudflare's open-source **decision model** on Workers AI - in the same family as Typesafe's **Jev** / **System One** API. Instead of generating free-form text, it takes a state plus typed questions and returns a **probability for every allowed answer** so agents can act without parsing LLM prose.

This experiment is a deployable Worker reference for `@cf/cloudflare/clef` and `@cf/cloudflare/clef-flash`: post a state and `noul` (binary) or `choice` (multiple-choice) questions, get structured probabilities back. Existing Jev integrations can switch by changing the endpoint and model.

## Features [#features]

* Jev / System One-style decision API on Workers AI (Clef and Clef-Flash)
* Binary (`noul`) and multiple-choice (`choice`) questions with probability distributions
* No free-form generation - bounded outputs agents can route on immediately
* Stateless edge Worker - under \~60s, no storage required
* Pasteable `src/lib/decision.ts` core for your own project

## API Reference [#api-reference]

### `POST /decision` [#post-decision]

Call Workers AI Clef or Clef-Flash to answer structured decision questions and return probability distributions. Compatible with Jev-style state + typed questions workflows.

**Request body**

```json
{
  "state": "Short description of the current state or context",
  "questions": [
    {
      "type": "noul",
      "question": "Will it rain tomorrow?"
    },
    {
      "type": "choice",
      "question": "What color is the sky?",
      "choices": ["blue", "gray", "red"]
    }
  ],
  "model": "@cf/cloudflare/clef"
}
```

**Fields**

* **`state`** `string` (required) - Context or state description
* **`questions`** `array` (required) - Array of question objects
  * **`type`** `string` - `"noul"` (binary) or `"choice"` (multiple-choice)
  * **`question`** `string` - The question text
  * **`choices`** `string[]` - For `choice` type, the list of possible answers
* **`model`** `string` (optional) - Model name, defaults to `@cf/cloudflare/clef`. Also supports `@cf/cloudflare/clef-flash`

**Example request**

```bash
curl -X POST https://clef-decision.example.workers.dev/decision \
  -H "Content-Type: application/json" \
  -d '{
    "state": "The weather forecast shows clouds moving in.",
    "questions": [
      { "type": "noul", "question": "Will it rain?" },
      { "type": "choice", "question": "When?", "choices": ["today", "tomorrow", "next week"] }
    ]
  }'
```

**Example response**

```json
{
  "model": "@cf/cloudflare/clef",
  "state": "The weather forecast shows clouds moving in.",
  "questions": [
    { "type": "noul", "question": "Will it rain?" },
    { "type": "choice", "question": "When?", "choices": ["today", "tomorrow", "next week"] }
  ],
  "answers": [
    { "type": "noul", "probability": 0.73 },
    { "type": "choice", "probabilities": [0.15, 0.65, 0.2] }
  ]
}
```

**Error responses**

* `400` - Invalid JSON body or missing required fields
* `502` - Workers AI error

**Error codes**

* `INVALID_JSON` - Request body is not valid JSON
* `INVALID_REQUEST` - Missing or invalid `state` or `questions`
* `AI_ERROR` - Workers AI returned an error

## Use Cases [#use-cases]

* **Agent routing** - escalate, auto-resolve, or assign a ticket without a human in the loop
* **Jev / System One alternative** - drop-in decision model on Workers AI with Clef or Clef-Flash
* Support triage, invoice workflows, and security incident classification
* Multi-step agent graphs that need bounded yes/no or choice probabilities
* Fast edge decisions where LLM free-form text would be slow or hard to parse

## Limitations [#limitations]

* Requires Workers AI binding (must deploy or use `--remote` locally)
* Question complexity limited by model context window
* Probability outputs are model-generated estimates, not guarantees
* No persistent storage or decision history
* Optimized for structured decision questions - not open-ended chat or free-form generation

## Use in your project [#use-in-your-project]

<UseInYourProject experiment="clef-decision" bindings="[&#x22;Workers AI (AI binding)&#x22;]" platform="[&#x22;Workers AI&#x22;]" files="[&#x22;src/lib/decision.ts&#x22;, &#x22;examples/usage.ts&#x22;]" sources="[{&#x22;path&#x22;:&#x22;src/lib/decision.ts&#x22;,&#x22;title&#x22;:&#x22;decision.ts&#x22;,&#x22;lang&#x22;:&#x22;ts&#x22;,&#x22;code&#x22;:&#x22;import type { Env } from \&#x22;../types/env\&#x22;;\n\nexport type DecisionQuestion =\n  | { type: \&#x22;noul\&#x22;; question: string }\n  | { type: \&#x22;choice\&#x22;; question: string; choices: string[] };\n\nexport type DecisionModel = \&#x22;@cf/cloudflare/clef\&#x22; | \&#x22;@cf/cloudflare/clef-flash\&#x22;;\n\nexport type DecisionResult = {\n  model: DecisionModel;\n  state: string;\n  questions: DecisionQuestion[];\n  answers: unknown;\n};\n\nexport async function runDecision(\n  env: Env,\n  state: string,\n  questions: DecisionQuestion[],\n  model: DecisionModel = \&#x22;@cf/cloudflare/clef\&#x22;\n): Promise<DecisionResult> {\n  const answers = await env.AI.run(model, {\n    state,\n    questions,\n  });\n\n  return {\n    model,\n    state,\n    questions,\n    answers,\n  };\n}&#x22;},{&#x22;path&#x22;:&#x22;examples/usage.ts&#x22;,&#x22;title&#x22;:&#x22;usage.ts&#x22;,&#x22;lang&#x22;:&#x22;ts&#x22;,&#x22;code&#x22;:&#x22;/**\n * Minimal paste example for docs (\&#x22;Use in your project\&#x22;).\n * Not part of the Worker runtime — copy into your own project.\n */\nimport { runDecision } from \&#x22;../src/lib/decision\&#x22;;\n\nexport default {\n  async fetch(request: Request, env: { AI: Ai }): Promise<Response> {\n    if (request.method !== \&#x22;POST\&#x22;) {\n      return Response.json(\n        { error: \&#x22;Method not allowed\&#x22;, code: \&#x22;METHOD_NOT_ALLOWED\&#x22; },\n        { status: 405 }\n      );\n    }\n\n    const body = (await request.json()) as {\n      state?: string;\n      questions?: Array<\n        { type: \&#x22;noul\&#x22;; question: string } | { type: \&#x22;choice\&#x22;; question: string; choices: string[] }\n      >;\n      model?: \&#x22;@cf/cloudflare/clef\&#x22; | \&#x22;@cf/cloudflare/clef-flash\&#x22;;\n    };\n\n    if (!body.state || !body.questions?.length) {\n      return Response.json(\n        { error: \&#x22;Missing state or questions\&#x22;, code: \&#x22;INVALID_REQUEST\&#x22; },\n        { status: 400 }\n      );\n    }\n\n    const result = await runDecision(env, body.state, body.questions, body.model);\n    return Response.json(result);\n  },\n};&#x22;}]" />

## Deployment [#deployment]

<Steps>
  <Step>
    ### Deploy to Cloudflare Workers [#deploy-to-cloudflare-workers]

    [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/shrinathsnayak/cloudflare-experiments/tree/main/apps/experiments/clef-decision)

    The Workers AI binding is automatically provisioned.
  </Step>

  <Step>
    ### Test your deployment [#test-your-deployment]

    ```bash
    curl -X POST https://clef-decision.your-subdomain.workers.dev/decision \
      -H "Content-Type: application/json" \
      -d '{
        "state": "The weather forecast shows clouds.",
        "questions": [{ "type": "noul", "question": "Will it rain?" }]
      }'
    ```
  </Step>
</Steps>

## Local Development [#local-development]

```bash
cd apps/experiments/clef-decision
npm install
npm run dev
```

The Worker runs at `http://localhost:8787` with `--remote` for AI binding access.

**Test locally**

```bash
curl -X POST http://localhost:8787/decision \
  -H "Content-Type: application/json" \
  -d '{
    "state": "test state",
    "questions": [{ "type": "noul", "question": "Is this working?" }]
  }'
```

## Configuration [#configuration]

### Bindings [#bindings]

This experiment requires a Workers AI binding declared in `wrangler.json`:

```json
{
  "ai": [
    {
      "binding": "AI"
    }
  ]
}
```

No additional secrets required.

## Cloudflare Features Used [#cloudflare-features-used]

* [Workers](https://developers.cloudflare.com/workers/) - Edge compute platform
* [Workers AI](https://developers.cloudflare.com/workers-ai/) - Run AI models at the edge
* Workers AI Clef decision API - Structured decision probabilities

## Next Steps [#next-steps]

<Cards>
  <Card title="Cloud AI Proxy" href="/experiments/cloud-ai-proxy" description="Call any Workers AI model" />

  <Card title="AI Website Summary" href="/experiments/ai-website-summary" description="Summarize webpages with Workers AI" />

  <Card title="Chat Agent" href="/experiments/chat-agent" description="Durable chat agent with Workers AI" />
</Cards>
