跳转至

04: Workflow

Not everything needs an agent.

If the steps are known ahead of time, Python should usually control the path. For a simple travel assistant, the path might be:

extract preferences -> draft itinerary -> format answer

Do not ask the model to freely choose the next step when the code already knows it. Fixed flow is cheaper and easier to test.

Code First

from __future__ import annotations

from pathlib import Path

from agent_patterns_lab.patterns.workflow_chaining import PromptStep, run_prompt_chain
from agent_patterns_lab.runtime import MockLLM, Tracer


def main() -> None:
    tracer = Tracer()
    model = MockLLM(
        [
            "Preferences: tea, local food, easy walking. Constraint: relaxed one-day trip.",
            "Draft: Morning West Lake, afternoon Tea Museum, evening Hefang Street.",
            "Final itinerary: West Lake -> China National Tea Museum -> Hefang Street.",
        ]
    )

    steps = [
        PromptStep(name="extract_preferences", user_prompt="Extract travel preferences from: {input}"),
        PromptStep(name="draft_itinerary", user_prompt="Draft an itinerary from these preferences:\n{input}"),
        PromptStep(name="format_final", user_prompt="Format this itinerary as a concise final answer:\n{input}"),
    ]

    out = run_prompt_chain(
        model,
        initial_input="Plan a relaxed one-day Hangzhou trip. I like tea, local food, and easy walking.",
        steps=steps,
        tracer=tracer,
    )
    print(out)

    trace_path = tracer.export_jsonl(Path(".traces") / "11_prompt_chaining.jsonl")
    print(f"[trace] {trace_path}")


if __name__ == "__main__":
    main()

Run:

uv run python examples/11_prompt_chaining.py

Expected output:

Final itinerary: West Lake -> China National Tea Museum -> Hefang Street.
[trace] .traces/11_prompt_chaining.jsonl

What Changed

The task is split into three PromptSteps:

steps = [
    PromptStep(name="extract_preferences", user_prompt="Extract travel preferences from: {input}"),
    PromptStep(name="draft_itinerary", user_prompt="Draft an itinerary from these preferences:\n{input}"),
    PromptStep(name="format_final", user_prompt="Format this itinerary as a concise final answer:\n{input}"),
]

Then they run in order:

out = run_prompt_chain(
    model,
    initial_input="Plan a relaxed one-day Hangzhou trip. I like tea, local food, and easy walking.",
    steps=steps,
    tracer=tracer,
)

This is Prompt Chaining: each step's output becomes the next step's input.

flowchart LR
  I["User request"] --> A["Extract preferences"]
  A --> B["Draft itinerary"]
  B --> C["Format answer"]
  C --> O["Final itinerary"]

What Workflow Fixes

The point of a workflow is that code owns the path.

Shape Who decides next
One-shot chatbot No next step
Workflow Python predefines the next step
Agent Loop Model chooses from current state

If the flow is fixed, workflows are good:

  • The trace shows exactly which step broke.
  • Each step can be tested.
  • Cost and latency are easier to control.
  • The model will not randomly pick an unrelated tool.

What It Does Not Fix

Workflow assumes you already know the steps.

Real travel planning often breaks that assumption:

  • The weather says afternoon rain. Should the next step search indoor places?
  • A place is closed. Should the assistant search again?
  • The budget is too low. Should it remove a paid attraction?
  • The route is too long. Should it estimate a new order?

If you encode all of that into a workflow, the code turns into a large if-else tree.

When the next step truly depends on a fresh observation, use an agent loop.

Next: 05: Agent Loop.