Study guides / CCAR-P / Domain 2

Solution Design & Architecture · Lesson 3 of 6

2.3 - Workflow, Agentic, or Augmented LLM: Choosing the Pattern

Decide when a single augmented call, a fixed workflow or an agent is the right architecture, and justify the choice on cost, reliability, latency and controllability rather than on how sophisticated it sounds.

The most consequential design decision after "is this a Claude task?" is who controls the flow: your code, or the model. Anthropic's vocabulary is worth using precisely. The augmented LLM is the building block: a model enhanced with retrieval, tools and memory. Workflows are systems where LLMs and tools are orchestrated through predefined code paths. Agents are systems where the LLM dynamically directs its own process and tool usage. Scenario questions describe a task and ask for the pattern; the defensible answer is normally the simplest pattern that meets the success criteria, with the reason stated.

The ladder of autonomy

PatternWho decides the next stepFits whenWhat you pay
Single callYou (there is one step)The answer follows from the input: classify, extract, summarise, draftLowest cost and latency
Augmented callYour code fetches context, or the model picks a tool within a turnOne logical step that needs outside knowledge or an actionExtra round trip per tool call
WorkflowYour code, along a predefined pathSteps are known in advance; you need predictability and per-step testingMore calls, more latency, no adaptivity
AgentThe model, in a loopSteps cannot be predicted or hardcoded and you can trust its judgementMore tokens, latency and variance; needs guardrails
Multi-agentA model orchestrating other modelsCovered in lesson 2.4Highest

Anthropic's guidance is to start with simple prompts, optimise them with evaluation, and add multi-step agentic systems only when simpler solutions fall short, because agentic systems trade latency and cost for task performance. Treat every step up the ladder as a cost you must justify with eval results.

When a single augmented call is enough

If you already know what to fetch, fetch it in code and put it in the context. The account record for the authenticated user, the ticket text, the top retrieved passages: none of these needs a loop. Let the model choose tools only when what to fetch depends on the request. Anthropic's tool-use documentation is explicit that tool use does not fit when the model can answer from training alone, when the interaction is one-shot Q&A with no side effects, or when tool-call latency would dominate a trivial response; every tool call is at least one extra round trip.

Memory belongs here too. The memory tool lets Claude store and retrieve information across conversations, but it is executed client-side by your application against storage you control. A single call that consults memory is still an augmented call; it becomes an agent only when the model keeps steering a multi-step process. Structured extraction, classification and summarisation with schema-constrained output are the archetypal single-call tasks.

When a workflow beats an agent

Choose a workflow when you can write the steps down before seeing the input. Anthropic describes five recurring workflow shapes: prompt chaining, routing, parallelization (sectioning and voting), orchestrator-workers and evaluator-optimizer. Lesson 2.5 covers chaining, routing, sectioning and plan-then-execute in depth, and lesson 2.4 covers orchestration. What matters for pattern choice is what a workflow gives you:

The price is rigidity: a workflow cannot change course when step 2 uncovers something unexpected. Also separate the pattern from the application shape. Independent items with no latency need (nightly re-tagging of a ticket archive) suit the Message Batches API, which the documentation prices at 50% less with most batches finishing within an hour and a 24-hour expiry. Batching is a delivery choice for independent single calls or per-item workflows, not a substitute for an agent, and it is the wrong choice for a user waiting on a reply. One product routinely mixes shapes: synchronous chat, a background agentic task, and a nightly batch job.

Common exam distractor

Three distractors recur. "Use an agent because the task is complex." Complexity is not the test; unpredictability of the steps is. A long but fixed procedure is a workflow. "Use an agent for thousands of independent items." Items that do not depend on each other need no loop; a batch of single calls is simpler, cheaper and more reliable. "Use the Batches API because it is cheaper" for anything a person is waiting on. The opposite error also exists: forcing a rigid pipeline onto open-ended investigation where the next step depends on what the last one found.

When an agent is warranted

