Cortex · guide

An answer that fits

Give an agent a schema and its answer is data of that shape. An answer that does not fit goes back to the model, in the same run.

Install it
pnpm add @niscorp/cortex @niscorp/signal zod openai
Step 1 of 4

The model

createSignal makes the client that calls the model. Cortex is handed this client, here for Groq, and Signal reads the key from GROQ_API_KEY.

The model
import { z } from 'zod';
import { createSignal } from '@niscorp/signal';
import { defineAgent, outputRetries, stepCount } from '@niscorp/cortex';

const llm = createSignal('groq');
Step 2 of 4

The shape of the answer

A Zod schema says what the answer has to be. Here, an order read out of a sentence: what was ordered, and how many.

The shape of the answer
const Order = z.object({
  item: z.string(),
  quantity: z.number().int().positive(),
});
Step 3 of 4

Your own check

validate is your own check, run after the schema passes. It can look things up. It answers { ok: true }, or { retry } with a sentence for the model.

Both kinds of refusal are handled the same way: the model’s answer and what was wrong with it are added to the conversation, and the model answers again. The run is not started over.

Your own check
const menu = ['coffee', 'tea', 'cake'];

const reader = defineAgent({
  id: 'order-reader',
  instructions: 'Read the order out of the message.',
  output: {
    schema: Order,
    validate: (output) =>
      menu.includes(output.data.item)
        ? { ok: true }
        : { retry: `We do not sell ${output.data.item}. The menu is: ${menu.join(', ')}.` },
  },
  stopWhen: [stepCount(6), outputRetries(3)],
});
Step 4 of 4

Run it, and watch for corrections

Every refusal is a retry event, carrying what the model was told. result.meta.outputRetries counts them.

outputRetries(3) ends the run at the third refused answer, with result.ok false and the last answer in result.error.lastOutput.

Run it, and watch for corrections
const run = reader.run('Two flat whites please', { llm });
run.onEvent((event) => {
  if (event.type === 'retry') console.log('sent back:', event.issues);
});

const result = await run.result;
if (result.ok) console.log(result.output.data, 'after', result.meta.outputRetries, 'corrections');
else console.log(result.error.code, result.error.message);
Then

Run it.

This file asks a model, so it is not run on this site. When the model first answers with an item that is not on the menu, it prints sent back: and the sentence from validate, and then an order for coffee after 1 correction. result.output.data is typed as the schema’s shape.

Run it
GROQ_API_KEY=your-key npx tsx answer.mts