Add derivation ai
Wire up a Folio `kind: ai` derivation — the YAML file under `derivations/`, the prompt template (or `prompt_ref`), `output: text` vs `output: json` with `output_schema`, and a one-row materialize smoke. Invoke when the user asks to "have an LLM fill a column", "auto-classify", "summarize each row", or anything that maps a free-text field to a structured value via Claude/OpenAI/etc.From its SKILL.md
npx -y skills add nyuta01/folio --skill add-derivation-aiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.4 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
Add an ai derivation to a Folio sheet
Author a derivations/<target>.yaml of kind: ai, declare the
contract field as x-derived: true, and verify with one
folio materialize call.
When this skill applies
- The user says "make Folio fill this column with an LLM" or "classify / summarize / extract / translate every row".
- The answer is fundamentally fuzzy — free-text classification, summarization, extraction from messy inputs.
- The user wants Folio to manage the cache, retries, prompt versioning,
and cost reporting — i.e. they don't just want a
forloop calling the SDK.
This skill does not apply when:
- The answer is deterministic (use
kind: pythonorkind: sql). - The data already exists in a CSV / JSON file (use
kind: import). - The data lives in another Folio sheet keyed by the same PK (use
kind: cross_sheet— see theadd-derivation-cross-sheetskill).
Prerequisites
- A working Folio sheet (see
folio-quickstart). You should already havecontract.yamlandrecords.jsonlandfolio validate <sheet>exits 0. - An
ANTHROPIC_API_KEY(or whichever provider your AIClient targets) in the environment, or aStubAIClientfor offline runs. folio --helpshows thematerializesubcommand.
Procedure
-
Pick the target field name — what column the LLM will fill. Convention: snake_case ASCII, the same as other fields. Add it to
contract.yamlwithx-derived: trueso it's clearly not a human-edited column:- name: industry_tag logicalType: string x-derived: true x-inputs: [company_name] # mirrors `inputs:` in the derivation -
List
inputs— every field the prompt reads. The cache hashes these, so an honest list is what makes the cache correct. If the prompt reads no field (very rare forai), useinputs: []. -
Choose
output: textoroutput: json. One target →text. Multi-target →jsonplusoutput_schema. There is no "list" or "tuple" output — you express that as a JSON object. -
Write the prompt. Use
prompt:for one-liners;prompt_ref:for anything multi-paragraph.prompt_refpaths are relative to the sheet root and the file's bytes go intoinput_hash, so editing the prompt invalidates the cache (correct behaviour). -
Create
derivations/<target>.yaml. Skeleton (single target, text output):# derivations/industry_tag.yaml targets: [industry_tag] inputs: [company_name] kind: ai model: claude-sonnet-4-6 prompt: | Industry of {{ company_name }} in one word. output: textMulti-target with structured JSON output:
# derivations/enrich.yaml targets: [industry, employee_count] inputs: [company_name, country] kind: ai model: claude-sonnet-4-6 prompt_ref: prompts/enrich.md output: json output_schema: industry: string employee_count: integer -
(Optional) Tune the loop.
materialization:is a sub-block:materialization: respect_human_override: true # default — skip cells a human edited retries: 0 # default retry_delay_seconds: 1.0Retries cover AIClient errors only (timeouts, rate-limits). A missing
output_schemakey is a deterministic content error — it does not retry. -
Validate, then run materialize on one record. Always smoke-test on a single record before letting the LLM loose on every row:
folio validate ./customers folio materialize ./customers industry_tag \ --actor agent:demo \ --ids cust_001folio materializetakes the target as a positional argument (one at a time; omit it to materialize every derivation), and--idsis comma-separated or repeated.The output is the §10.6 envelope:
{"materialized": 1, "skipped": 0, "failures": [], "total_cost": 0.0021} -
Inspect the value & provenance. Read it back:
folio list ./customers --filter "id = ?" --param cust_001 folio provenance ./customers cust_001 industry_tagfolio provenancetakes the record ID and field as positional arguments. The provenance line includesmodel,input_hash, andcost_usd.
Verify
folio validate <sheet>
folio materialize <sheet> <field> --actor agent:demo --ids <one_id>
Both should exit 0 and the envelope's failures should be [].
Tips & idioms
- Substitution shape.
{{ field }}substitutes the JSON-encoded value — strings get quotes, integers stay bare, arrays become bracket lists. This keeps prompts safe against quotes / newlines in the data. - Deterministic first, AI fallback. Common pattern: a
pythonderivation that handles the easy cases, then anaiderivation on the long tail. Keep the AI rows scarce — the cache is the savings. - Prompts in their own file. Once a prompt is more than ~3 lines,
use
prompt_ref:and put the file underprompts/. Reviewers hate diffs of multi-line YAML strings. - Multi-target = one cache key. All targets in a multi-target
derivation share an
input_hash. They update together or stay cached together — that's the invariant.
Common mistakes (don't make them)
- Forgetting
x-derived: trueincontract.yaml. The materialize loop still runs, butfolio statusand human editors will treat the field as a normal user-editable column. output: textwith multipletargets. Folio will reject the contract — text output writes one cell. Useoutput: json+output_schema.- Listing inputs the prompt doesn't actually read. The cache
re-runs every time those (irrelevant) fields change. Keep
inputs:honest. - Editing the prompt without expecting a re-run. The prompt body is
in the
input_hash. That's the design — it means an old, outdated answer cannot stick around silently. - Hardcoding an API key in
derivations/*.yaml. Folio reads keys from the environment. The YAML stays clean and shippable.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.