Normandy.DSL.Agent (normandy v0.6.2)

View Source

DSL for defining AI agents with a clean, declarative syntax.

Provides macros to simplify agent creation with sensible defaults and expressive configuration options.

Examples

defmodule MyResearchAgent do
  use Normandy.DSL.Agent

  agent do
    name "Research Agent"
    description "Conducts research and analysis"

    model "claude-3-5-sonnet-20241022"
    temperature 0.7
    max_tokens 4096

    system_prompt """
    You are a helpful research assistant.
    Provide thorough, well-researched answers.
    """

    # Optional: Define tools
    tool CalculatorTool
    tool SearchTool
  end
end

# Create an instance
{:ok, agent} = MyResearchAgent.new(client: my_client)

# Run the agent
{updated_agent, response} = MyResearchAgent.run(agent, "Research quantum computing")

Without DSL (for comparison)

config = %{
  client: my_client,
  model: "claude-3-5-sonnet-20241022",
  temperature: 0.7,
  max_tokens: 4096
}

agent = Normandy.Agents.BaseAgent.init(config)
# ... configure system prompt, tools, etc.

Features

  • Clean, declarative syntax
  • Sensible defaults
  • Compile-time validation
  • Generated helper functions
  • Tool registration support
  • Memory management helpers

Summary

Functions

Defines an agent configuration block.

Sets the background context.

Sets the agent description.

Configures guardrails for the agent.

Sets the maximum number of messages in memory.

Sets the max tokens.

Sets the maximum number of tool calls executed concurrently inside a single tool-loop iteration. Defaults to 1 (sequential — matches pre-0.5.0 observable behaviour). Values > 1 opt the agent into bounded parallel tool execution with up to N tools running at once. The final result list passed back to the LLM stays in the LLM's tool-call order (Task.async_stream is invoked with ordered: true).

Sets the LLM model.

Sets the agent name.

Sets the output instructions.

Sets the internal steps.

Sets the chunk size (bytes of accumulated text) at which incremental output guardrails run. Only takes effect when streaming_mode :incremental is set. Default: 200.

Selects the output-guardrail streaming mode for this agent.

Sets the system prompt.

Sets the temperature.

Registers a tool module.

Functions

agent(list)

(macro)

Defines an agent configuration block.

Available Options

  • name - Agent name (optional)
  • description - Agent description (optional)
  • model - LLM model to use (required)
  • temperature - Sampling temperature 0.0-1.0 (default: 0.7)
  • max_tokens - Maximum tokens in response (default: 4096)
  • system_prompt - System prompt text (optional)
  • background - Background context for prompt (optional)
  • steps - Internal reasoning steps (optional)
  • output_instructions - Output formatting instructions (optional)
  • tool - Register a tool module (can be called multiple times)
  • max_messages - Maximum messages in memory (optional)

Examples

agent do
  name "My Agent"
  model "claude-3-5-sonnet-20241022"
  temperature 0.8

  system_prompt "You are helpful."
end

background(value)

(macro)

Sets the background context.

description(value)

(macro)

Sets the agent description.

guardrails(stage, specs)

(macro)

Configures guardrails for the agent.

stage is either :input or :output. specs is a list of guard specifications — either a module atom or {module, opts} tuple. See Normandy.Guardrails for the full contract.

Calling this macro twice for the same stage replaces the previous list.

Example

agent do
  model "claude-sonnet-4-6"

  guardrails :input, [
    {Normandy.Guardrails.Builtins.MaxLength, limit: 10_000},
    {Normandy.Guardrails.Builtins.ForbiddenSubstrings, terms: ["ignore previous"]}
  ]

  guardrails :output, [
    {Normandy.Guardrails.Builtins.RegexGuard,
     patterns: [~r/\b\d{3}-\d{2}-\d{4}\b/], mode: :deny}
  ]
end

max_messages(value)

(macro)

Sets the maximum number of messages in memory.

max_tokens(value)

(macro)

Sets the max tokens.

max_tool_concurrency(value)

(macro)

Sets the maximum number of tool calls executed concurrently inside a single tool-loop iteration. Defaults to 1 (sequential — matches pre-0.5.0 observable behaviour). Values > 1 opt the agent into bounded parallel tool execution with up to N tools running at once. The final result list passed back to the LLM stays in the LLM's tool-call order (Task.async_stream is invoked with ordered: true).

Streaming callback ordering: BaseAgent.stream_with_tools/3 invokes callback.(:tool_result, result) from inside each worker as soon as that tool finishes. At max_tool_concurrency > 1, callers therefore observe :tool_result events in completion order, not LLM-call order — a 100ms tool that fires after a 500ms tool will still emit its :tool_result first. If you need LLM-order callback handling, set max_tool_concurrency: 1 or buffer events and reorder them yourself after the stream completes.

Process-semantics note: tool invocations run inside Task.async_stream workers, even at max_tool_concurrency: 1. Streaming callbacks fired while a tool is executing therefore run in those worker processes — capture parent = self() outside the callback before sending messages to the test/owner process. Streaming callbacks fired outside tool execution (e.g. :text_delta before any tool_use) still run in the caller's process.

model(value)

(macro)

Sets the LLM model.

name(value)

(macro)

Sets the agent name.

output_instructions(value)

(macro)

Sets the output instructions.

steps(value)

(macro)

Sets the internal steps.

streaming_chunk_size(value)

(macro)

Sets the chunk size (bytes of accumulated text) at which incremental output guardrails run. Only takes effect when streaming_mode :incremental is set. Default: 200.

streaming_mode(value)

(macro)

Selects the output-guardrail streaming mode for this agent.

:accumulate (default) runs guards on the full message after the stream completes; :incremental runs guards at chunk boundaries and halts the stream on violation. See Normandy.Guardrails for the full semantics.

system_prompt(value)

(macro)

Sets the system prompt.

temperature(value)

(macro)

Sets the temperature.

tool(module)

(macro)

Registers a tool module.