Plan without executing
Generate a plan with agent.plan, review it, then run it with agent.execute. The model's output is just text until you choose to run it.
Before you start
agent.run(task) does two things back to back: asks the model for a plan, then
runs it. Most of the time that is exactly what you want. Sometimes you want to
see the plan first. agent.plan(task) does only the first half: it returns the
plan and runs nothing.
Same agent as Track 9#
The same shopping cart: new_cart, add_item, and cart_total. This track
just calls it in two steps instead of one.
Plan, look, then run#
# main.py
from cart import ShoppingCart
from opensymbolicai.llm import LLMConfig
QUERY = (
"add 2 apples at 3 dollars each, 2 loaves of bread at 2 dollars each, "
"and 3 cartons of milk at 4 dollars each, then total it up"
)
llm = LLMConfig(provider="ollama", model="qwen2.5-coder:7b")
agent = ShoppingCart(llm=llm)
plan_result = agent.plan(QUERY) # PlanResult, nothing has run yet
print(plan_result.plan) # the program, as text
print(plan_result.usage) # tokens the planning cost
print(plan_result.time_seconds) # seconds it took
# Review the plan here: read it, log it, gate it.
exec_result = agent.execute(plan_result.plan) # now it runs
print(exec_result.get_value())uv run main.pyOutput:
cart = new_cart()
cart = add_item(cart, "apples", 3, 2)
cart = add_item(cart, "bread", 2, 2)
cart = add_item(cart, "milk", 4, 3)
total = cart_total(cart)
usage: input_tokens=396 output_tokens=63
time (s): 1.4777
22.0What you're looking at#
agent.plan(QUERY) returns a PlanResult. It holds the plan as a string in
.plan, the tokens the planning cost in .usage, and the seconds it took in
.time_seconds.
Nothing ran. A PlanResult has no result value and no trace, because there is
nothing to trace. No new_cart was called, no cart was built, no total was
computed. You are holding the program the model wrote, as text, and it has not
touched your primitives.
The cart only gets built at agent.execute(plan_result.plan). That call runs
the five lines and gives back an ExecutionResult, whose get_value() is the
22.0 you would have gotten straight from agent.run.
Review before execute#
The space between plan and execute is a checkpoint. The plan is just a
string sitting in a variable, so you can do anything you like with it before it
runs: print it for a human to approve, log it, compare it against the last plan,
or decide not to run it at all.
execute checks the plan too. It will only run calls to your primitives and a
small set of allowed operations, and it raises rather than run a plan that
reaches for anything else.
plan and execute, or just run#
agent.run(task) is plan followed by execute in one call, with metrics
bundled onto the result. Reach for run when you are happy to plan and execute
in one step. Reach for plan and execute when you want to look at the plan,
or hold it, before it runs.