Crux
GuidesPrompts

Prompts

Define typed, composable prompts that run with any SDK adapter.

prompt() is the main unit of LLM work in Crux. It describes what the model should see, what input the call accepts, what output shape it should return, and which reusable capabilities should be composed in.

The prompt does not call a model by itself. Adapters such as @crux/ai, @crux/openai, @crux/google, and @crux/anthropic execute the same prompt definition against their SDKs.

support-prompt.ts
import { ,  } from '@crux/core'
import {  } from 'zod'

const  = ({
  : 'brand',
  : 'Use a concise, helpful support tone.',
})

export const  = ({
  : 'answer-support',
  : [],
  : .({
    : .(),
  }),
  : 'You are a product support assistant.',
  : ({  }) => .,
  : { : 0.2 },
})

The Mental Model

A prompt owns the call contract.

prompt({
  id: 'answer-support',
  use: [brand, docs, memory],
  input: InputSchema,
  output: OutputSchema,
  system: 'Who the assistant is.',
  prompt: ({ input }) => `What the user asked: ${input.question}`,
})

Think of the fields this way:

FieldWhat it owns
idStable identity for logs, evals, cache keys, and devtools
inputRuntime data this prompt accepts
outputStructured output schema. Omit it for plain text generation.
systemThe prompt's role, policy, and high-level instructions
promptThe user/task message for single-turn calls
messagesFull message history for chat or few-shot calls
useReusable capabilities: contexts, memory, retrieval, grounding, blackboards, skills, and custom injectables
toolsPrompt-local tools for this call
settingsDefault generation settings such as temperature and max tokens
testsInline eval cases for CI and local regression checks

The normal path is composition through use. Manual helpers like asContext() and asTools() exist for advanced or framework-specific wiring, but users should not need them for ordinary Crux prompts.

Every entry in use is injectable in the product sense: it can add instructions, tools, constraints, metadata, or lifecycle behavior during prompt resolution. Some entries use the generic injectable() API; others, such as skills, memory, blackboards, retrieval, and grounding, have dedicated first-class APIs.

Text Output

Omit output when the model should return text.

text-prompt.ts
import {  } from '@crux/core'
import {  } from 'zod'

const  = ({
  : 'rewrite',
  : .({
    : .(),
    : .(['clear', 'friendly', 'direct']),
  }),
  : 'Rewrite text without changing the meaning.',
  : ({  }) => `Tone: ${.}\n\n${.}`,
})
run.ts
import { generate } from '@crux/ai'
import { openai } from '@ai-sdk/openai'

const result = await generate(rewrite, {
  model: openai('gpt-4o-mini'),
  input: {
    text: 'The deployment failed due to credentials.',
    tone: 'friendly',
  },
})

result.text

Structured Output

Add output when downstream code needs typed data. Adapters switch to structured generation and validate the result with Zod.

structured-prompt.ts
import {  } from '@crux/core'
import {  } from 'zod'

const  = ({
  : 'classify-ticket',
  : .({
    : .(),
    : .(),
  }),
  : .({
    : .(['billing', 'auth', 'bug', 'other']),
    : .(['low', 'normal', 'high']),
    : .(),
  }),
  : 'Classify support tickets for routing.',
  : ({  }) => `${.}\n\n${.}`,
})
run.ts
const result = await generate(classifyTicket, {
  model,
  input: {
    subject: 'SSO stopped working',
    body: 'Our Okta login fails after rotating certificates.',
  },
})

result.object.category
result.object.urgency

Compose Capabilities With use

use is the main Crux composition surface. Put the capability in the array; Crux resolves the right context, tools, constraints, metadata, and lifecycle behavior.

composition.ts
import { prompt, context } from '@crux/core'
import { memory, recentMessages, facts } from '@crux/core/memory'
import { grounding } from '@crux/core/retrieval'
import { skill } from '@crux/core/skill'

const policy = context({
  id: 'support-policy',
  system: 'Do not invent product behavior. Ask for clarification when needed.',
})

const userMemory = memory({
  id: 'user-memory',
  store,
  namespace: ({ input }) => `user:${input.userId}`,
  blocks: [
    recentMessages({ id: 'recent', maxMessages: 8 }),
    facts({ id: 'facts', embed: dense }),
  ],
})

