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")
end

16.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_attempts

16.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()
end

16.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