Sections in this lesson

Agents vs Workflows

Choose between deterministic code, fixed LLM workflows and model-directed tool loops using task uncertainty and operational cost.

Reviewed: · Content owner: Interview Preparator

What you will learn

  • Identify who chooses the next operation.
  • Compare predictability, flexibility and verification cost.
  • Define typed outcomes before choosing an agent framework.

Before you start

The flow

  1. 1.Clarify task
  2. 2.Choose control flow
  3. 3.Define contracts
  4. 4.Bound execution
  5. 5.Evaluate

Locate the decision maker

In a workflow, code chooses the sequence or allowed branches. A model may classify a ticket, but the application still decides what follows. In an agent loop, the model proposes the next operation based on observations. A tool call by itself does not establish meaningful autonomy.

Use deterministic code for exact validation and calculations. Use a fixed workflow when the task has a stable sequence, such as retrieve → draft → review. Introduce an agent when intermediate results genuinely determine an uncertain next action.

Use typed proposals, keep authority in the host

AgentContracts separates Finish, Stop and ToolCall. A model proposal cannot choose the authenticated tenant or override a permission decision. The host supplies scope and approvals, while the runtime applies budgets and validates outputs.

More autonomous steps add latency, expense and failure combinations. Start with one bounded agent or a workflow; multi-agent coordination is justified only when specialization or isolation produces measurable value.

Select by task evidence

Compare a simple baseline with the agent on the same tasks. Measure completion, correct abstention, denied-action behavior, tool amplification and recovery, not just fluent prose. A task with a known three-step procedure is a poor reason to add an open-ended planner.

The Java reference uses synthetic model decisions to test control and policy. It is not a comparison benchmark between live models or frameworks.

Try it

AgentContracts.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.interviewpreparator.agents;
import java.util.List;
import java.util.Map;
public final class AgentContracts {
private AgentContracts() {}
public record Scope(String tenant, String user, String conversation, String runId) {
public Scope {
for (String value : List.of(tenant, user, conversation, runId)) {
if (!value.matches("[a-zA-Z0-9-]{1,80}")) throw new IllegalArgumentException("Invalid trusted scope");
}
}
}
public sealed interface Decision permits Finish, Stop, ToolCall {}
public record Finish(String text) implements Decision {}
public record Stop(String reason) implements Decision {}
public record ToolCall(String callId, String tool, String resource) implements Decision {}
public record ModelReply(Decision decision, int tokens, long costMicros) {}
public record Receipt(ToolCall call, String result) {}
public enum Status { RUNNING, WAITING_APPROVAL, COMPLETED, STOPPED, DENIED,
STEP_LIMIT, TOOL_LIMIT, TOKEN_LIMIT, COST_LIMIT, TIMED_OUT, MODEL_ERROR, TOOL_ERROR, INVALID_OUTPUT }
public record State(Status status, int steps, int toolAttempts, int tokens, long costMicros,
long expiresAtMillis, ToolCall pending, List<String> observations, Map<String, Receipt> receipts,
String answer) {
public State { observations = List.copyOf(observations); receipts = Map.copyOf(receipts); }
}
public record Snapshot(long version, State state) {}
public interface RunStore {
Snapshot createOrLoad(Scope scope, State initial);
Snapshot save(Scope scope, long expectedVersion, State state);
}
@FunctionalInterface public interface Model { ModelReply next(State state) throws Exception; }
public enum Permission { ALLOW, DENY, APPROVAL_REQUIRED }
public interface Tools {
Permission authorize(Scope scope, ToolCall call, java.util.Set<String> approvedResources);
boolean isReadOnly(ToolCall call);
String execute(Scope scope, ToolCall call, java.util.Set<String> approvedResources) throws Exception;
}
public static final class TransientReadFailure extends Exception {}
public static final class Conflict extends RuntimeException {
public Conflict() { super("Checkpoint version conflict"); }
}
}

examples/spring-ai-lab/src/main/java/com/interviewpreparator/agents/AgentContracts.java · mvn -f examples/spring-ai-lab/pom.xml -Pintegration verify. Java examples target 21. Models and usage are fixtures; no live inference or billing guarantee is implied.

Interview questions

Is a chatbot with one tool automatically an agent?

The useful distinction is who selects subsequent actions and adapts to observations; labels alone do not describe autonomy.

When is a workflow preferable?

When the process is known, its branches are bounded and predictable verification matters more than open-ended exploration.

Why keep decisions typed?

To validate explicit outcomes and deny malformed actions before they reach execution.

Practice

Design a workflow and an agent version of a document-question task; justify the simpler option with a fixed evaluation set.

Check your solution

  • The next-action owner is explicit.
  • Scope and authorization are outside model output.
  • Added autonomy has a measurable benefit.

Official references

Continue learning

Category sequence

Agentic AI

Lesson 1 / 6

Was this lesson useful?

Change notes: Phase 3: bilingual agent engineering lesson and executable reference.