1  Syntax Quick Reference

Tactus .tac files are Lua with a small, assignment-based DSL for declaring models, tools, agents, procedures, and tests.

1.1 File Skeleton

-- Standard library tools
local done = require("tactus.tools.done")

-- Custom tools (optional)
search = Tool {
  description = "Search the web",
  input = { query = field.string{required = true} },
  function(args) return "..." end
}

-- Agents (assignment-based; tools are variables, not strings)
researcher = Agent {
  model = "openai/gpt-4o-mini",
  system_prompt = "Research {input.topic}. Call done when finished.",
  tools = {search, done}
}

-- Models (stateless prediction; registry-backed or other backends)
Model "sentiment" {
  type = "registry",
  name = "sentiment",
  version = "latest",
  input = { text = "string" },
  output = { label = "string", confidence = "float" },

  -- Optional: training config (used by `tactus train` and `tactus models evaluate`)
  training = {
    data = {
      source = "hf",
      name = "imdb",
      train = "train",
      test = "test",
      text_field = "text",
      label_field = "label"
    },
    candidates = {
      { name = "nb-tfidf", trainer = "naive_bayes" }
    }
  }
}

-- Optional: declare the allowed stage names
Stages({"researching", "writing", "complete"})

-- The procedure (unnamed; defaults to "main")
Procedure {
  input = { topic = field.string{required = true} },
  output = { findings = field.string{required = true} },
  function(input)
    Stage.set("researching")
    repeat researcher() until done.called()
    return {findings = done.last_result()}
  end
}

-- Optional: tests (BDD, in-file)
Specifications([[
Feature: Research
  Scenario: Completes
    When the researcher agent takes turns
    Then the done tool should be called
]])

1.2 Declarations

1.2.1 Tools

Define a Lua function tool:

slugify = Tool {
  description = "Convert text to a slug",
  input = { text = field.string{required = true} },
  function(args)
    return string.lower(args.text):gsub("%s+", "-")
  end
}

Import a standard library tool:

local done = require("tactus.tools.done")

1.2.2 Agents

worker = Agent {
  model = {
    name = "openai/gpt-4o",
    temperature = 0.3,
    max_tokens = 1200
  },
  prepare = function()
    return {now = os.date()}
  end,
  system_prompt = [[
Time: {prepared.now}
Task: {input.task}
  ]],
  tools = {done}
}

Callable agent syntax:

worker()
worker({message = "Focus on edge cases"})
worker({tools = {}})                -- no tools this turn
worker({tools = {search, done}})    -- restrict tools this turn

1.2.3 Procedures

Procedures are declared with Procedure { ... } (unnamed, defaults to "main"). Sub-procedures can be assigned to variables:

summarize = Procedure {
  name = "summarize",
  input = { text = field.string{required = true} },
  output = { summary = field.string{required = true} },
  return_prompt = "Return a short summary.",
  function(input)
    worker({message = input.text})
    return {summary = done.last_result()}
  end
}

Procedure {
  function(input)
    local result = summarize.run({text = "..."})
    return {summary = result.summary}
  end
}

1.2.4 Toolsets

Use a Toolset when you want a bundle of tools.

math_tools = Toolset {
  type = "lua",
  tools = {
    {
      name = "add",
      description = "Add numbers",
      parameters = {
        a = {type = "number", required = true},
        b = {type = "number", required = true}
      },
      handler = function(args) return tostring(args.a + args.b) end
    }
  }
}

calculator = Agent { model = "openai/gpt-4o-mini", tools = {math_tools, done} }

1.3 Schemas (Input/Output/State)

1.3.1 Field Types

Type Builder
String field.string{...}
Number field.number{...}
Integer field.integer{...}
Boolean field.boolean{...}
Array field.array{...}
Object field.object{...}

1.3.2 Common Field Options

Option Meaning
required = true Must be provided / returned
default = ... Optional field with a default
description = "..." Shows in CLI/IDE forms and docs
enum = {...} Restrict values (strings)

1.4 Templates

Templates are {...} substitutions (re-evaluated before each agent turn).

Namespace Example
input {input.topic}
state {state.count}
prepared {prepared.file_contents}
context {context.parent_id}
env {env.API_KEY}

1.5 Control Flow (Lua)

-- repeat/until is the common “agent loop”
repeat
  worker()
until done.called() or Iterations.exceeded(10)

-- standard Lua conditionals and loops also apply
if Stop.requested() then
  Log.warn("Stopping early", {reason = Stop.reason()})
end