Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Tutorial: Expense approval with HITL

Combine LLM classification, a sandboxed (ScaiBunker) policy check, and a human review queue to approve expenses end-to-end.

The shape#

flowchart LR Entry["API Entry"] Classify["Classify<br/>(Flexible)"] Provision["Provision Sandbox"] Policy["Policy Check<br/>(Exec)"] Review["Review<br/>(HITL)"] Notify["Notify<br/>(Queue Publish)"] Destroy["Destroy Sandbox"] Entry --> Classify --> Provision --> Policy --> Review --> Notify Provision --> Destroy

Steps#

1. Start from the example#

Catalog…ExamplesExpense ApproverOpen. The flow has six nodes pre-wired.

2. The classifier#

node_classify (Flexible Prompt) extracts {amount, category, vendor, justification} from the submitted text. Returns structured output via output_schema.

3. Provision a sandbox#

node_provision (Provision Sandbox) compiles to scaibunker.create(...). Configured:

  • Image: python-3.12
  • Lifecycle: ephemeral (per-invocation)
  • CPU: 1000 millicores
  • Memory: 512 MB
  • Network: registry (default — image registry + minimal allowlist)

Output is a bunker handle that flows into the next node.

4. Policy check#

node_policy_check (Execute Command) runs the policy script in the sandbox:

  • Command: ["python", "/policy/check.py", "--amount", "{{classify.amount}}", "--category", "{{classify.category}}"]
  • Timeout (s): 30

The policy script lives in your reference data and is mounted into the sandbox at runtime. Output is a JSON object like {verdict: "auto-approve" | "needs-review" | "auto-reject", reason: "..."}.

5. HITL Review (when needed)#

node_review (HITL Review) is gated by an upstream Decision node that branches on policy_check.verdict. When the verdict is needs-review, the flow pauses and publishes a review request to the configured ScaiQueue queue.

Sections of the review form:

  • Context: the original expense, classification, policy verdict + reason.
  • Input: editable approval amount (in case the reviewer wants to partial-approve).
  • Decision: approve, reject, edit_and_approve.

6. Notify#

node_notify (Queue Publish) publishes a decision-made message to the queue downstream systems consume:

jsonc
{ "queue": "expense-review",
  "message_type": "decision-made",
  "payload": {
    "decision": "{{node_review.decision}}",
    "amount": "{{node_classify.amount}}"
  } }

7. Destroy the sandbox#

node_destroy (Destroy Sandbox) runs in parallel to the review path, releasing resources as soon as the policy check is done. Wired via a sequential edge from node_provision.out directly to node_destroy.in_bunker — fire-and-forget.

8. Flow-level config#

  • Models: one primary (the classifier model).
  • Bunker:
    jsonc
    { "lifecycle": "ephemeral", "image": "python-3.12",
      "cpu_millicores": 1000, "memory_mb": 512,
      "network_profile": "registry" }
    
  • ScaiQueue:
    jsonc
    { "scope": "finance-approvals",
      "queues": [
        { "slug": "expense-review", "purpose": "review", "consumer_mode": "fifo", "max_retries": 3 },
        { "slug": "expense-escalation", "purpose": "escalation", "consumer_mode": "priority", "max_retries": 5 }
      ] }
    
  • Plugins: scaibunker, scaiqueue (auto-added by the compiler).

9. Deploy#

The deploy pipeline:

  1. Provisions the finance-approvals scope + two queues in ScaiQueue.
  2. Creates the Core in ScaiGrid.

10. Invoke#

bash
1
2
3
4
curl -X POST "https://scaigrid.scailabs.ai/v1/modules/scaicore/cores/${CORE_ID}/invoke" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"input": {"text": "Conference ticket, $847 for ScaiCon 2026, billed by Acme Events"}}'

For high-amount or off-category expenses, the invocation pauses at the HITL Review. Reviewers see a card in ScaiQueue's review UI; clicking approve resolves the checkpoint and the flow continues to publish the decision.

What you learned#

  • ScaiBunker integrates as five compute_* node kinds; flow-level bunker capability + per-call args.
  • HITL Review under Option B: one checkpoint with hitl_target, runtime owns the publish + resolve.
  • Pre-deploy ScaiQueue provisioning is automatic when flow.config.scaiqueue is set.
  • Parallel branches (review path + destroy path) are just two outgoing edges from the same source port.

Next#

  • Publish as chat model — expose this flow as a chat surface so a chat client can submit expenses conversationally.
Updated 2026-05-18 16:05:24 View source (.md) rev 3