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.
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:
| Field | What it owns |
|---|---|
id | Stable identity for logs, evals, cache keys, and devtools |
input | Runtime data this prompt accepts |
output | Structured output schema. Omit it for plain text generation. |
system | The prompt's role, policy, and high-level instructions |
prompt | The user/task message for single-turn calls |
messages | Full message history for chat or few-shot calls |
use | Reusable capabilities: contexts, memory, retrieval, grounding, blackboards, skills, and custom injectables |
tools | Prompt-local tools for this call |
settings | Default generation settings such as temperature and max tokens |
tests | Inline 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.
import { } from '@crux/core'
import { } from 'zod'
const = ({
: 'rewrite',
: .({
: .(),
: .(['clear', 'friendly', 'direct']),
}),
: 'Rewrite text without changing the meaning.',
: ({ }) => `Tone: ${.}\n\n${.}`,
})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.textStructured Output
Add output when downstream code needs typed data. Adapters switch to structured generation and validate the result with Zod.
import { } from '@crux/core'
import { } from 'zod'
const = ({
: 'classify-ticket',
: .({
: .(),
: .(),
}),
: .({
: .(['billing', 'auth', 'bug', 'other']),
: .(['low', 'normal', 'high']),
: .(),
}),
: 'Classify support tickets for routing.',
: ({ }) => `${.}\n\n${.}`,
})const result = await generate(classifyTicket, {
model,
input: {
subject: 'SSO stopped working',
body: 'Our Okta login fails after rotating certificates.',
},
})
result.object.category
result.object.urgencyCompose 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.
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.
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.
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.
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.
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 ?? {}))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
| Need | Use |
|---|---|
| One model call with typed input/output | prompt() |
| Reusable instructions or input fields | context() in use |
| Runtime conditional instructions | context({ when }), when(), or match() |
| Durable user/session recall | memory() in use |
| Retrieve documents as evidence | retriever(), retrievalPipeline(), or grounding() in use |
| Markdown instruction sets loaded on demand | skill() in use |
| Shared coordination state | blackboard() in use |
| Citation and answer constraints | grounding() or constraints |
| CI regression testing | tests or evaluate() |
| Provider-specific prompt tweaks | adapt |
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
usearray. - Skills inject a lightweight index plus
LoadSkillandLoadReferencetools 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
Execution
Run prompts with each SDK adapter: generate, stream, multi-turn, and framework adapters.
Semantic Response Cache
Use dense embeddings to reuse safe prompt results across similar requests.
Contexts
Compose reusable instructions, runtime data, tools, and nested capabilities.
API Reference
Full prompt() signature, options, methods, and types.