const docsGrounding = grounding({
  id: 'docs-grounding',
  retriever: docs,
  inject: 'both',
  citations: true,
})

const supportTroubleshooting = skill.fromFile('./skills/support-troubleshooting/SKILL.md')

const support = prompt({
  id: 'support',
  use: [policy, userMemory, docsGrounding, supportTroubleshooting],
  input: z.object({
    userId: z.string(),
    question: z.string(),
  }),
  system: 'Answer the support question.',
  prompt: ({ input }) => input.question,
})

This is intentionally boring at the call site. The prompt author names the dependencies; each primitive decides what it contributes.

Put Reusable Wiring In A Context

If several prompts should share the same policy plus the same retrieval, memory, or skills, package that as a context.

capability-context.ts
const supportKnowledge = context({
  id: 'support-knowledge',
  use: [docsGrounding, userMemory, supportTroubleshooting],
  system: 'Use product docs first. Use memory only for user-specific details. Load the troubleshooting skill when the question requires diagnostic steps.',
})

const reply = prompt({
  id: 'reply',
  use: [supportKnowledge],
  input: z.object({
    userId: z.string(),
    question: z.string(),
  }),
  system: 'You are a support assistant.',
  prompt: ({ input }) => input.question,
})

Use direct prompt({ use: [...] }) for one-off wiring. Use context({ use: [...] }) when you want a reusable product capability.

System + Prompt vs Messages

Use system + prompt for the common single-turn shape.

single-turn.ts
import {  } from '@crux/core'
import {  } from 'zod'

const  = ({
  : 'title-ideas',
  : .({ : .() }),
  : 'Generate practical article titles.',
  : ({  }) => `Topic: ${.}`,
})

Use messages when you need a full chat transcript, few-shot examples, or role-specific turns.

messages.ts
import {  } from '@crux/core'
import {  } from 'zod'

const  = ({
  : 'chat-reply',
  : .({
    : .(),
  }),
  : 'Answer as a careful assistant.',
  : ({  }) => [
    { : 'user', : 'Answer in one sentence.' },
    { : 'assistant', : 'I will keep responses concise.' },
    { : 'user', : . },
  ],
})

Do not define both prompt and messages unless the adapter guide explicitly documents that combination. messages is for cases where the message list is the source of truth.

Inspect Before You Run

Use .resolve() or .inspect() when debugging composition. This is the fastest way to see what use actually injected.

inspect.ts
const resolved = await support.resolve({
  input: {
    userId: 'user_123',
    question: 'How do I configure SSO?',
  },
  provider: 'openai',
  modelId: 'gpt-4o',
})

console.log(resolved.system)
console.log(Object.keys(resolved.tools ?? {}))
budget-debug.ts
const inspection = await support.inspect({
  input: {
    userId: 'user_123',
    question: 'How do I configure SSO?',
  },
  tokenBudget: 4000,
})

console.log(inspection.system.parts)
console.log(inspection.droppedContexts)
console.log(inspection.excludedContexts)

What To Reach For

NeedUse
One model call with typed input/outputprompt()
Reusable instructions or input fieldscontext() in use
Runtime conditional instructionscontext({ when }), when(), or match()
Durable user/session recallmemory() in use
Retrieve documents as evidenceretriever(), retrievalPipeline(), or grounding() in use
Markdown instruction sets loaded on demandskill() in use
Shared coordination stateblackboard() in use
Citation and answer constraintsgrounding() or constraints
CI regression testingtests or evaluate()
Provider-specific prompt tweaksadapt

How This Fits With The Rest Of Crux

  • Contexts package reusable system text, inputs, tools, and nested capabilities.
  • Retrieval and grounding inject evidence directly or expose search tools on demand.
  • Memory injects private recall and capture behavior through the same use array.
  • Skills inject a lightweight index plus LoadSkill and LoadReference tools so the model can pull Markdown instructions on demand.
  • Blackboards inject shared state and focused state-update tools for multi-agent coordination.
  • Tools can live on prompts, contexts, or injected primitives.
  • Semantic response cache reuses safe prompt results for similar inputs.
  • Quality runs prompt and RAG regression evaluations in CI and devtools.

Pick A Topic

On this page