Lesson 12 · Domain 4 — Prompt Engineering & Structured Output (20% of exam)

Batch Processing & Independent Review

Closing out Domain 4 with two efficiency questions: when should a workload run asynchronously instead of live, and when should review happen in a different "mind" than the one that generated the work?

The Message Batches API: the tradeoff in one line

50% cheaper, up to a 24-hour processing window, no latency SLA. That's the whole shape of the decision. It fits non-blocking, latency-tolerant workloads well — overnight reports, weekly audits, nightly test generation. It's the wrong tool the moment something is on a blocking path, like a pre-merge CI check that needs to complete in minutes: there's no guarantee it will, and occasionally it won't.

One structural limit to remember: the Batch API doesn't support multi-turn tool calling within a single request — it can't execute a tool mid-request and reason over the result inside that same batched call.

const batch = await client.messages.batches.create({
  requests: documents.map((doc) => ({
    custom_id: doc.id, // correlates each response back to its request
    params: {
      model: "claude-sonnet-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: extractionPrompt(doc) }],
      tools: [extractionTool],
    },
  })),
});
// Poll (or wait up to 24h), then match results back to documents via custom_id.

custom_id is what makes batch results usable at scale — it's how you know which response belongs to which original request once results come back out of order. When a batch partially fails, resubmit only the failed custom_ids (with whatever fix they needed — chunking a document that exceeded context limits, say) rather than resubmitting the whole batch. And refine your prompt against a small sample before scaling to the full volume — a batch is expensive to iterate on once it's already 24 hours deep.

Why the same session shouldn't review its own work

A model that just generated something carries its own reasoning forward in context. It's not lying to itself exactly — it's that the reasoning that led to a decision is still right there, making that decision feel settled rather than something to re-examine. An independent instance, with none of that prior reasoning in its context, catches more — it has nothing invested in the original answer being right.

Multi-pass review for large surface areas

A single pass over a large multi-file change dilutes attention and tends to produce vaguer, sometimes contradictory findings. Splitting the work — a focused per-file pass for local issues, plus a separate cross-file integration pass for things like data flowing incorrectly between files — produces sharper results than either alone. This is the same "attention dilution" idea from Lesson 4's task decomposition, applied specifically to review work.

Matching latency requirements to API choice
If you only remember one thing from this lesson: ask "does this block something a person or a pipeline is waiting on right now?" Yes → synchronous Messages API. No, and it can tolerate up to 24 hours → Batches API, at half the cost. Getting this backwards either wastes money (paying full price for something that could've batched) or breaks a deadline (batching something that needed to finish in minutes).

Why this matters beyond the exam

Primary source: Anthropic's Claude API docs cover the Message Batches API directly, including custom_id correlation and processing windows — this lesson closes Domain 4, compressing Task Statements 4.5 and 4.6.

Check your understanding

Q1. You need pre-merge PR checks to block a build within minutes. Should you use the Message Batches API?
Q2. 40 of 500 batched extraction requests fail validation. What's the most efficient recovery?
Q3. Why does an independent Claude instance often catch more issues reviewing code than the same instance that generated it?

That's all of Domain 4. Ready for Domain 5, the last one — context management and reliability? Say the word when you're ready.