Anthropic describes agents as suited to open-ended problems where it is difficult or impossible to predict the number of steps and you cannot hardcode a path, where you have some trust in the model's decision-making, and in trusted environments, with extensive testing in sandboxes and appropriate guardrails. Turn that into architect checks before you approve one:

  1. Ground-truth feedback. Does each step return something real (tool output, test results, a record) that lets the agent detect it is wrong? Without it, errors compound silently.
  2. Bounded blast radius. Are actions reversible or scoped by least privilege?
  3. Economics. Anthropic's research-system write-up reports that agents typically use about four times the tokens of chat interactions. Is the value per task high enough?
  4. Bounds you can enforce. A maximum number of turns and a spend cap. The Agent SDK exposes a per-agent turn limit and a query-level budget option for exactly this.
  5. An evaluation plan that grades outcomes, not just steps (Domain 3).

Framework choice is a separate, smaller decision. Anthropic advises starting with direct API calls because many patterns take a few lines and frameworks add abstraction that can hide prompts and responses; if you adopt one, such as the Claude Agent SDK, understand what it does underneath. The SDK earns its place when you need its loop, built-in tools and subagent support rather than code you would otherwise own and debug.

Key concept: pattern follows control flow

Ask in order: (1) Is there more than one model step? If not, use a single or augmented call. (2) Can the steps be written down in advance? If yes, use a workflow. (3) Are the items independent and is nobody waiting? If yes, batch them. (4) Only if the path genuinely cannot be predicted, and you can bound and verify the loop, use an agent. State the trade-off you accepted: what you paid in cost, latency or variance, and what you got in adaptability.

Worked comparison

One team, one week, three requests. (1) "Answer an account question within two seconds": a single augmented call with the account record fetched in code, streamed. (2) "Re-tag every ticket from last year under the new taxonomy": independent items with no latency need, so a batch of single structured-output calls. (3) "Investigate this production incident across three services": each query depends on what the last one returned and the step count is unknown, so a bounded agent with read-only tools. Same model family, three architectures, because the requirements chose the shape.

Exam traps

Practice question

An e-commerce team wants to automate refund requests. Every request follows the same steps in the same order: classify the request, look up the order, check the refund policy, draft the customer reply. Compliance requires each step's output to be logged and behaviour to be consistent between runs. Which architecture is the best fit?

  • A A single agent with order-lookup, policy and email tools that decides the order of steps itself, for maximum flexibility and so it can handle unexpected cases without code changes

    The path is already known, so the agent's adaptivity buys nothing while adding variance, unpredictable cost and a harder audit trail. Flexibility is a cost here, not a benefit.

  • B A multi-agent system with one specialist agent per step, coordinated by an orchestrator that assigns the work and merges each agent's output into the final reply

    Nothing here calls for context isolation, parallelism or specialisation, so the orchestrator adds token overhead and handoff losses without a benefit (lesson 2.4). It is also less predictable than a fixed path.

  • C A single prompt containing the full refund policy and instructions, with no tools or lookups, to avoid extra round trips and keep latency as low as possible

    The order details are live data held in a system of record. A prompt without a lookup would have to guess, so the design misses the tool or code-fetch step the task needs.

  • D A fixed workflow: code runs the four steps in order, calling Claude to classify and to draft and fetching the order itself, with each step's output logged and evaluated Correct

    Known steps, a need for consistency and per-step auditability are the classic signals for a workflow. Control flow stays in code, each step can be tested, and cost and latency are bounded.

Build exercise: Prototype one task three ways and write the decision record

Advanced · 2 hours

