How-To Guides
How to Choose a Strategy
How to Choose a Strategy
Not every problem needs a 12-step reflexion loop with persistent memory. This guide helps you pick the right level of complexity for your use case, from one-shot to full crucible.
The decision tree
Start here
│
▼
Is one LLM call enough?
│
├── YES → Simple Q&A (1 step, no gate)
│
└── NO → Would a second pass improve quality?
│
├── YES → Draft and Refine (2 steps)
│
└── NO → Are there multiple valid approaches?
│
├── YES → Can bad approaches be scored?
│ │
│ ├── YES → Tree-of-Thoughts (fan-out + gate)
│ │
│ └── NO → Self-Consistency (fan-out + vote)
│
└── NO → Does the task benefit from iterative critique?
│
├── YES → Reflexion (generate + critique + loop)
│
└── NO → Does the task need accumulated knowledge?
│
├── YES → Add Memory (slate read + write)
│
└── NO → Does the task decompose into sub-problems?
│
├── YES → Recursion or Dynamic Steps
│
└── NO → Draft and Refine is enoughComplexity vs. payoff
Every step you add costs an LLM call. Every loop multiplies cost. The question is: does the quality improvement justify the cost?
| Strategy | LLM calls | Best for |
|---|---|---|
| One-shot | 1 | Simple factual Q&A, formatting, translation |
| Draft + Refine | 2 | Writing, summarization, code generation |
| Self-Consistency (N=5) | 6 | Math, logic, high-variance tasks |
| ToT (N=5, 2 phases) | 11 | Complex reasoning, design, planning |
| Reflexion (3 iterations) | 6-12 | Tasks where verbal critique helps |
| ToT + Reflexion + Memory | 15-30 | Hard problems, research, expert tasks |
| Dynamic (planner + executor) | 5-20 | Tasks where strategy itself varies |
Rule of thumb: Start simple. If the output quality isn't good enough, add one level of complexity. Measure. Repeat.
# Level 1: One-shot (1 call)
steps:
- id: answer
fields: [{ name: Q, type: text, from: input.context }]
systemPrompt: "Answer."
# Level 2: Draft + Refine (2 calls)
steps:
- id: draft
fields: [{ name: Q, type: text, from: input.context }]
systemPrompt: "Draft an answer."
- id: refine
fields:
- { name: Q, type: text, from: input.context }
- { name: Draft, type: ingest, from: { stepId: draft, loopRef: current } }
systemPrompt: "Improve the draft."
# Level 3: ToT (2N+1 calls for N branches)
steps:
- id: generate
nodes: 5
# ...
- id: verify
nodes: 5
if: { jsonMatches: { properties: { score: { minimum: 4 } } } }
# ...
- id: synthesize
# ...When to add memory
Memory (slates) adds complexity and cost. Add it only when accumulated knowledge genuinely improves future calls.
Add memory when:
- The ladder handles similar queries repeatedly (customer support, code review for the same codebase).
- Past failures are instructive (avoid the same mistake twice).
- Verified facts accumulate over time (knowledge base building).
- Context from prior calls is relevant (conversational continuity).
Don't add memory when:
- Each call is independent (one-shot Q&A on different topics).
- The slate would grow faster than it's useful.
- The cost of reading/writing exceeds the value of the knowledge.
Minimal memory pattern:
slates:
- title: Memory
folders:
- name: facts
tokenLimit: 200
files: [{ name: core.md, init: blank }]
steps:
- id: answer
fields:
- { name: Q, type: text, from: input.context }
- { name: Facts, type: slateRead, from: { slate: Memory, folder: facts, file: core.md }, fallback: "" }
systemPrompt: "Answer. Use Facts if relevant."
slateWrite:
to: { slate: Memory, folder: facts, file: core.md }
field: fact
on: append
match: { type: object, required: [fact], properties: { fact: { type: string, minLength: 10 } } }One slate, one folder, one file. The step reads prior facts, answers, and writes any durable fact it discovers. Minimal overhead, maximal persistence.
When to use dynamic steps
Dynamic steps add the most complexity — the ladder rewrites its own strategy mid-execution. Use them sparingly.
Use dynamic steps when:
- The optimal strategy varies by input (some queries need search, others need decomposition, others need verification).
- A fixed ladder would be either too simple for hard cases or too expensive for easy ones.
- You're building an agent that adapts its approach based on observed state.
Don't use dynamic steps when:
- A fixed pattern (ToT, reflexion, etc.) covers your use case.
- The strategy doesn't vary by input.
- The added latency and cost of the planner step isn't justified.
Cost of dynamic steps: Each dynamic execution requires a planner LLM call (to generate the step config) plus the execution of the generated step. That's a minimum of 2 LLM calls before any useful work happens. For simple tasks, this overhead exceeds the benefit.
When in doubt: Start with a fixed pattern. If you find yourself writing conditional logic in the system prompt ("if the input is X, do Y; if Z, do W"), that's a signal that dynamic steps might help — the strategy itself is branching.