Live Coding Interview Prep

Course Content

Live Coding Interview Prep

7 sections · 50 lessons

Build a planning + execution agent workflow.


The price plan as a dependency tree3 calculator: 928001 price: 799002 price: 12900
Steps 1 and 2 have no dependencies and run in round one; step 3 waits for both, then substitutes their results.

What you need to know

Plan-and-execute separates deciding from doing:

ReAct loop

  • One model call per step
  • Adapts after every observation
  • Cost grows with steps
  • Hard to review before it acts

Plan-and-execute

  • One call to plan, one to answer
  • Plan fixed before any result exists
  • Cheaper, and steps can run in parallel
  • The plan can be shown to a human first

The plan is a directed acyclic graph (DAG): steps are nodes, depends_on lists are edges. A step can run once all its dependencies are done. Running steps in such an order is a topological sort. If at some point no remaining step is runnable, the graph has a cycle (1 waits for 2, 2 waits for 1) or depends on a step that does not exist.

Results flow between steps with placeholders: "{{1}}" inside an argument means "the result of step 1".

Python
import json, refrom collections.abc import CallablePLAN_PROMPT = (    "Break the goal into at most 5 steps. Reply with JSON only:\n"    '{"steps": [{"id": 1, "tool": "search", "args": {"query": "..."}, "depends_on": []}]}\n'    'Write "{{2}}" inside args to use the result of step 2.\n\nGoal: ')REF = re.compile(r"\{\{(\d+)\}\}")def substitute(value, results: dict[int, object]):    """Replace {{n}} placeholders, recursively, with step results."""    if isinstance(value, str):        return REF.sub(lambda m: str(results[int(m.group(1))]), value)    if isinstance(value, dict):        return {k: substitute(v, results) for k, v in value.items()}    if isinstance(value, list):        return [substitute(v, results) for v in value]    return valuedef execute_plan(steps: list[dict], tools: dict[str, Callable]) -> dict[int, object]:    """Run steps in dependency order; failures are recorded, not raised."""    ids = {s["id"] for s in steps}    for s in steps:        if missing := set(s.get("depends_on", [])) - ids:            raise ValueError(f"step {s['id']} depends on unknown steps {sorted(missing)}")    results: dict[int, object] = {}    remaining = list(steps)    while remaining:        runnable = [s for s in remaining if set(s.get("depends_on", [])) <= results.keys()]        if not runnable:            raise ValueError("plan has a dependency cycle")        for step in runnable:                    # these are independent: could run in parallel            fn = tools.get(step["tool"])            try:                if fn is None:                    raise KeyError(f"unknown tool {step['tool']}")                results[step["id"]] = fn(**substitute(step.get("args", {}), results))            except Exception as exc:                results[step["id"]] = f"error: {type(exc).__name__}: {exc}"            remaining.remove(step)    return resultsdef plan_and_execute(goal: str, llm_fn: Callable[[str], str], tools: dict) -> str:    steps = json.loads(llm_fn(PLAN_PROMPT + goal))["steps"][:5]    results = execute_plan(steps, tools)    summary = "\n".join(f"Step {k}: {v}" for k, v in sorted(results.items()))    return llm_fn(f"Goal: {goal}\n\nResults:\n{summary}\n\nWrite the final answer.")

The tricky parts:

  • Validate ids up front. A step that depends on step 9 when there is no step 9 would otherwise look exactly like a cycle; checking first gives a clear error.
  • results.keys() works as a set, so set(depends_on) <= results.keys() asks "are all dependencies done?".
  • Failed steps still produce a result (the error text), so dependent steps can run and the final answer can say what failed. A stricter version skips dependants of a failed step.
  • Each runnable batch has no internal dependencies, which is why it could go to a thread pool.

Complexity: with V steps and E dependency edges, this simple loop scans the remaining steps once per round, and a straight chain needs V rounds, so it is O(V·(V + E)) in the worst case. Kahn's algorithm (tracking how many unfinished dependencies each step has) brings it to O(V + E). With 5 steps either is instant; saying which is which is what the interviewer is checking. Model calls: 2, plus 1 per replan.

A real-life example

Goal: "Total price of an iPhone 16 and AirPods in INR". The planner returns three steps, with step 3 depending on steps 1 and 2:

Python
PRICES = {"iphone 16": 79900, "airpods 4": 12900}tools = {"price": lambda item: PRICES[item.lower()],         "calculator": lambda expression: sum(int(x) for x in expression.split("+"))}plan = [{"id": 1, "tool": "price", "args": {"item": "iPhone 16"}, "depends_on": []},        {"id": 2, "tool": "price", "args": {"item": "AirPods 4"}, "depends_on": []},        {"id": 3, "tool": "calculator", "args": {"expression": "{{1}}+{{2}}"}, "depends_on": [1, 2]}]print(execute_plan(plan, tools))                       # {1: 79900, 2: 12900, 3: 92800}cyclic = [{"id": 1, "tool": "price", "args": {}, "depends_on": [2]},          {"id": 2, "tool": "price", "args": {}, "depends_on": [1]}]try:    execute_plan(cyclic, tools)except ValueError as e:    print(e)                                           # plan has a dependency cycle
rounddone beforerunnableresults after
1{}steps 1, 2{1: 79900, 2: 12900}
2{1, 2}step 3, args become "79900+12900"{…, 3: 92800}

In the cyclic plan, round 1 finds no step whose dependencies are done, and the executor raises instead of looping forever.

Travel-booking agents use this shape: search flights and hotels in parallel, then compute the total, then ask the user to approve before booking anything.

Follow-up questions to expect

  • "The plan was wrong because step 1 returned something unexpected — now what?" — Replan: send the goal, the old plan and the results so far back to the planner, and cap it at one or two replans.
  • "How do you stop the model writing a dangerous plan?" — Validate every step against the tool allowlist and argument rules before executing anything, and require human approval for destructive steps. The plan being inspectable is the main safety benefit of this pattern.
  • "When is ReAct better?" — When each step depends on reading the previous result in a way you cannot predict, such as exploring an unknown website.