> ## Documentation Index
>
> Fetch the complete documentation index at: https://lithi.ai/llms.txt
>
> Use this file to discover all available pages before exploring further.

---
title: Let your AI agent run a batch and read typed results
description: >-
  Have your AI agent classify a small synthetic collection and read typed
  results back. The agent plans and quotes; you approve the spend before
  anything runs.
route: /docs/compute/examples/agent-workbench
page_id: docs-compute-examples-agent-workbench
page_type: tutorial
content_layer: guided documentation
surface_profile: guided_docs
audience: People whose AI agent will submit and read Compute work on their behalf
voice: D
reader_question: How does my own AI agent send a batch to Compute and get typed results back?
primary_action: Run a twenty-item agent classification test
source_locale: en-US
source_status: APPROVED
source_version: docs-compute-examples-agent-workbench-en-us-2026-09-05-v1
last_updated: '2026-09-05'
robots: index
claim_registry_pinned: true
claim_registry_resync: "npm run governed:tsx -- scripts/build-compute-public-claim-registry.ts --write"
---

## What you get

A batch your agent submitted, and results your agent can act on without reading prose.

- One typed row per item in `data/part-*`, carrying `lithi_result`, `lithi_confidence` and `lithi_evidence`.
- A typed status per item in `lithi_status`, so a refusal is never mistaken for a label.
- Anything the workflow declined to label in `abstentions/part-*`, with a reason and a next action.
- `metrics.json` and `receipt.json`, so your agent can reconcile what it asked for against what it was charged for.

Every result carries evidence. A label your agent cannot trace back to the text it came from is not usable, however confident it looks.

## What you need

Twenty synthetic items and a declared label set. Synthetic matters here, because you are testing the loop rather than the data.

```text
request_id,channel,language,text
REQ-2001,web_form,en,Card was charged twice on the same order.
REQ-2002,web_form,en,How do I add a second workspace member?
REQ-2003,chat,es,Necesito cancelar antes de la renovacion.
REQ-2004,chat,en,,
REQ-2005,web_form,en,Please export everything you hold about me.
```

Declare `request_id` as your `item_id_field`, and declare your labels as a closed list with types. Row four is empty, so it should be refused at validation rather than labeled. Row five should land in the label you use for a rights request, which is the one you least want quietly mislabeled.

Your agent also needs a work key. A work key holds work scopes only. It can quote, submit and read results. It can never invite a member, register a destination or touch finance.

## Run it

1. Have the agent shape the request. `lithi.plan()` and `lithi.recommend()` are local helpers: they compare workflows and draft parameters, and they neither quote, admit nor run paid work.
2. Have the agent quote with `lithi.quote`. The quote carries a maximum charge, an expiry and a signature, and it authorizes nothing at all.
3. Read the quote yourself and approve it. Approval is a separate act by a person or a signed policy, and it is the only thing that lets paid work start.
4. Submit with `lithi.batch` for the collection, or `lithi.run` for a single item.
5. Poll `lithi.status` until the job is terminal, then fetch with `lithi.result`.

Those five tools are the entire work surface your agent gets. There is no sixth, and no management action hides inside them: [how work keys and management stay separate](/docs/compute/concepts/work-and-management).

## Read the result

Open `result-manifest.json` before reading a single row. Part files can exist while a job is still running, so their presence never means the work finished.

Then have the agent branch on `lithi_status`, not on the text of `lithi_result`. A label, a refusal and an abstention are different outcomes, and every non-success carries a safe reason and a concrete next action to take.

Read three things by hand on this first run. Check that the empty row was refused with a typed code. Check that the rights request got the right label. Then open the cited evidence for the five lowest-confidence rows and confirm the text supports the label.

If the job returns `REPLAN_REQUIRED`, the world moved between the quote and admission. Have the agent quote again rather than retrying the old submission: [what a quote does and does not authorize](/docs/compute/quotes).

## Where it stops

Your agent gets typed results. It does not get authority.

Nothing is sent, published, filed or written back. No reply goes out, no ticket is updated, and no record in your own systems changes because a label arrived. The agent proposes; you decide.

The approval gate stays with you even when the agent runs unattended. A quote is a price, not permission, and an unapproved quote simply expires. Set a spend ceiling so an agent loop cannot approve its way past what you intended.

No accuracy is promised. Confidence is a measurement, and the label set is yours, so a wrong label usually means the label list needs a clearer boundary. A person reviews the low-confidence rows and the abstentions before any of this touches a real collection.
