5  Agent Interactions

This chapter describes how to call an Agent, how to control its capabilities per turn, and how to structure agent usage so that your procedures remain testable.

5.1 The Canonical Call Pattern

Agents are callable. You call the agent value like a function:

result = triage_agent({message = "Classify this message."})

Important notes:

  • triage_agent must be declared as triage_agent = Agent { ... } at top level.
  • Lookup patterns like Agent("triage_agent")(...) are deprecated in the current DSL.

5.2 Inputs: message, context fields, and overrides

The call input is a table. The most common field is message:

triage_agent({message = "Please reset my password"})

Anything besides message and a small set of override keys is treated as context and may be used for prompt templates or logging.

5.3 Per-turn Overrides (Capability Control)

Per-turn overrides are the core technique for keeping agent behavior safe and deterministic.

5.3.1 Tools

Override tools per call:

-- Full tools turn
triage_agent({message = "Gather facts.", tools = {lookup_customer, done}})

-- No-tools summarization/decision turn
triage_agent({message = "Summarize and decide. No new tool calls.", tools = {}})

5.3.2 Generation params

You can also override common generation parameters per call:

triage_agent({
  message = "Write the response.",
  temperature = 0.2,
  max_tokens = 200
})

5.4 Reading the Result

Agent calls return a result object. Depending on configuration and backend, you may interact with it in a few common ways:

  • result.output (most common)
  • triage_agent.output (convenience: last response text)

If you need a stable, typed output contract, prefer a Model for inference or structure outputs via your Procedure schema and validation.

5.5 Making Agents Testable

Agents are non-deterministic; your correctness story should not depend on the exact free-form text.

Instead, structure correctness around:

  • tool calls (and tool results)
  • explicit procedure state
  • bounded loops and stopping conditions (often via a done tool)
  • specifications (BDD) that assert observable behavior

5.6 Mocking Agents in Specs (CI-safe)

In CI you usually do not want to call a real LLM. Use Mocks { ... } to provide deterministic agent behavior:

Mocks {
  triage_agent = {
    tool_calls = {
      {tool = "lookup_customer", args = {id = "123"}},
      {tool = "done", args = {reason = "Classified"}}
    },
    message = "billing"
  }
}

Run specs in mock mode:

tactus test file.tac --mock