Use casesDocument pipeline

Build a document pipeline

Invoices, contracts, forms, applications - the shape is always the same. A file arrives, some fields need pulling out of it, most of them are obvious, and a few are not. The ones that are not must reach a human rather than being quietly guessed.

This is deliberately not a chat agent. The order of steps matters, the run has to survive a restart, and nothing may be processed twice. That combination is what workflows are for.

By the end you will have a pipeline that extracts typed fields from an uploaded document, routes low-confidence results to a person for approval, and returns a downloadable artifact.

Accept the document

Files come in through the attachment API and are referenced by identifier in the run input. The file store keeps them durably, outside any execution workspace - see Use attachments.

For this walkthrough the workflow takes the attachment id and the document type as inputs.

Extract typed fields

The extraction step is an agent node with a schema, so you get typed fields back instead of prose you have to parse. Define the schema at file scope and reference it:

axg 1

schema Invoice """
{
  "type": "object",
  "properties": {
    "vendor":     { "type": "string" },
    "total":      { "type": "number" },
    "currency":   { "type": "string" },
    "due_date":   { "type": "string" },
    "confidence": { "type": "number" }
  },
  "required": ["vendor", "total", "currency", "confidence"]
}
"""

workflow extract_invoice {
  input attachment_id
  input document_type

  extract = agent extractor:
    "Extract the invoice fields from the attached document ({{ input.document_type }}).
     Set confidence between 0 and 1 for how certain you are of the total and due date."
    schema Invoice

Asking the model for its own confidence is what makes the next step possible. It is not a calibrated probability, and it does not need to be - it just needs to be lower when the document was a bad scan than when it was clean.

Branch on confidence

Route confident extractions straight through, and doubtful ones to a person:

  extract -> review when extract.structured.confidence < 0.85
  extract -> record else

Every arm must reach its own return, which the compiler checks before the workflow ever runs.

Pause for a human

The review arm stops the run and waits. This is durable - the run is not holding a connection open, and it survives a server restart while it waits:

  review = approval:
    "Low-confidence extraction from {{ input.attachment_id }}.
     Vendor: {{ extract.structured.vendor }}
     Total: {{ extract.structured.total }} {{ extract.structured.currency }}
     Approve to record, or reject to send for manual entry."

  review -> record when review.approved
  review -> rejected else

See Pause for human approval for the exact node fields and how a client answers the request.

Record the result and hand back an artifact

  record = agent recorder:
    "Record this invoice and produce a one-page summary:
     {{ extract.structured }}"

  record -> return {
    status:  "recorded",
    vendor:  extract.structured.vendor,
    total:   extract.structured.total,
    summary: record.response
  }

  rejected -> return {
    status: "manual_entry_required",
    vendor: extract.structured.vendor
  }
}

Anything a person should keep - the summary, an export, a corrected copy - belongs in an artifact rather than in the run output or a sandbox directory.

Check it compiles, then submit

axl workflow check ./extract_invoice.axg

That runs the same compiler and validator the server runs, entirely locally. Unknown node references, cycles, and missing fields fail here with line numbers rather than at submit time.

Then submit it and watch:

axl workflow submit ./extract_invoice.axg \
  --input '{"attachment_id":"att_123","document_type":"invoice"}' --follow

What you now have

  • Typed extraction, validated against a schema rather than parsed out of prose
  • A confidence threshold you control, sending only the doubtful cases to a person
  • A durable pause for approval that survives restarts and does not hold a connection
  • Exactly-once execution of the recording step, across multiple server instances
  • Compile-time validation of the whole graph before anything runs
  • A downloadable artifact instead of a file stranded in a workspace

Tuning it in production

Too much goes to review. Raise the threshold, and look at what the low-confidence cases have in common - often one document layout the extraction prompt does not describe well.

Too little goes to review. Lower it, and consider scoring specific fields rather than the whole extraction. A model is usually more certain about a vendor name than a due date.

It needs to run on a schedule. Register the definition by name and point a schedule at it.

Next

On this page