16 Common Patterns
16.1 Agent vs Model
Quick rule:
- Use an Agent when you need multi-turn reasoning, tool calls, or conversation state.
- Use a Model when you want a stateless, schema-first prediction (
input -> output) that you can train, version, and evaluate.
16.2 The Done Tool Pattern
Standard pattern for agent completion:
local done = require("tactus.tools.done")
worker = Agent {
model = "openai/gpt-4o-mini",
system_prompt = "Do the task. Call done when finished.",
tools = {done}
}
Procedure {
output = {result = field.string{required = true}},
function(input)
repeat worker() until done.called() or Iterations.exceeded(20)
return {result = done.last_result() or ""}
end
}16.3 Tool Result Summarization
Force agent to explain tool results:
repeat
researcher()
if search.called() then
researcher({
message = "Summarize the tool results in 2–3 sentences.",
tools = {} -- no tools = must respond with text
})
end
until done.called()16.4 Human Approval Gate
local approved = Human.approve({
message = "Proceed with deployment?",
context = {version = version, env = environment}
})
if approved then
deploy()
else
Log.info("Cancelled by user")
end16.5 Multi-Agent Handoff
-- Researcher finds information
repeat
researcher()
until findings_ready.called()
-- Writer creates content
writer({message = "Write about: " .. (findings_ready.last_result() or "")})
repeat
writer()
until done.called()16.6 Retry with Backoff
local max_attempts = 3
local attempt = 0
repeat
attempt = attempt + 1
worker()
if not done.called() and attempt < max_attempts then
worker({message = "Try again. Attempt " .. attempt .. " of " .. max_attempts})
end
until done.called() or attempt >= max_attempts16.7 Batch Processing
local items = input.items
local results = {}
for i, item in ipairs(items) do
state.current_item = i
processor({message = "Process: " .. item})
repeat
processor()
until item_done.called() or Iterations.exceeded(20)
results[i] = item_done.last_result()
end16.8 Iteration Safety
Always include max iterations:
repeat
worker()
until done.called() or Iterations.exceeded(20)
if not done.called() then Log.warn("Max turns reached without completion") end