Patterns
Adaptive Plans
Dynamic planning where an LLM generates a plan, executes it, and revises the plan on failure -- up to a maximum number of revisions.
Overview
Adaptive Plans combine LLM-powered plan generation with automatic error-driven revision. An LLM planner generates a PlanNode tree, the runtime compiles and executes it, and if execution fails, the error is fed back to the planner to produce a revised plan.
Signature
function adaptivePlan<O>(opts: {
planner: Step<string, PlanNode>;
agents: Record<string, (prompt: string) => Step<string, unknown>>;
constraints?: PlanConstraints;
maxRevisions: number;
executeStep?: ExecuteStepFn;
}): Step<string, O>| Parameter | Type | Purpose |
|---|---|---|
planner | Step<string, PlanNode> | A step that takes a task description and returns a PlanNode tree |
agents | Record<string, (prompt: string) => Step> | Agent factories keyed by role name |
constraints | PlanConstraints | Optional constraints on execution |
maxRevisions | number | Maximum number of plan revisions before giving up |
executeStep | ExecuteStepFn | Optional custom step executor |
Example
import { adaptivePlan, react, step, PlanNodeSchema } from '@noetic/core';
const planner = step.llm({
id: 'plan-generator',
model: 'gpt-4o',
system: 'Generate a task plan as a PlanNode tree. Available agents: researcher, coder.',
output: PlanNodeSchema,
});
const agents = {
researcher: (prompt: string) => react({
model: 'gpt-4o',
system: prompt,
tools: [searchTool],
}),
coder: (prompt: string) => react({
model: 'gpt-4o',
system: prompt,
tools: [codeTool, testTool],
}),
};
const agent = adaptivePlan({
planner,
agents,
maxRevisions: 3,
});How It Works
- The
plannerstep receives the user input and generates aPlanNodetree. - The tree is compiled via
compilePlan()into an executableStep. - The compiled step is executed.
- If execution throws an error, the error message is appended to the original input:
"{input}\n\nPrevious plan failed: {error.message}". - The planner generates a revised plan with this context.
- Steps 2-5 repeat until execution succeeds or
maxRevisionsis exhausted.
On the final revision, if execution still fails, the error is thrown to the caller.
When to Use
- Tasks where the optimal decomposition is not known upfront
- Complex workflows where sub-tasks may fail unpredictably
- Scenarios where you want the agent to self-correct its approach
Tips
- Keep
maxRevisionslow (2-5). If the plan fails repeatedly, the problem is likely in the agents or tools, not the plan structure. - Use
PlanConstraints.validateto reject invalid plans before execution. - The
plannercan be any step that returns aPlanNode-- it does not have to be an LLM call.
Next Steps
- Task Trees -- static plan compilation
- Ralph Wiggum -- self-refinement at the output level rather than the plan level