Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill helps an LLM generate correct DSPy signature code using @ax-llm/ax. Use when the user asks about signatures, s(), f(), field types, string syntax, fluent builder API, validation constraints, or type-safe inputs/outputs.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 189% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 193% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 222% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 255% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 176% | 0% |
[description] input1:type, input2:type -> output1:type, output2:type| Type | Syntax | TypeScript | Example | |------|--------|-----------|---------| | String | :string | string | userName:string | | Number | :number | number | score:number | | Boolean | :boolean | boolean | isValid:boolean | | JSON | :json | any | metadata:json | | Date | :date | Date | birthDate:date | | DateTime | :datetime | Date | timestamp:datetime | | DateRange | :dateRange | { start: Date; end: Date } | travelDates:dateRange | | DateTimeRange | :datetimeRange | { start: Date; end: Date } | meetingWindow:datetimeRange | | Image | :image | {mimeType, data} | photo:image (input only) | | Audio | :audio | input: AxAudioInput; output: AxChatAudioOutput | recording:audio, speech:audio | | File | :file | {mimeType, data} | document:file (input only) | | URL | :url | string | website:url | | Code | :code | string | pythonScript:code | | Class | :class "a, b, c" | "a" \| "b" \| "c" | mood:class "happy, sad" |
Date, datetime, and range fields are AI-friendly but strict. They accept ISO-style values, trim minor whitespace/casing issues, and parse ranges as { "start": "...", "end": "..." }, [start, end], start/end, or natural delimiters like start to end; invalid values and reversed ranges should fail validation rather than being silently autocorrected.
typescript'tags:string[] -> processedTags:string[]' // arrays 'query:string, context?:string -> response:string' // optional with ? 'problem:string -> reasoning!:string, solution:string' // internal with !
The string form is constraint-complete: everything the fluent API expresses (except Standard Schema fields) can be written in the string. A type takes an optional comma-separated, order-free modifier bag in parentheses, and objects declare structured fields inline.
typescript`userAge:number(min 0, max 120), contactEmail:string(format email, cache), codeSnippet:code(python) -> userName:string(pattern "^[a-z_]+$" "lowercase name"), tagList:string(item "a short tag")[] "all tags", profileList:object{ fullName:string, userAge?:number(min 0) }[] "matched profiles"`
| Modifier | Applies to | Effect | |----------|-----------|--------| | min N / max N | string, number | String length bounds / numeric value bounds | | format email\|uri\|date\|date-time | string | Format validation | | pattern "regex" ["desc"] | string | Regex validation with optional description | | cache | top-level input | Prefix-cache breakpoint | | item "desc" | arrays | Per-item description: tags:string(item "a tag")[] | | <language> | code | Language of the snippet: snippet:code(python) |
object{ field:type, opt?:type } nests recursively; append [] for an array of objects.userAge?:number), never after the type.min on a boolean) is a parse error, where the fluent API silently ignores it.object{ ... }, the ! internal marker, media types, cache, and item are rejected (they only apply at the top level).\d is written pattern "\\d+".AxSignature.toString() renders every construct back to this grammar losslessly, so a signature round-trips — this is what lets a whole flow serialize its node contracts into mermaid %%ax directives (see the ax-flow skill).Real-world contracts, one line each — every entry below parses with s() as written (# lines are captions, not part of the signature):
text# Support triage: several class outputs plus a capped reply draft ticketText:string -> priorityClass:class "p0, p1, p2", sentimentClass:class "angry, neutral, happy", replyDraft:string(max 500) # Invoice extraction: regex-validated id, bounded totals, structured line items invoiceText:string -> invoiceNumber:string(pattern "^INV-\\d+$" "INV- then digits"), totalAmount:number(min 0), lineItems:object{ description:string, quantity:number(min 1), unitPrice:number }[] # Contact enrichment: optional format-validated outputs bioText:string -> contactEmail?:string(format email), websiteUrl?:string(format uri), birthDate?:string(format date) # RAG: cached corpus input plus per-item described citations corpusText:string(cache), userQuestion:string -> answerText:string, citedChunks:string(item "verbatim quote")[] # Code generation: language-tagged code outputs taskBrief:string -> pythonScript:code(python), testCases:code(python), riskNotes?:string # Chain of thought: internal reasoning stripped from the result problemText:string -> reasoning!:string, solutionText:string # Resume parsing: nested objects inside nested arrays resumeText:string -> candidateProfile:object{ fullName:string, yearsExperience:number(min 0), skillList:string[], education:object{ schoolName:string, degreeName?:string }[] } # Lead scoring: signature-level description, bounded score, class next step "Score sales leads" leadNotes:string -> fitScore:number(min 0, max 100) "0-100 fit", nextStep:class "call, email, drop" # Multimodal: top-level image input with an optional question productPhoto:image, question?:string -> productDescription:string, detectedObjects:string[] # Meeting audio: audio input, capped summary, per-item action list meetingAudio:audio -> meetingSummary:string(max 1000), actionItems:string(item "one action item")[] # Moderation: class verdict plus structured flagged spans postText:string -> moderationVerdict:class "allow, review, block", flaggedSpans:object{ spanText:string, reasonNote:string }[] # Translation: optional locale input sourceText:string, targetLocale?:string -> translatedText:string, glossaryHits:string[] # Text-to-SQL: cached schema plus SQL-tagged output schemaText:string(cache), questionText:string -> sqlQuery:code(sql), queryNotes?:string(max 200) # Calendar extraction: datetime fields and an optional end emailText:string -> eventTitle:string, startsAt:datetime, endsAt?:datetime, attendeeNames:string[] # Booking window: date range, bounded party size, and flexibility flag requestText:string -> stayWindow:dateRange, partySize:number(min 1, max 12), flexibleDates:boolean # Contract dates: date fields plus bounded notice period contractText:string -> effectiveDate:date, expiryDate?:date, autoRenews:boolean, noticeDays?:number(min 0) # Link audit: URL arrays and an optional primary URL pageText:string -> referencedUrls:url[], primaryUrl?:url # Config generation: JSON output plus per-item warnings requirementsText:string -> serviceConfig:json, setupWarnings:string(item "one warning")[] # Claims gate: cached policy, bounded confidence, and optional citation claimText:string, policyText:string(cache) -> isCovered:boolean, confidenceScore:number(min 0, max 1), citedClause?:string # Earnings extraction: structured period data plus a class outlook filingText:string(cache) -> revenueByPeriod:object{ periodLabel:string, amountUsd:number }[], guidanceTone:class "raise, hold, cut" # Pull request review: diff code, cached guide, structured comments, and verdict diffText:code(diff), styleGuide?:string(cache) -> reviewComments:object{ filePath:string, lineNumber:number(min 1), commentText:string(max 300) }[], overallVerdict:class "approve, revise" # Incident triage: severity class, optional service, and per-item runbook steps alertLog:string -> incidentSeverity:class "sev1, sev2, sev3", suspectedService?:string, runbookSteps:string(item "one step")[] # Product listing: image and file inputs with constrained listing outputs productPhoto:image, priceSheet?:file -> listingTitle:string(max 80), bulletPoints:string(item "one selling point")[], priceUsd?:number(min 0) # Study cards: nested object array with an optional difficulty tag chapterText:string -> flashCards:object{ questionText:string, answerText:string, difficultyTag?:string }[]
typescriptimport { ax, s } from '@ax-llm/ax'; const gen = ax('input:string -> output:string'); const sig = s('query:string -> response:string');
typescriptimport { f } from '@ax-llm/ax'; const sig = f() .input('userMessage', f.string('User input')) .input('contextData', f.string('Additional context').optional()) .input('tags', f.string('Keywords').array()) .output('responseText', f.string('AI response')) .output('confidenceScore', f.number('Confidence 0-1')) .output('debugInfo', f.string('Debug info').internal()) .build();
.input() and .output() accept any Standard Schema v1 compatible library — no wrapper, no adapter. Three shapes work everywhere:
typescriptimport { z } from 'zod'; import { f } from '@ax-llm/ax'; // Shape A: per-field schema — name first, then the schema, then optional ax hints const sig = f() .input('contextData', z.string().describe('Background context'), { cache: true }) .input('userQuestion', z.string().describe('Question to answer')) .output('reasoning', z.string().describe('Step-by-step thinking'), { internal: true }) .output('answer', z.string().describe('Final answer')) .build(); // Shape B: whole-object schema — decomposed into fields in declaration order const sig2 = f() .description('Answer questions from retrieved context') .input( z.object({ contextData: z.string().describe('Background context'), userQuestion: z.string().describe('Question to answer'), }), { fields: { contextData: { cache: true } } } // companion options map ) .output( z.object({ reasoning: z.string().describe('Step-by-step thinking'), answer: z.string().describe('Final answer'), }), { fields: { reasoning: { internal: true } } } ) .build();
Validation constraints from zod flow into ax's prompt validation:
typescript// String constraints: .email(), .url(), .min(), .max(), .regex() // Number constraints: .min(), .max() // Arrays: z.array(z.string()) // Enums: z.enum([...]) — NOTE: enum maps to ax class type, output fields only const sig3 = f() .input(z.object({ emailAddress: z.string().email().describe('Contact email'), username: z.string().min(3).max(20).describe('Handle'), score: z.number().min(0).max(100).describe('Numeric score'), })) .output(z.object({ priority: z.enum(['low', 'medium', 'high']).describe('Priority'), summary: z.string().describe('Result'), })) .build();
Companion options (AxFieldOptions) carry ax-specific hints that schema libraries don't represent:
| Option | Effect | |--------|--------| | { cache: true } | Mark input field as a prefix-cache breakpoint | | { internal: true } | Mark output field as internal scratchpad (stripped from result) |
The same Standard Schema shapes work on fn() tools via .arg(), .returns(), and .returnsField() — argument types are inferred from the schema:
typescriptimport { z } from 'zod'; import { fn } from '@ax-llm/ax'; // Whole-object zod on a tool — AI-SDK-style const lookupProduct = fn('lookupProduct') .description('Look up a product by name and return its current details') .arg( z.object({ productName: z.string().min(1).describe('Exact product name'), includeSpecs: z.boolean().optional(), }) ) .returns( z.object({ price: z.number(), inStock: z.boolean(), rating: z.number().min(1).max(5), }) ) .handler(async ({ productName, includeSpecs }) => ({ price: 79.99, inStock: true, rating: 4.3, })) .build(); // Per-argument form — mix with f.*() args, attach ax hints const searchDocs = fn('searchDocs') .description('Search indexed docs') .arg('query', z.string().min(1), { cache: true }) .arg('limit', z.number().int().positive().optional()) .returnsField('results', z.array(z.string())) .handler(async ({ query }) => []) .build();
typescriptimport { s, f } from '@ax-llm/ax'; const sig = s('base:string -> result:string') .appendInputField('extra', f.json('Metadata').optional()) .appendOutputField('score', f.number('Quality score'));
Type creators:
f.string(desc), f.number(desc), f.boolean(desc), f.json(desc)f.image(desc), f.audio(desc), f.file(desc), f.url(desc)f.email(desc), f.date(desc), f.datetime(desc), f.dateRange(desc), f.datetimeRange(desc)f.class(['a','b','c'], desc), f.code(desc)f.object({ field: f.string() }, desc)Chainable modifiers (method chaining only, no nesting):
.optional() - make field optional.array() / .array('list description') - make field an array.internal() - output only, hidden from final output.cache() - input only, mark for prompt cachingtypescript// Correct: pure fluent chaining f.string('description').optional().array() f.string('context').cache().optional() f.object({ field: f.string() }, 'item desc').array('list desc') // Wrong: nested function calls (removed) f.array(f.string('description')) // REMOVED f.optional(f.string('description')) // REMOVED f.internal(f.string('description')) // REMOVED
typescriptf.string('username').min(3).max(20) f.string('email').email() f.string('website').url() f.string('birthDate').date() f.string('timestamp').datetime() f.string('pattern').regex('^[A-Z0-9]')
typescriptf.number('age').min(18).max(120) f.number('score').min(0).max(100)
typescriptconst sig = f() .input('formData', f.string('Raw form data')) .output('user', f.object({ username: f.string('Username').min(3).max(20), email: f.string('Email').email(), age: f.number('Age').min(18).max(120), bio: f.string('Bio').max(500).optional(), website: f.string('Website').url().optional(), tags: f.string('Tag').min(2).max(30).array() }, 'User profile')) .build();
typescriptconst sig = f() .input('staticContext', f.string('Context').cache()) .input('userQuery', f.string('Dynamic query')) .output('answer', f.string('Response')) .build();
Good: userQuestion, customerEmail, analysisResult, confidenceScore Bad: text, data, input, output, a, x, val (too generic), 1field (starts with number)
AxChatAudioOutput.audio[] is not supported.typescript// Chain of Thought 'problem:string -> reasoning!:string, solution:string' // Classification 'email:string -> priority:class "urgent, normal, low"' // Multi-modal input 'imageData:image, question?:string -> description:string, objects:string[]' // Scripted speech output 'question:string -> speech:audio, summary:string' // Data Extraction 'invoiceText:string -> invoiceNumber:string, totalAmount:number, lineItems:json[]' // Constrained string form (no fluent builder needed) 'reviewText:string(max 2000) -> rating:number(min 1, max 5), themes:string(item "a theme")[]' // Nested object output in the string form 'profileText:string -> profile:object{ fullName:string, age?:number(min 0) }' // With description '"Answer TypeScript questions" question:string -> answer:string, confidence:number'
string(max 500), number(min 0, max 10), string(format email)) and inline object{ ... } before switching to fluent/zod just for constraints. Reserve fluent/Standard Schema for zod/valibot-backed fields.f() fluent builder, NOT nested f.array(f.string()) -- those are removed.text, data, input)..internal() / { internal: true } is output-only (for chain-of-thought reasoning)..cache() / { cache: true } is input-only (for prompt caching).f.email(), f.url(), f.date(), f.datetime() are shorthand for f.string().email() etc.; f.dateRange() and f.datetimeRange() return { start: Date; end: Date }.z.enum() maps to ax's class type — only valid on output fields.f.image() / f.audio() / f.file() — zod has no equivalent.Fetch these for full working code:
Other measured skills in the registry, with their headline benchmark lift.