You'll practice:

  1. Choose a task with a lookup and a judgement (for example: 'is this order eligible for a refund and what should we tell the customer?'). Write 10 test cases with expected outcomes and three success criteria including latency and cost.

    You cannot compare patterns without a shared test set and criteria. The comparison, not the implementation, is the deliverable.

    You should see: A cases.json with 10 inputs and expected eligibility decisions, and a list of three measurable criteria.

    Hints
    1. What is the smallest set of cases that includes at least two edge cases where a wrong pattern would fail?
    2. Include a case with a missing order, one outside the return window and one ambiguous request.
    3. Example criteria: at least 9 of 10 decisions correct; median latency under [n] seconds; total tokens per case under [n]. Keep the orders in a small dict acting as your system of record.
  2. Implement Version A, the augmented single call: fetch the order and policy in code, place them in the prompt, and make one call.

    This is the baseline. If it meets the criteria, nothing further is justified.

    You should see: A function that returns a decision and a reply from exactly one model call per case.

    Hints
    1. Which information can your code obtain without asking the model?
    2. Look up the order by ID before the call and include it and the policy text in the user message.
    3. import anthropic
      client = anthropic.Anthropic()
      MODEL = 'claude-sonnet-5'  # check the models overview page for current IDs
      def version_a(case, orders, policy):
          order = orders.get(case['order_id'], 'ORDER NOT FOUND')
          prompt = f'Policy:\n{policy}\n\nOrder: {order}\n\nRequest: {case["request"]}\n\nDecide eligibility and draft a reply.'
          r = client.messages.create(model=MODEL, max_tokens=500, messages=[{'role': 'user', 'content': prompt}])
          return next(b.text for b in r.content if b.type == 'text'), r.usage
  3. Implement Version B, the workflow: three code-controlled steps (classify, decide against the policy, draft the reply), each a separate call with its output logged.

    This shows what per-step control costs in calls and latency and what it buys in inspectability.

    You should see: Three logged step outputs per case, and a total call count you can compare with Version A.

    Hints
    1. Where would a failure be easiest to locate if each step were separate?
    2. Pass the previous step's output as the next step's input and store each output in a list of dicts.
    3. steps = [('classify', 'Classify this request as refund, exchange or other: '), ('decide', 'Given the policy and order, is it eligible? Answer yes or no with a reason: '), ('draft', 'Draft a reply to the customer given this decision: ')]
      # for each step: call client.messages.create with steps[i][1] + previous_output, append {'step': name, 'output': text} to the log
  4. Implement Version C, a bounded agent: give the model get_order and get_policy tools and run a manual loop keyed on stop_reason with a maximum number of turns and is_error on tool failures.

    Building the loop yourself shows what an agent adds (model-chosen steps) and what you must add (bounds and error handling).

    You should see: The loop ends on any stop reason other than tool_use, raises when the turn limit is exceeded, and returns tool errors to the model with is_error set.

    Hints
    1. What would stop the loop if the model kept calling tools forever?
    2. Loop for at most max_turns; append the assistant content, then a user message whose content is a list of tool_result blocks.
    3. def run_agent(question, tools, execute, max_turns=6):
          messages = [{'role': 'user', 'content': question}]
          for _ in range(max_turns):
              r = client.messages.create(model=MODEL, max_tokens=1024, tools=tools, messages=messages)
              if r.stop_reason != 'tool_use':
                  return r
              messages.append({'role': 'assistant', 'content': r.content})
              results = []
              for b in r.content:
                  if b.type == 'tool_use':
                      try:
                          out, err = str(execute(b.name, b.input)), False
                      except Exception as e:
                          out, err = 'Tool failed: ' + str(e), True
                      results.append({'type': 'tool_result', 'tool_use_id': b.id, 'content': out, 'is_error': err})
              messages.append({'role': 'user', 'content': results})
          raise RuntimeError('max_turns exceeded')
  5. Run all three versions on the 10 cases and tabulate correct decisions, calls per case, total input plus output tokens (from response.usage) and latency.

    The decision has to rest on measurements. Token and call counts make the cost of autonomy visible.

    You should see: A three-row comparison table. Expect the agent to use more calls and tokens than A; whether it earns them depends on your cases.

    Hints
    1. How will you count tokens across several calls per case?
    2. Sum usage.input_tokens and usage.output_tokens over every response for a case, and time each case with time.time().
    3. Keep a dict per version with correct, calls, tokens and seconds; increment tokens with r.usage.input_tokens + r.usage.output_tokens after every call.
  6. Write a one-page decision record: the chosen pattern, the rejected ones, the measured trade-off, and the specific evidence that would justify moving up to a workflow or an agent later.

    Architects are judged on justification. A record that names triggers for revisiting the choice is what a reviewer can act on (lesson 5.2).

    You should see: A record that starts from the simplest pattern that met the criteria and lists at least two concrete triggers, such as a failure class the baseline cannot handle.

    Hints
    1. If Version A met the criteria, what would have to change for you to reconsider?
    2. Choose the simplest version that met all three criteria, and state what a failing eval case would have to look like to justify the next rung.
    3. Example: Chosen: Version A. Rejected: B (adds two calls and latency for no accuracy gain on 10 of 10 cases), C ([x] times the tokens). Revisit if: policy checks need more than one lookup that depends on the previous result, or accuracy on a 50-case set drops below [target].

Sources