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 InvoiceAsking 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 elseEvery 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 elseSee 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.axgThat 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"}' --followWhat 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
- Workflow how-to recipes - retries, timeouts, parallelism, and more.
- Workflow operations - submitting, streaming, and operating runs.
- Run recurring work - firing this pipeline on a clock.
- Research agent - a workflow shaped around breadth instead of sequence.